Greasy Fork

Greasy Fork is available in English.

Yandex Eda price calculator

12/6/2024, 7:41:34 AM

目前为 2024-12-06 提交的版本。查看 最新版本

// ==UserScript==
// @name        Yandex Eda price calculator
// @namespace   Violentmonkey Scripts
// @match       *://eda.yandex.ru/*
// @grant       none
// @version     1.5
// @author      Insaf Burangulov
// @description 12/6/2024, 7:41:34 AM
// @license     MIT
// ==/UserScript==

// Главный селектор карточки
const CARD_PRICE_ROOT_CLASS = 'UiKitDesktopProductCard_descriptionWrapper';

// Селектор, исключающий для карточки повторный пересчет цены
const CARD_PRICE_CALCULATED_CLASS = 'calculated-by-price';

// Внутренний селектор карточки, содержащий вес/объем товара
const CARD_WEIGHT_CLASS = 'UiKitDesktopProductCard_weight';

const CARD_PRICE_SELECTOR = '.UiKitDesktopProductCard_priceWrapper span';

// Поиск карточек товаров
function findCardsForCalculating(root = document) {
    return root
        .querySelectorAll('.' + CARD_PRICE_ROOT_CLASS + ':not(.' + CARD_PRICE_CALCULATED_CLASS + ')');
}

// Извлечение количества и единицы измерения из карточки
function extractWeight(card) {
    const weightDiv = card.querySelector('.' + CARD_WEIGHT_CLASS);
    if (!weightDiv) {
        return false;
    }

    const weightData = weightDiv
        .textContent
        .split(/\s| /g)
    if (weightData.length < 2) {
        return false;
    }

    weightData[0] = parseFloat(weightData[0]);
    return weightData;
}

// Извлечение узла, содержащего цену товара
function extractPriceNode(card) {
    return card.querySelector(CARD_PRICE_SELECTOR);
}

// Извлечение цены товара
function extractPriceData(node) {
    const regex = /([0-9\s,]+)(\p{Sc}|\p{L})/gu;
    const match = node.textContent.match(regex);

    if (!match) {
        return false;
    }

    // Удаление пробелов и конвертация строки в число
    const amount = parseFloat(match[0]
        .replace(/\s/g, '')
        .replace(/,/g, '.'));

    // Извлечение символа валюты
    const currency = match[0]
        .replace(/[0-9\s,]/g, '')
        .trim();

    return [amount, currency];
}

// Функции расчета цены
const priceCalculators = {
    'г': (price, weight) => Math.round((price / weight) * 1000),
    'мл': (price, weight) => Math.round((price / weight) * 1000),
    'кг': (price, weight) => Math.round(price / weight),
    'л': (price, weight) => Math.round(price / weight),
    'шт': (price, weight) => Math.round(price / weight),
};

// Расчет цены
function calculatePrice(price, weight, type) {
    return priceCalculators[type] ? priceCalculators[type](price, weight) : false;
}

// Установка текста цены
function setPriceText(price, currencyChar, node) {
    if (price !== parseInt(node.textContent)) {
        node.textContent = node.textContent + '/' + price + '\u2009' + currencyChar;
    }
}

// Метка карточки как просчитанной
function markCardAsCalculated(card) {
    card.classList.add(CARD_PRICE_CALCULATED_CLASS);
}

// Обработка карточки товара
function processCard(card) {
    const weightData = extractWeight(card);
    if (!weightData) {
        return;
    }

    const weight = weightData[0];
    const type = weightData[1];

    const priceNode = extractPriceNode(card);
    if (!priceNode) {
        return;
    }

    const priceData = extractPriceData(priceNode);
    if (!priceData) {
        return;
    }

    const price = priceData[0];
    const currencyChar = priceData[1];

    const calculatedPrice = calculatePrice(price, weight, type);
    if (!calculatedPrice) {
        return;
    }

    setPriceText(calculatedPrice, currencyChar, priceNode);
    markCardAsCalculated(card);
}

// Расчет цены на загруженные карточки товаров
function calc_prices() {
    const cards = findCardsForCalculating();
    cards.forEach(processCard)
}

function createObserver() {
    let oldCardsCount = findCardsForCalculating().length;

    return new MutationObserver(() => {
        let newCardsCount = findCardsForCalculating().length;

        if (newCardsCount !== oldCardsCount) {
            oldCardsCount = newCardsCount;
            calc_prices();
        }
    });
}

const observer = createObserver();
observer.observe(document.body, {childList: true, subtree: true, attributes: false});