NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Best price helper for marketplace // @namespace http://tampermonkey.net/ // @description Считаем стоимость за штуку/за кг/за л // @author Apkawa // @license MIT // @icon https://www.google.com/s2/favicons?domain=ozon.ru // @match https://ozon.ru/* // @match https://www.ozon.ru/* // @match https://lenta.com/* // @match https://okeydostavka.ru/* // @match https://www.okeydostavka.ru/* // @match https://perekrestok.ru/* // @match https://www.perekrestok.ru/* // @match https://wildberries.ru/* // @match https://www.wildberries.ru/* // @homepage https://github.com/Apkawa/best_price_userscript // @homepageURL https://github.com/Apkawa/best_price_userscript // @supportURL https://github.com/Apkawa/best_price_userscript/issues // @downloadURL https://github.com/Apkawa/best_price_userscript/raw/release/release/best_price/best_price.user.js // @updateURL https://github.com/Apkawa/best_price_userscript/raw/release/release/best_price/best_price.user.js // @version 0.5.11 // ==/UserScript== (() => { "use strict"; function getElementByXpath(xpath, root = document) { const e = document.evaluate(xpath, root, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; return e && e; } function waitElement(match, callback, root = document.body) { const observer = new MutationObserver((mutations => { let matchFlag = false; mutations.forEach((mutation => { if (!mutation.addedNodes) return; for (let i = 0; i < mutation.addedNodes.length; i++) { const node = mutation.addedNodes[i]; matchFlag = match(node); } })); if (matchFlag) { _stop(); callback(); _start(); } })); let isStarted = false; function _start() { if (isStarted) return; observer.observe(root || document.body, { childList: true, subtree: true, attributes: false, characterData: false }); isStarted = true; } function _stop() { observer.disconnect(); isStarted = false; } _start(); return () => { _stop(); }; } function waitCompletePage(callback, options = {}) { const { root = document.body, runOnce = true, sync = true, delay = 150 } = options; let t; let lock = false; const run = () => { const stop = waitElement((() => true), (() => { if (t) clearTimeout(t); t = setTimeout((() => { if (lock) return; lock = true; if (runOnce || sync) stop(); callback(); if (sync && !runOnce) setTimeout(run, delay); lock = false; }), delay); }), root); return stop; }; return run(); } function E(tag, attributes = {}, ...children) { const element = document.createElement(tag); for (const [k, v] of Object.entries(attributes)) element.setAttribute(k, v); const fragment = document.createDocumentFragment(); children.forEach((child => { if (typeof child === "string") child = document.createTextNode(child); fragment.appendChild(child); })); element.appendChild(fragment); return element; } function ElementGetOrCreate(root, options = {}) { var _a; const { className = "GM-wrap", pos = "appendChild" } = options; if (!root) return null; let wrapEl = (_a = root.parentElement) === null || _a === void 0 ? void 0 : _a.querySelector("." + className); if (!wrapEl) { wrapEl = E("div", { class: className }); root[pos](wrapEl); } return wrapEl; } function copyElementToNewRoot(el, toRoot, options = {}) { var _a; const { className = "GM-cloned", pos = "appendChild" } = options; if (!el) { console.warn(`el is ${typeof el}`); return; } let elList = []; if (el instanceof HTMLElement) elList = [el]; else elList = el; for (const e of ((_a = toRoot.parentElement) === null || _a === void 0 ? void 0 : _a.querySelectorAll("." + className)) || []) e.remove(); for (const _el of elList) { const clonedEl = _el.cloneNode(true); clonedEl.classList.add(className); toRoot[pos](clonedEl); } } function isFunction(x) { return typeof x === "function"; } function matchLocation(...patterns) { const s = document.location.href; for (const p of patterns) { if (isFunction(p) && p(s)) return true; if (RegExp(p).test(s)) return true; } return false; } function GM_addStyle(css) { const style = document.getElementById("GM_addStyleBy8626") || function () { const style = document.createElement("style"); style.type = "text/css"; style.id = "GM_addStyleBy8626"; document.head.appendChild(style); return style; }(); const sheet = style.sheet; sheet === null || sheet === void 0 ? void 0 : sheet.insertRule(css, (sheet.rules || sheet.cssRules || []).length); } function isRegexp(value) { return toString.call(value) === "[object RegExp]"; } function mRegExp(regExps) { return RegExp(regExps.map((function (r) { if (isRegexp(r)) return r.source; return r; })).join("")); } function round(n, parts = 2) { const i = Math.pow(10, parts); return Math.round(n * i) / i; } Object.keys; const entries = Object.entries; const values = Object.values; var __rest = void 0 && (void 0).__rest || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") { var i = 0; for (p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; const WORD_BOUNDARY_END = /(?=\s*|[.,);/]|$)/; const WEIGHT_REGEXP = mRegExp([/(?<value>\d+[,.]\d+|\d+)/, /\s?/, "(?<unit>", "(?<weight_unit>(?<weight_SI>кг|килограмм(?:ов|а|))|г|грамм(?:ов|а|)|гр)", "|(?<volume_unit>(?<volume_SI>л|литр(?:ов|а|))|мл)", "|(?<length_unit>(?<length_SI>м|метр(?:ов|а|)))", ")\\.?", WORD_BOUNDARY_END]); const QUANTITY_UNITS = ["шт", "рулон", "пакет", "уп", "упаков(?:ок|ки|ка)", "салфет(?:ок|ки|ка)", "таб", "капсул"]; const QUANTITY_REGEXP = RegExp(`(?<quantity>\\d+)\\s?(?<quantity_unit>${QUANTITY_UNITS.join("|")})\\.?`); const QUANTITY_2_REGEXP = RegExp(`(?<quantity_2>\\d+)\\s?(?<quantity_2_unit>${QUANTITY_UNITS.join("|")})\\.?`); const COMBINE_DELIMETER_REGEXP = /\s*?(?:[xх*×/]|по)\s*?/; const COMBINE_QUANTITY_LIST = [mRegExp([/(?<quantity_2>\d+)/, COMBINE_DELIMETER_REGEXP, QUANTITY_REGEXP]), mRegExp([QUANTITY_REGEXP, COMBINE_DELIMETER_REGEXP, /(?<quantity_2>\d+)/]), mRegExp([QUANTITY_2_REGEXP, COMBINE_DELIMETER_REGEXP, QUANTITY_REGEXP])]; const COMBINE_QANTITY_WEIGHT_REGEXP_LIST = [mRegExp([WEIGHT_REGEXP, COMBINE_DELIMETER_REGEXP, QUANTITY_REGEXP]), mRegExp([QUANTITY_REGEXP, COMBINE_DELIMETER_REGEXP, WEIGHT_REGEXP]), mRegExp([/(?<quantity>\d+)/, COMBINE_DELIMETER_REGEXP, WEIGHT_REGEXP]), mRegExp([WEIGHT_REGEXP, COMBINE_DELIMETER_REGEXP, /(?<quantity>\d+)/])]; function parseGroups(groups, allowSum = true) { const result = { quantity: 1, units: [] }; if (groups.value) { const valueStr = groups === null || groups === void 0 ? void 0 : groups.value; const unit = groups === null || groups === void 0 ? void 0 : groups.unit; if (valueStr && unit) { let value = parseFloat(valueStr.replace(",", ".")); let unit = null; if (groups.weight_unit) { if (!groups.weight_SI) value /= 1e3; unit = "кг"; } if (groups.volume_unit) { if (!groups.volume_SI) value /= 1e3; unit = "л"; } if (groups.length_unit) { if (!groups.length_SI) value /= 1e3; unit = "м"; } if (!unit) throw "Unknown unit"; result.units.push({ unit, value, total: value }); } } if (groups.quantity) { const valueStr = groups === null || groups === void 0 ? void 0 : groups.quantity; if (valueStr) result.quantity = parseInt(valueStr); } if (allowSum && result.quantity > 1) for (const u of result.units) u.total = result.quantity * u.value; return result; } function parseTitle(title) { var _a; for (const r of COMBINE_QANTITY_WEIGHT_REGEXP_LIST) { const rMatch = r.exec(title); if (rMatch) return parseGroups(rMatch.groups); } let groups = {}; const weightMatch = WEIGHT_REGEXP.exec(title); if (weightMatch === null || weightMatch === void 0 ? void 0 : weightMatch.groups) groups = weightMatch.groups; let quantity = 0; for (const r of COMBINE_QUANTITY_LIST) { const rMatch = (_a = r.exec(title)) === null || _a === void 0 ? void 0 : _a.groups; if ((rMatch === null || rMatch === void 0 ? void 0 : rMatch.quantity) && (rMatch === null || rMatch === void 0 ? void 0 : rMatch.quantity_2)) { quantity = parseInt(rMatch.quantity) * parseInt(rMatch.quantity_2); break; } } if (quantity) groups.quantity = quantity.toString(); else { const quantityMatch = QUANTITY_REGEXP.exec(title); if (quantityMatch === null || quantityMatch === void 0 ? void 0 : quantityMatch.groups) groups = Object.assign(Object.assign({}, groups), quantityMatch.groups); } let allowSum = true; if (groups === null || groups === void 0 ? void 0 : groups.value) allowSum = false; return parseGroups(groups, allowSum); } function parseTitleWithPrice(title, price) { const _a = parseTitle(title), { units } = _a, titleParsed = __rest(_a, ["units"]); const res = Object.assign(Object.assign({}, titleParsed), { units: [], quantity_price: null, quantity_price_display: null }); if ((!res.quantity || res.quantity == 1) && !units.length) return null; for (const u of units) { const p = round(price / u.total); res.units.push(Object.assign(Object.assign({}, u), { price: p, price_display: `${p} ₽/${u.unit || "?"}` })); } if (res.quantity > 1) { res.quantity_price = round(price / res.quantity); res.quantity_price_display = `${res.quantity_price} ₽/шт`; } return res; } const BEST_PRICE_CLASS_NAME = "GM-best-price"; const BEST_PRICE_WRAP_CLASS_NAME = "GM-best-price-wrap"; const ORDER_NAME_LOCAL_STORAGE = "GM-best-price-default-order"; const MAX_NUMBER = 99999999999; function renderBestPrice(titleInfo, extraStyle = {}) { const wrapEl = document.createElement("div"); wrapEl.className = BEST_PRICE_CLASS_NAME; if (!titleInfo) return wrapEl; for (const u of titleInfo.units) { const el = document.createElement("p"); el.innerHTML = u.price_display; wrapEl.appendChild(el); } if (titleInfo.quantity_price_display) { const qtyEl = document.createElement("p"); qtyEl.innerHTML = titleInfo.quantity_price_display; wrapEl.appendChild(qtyEl); } if (wrapEl.childNodes.length) { wrapEl.style.border = "1px solid red"; wrapEl.style.padding = "5px"; wrapEl.style.margin = "5px"; wrapEl.style.width = "fit-content"; for (const [k, v] of entries(extraStyle || {})) if (typeof v == "string") wrapEl.style[k] = v; } return wrapEl; } function byPropertiesOf(sortBy) { function compareByProperty(arg) { let key; let sortOrder = 1; if (typeof arg === "string" && arg.startsWith("-")) { sortOrder = -1; key = arg.substr(1); } else key = arg; return function (a, b) { const result = a[key] < b[key] ? -1 : a[key] > b[key] ? 1 : 0; return result * sortOrder; }; } return function (obj1, obj2) { let i = 0; let result = 0; const numberOfProperties = sortBy === null || sortBy === void 0 ? void 0 : sortBy.length; while (result === 0 && i < numberOfProperties) { result = compareByProperty(sortBy[i])(obj1, obj2); i++; } return result; }; } function sort(arr, ...sortBy) { arr.sort(byPropertiesOf(sortBy)); } const PREFIX = "bp_"; function storeParsedTitleToElement(cardEl, parsedTitle) { cardEl.classList.add(BEST_PRICE_WRAP_CLASS_NAME); if (!parsedTitle) return; storeDataToElement(cardEl, parsedTitle); } function storeDataToElement(el, data) { const ds = el.dataset; for (const [k, v] of entries(data)) ds[PREFIX + k] = JSON.stringify(v); } function loadParsedTitleFromElement(cardEl) { const pairs = Object.entries(cardEl.dataset).map((([k, v]) => { if (k.startsWith(PREFIX)) return [k.replace(RegExp("^" + PREFIX), ""), JSON.parse(v || "")]; return [null, null]; })).filter((([k]) => k)); if (pairs.length > 0) return Object.fromEntries(pairs); return null; } const BEST_ORDER_BUTTON_CLASS_NAME = "GM-best-price-button-wrap"; function addStyles() { GM_addStyle(`button.${BEST_ORDER_BUTTON_CLASS_NAME} {\nborder: 1px solid gray !important; padding: 5px !important; margin: 3px !important; }\n`); GM_addStyle(`button.${BEST_ORDER_BUTTON_CLASS_NAME}.active { border: 2px solid red !important; }`); } addStyles(); function initReorderCatalog(catalogRoot, buttonRoot) { var _a, _b, _c; const buttonWrap = buttonRoot; if (!buttonWrap) return; const catalogRecords = []; let i = 0; for (const wrapEl of catalogRoot.querySelectorAll(":scope > *")) { const el = wrapEl.classList.contains(BEST_PRICE_WRAP_CLASS_NAME) ? wrapEl : wrapEl.querySelector("." + BEST_PRICE_WRAP_CLASS_NAME); if (!el) { console.warn("!", el); continue; } const ds = Object.assign({ initial_order: "0" }, loadParsedTitleFromElement(el)); if (!ds) continue; i += 1; let initial_order = parseInt(ds.initial_order || "0"); if (!initial_order) { initial_order = i; ds.initial_order = i.toString(); storeDataToElement(el, { initial_order: i }); } const record = { el: wrapEl, initial_order, weight_price: ((_b = (_a = ds.units) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.price) ? ds.units[0].price : MAX_NUMBER, quantity_price: ds.quantity_price ? ds.quantity_price : MAX_NUMBER }; catalogRecords.push(record); console.debug("Catalog order record: ", record); } const buttons = { initial_order: E("button", { class: BEST_ORDER_BUTTON_CLASS_NAME }, "Reset"), weight_price: E("button", { class: BEST_ORDER_BUTTON_CLASS_NAME }, "by Weight"), quantity_price: E("button", { class: BEST_ORDER_BUTTON_CLASS_NAME }, "by Quantity") }; function buttonClickHandler(orderState) { console.log(orderState); localStorage.setItem(ORDER_NAME_LOCAL_STORAGE, orderState); sort(catalogRecords, orderState); refreshCatalog(); setActiveButton(buttons[orderState]); } for (const [k, b] of entries(buttons)) b.onclick = () => { buttonClickHandler(k); }; const defaultOrder = localStorage.getItem(ORDER_NAME_LOCAL_STORAGE); if (defaultOrder) if (defaultOrder === "initial_order") setActiveButton(buttons[defaultOrder]); else buttonClickHandler(defaultOrder); function refreshCatalog() { const wrap = catalogRoot; if (!wrap) return; const elements = document.createDocumentFragment(); for (const c of catalogRecords) elements.appendChild(c.el); wrap.innerHTML = ""; wrap.appendChild(elements); } function setActiveButton(button) { for (const b of values(buttons)) b.classList.remove("active"); button.classList.add("active"); } (_c = buttonWrap.querySelector("." + BEST_ORDER_BUTTON_CLASS_NAME)) === null || _c === void 0 ? void 0 : _c.remove(); buttonWrap.appendChild(E("div", { class: BEST_ORDER_BUTTON_CLASS_NAME }, ...values(buttons))); } function getPriceFromElement(el) { var _a, _b; const priceText = (_b = (_a = el === null || el === void 0 ? void 0 : el.textContent) === null || _a === void 0 ? void 0 : _a.split("₽")[0]) === null || _b === void 0 ? void 0 : _b.trim(); if (priceText) return parseFloat(priceText.replace(" ", "").replace(" ", "").replace(" ", "").replace(/\s/g, "")); return null; } function getPrice(sel, root = document.body) { const priceEl = (root || document.body).querySelector(sel); return getPriceFromElement(priceEl); } function processProductCard(cardEl, options) { var _a, _b, _c; const { price_sel, title_sel, to_render, force } = options; if (!force && cardEl.classList.contains(BEST_PRICE_WRAP_CLASS_NAME)) return; const price = getPrice(price_sel, cardEl); const title = (_b = (_a = cardEl.querySelector(title_sel)) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim(); if (!title || !price) { console.warn("Not found price or title", title, price, cardEl); storeParsedTitleToElement(cardEl, null); return; } console.debug(title, price); const parsedTitle = parseTitleWithPrice(title, price); const renderedPrice = renderBestPrice(parsedTitle, options.extra_style); let to_render_sel = ""; let to_render_pos = "after"; if (typeof to_render === "string") to_render_sel = to_render; else { to_render_sel = to_render.sel; to_render_pos = to_render.pos || to_render_pos; } const to_render_els = cardEl.querySelectorAll(to_render_sel); for (const to_render_el of to_render_els) for (const e of ((_c = to_render_el === null || to_render_el === void 0 ? void 0 : to_render_el.parentElement) === null || _c === void 0 ? void 0 : _c.querySelectorAll("." + BEST_PRICE_CLASS_NAME)) || []) e.remove(); let i = 0; for (const to_render_el of to_render_els) { let r = renderedPrice; if (i > 0) r = renderedPrice.cloneNode(true); to_render_el === null || to_render_el === void 0 ? void 0 : to_render_el[to_render_pos](r); i += 1; } storeParsedTitleToElement(cardEl, parsedTitle); } function initProductPage() { var _a; const productRoot = document.querySelector('[data-widget="container"]'); if (!productRoot) return; const title = (_a = document.querySelector("[data-widget='webProductHeading']")) === null || _a === void 0 ? void 0 : _a.textContent; if (!title) return; processProductCard(productRoot, { price_sel: '[data-widget="webOzonAccountPrice"], [data-widget="webPrice"]', title_sel: '[data-widget="webProductHeading"]', to_render: { sel: '[data-widget="webPrice"]', pos: "appendChild" }, force: false }); } function processProductCardOld(cardEl) { const wrapEl = getElementByXpath("(a|div/a)/following-sibling::div[1]", cardEl); if (!wrapEl || (wrapEl === null || wrapEl === void 0 ? void 0 : wrapEl.querySelector(".GM-best-price"))) { storeParsedTitleToElement(cardEl, null); return; } const price = getPriceFromElement(wrapEl.querySelector("div")); const titleEl = wrapEl.querySelector("a span.tsBodyL, " + "a span.tsBodyM:not([style]), " + 'a span.tsBodyM[style="color:;"], ' + "a span.tsBody500Medium "); const title = titleEl === null || titleEl === void 0 ? void 0 : titleEl.textContent; if (!title || !price) { storeParsedTitleToElement(cardEl, null); return; } console.log(title, price); const parsedTitle = parseTitleWithPrice(title, price); titleEl === null || titleEl === void 0 ? void 0 : titleEl.before(renderBestPrice(parsedTitle)); storeParsedTitleToElement(cardEl, parsedTitle); } function initCatalog() { const catalogEl = document.querySelector(".widget-search-result-container > div"); if (catalogEl === null || catalogEl === void 0 ? void 0 : catalogEl.querySelector("." + BEST_PRICE_WRAP_CLASS_NAME)) return; const cardList = document.querySelectorAll(".widget-search-result-container > div > div" + ",[data-widget='skuLine'] > div:nth-child(2) > div" + ",[data-widget='skuGridSimple'] > div:nth-child(2) > div" + ",[data-widget='skuGridSimple'] > div:nth-child(1) > div" + ",[data-widget='skuLine'] > div:nth-child(1) > div" + ",[data-widget='skuLineLR'] > div:nth-child(2) > div" + ",[data-widget='skuGrid'][style] > div:nth-child(2) > div" + ",[data-widget='skuGrid'] > div:nth-child(2) > div" + ",[data-widget='skuGrid']:not([style]) > div:nth-child(1) > div" + ",[data-widget='skuShelfGoods'] > div:nth-child(2) > div > div > div > div"); for (const cardEl of cardList) processProductCardOld(cardEl); const buttonWrapEl = document.querySelector('[data-widget="searchResultsSort"]'); if (catalogEl) { const el = catalogEl.querySelector(":scope > div"); const isDetailCatalog = el && getComputedStyle(el).gridColumnStart === "span 12"; if (isDetailCatalog) console.warn("is detail catalog, reorder disabled"); else buttonWrapEl && initReorderCatalog(catalogEl, buttonWrapEl); const paginator = document.querySelector('[data-widget="megaPaginator"] > div:nth-child(2)'); const paginatorWrap = document.querySelector(".widget-search-result-container"); if (paginator === null || paginator === void 0 ? void 0 : paginator.querySelector("a")) paginatorWrap && copyElementToNewRoot(paginator, paginatorWrap, { pos: "before" }); } } (function () { "use strict"; if (!matchLocation("^https://(www\\.|)ozon\\.ru/.*")) return; console.log("OZON.ru"); waitCompletePage((() => { if (matchLocation("^https://(www\\.|)ozon\\.ru/product/.*")) initProductPage(); if (matchLocation("^https://(www\\.|)ozon\\.ru/")) initCatalog(); if (matchLocation("^https://(www\\.|)ozon\\.ru/(category|highlight|search|my|product|brand)/.*")) initCatalog(); }), { runOnce: false }); })(); function lenta_com_initProductPage() { const productRoot = document.querySelector(".product-page_info-block"); if (!productRoot) return; processProductCard(productRoot, { price_sel: ".product-price .main-price", title_sel: "lu-product-page-name h1", to_render: { sel: ".product-base-info_content", pos: "after" }, force: false }); } function processProductCardCatalog(cardEl) { var _a, _b; if (cardEl.classList.contains(BEST_PRICE_WRAP_CLASS_NAME)) return; const price = getPriceFromElement(cardEl.querySelector(".product-price .main-price")); const title = (_b = (_a = cardEl.querySelector(".lu-product-card-name")) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim(); if (!title || !price) { storeParsedTitleToElement(cardEl, null); return; } console.log(title, price); const parsedTitle = parseTitleWithPrice(title, price); cardEl === null || cardEl === void 0 ? void 0 : cardEl.appendChild(renderBestPrice(parsedTitle)); storeParsedTitleToElement(cardEl, parsedTitle); } function lenta_com_initCatalog() { const cardList = document.querySelectorAll("lu-grid .lu-grid__item:has(:not(lu-placeholder))" + ",lu-slider .product-card:has(:not(lu-placeholder))"); for (const cardEl of cardList) processProductCardCatalog(cardEl); const catalogWrapEl = document.querySelector("lu-catalog-list lu-grid > div"); const buttonWrapEl = ElementGetOrCreate(document.querySelector("lu-catalog-list .catalog-list"), { pos: "before" }); if (catalogWrapEl && buttonWrapEl) initReorderCatalog(catalogWrapEl, buttonWrapEl); const catalogEl = document.querySelector("lu-catalog-list .catalog-list"); const paginationRootWrap = ElementGetOrCreate(catalogEl, { pos: "before", className: "GM-pagination-clone" }); paginationRootWrap && copyElementToNewRoot(catalogEl === null || catalogEl === void 0 ? void 0 : catalogEl.querySelectorAll(".pagination"), paginationRootWrap); } (function () { "use strict"; if (!matchLocation("^https://lenta\\.com/.*")) return; GM_addStyle(`:root {\n --product-card-height-mobile: 384px !important;\n --products-slider-height: 400px !important;\n }`); console.log("Lenta.com"); waitCompletePage((() => { if (matchLocation("^https://lenta\\.com/product/.*")) lenta_com_initProductPage(); if (matchLocation("^https://lenta\\.com/(catalog|search|brand|product)/.*")) lenta_com_initCatalog(); }), { runOnce: false }); })(); function okeydostavka_ru_initProductPage() { const init = () => { var _a, _b, _c, _d; const productWrapEl = document.querySelector(".product_main_info"); if (!productWrapEl) return; const title = (_b = (_a = productWrapEl === null || productWrapEl === void 0 ? void 0 : productWrapEl.querySelector("h1.main_header")) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim(); const price = parseFloat(((_c = productWrapEl === null || productWrapEl === void 0 ? void 0 : productWrapEl.querySelector('.product-price > meta[itemprop="price"]')) === null || _c === void 0 ? void 0 : _c.content) || ""); if (!price || !title) return; console.log(title, price); const parsedTitle = parseTitleWithPrice(title, price); (_d = productWrapEl === null || productWrapEl === void 0 ? void 0 : productWrapEl.querySelector(".product-price")) === null || _d === void 0 ? void 0 : _d.after(renderBestPrice(parsedTitle)); }; waitCompletePage((() => { init(); })); } function okeydostavka_ru_processProductCard(cardEl) { var _a, _b; if (cardEl.classList.contains(BEST_PRICE_WRAP_CLASS_NAME)) return; const priceEl = cardEl === null || cardEl === void 0 ? void 0 : cardEl.querySelector(".price_and_cart .product-price"); const price = getPriceFromElement(priceEl === null || priceEl === void 0 ? void 0 : priceEl.querySelector(":scope > span.price")); const title = (_b = (_a = cardEl.querySelector(".product-name a")) === null || _a === void 0 ? void 0 : _a.getAttribute("title")) === null || _b === void 0 ? void 0 : _b.trim(); if (!title || !price) { storeParsedTitleToElement(cardEl, null); return; } console.log(title, price); const parsedTitle = parseTitleWithPrice(title, price); priceEl === null || priceEl === void 0 ? void 0 : priceEl.after(renderBestPrice(parsedTitle)); storeParsedTitleToElement(cardEl, parsedTitle); } function okeydostavka_ru_initCatalog() { const init = () => { const cardList = document.querySelectorAll(".product_listing_container li" + ", .also-products li > div.product" + ", .similar-products li > div.product" + ", .catalogEntryRecommendationWidget li > div.product"); for (const cardEl of cardList) okeydostavka_ru_processProductCard(cardEl); const catalogWrapEl = document.querySelector(".product_listing_container > ul"); const buttonWrapEl = ElementGetOrCreate(catalogWrapEl, { pos: "before" }); if (catalogWrapEl && buttonWrapEl) initReorderCatalog(catalogWrapEl, buttonWrapEl); waitCompletePage((() => { init(); })); }; waitCompletePage((() => { init(); })); } (function () { "use strict"; if (!matchLocation("^https://(www\\.|)okeydostavka.ru/.*")) return; if (document.querySelector(".product_main_info")) okeydostavka_ru_initProductPage(); okeydostavka_ru_initCatalog(); })(); function auchan_ru_initProductPage() { const init = () => { var _a, _b, _c; if (document.querySelector("main .GM-best-price")) return; const title = (_b = (_a = document.querySelector("main h1#productName")) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim(); const price = getPrice("main .fullPricePDP"); if (!price || !title) return; console.log(title, price); const parsedTitle = parseTitleWithPrice(title, price); (_c = document.querySelector("main .fullPricePDP")) === null || _c === void 0 ? void 0 : _c.after(renderBestPrice(parsedTitle)); }; init(); } function auchan_ru_processProductCard(cardEl, priceSel, titleSel, renderPriceSel) { var _a, _b, _c; if (cardEl.classList.contains(BEST_PRICE_WRAP_CLASS_NAME)) return; const price = getPriceFromElement(cardEl.querySelector(priceSel)); const title = (_b = (_a = cardEl.querySelector(titleSel)) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim(); if (!title || !price) { storeParsedTitleToElement(cardEl, null); return; } console.log(title, price); const parsedTitle = parseTitleWithPrice(title, price); (_c = cardEl.querySelector(renderPriceSel)) === null || _c === void 0 ? void 0 : _c.after(renderBestPrice(parsedTitle)); storeParsedTitleToElement(cardEl, parsedTitle); } function auchan_ru_initCatalog() { const init = () => { const cardList = document.querySelectorAll("article.productCard"); for (const cardEl of cardList) auchan_ru_processProductCard(cardEl, ".productCardPriceData > div", ".linkToPDP", ".productCardPriceData "); const catalogWrapEl = getElementByXpath('//article[contains(@class,"productCard")]/ancestor::main//article/../..'); if (!document.querySelector(".GM-wrap")) { const buttonWrapEl = ElementGetOrCreate(document.querySelector("#categoriesThirdLvlList"), { pos: "after" }); if (catalogWrapEl && buttonWrapEl) initReorderCatalog(catalogWrapEl, buttonWrapEl); } const catalogEl = document.querySelector(".catalog-view__main"); const paginationRootWrap = ElementGetOrCreate(catalogEl, { pos: "before", className: "GM-pagination-clone" }); paginationRootWrap && copyElementToNewRoot(catalogEl === null || catalogEl === void 0 ? void 0 : catalogEl.querySelectorAll(".pagination"), paginationRootWrap); }; init(); } function initSearchResults() { const cardList = document.querySelectorAll(".digi-product"); for (const cardEl of cardList) auchan_ru_processProductCard(cardEl, ".digi-product-price-variant_actual", ".digi-product__label", ".price-and-cart"); const catalogWrapEl = document.querySelector(".digi-products-grid"); if (!document.querySelector(".digi-search .GM-wrap")) { const buttonWrapEl = ElementGetOrCreate(document.querySelector(".digi-main-results-actions"), { pos: "after" }); if (catalogWrapEl && buttonWrapEl) initReorderCatalog(catalogWrapEl, buttonWrapEl); } } (function () { "use strict"; if (!matchLocation("^https://(www\\.|)auchan\\.ru/.*")) return; waitCompletePage((() => { if (document.querySelector("#productName")) auchan_ru_initProductPage(); else if (document.querySelector(".digi-products")) initSearchResults(); else auchan_ru_initCatalog(); }), { runOnce: false }); })(); function perekrestok_ru_initProductPage() { var _a; const productRoot = document.querySelector("main"); if (!productRoot) return; const productId = (_a = productRoot.querySelector('[itemprop="sku"]')) === null || _a === void 0 ? void 0 : _a.getAttribute("content"); if (productId && productRoot.dataset.productId !== productId) { productRoot.classList.remove(BEST_PRICE_WRAP_CLASS_NAME); productRoot.dataset.productId = productId; } processProductCard(productRoot, { price_sel: "div.price-new", title_sel: "h1.product__title", to_render: { sel: "div.price-new", pos: "after" } }); } function perekrestok_ru_initCatalog() { const cardList = document.querySelectorAll(".product-card-wrapper" + ", .swiper-slide"); for (const cardEl of cardList) processProductCard(cardEl, { price_sel: "div.price-new", title_sel: "span.product-card__link-text", to_render: "div.product-card__control" }); for (const group of document.querySelectorAll(".catalog-content-group__list")) { const cards = group.querySelectorAll(":scope > div:not(.GM-fix) > div > div > div"); const cardsWrap = ElementGetOrCreate(group, { className: "GM-fix" }); for (const c of cards) { c.style.width = "220px"; cardsWrap === null || cardsWrap === void 0 ? void 0 : cardsWrap.appendChild(c); } const buttonWrapEl = ElementGetOrCreate(group, { pos: "before" }); if (buttonWrapEl && cardsWrap) { cardsWrap.style.display = "flex"; cardsWrap.style.flexWrap = "wrap"; initReorderCatalog(cardsWrap, buttonWrapEl); } } } (function () { "use strict"; const prefix = "https://(www\\.|)perekrestok\\.ru"; if (!matchLocation(prefix)) return; console.log("Perekrestok.ru"); waitCompletePage((() => { if (matchLocation(prefix + "/cat/\\d+/p/")) perekrestok_ru_initProductPage(); perekrestok_ru_initCatalog(); }), { runOnce: false, delay: 300 }); })(); const extraStyle = { fontSize: "1rem", color: "black" }; function wildberries_ru_initProductPage() { const productRoot = document.querySelector(".product-page"); if (!productRoot) return; processProductCard(productRoot, { price_sel: ".price-block__final-price", title_sel: ".product-page__header h1", to_render: ".price-block", extra_style: extraStyle, force: true }); const cardList = document.querySelectorAll(".product-card"); for (const cardEl of cardList) processProductCard(cardEl, { price_sel: ".price__lower-price", title_sel: ".product-card__name", to_render: ".product-card__price", extra_style: extraStyle }); } function initPopup() { const productPopupRoot = document.querySelector(".popup .product"); if (!productPopupRoot) return; processProductCard(productPopupRoot, { price_sel: ".price-block__final-price", title_sel: ".product__header", to_render: ".price-block", extra_style: extraStyle }); } function wildberries_ru_initCatalog() { const cardList = document.querySelectorAll(".product-card > .product-card__wrapper"); for (const cardEl of cardList) processProductCard(cardEl, { price_sel: ".price__lower-price", title_sel: ".product-card__name", to_render: ".product-card__price", extra_style: extraStyle }); const catalogWrapEl = document.querySelector(".product-card-list"); const buttonWrapEl = ElementGetOrCreate(document.querySelector(".catalog-page__main"), { pos: "before" }); if (catalogWrapEl && buttonWrapEl) initReorderCatalog(catalogWrapEl, buttonWrapEl); const paginationRootWrap = ElementGetOrCreate(catalogWrapEl, { pos: "before", className: "GM-pagination-clone" }); paginationRootWrap && copyElementToNewRoot(document.querySelectorAll(".pager-bottom:not(.GM-cloned)"), paginationRootWrap); } (function () { "use strict"; const prefix = "https://(www\\.|)wildberries\\.ru/"; if (!matchLocation(prefix)) return; console.debug("Wildberries.ru"); waitCompletePage((() => { wildberries_ru_initProductPage(); initPopup(); wildberries_ru_initCatalog(); }), { runOnce: false, delay: 200 }); })(); })();