NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Sbermarket price per unit calculator // @namespace http://tampermonkey.net/ // @version 1.6 // @description Добавление цены за единицу объема // @author anador // @license MIT // @include https://sbermarket.ru/* // @grant none // ==/UserScript== (function () { function getPageType() { // любая страница со списком продуктов if (document.querySelectorAll('.product') && document.querySelectorAll('.product').length > 0) { return "productList"; } // страница избранного else if (location.pathname === '/user/favorites') { //console.log('favorites found'); return 'favorites'; } else { //console.log('nothing found'); return false; } } //вычисление цены за ед. измерения function countPricePerUnit(price, unit, volumeValue) { if (unit === 'г') { return { outputPrice: price / (volumeValue * 0.001), outputUnit: "кг", } } else if (unit === 'кг') { return { outputPrice: price / (volumeValue), outputUnit: "кг", } } else if (unit === 'л') { return { outputPrice: price / (volumeValue), outputUnit: "л", } } else if (unit === 'мл') { return { outputPrice: price / (volumeValue * 0.001), outputUnit: "л", } } else { return false; } }; //вставка элемента с ценой за ед. измерения function insertCountedPrice(elem, price, unit) { elem.insertAdjacentHTML('afterend', `<p class="pricePerUnit" style=" bottom: 0; color: #9fabb7; font-size: 13px; line-height: 1; position: absolute; right: 0px; "> ${price} ₽ / ${unit} </p>`) } //вставка элемента с ценой за ед. измерения для favorites function insertCountedPriceForFavorites(elem, price, unit) { elem.insertAdjacentHTML('afterend', `<p class="pricePerUnit" style=" position: absolute; bottom: 10px; font-size: 13px; color: #8f8e94; right: 12px; // font-weight: 700; "> ${price} ₽ / ${unit} </p>`) } //вставка элемента с ценой за ед. измерения для popup function insertCountedPriceForPopup(elem, price, unit) { elem.insertAdjacentHTML('afterend', `<p class="pricePerUnit" style=" position: absolute; margin-top: -38px; right: 0px; color: #8f8e94; font-size: 15px; font-weight: 400; letter-spacing: .2px; "> ${price} ₽ / ${unit} </p>`) } //вставка элемента с ценой за ед. измерения для выпадающих результатов поиска function insertCountedPriceForLiveSearchResults(elem, price, unit) { elem.insertAdjacentHTML('afterend', `<span class="pricePerUnit" style=" color: #888; font-size: 13px; line-height: 1; margin-left: 1em; "> ${price} ₽ / ${unit} </p>`) } function productsHandler() { productsList = document.querySelectorAll('.product'); productsList.forEach(el => { if (el.querySelector('div[class="price price--default"]') && el.querySelector('div[class="price price--default"]').innerText) { priceString = el.querySelector('div[class="price price--default"]').innerText.replace(/ /g, '').replace(/,/g, '.'); price = parseFloat(priceString); volumeString = el.querySelector('.product__volume').innerText; volumeValue = volumeString.split(' ')[0].replace(/,/, '.'); volumeUnit = volumeString.split(' ')[1]; if (countPricePerUnit(price, volumeUnit, volumeValue) !== false) { pricePerUnit = countPricePerUnit(price, volumeUnit, volumeValue).outputPrice.toFixed(2); finalUnit = countPricePerUnit(price, volumeUnit, volumeValue).outputUnit; //вставка элемента pricePerUnitWithComma = pricePerUnit.replace(/\./, ','); // замена точки между разрядами на запятую обратно if (!el.querySelector('.pricePerUnit')) { insertCountedPrice(el.querySelector('.product__volume'), pricePerUnitWithComma, finalUnit); } } } }); //обсервер для отслеживания подрузки товаров при скролле observer = new MutationObserver((mutationsList) => { //if (mutationsList[0].removedNodes.length > 0) { //пока убрал, сейчас работает и без этого observer.disconnect(); //console.log(mutationsList) console.log('Products changed'); productsHandler(); //} }); if (document.querySelector('.load_container')) { observer.observe(document.querySelector('.load_container'), { childList: true, //subtree: true, }) } } function favoritesHandler() { productsList = document.querySelectorAll('a[class^="favorites_"]'); productsList.forEach(el => { if (el.querySelectorAll('div>div>div>span')[0] && el.querySelectorAll('div>div>div>span')[0].innerText) { priceString = el.querySelectorAll('div>div>div>span')[0].innerText.replace(/ /g, '').replace(/,/g, '.').replace(/ /g,''); console.log(priceString) price = parseFloat(priceString); volumeString = el.lastChild.lastChild.innerText; volumeValue = volumeString.split(' ')[0].replace(/,/, '.'); volumeUnit = volumeString.split(' ')[1]; if (countPricePerUnit(price, volumeUnit, volumeValue) !== false) { pricePerUnit = countPricePerUnit(price, volumeUnit, volumeValue).outputPrice.toFixed(2); finalUnit = countPricePerUnit(price, volumeUnit, volumeValue).outputUnit; //вставка элемента pricePerUnitWithComma = pricePerUnit.replace(/\./, ','); // замена точки между разрядами на запятую обратно if (!el.querySelector('.pricePerUnit')) { insertCountedPriceForFavorites(el.lastChild.lastChild, pricePerUnitWithComma, finalUnit); } } } }); //обсервер для отслеживания подгрузки товаров/перезагрузки списка observerFavoritesList = new MutationObserver((mutationsList) => { //дополнительная проверка, что в мутации добавляется элемент для случая удаления из избранного, когда элементы только удаляются if (mutationsList[0].addedNodes.length > 0) { console.log('Favorites changed'); favoritesHandler(); } }); if (document.querySelectorAll('.favorite-product') && document.querySelectorAll('.favorite-product').length > 0) { observerFavoritesList.observe(document.querySelector('.ui-content-wrapper').firstChild.lastChild, { childList: true, //subtree: true, }) observerFavoritesList.observe(document.querySelector('.favorites-list'), { childList: true, //subtree: true, }) } } function popupHandler() { el = document.querySelector('div[class^=frames_module]'); if (el.querySelector('meta[itemprop="price"]')) { //проверка для случая открытия напрямую товара не в наличии // priceString = el.querySelector('meta[itemprop="price"]').content.replace(/ /g, '').replace(/,/g, '.'); // строка убрана, стоимость получается сразу в верном формате priceString = el.querySelector('meta[itemprop="price"]').content; price = parseFloat(priceString); volumeString = el.querySelector('div[class^="product_cards"] div[itemprop="offers"]>p').innerText; volumeValue = volumeString.split(' ')[0].replace(/,/, '.'); volumeUnit = volumeString.split(' ')[1]; if (countPricePerUnit(price, volumeUnit, volumeValue) !== false) { pricePerUnit = countPricePerUnit(price, volumeUnit, volumeValue).outputPrice.toFixed(2); finalUnit = countPricePerUnit(price, volumeUnit, volumeValue).outputUnit; //вставка элемента pricePerUnitWithComma = pricePerUnit.replace(/\./, ','); // замена точки между разрядами на запятую обратно if (!el.querySelector('.pricePerUnit')) { insertCountedPriceForPopup(el.querySelector('div[class^="product_cards"] div[itemprop="offers"]>p'), pricePerUnitWithComma, finalUnit); } } } //снова вешаем обсервер, потому что прошлый закрыли дисконнектом if (typeof observerPopup !== 'undefined') { //проверяем, объявлен ли обсервер для случая загрузки страницы товара, когда запущен продактс обсервер, а попап открыт сразу observerPopup.observe(document.querySelector('div[class^=frames_module]'), { childList: true, subtree: true, }) } } function liveSearchResultsHandler() { productsList = document.querySelectorAll('.header-search-list-product'); productsList.forEach(el => { if (el.querySelector('div[class="header-search-list-product__price"]') && el.querySelector('div[class="header-search-list-product__price"]').innerText) { priceString = el.querySelector('div[class="header-search-list-product__price"]').innerText.replace(/ /g, '').replace(/,/g, '.'); //изменен символ пробела на   price = parseFloat(priceString); volumeString = el.querySelector('span[class="header-search-list-product__price-unit"]').innerText.trim(); //добавлен trim volumeValue = volumeString.split(' ')[0].replace(/,/, '.'); volumeUnit = volumeString.split(' ')[1].replace(/\./g, ''); //добавлена замена для точки if (countPricePerUnit(price, volumeUnit, volumeValue) !== false) { pricePerUnit = countPricePerUnit(price, volumeUnit, volumeValue).outputPrice.toFixed(2); finalUnit = countPricePerUnit(price, volumeUnit, volumeValue).outputUnit; //вставка элемента pricePerUnitWithComma = pricePerUnit.replace(/\./, ','); // замена точки между разрядами на запятую обратно if (!el.querySelector('.pricePerUnit')) { insertCountedPriceForLiveSearchResults(el.querySelector('span[class="header-search-list-product__price-unit"]'), pricePerUnitWithComma, finalUnit); } else { el.querySelector('.pricePerUnit').innerText = `${pricePerUnitWithComma} ₽ / ${finalUnit}`; //если уже цена есть, перезаписываем ее } } else { //обработка случая, когда прошлый товар для этой позиции был в подходящих единицах, а новый нет, тогда убираем рассчитанную старую цену if (el.querySelector('.pricePerUnit')) { el.querySelector('.pricePerUnit').remove(); } } } }); //обсервер для отслеживания изменения выпадающих резульатов поиска observerLiveSearchResultUpdates = new MutationObserver(() => { console.log('live search results changed'); observerLiveSearchResultUpdates.disconnect(); liveSearchResultsHandler(); }); if (document.querySelector('div[data-qa="result-list"]')) { //такая проверка, чтобы скрипт не падал при неудаче запуска обсервера observerLiveSearchResultUpdates.observe(document.querySelector('div[data-qa="result-list"]'), { childList: true, subtree: true, characterData: true, //обязательно, для отслеживания изменния текста в элементах }) } } function init() { // проверка типа содержимого страницы if (getPageType() == 'productList') { productsHandler(); //проверка, есть открытый при загрузке ли попап с товаром if (document.querySelector('div[itemscope]')) { popupHandler(); } } else if (getPageType() == 'favorites') { // обсервер для body, чтобы поймать момент загрузки элемента списка товаров observerFavoritesListInit = new MutationObserver(() => { if (document.querySelectorAll('a[class^="favorites_"]') && document.querySelectorAll('a[class^="favorites_"]').length > 0) { observerFavoritesListInit.disconnect(); console.log('favorites-list found'); favoritesHandler(); } }); observerFavoritesListInit.observe(document.body, { childList: true, subtree: true, }) } // обсервер для появления попапа observerPopup = new MutationObserver(() => { if (document.querySelector('meta[itemprop="price"]')) { observerPopup.disconnect(); console.log('popup changed'); popupHandler() } }); if (document.querySelector('div[class^=frames_module]')) { observerPopup.observe(document.querySelector('div[class^=frames_module]'), { childList: true, subtree: true, }) } // обсервер для подсказок из поиска observerLiveSearchResults = new MutationObserver(() => { //observerFavoritesListInit.disconnect(); console.log('search results changed'); liveSearchResultsHandler(); }); if (document.querySelector('.header-search-wrapper')) { observerLiveSearchResults.observe(document.querySelector('.header-search-wrapper'), { childList: true, //subtree: true, }) } }; init(); })();