Greasy Fork is available in English.

Instamart/Sbermarket price calculator

Parse sbermarket for better product price indication.

// ==UserScript==
// @name         Instamart/Sbermarket price calculator
// @namespace    http://tampermonkey.net/
// @version      0.4
// @description  Parse sbermarket for better product price indication.
// @author       Lunat1q
// @include      https://sbermarket.ru/metro/*
// @include      https://sbermarket.ru/auchan/*
// @include      https://instamart.ru/metro/*
// @grant        none
// ==/UserScript==


(function() {
    'use strict';

    var visitedProducts = [];
    var selector = "#wrap > div.body > div.products_with_filters_wrapper > div.products_container > div > ul";
    var products = $.find(selector)[0];
    if (products == undefined)
    {
        products = $.find("#wrap > div.body > div.key-content-wrapper.js-key-content-wrapper > div > div.products_container > div > ul")[0];
    }
    var prevCount = 0;
    var priceDict = {};
    var working = false;
    var prevSorted = 0;


    determineProductsPrice();
    sortByPrice();

    MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

    var observer = new MutationObserver(function(mutations, observer) {
        determineProductsPrice();
        setTimeout(function() {sortByPrice();}, 1000);
    });

    observer.observe(products, {
        subtree: false,
        childList: true
    });

    function determineProductsPrice()
    {
        let childElements = products.childElementCount;
        if (prevCount >= childElements)
        {
            return;
        }
        prevCount = childElements;
        let maxAmount = 0;
        let before = visitedProducts.length
        for (let i = 0; i < childElements; i++)
        {
            let product = products.children[i];
            if (product.childElementCount > 0)
            {
                let prodLink = $(product).children("a");
                let productPrice = prodLink.children("div.product__price");
                let prodPriceTag = productPrice[0];
                if (visitedProducts.includes(prodPriceTag))
                {
                    continue;
                }

                visitedProducts.push(prodPriceTag);
                let priceTag = productPrice.children("div").children("div.price-discount-sale").children("div.price.price--default"); // > div.product__price > div > div.price-discount-sale > div.price.price--default

                if (prodPriceTag == undefined)
                {
                    priceDict[product.textContent] = 9999999999;
                    continue;
                }

                if (priceTag.length == 0)
                {
                    priceTag = productPrice.children("div");
                }
                //#wrap > div.body > div.products_with_filters_wrapper > div.products_container > div > ul > li:nth-child(5) > a > div.product__price > div
                let money = getMoneyAmount(priceTag[0]);

                let parseResult = getVolumeAsKg(prodLink.children("p.product__volume")[0]) //#wrap > div.body > div.products_with_filters_wrapper > div.products_container > div > ul > li:nth-child(1) > a > p.product__volume

                let realPrice = money / parseResult.price;
                let realPriceTag = prodPriceTag.appendChild(document.createElement('p'))
                realPriceTag.innerText = realPrice.toFixed(1) + " ₽/" + parseResult.name;
                priceDict[product.textContent] = realPrice;
            }
        }

        console.log("Real price detected for: " + (visitedProducts.length - before) + "items");
    }

    function sortByPrice()
    {
        if (prevSorted >= products.childElementCount)
        {
            return;
        }
        prevSorted = products.childElementCount;
        let arr = Array.from(products.getElementsByTagName("LI"));
        arr = arr.sort((a, b) =>
        {
            let price1 = priceDict[a.textContent];
            let price2 = priceDict[b.textContent];
            return price1 > price2 ? 1 : -1;
        });
        arr.forEach(li => products.appendChild(li));
        working = false;
    }

    function getMoneyAmount(item)
    {
        if (item == undefined)
        {
            return 999999999;
        }
        return parseFloat(item.innerText.substring(0, item.innerText.length - 2).replace(" ", "").replace(",", "."));
    }

    function getVolumeAsKg(item)
    {
        let volumeText = item.innerText;
        let multiplier = 1;
        let subStrLen = 0;
        let volumeName = "";
        if (volumeText.includes("кг"))
        {
            multiplier = 1;
            subStrLen = 3;
            volumeName = "кг";
        }
        else if (volumeText.includes("г"))
        {
            multiplier = 0.001;
            subStrLen = 2;
            volumeName = "кг";
        }
        else if (volumeText.includes("мл"))
        {
            multiplier = 0.001;
            subStrLen = 3;
            volumeName = "л";
        }
        else if (volumeText.includes("л"))
        {
            multiplier = 1;
            subStrLen = 2;
            volumeName = "л";
        }
        else if (volumeText.includes("см"))
        {
            return { name: "шт", price: 1 };
        }
        else if (volumeText.includes("шт"))
        {
            volumeName = "шт";
        }

        let textToParse = item.innerText.substring(0, item.innerText.length - subStrLen).replace(",", ".");
        return { name: volumeName, price: parseFloat(textToParse) * multiplier };
    }
})();