// ==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
// @version 0.5.12
// ==/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 readDataFromElement(el) {
const pairs = Object.entries(el.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 {};
}
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 parsePrice(text) {
var _a, _b;
text = (_a = text.split("₽")[0]) === null || _a === void 0 ? void 0 : _a.trim();
if (!text) return null;
text = text.replace(" ", "").replace(" ", "").replace(" ", "").replace(/\s/g, "");
const price = (_b = text.match(/\d+(\s*[,.]\s*\d+)?/)) === null || _b === void 0 ? void 0 : _b[0].trim();
if (price) return parseFloat(price);
return null;
}
function getPriceFromElement(el) {
var _a;
const priceText = (_a = el === null || el === void 0 ? void 0 : el.textContent) === null || _a === void 0 ? void 0 : _a.trim();
if (priceText) return parsePrice(priceText);
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() {
var _a;
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);
let catalogEl = document.querySelector(".widget-search-result-container > div");
const buttonWrapEl = document.querySelector('[data-widget="searchResultsSort"]');
if (!catalogEl) return;
const el = catalogEl.querySelector(":scope > div");
const isDetailCatalog = el && getComputedStyle(el).gridColumnStart === "span 12";
if (isDetailCatalog) console.warn("is detail catalog, reorder disabled"); else {
const catalogs = document.querySelectorAll(".widget-search-result-container > div");
const items = [];
for (const catEl of catalogs) {
items.push(...catEl.querySelectorAll(":scope > div"));
catEl.innerHTML = "";
}
console.log(readDataFromElement(catalogEl));
if (!((_a = readDataFromElement(catalogEl)) === null || _a === void 0 ? void 0 : _a["cloned"])) {
const newCatEl = catalogEl.cloneNode(true);
catalogEl.replaceWith(newCatEl);
catalogEl = newCatEl;
storeDataToElement(catalogEl, {
cloned: true
});
}
catalogEl.append(...items);
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);
const productEl = (cardEl === null || cardEl === void 0 ? void 0 : cardEl.querySelector(".product")) || cardEl;
productEl === null || productEl === void 0 ? void 0 : productEl.appendChild(renderBestPrice(parsedTitle));
cardEl.querySelectorAll("[onclick^='gtm']").forEach((el => {
el.removeAttribute("onclick");
}));
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;
console.log("Auchan.ru");
waitCompletePage((() => {
if (document.querySelector("#productName")) auchan_ru_initProductPage(); else if (document.querySelector(".digi-product")) 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");
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
});
})();
})();