// ==UserScript==
// @name atbmarket.com - price for kilogram
// @name:uk atbmarket.com - ціна за кілограм
// @namespace http://tampermonkey.net/
// @version 1.15
// @description Shows price per kilo for products in catalog/search/product page. Improves voice search expirience with dictanote.co chrome extension
// @description:uk Показує ціну за кілограм для продуктів у каталозі/пошуку/на сторінці продукту. Покращує роботу голосового пошуку за допомогою розширення dictanote.co для Google Chrome
// @author Untiy16
// @license MIT
// @match https://www.atbmarket.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=atbmarket.com
// @grant GM_addStyle
// @require https://code.jquery.com/jquery-3.7.1.min.js
// ==/UserScript==
'use strict';
//regex to parse weight from product title
const regex = /(\d+(?:[\.,]\d+)?)\s*(г|кг|мл|л)(?=($|\s|[^a-zA-Z0-9_а-яА-я]))/m;
const regexTeaBag = /(\d+)\s*(ф\/п)\s*(\*|x|X|х|Х)\s*(\d+(?:[\.,]\d+)?)/m;
let isActive = (localStorage.getItem('pricePerKiloMode') ?? 'true') === 'true';
//Add toggle to quick disable/enable 'price for kilo' functionality
$('.top-header__phone-tablet')
.clone()
.insertAfter($('.top-header__phone-tablet'))
.html(`Ціна за кілограм <input type="checkbox" id="pricePerKiloMode" ${isActive ? 'checked' : ''}>`);
//add class to body and state to localStorage
$(pricePerKiloMode)
.change(function() {
isActive = pricePerKiloMode.checked;
localStorage.setItem('pricePerKiloMode', isActive);
$('body').toggleClass('price-per-kilo_global--hidden', !isActive);
})
.change();
//-------------- TRIGGER VOICE INPUT SECTION START ---------------//
setInterval(() => {
if ($('#q').is(':visible') && $('#voicein_container').is(':visible')) {
//trigger site search after voice input
if (q.value != '' && Multisearch.config.hash != encodeURI(q.value)) {
Multisearch.config.hash = encodeURI(q.value);
location.href = `${location.pathname}${Multisearch.config.search_path}${q.value}`;
// location.reload();
}
//erace previous text from search input before inserting
if ($('#q').is(':visible') && $('#voicein_container').length && $($('#voicein_container')[0].shadowRoot).find('#voicein_voicebox').is(':visible')) {
q.value = '';
}
}
}, 1000);
//-------------- TRIGGER VOICE INPUT SECTION END ---------------//
//-------------- LIST SECTION START ---------------//
//add price per kilo blocks to products in catalog
function handleCatalogItem(item) {
let title = $(item).find('.catalog-item__title').text().trim();
let match = getMatch(title);
if (match !== null) {
$(item)
.find('> .catalog-item__bottom')
.clone()
.insertAfter($(item).find('> .catalog-item__bottom'))
.addClass('price_per_kilo catalog-item__price-per-kilo catalog-item__price-per-kilo--value catalog-item__price-per-kilo--hidden')
.find('.catalog-item__counter').remove();
let $priceBlock = $(item).find('> .catalog-item__bottom').last();
insertPricesPerKilo($priceBlock, getFormatedWeight(match));
}
}
$(document).on('mouseenter mouseleave', 'article.catalog-item', function(e) {
if (!$(this).find('.catalog-item__price-per-kilo').length) {
handleCatalogItem(this);
}
$(this).find('.catalog-item__price-per-kilo').toggleClass('catalog-item__price-per-kilo--hidden', e.type === 'mouseleave');
insertUnitWeight($(this));
});
// (new URLSearchParams(window.location.search)).get('page');
$('.catalog-page__sort').prepend('<button class="sort_by_price_pe_kilo">Сортувати за ціною за кілограм</button>');
$('.sort_by_price_pe_kilo').on('click', function () {
let runSort = function () {
$('.sort_by_price_pe_kilo').text('Готово!').prop('disabled', 1);
sortCatalogByPricePerKilo();
};
let currentPage = parseInt((new URLSearchParams(window.location.search)).get('page'));
if (!isNaN(currentPage) && currentPage > 1) {
location.href = `${location.href.replace(/&page=\d+/gm, '').replace(/\page=\d+&/gm, '').replace(/\page=\d+/gm, '')}${location.href.includes('?') ? '&' : '?'}runSortByPricePerKilo=1`;
return false;
}
let havePagination = $('.product-pagination .product-pagination__item').length;
if (havePagination) {
$('.main-container').addClass('main-container--loading');
loadPaginatedProductsRecursively(
$('.product-pagination__item.active + .product-pagination__item a').attr('href'),
() => {
$('.main-container').removeClass('main-container--loading');
$('.product-pagination').hide();
runSort();
},
() => {
$('.main-container').removeClass('main-container--loading');
}
);
} else {
runSort();
}
});
if (location.href.includes('runSortByPricePerKilo=1')) {
$('.sort_by_price_pe_kilo').click();
}
//-------------- LIST SECTION END ---------------//
//--------------PROD PAGE SECTION START ---------------//
//add price per kilo block on product page
if (location.pathname.includes('/product/')) {
insertUnitWeight($('.product-about__buy-row'));
let title = $('.product-page__title').text().trim();
let match = getMatch(title);
if (match !== null) {
$('.product-about__buy-row').clone().insertAfter($('.product-about__buy-row'));
let $priceBlock = $('.product-about__buy-row').last();
$priceBlock.css({'grid-row': 'unset'});
$priceBlock.addClass('price_per_kilo product-about__price-per-kilo product-about__price-per-kilo--value');
$priceBlock.find('.product-about__counter').remove();
insertPricesPerKilo($priceBlock, getFormatedWeight(match));
}
}
//--------------PROD PAGE SECTION END ---------------//
//--------------SEARCH SECTION START ---------------//
//add price per kilo and relevant price to products in search popap
$(document).on('mouseenter', '.multi-search .multi-item', function() {
let $this = $(this);
if ($this.find('.search-custom-price').length || $this.hasClass('search-custom-price--loading')) {
return false;
}
$this.addClass('search-custom-price--loading');
let title = $this.find('.multi-content a span').text().trim();
let match = getMatch(title);
let url = $this.find('.multi-content a').attr('href');
$.get(url, function(data) {
let $data = $(data).find('.product-main .product-price--weight');
let price = parseFloat($data.find('.product-price__top span').text());
let oldPrice = parseFloat($data.find('.product-price__bottom span').text());
let card = parseFloat($data.find('.atbcard-sale__price-top span').text());
$this.find('.multi-oldPrice, .multi-price').hide();
$(getSearchPricesHtml(match ? getFormatedWeight(match) : 0, price, oldPrice, card)).insertBefore($this.find('.b-addToCart'));
}).always(function() {
$this.removeClass('search-custom-price--loading');
});
});
//--------------SEARCH SECTION END ---------------//
//-------------- HELPERS ---------------//
function getSearchPricesHtml(weight, price, oldPrice = 0, card = 0) {
let picePerK
return `
<div class="search-custom-price">
${oldPrice ? `<div class="search-custom-price_bottom">${addTracingZero(oldPrice)}</div>` : ''}
<div class="search-custom-price_top">${addTracingZero(price)}</div>
${card ? `<div class="search-custom-price_card">${addTracingZero(card)}</div>` : ''}
</div>
${weight ?
`
<div class="price_per_kilo search-custom-price__price-per-kilo search-custom-price__price-per-kilo--text">-- Ціна за кілограм --</div>
<div class="search-custom-price price_per_kilo search-custom-price__price-per-kilo .search-custom-price__price-per-kilo--text">
${oldPrice ? `<div class="search-custom-price_bottom">${getPricePerKilo(oldPrice, weight).price}</div>` : ''}
<div class="search-custom-price_top">${getPricePerKilo(price, weight).price}</div>
${card ? `<div class="search-custom-price_card">${getPricePerKilo(card, weight).price}</div>` : ''}
</div>`
:
''
}
`;
}
function getPricePerKilo(price, weight) {
price = Math.round((price * 1000 / weight) * 100) / 100;
price = price.toString();
let intPart = price.split('.')[0];
let decimalPart = price.split('.')[1];
decimalPart = decimalPart ? decimalPart : '0';
decimalPart = addTracingZero(decimalPart, true);
return {
'price': addTracingZero(price),
'int': intPart,
'decimal': decimalPart,
}
}
function addTracingZero(price, isDecimalPart = false) {
price = price.toString();
// price = !price.includes('.') && !isDecimalPart ? `${price}.00` : price;
return (
(isDecimalPart && price.length === 2) ||
(price.includes('.') && price.split('.')[1].length === 2) ||
(!price.includes('.') && !isDecimalPart)
)
?
price
:
`${price}0`;
}
function getFormatedWeight(match) {
match[1] = match[1].replaceAll(',', '.');
return (match[2] === 'кг'
|| match[2] === 'л')
? match[1] * 1000 : parseFloat(match[1]);
}
function insertPricesPerKilo($priceBlock, weight) {
let hasUnitSwitch = $priceBlock.find('.change-weight').length > 0;
if (hasUnitSwitch) {
return;
}
let $cardPrice = $priceBlock.find('.atbcard-sale__price-top');
if ($cardPrice.length) {
let price = getPricePerKilo($cardPrice.attr('value'), weight);
$cardPrice.html(`<span>${price.int}.<sup class="product-price__coin">${price.decimal}</sup></span>`);
}
let $bottomPrice = $priceBlock.find('.product-price__bottom');
if ($bottomPrice.length) {
let price = getPricePerKilo(parseFloat($bottomPrice[0].innerText), weight);
$bottomPrice = $bottomPrice.find('> span').eq(0);
$bottomPrice.html(`<span>${price.int}.<sup class="product-price__coin">${price.decimal}</sup></span>`);
}
let $topPrice = $priceBlock.find('.product-price__top');
if ($topPrice.length) {
let price = getPricePerKilo(parseFloat($topPrice[0].innerText), weight);
$topPrice = $topPrice.find('> span').eq(0);
$topPrice.html(`<span>${price.int}.<sup class="product-price__coin">${price.decimal}</sup></span>`);
$topPrice.next().find('span').text('/кг');
}
}
function getLowestPricePerKilo($priceBlock, weight) {
let prefixSelector = '.product-price--weight';
if (weight === null) {
weight = 1000;
}
let pricesPerKilo = [];
let $topPrice = $priceBlock.find(`${prefixSelector} .product-price__top`);
if ($topPrice.length) {
pricesPerKilo.push(getPricePerKilo(parseFloat($topPrice[0].innerText), weight).price);
}
let $bottomPrice = $priceBlock.find(`${prefixSelector} .product-price__bottom`);
if ($bottomPrice.length) {
pricesPerKilo.push(getPricePerKilo(parseFloat($bottomPrice[0].innerText), weight).price);
}
let $cardPrice = $priceBlock.find(`${prefixSelector} .atbcard-sale__price-top`);
if ($cardPrice.length) {
pricesPerKilo.push(getPricePerKilo($cardPrice.attr('value'), weight).price);
}
return pricesPerKilo.length ? Math.min(...pricesPerKilo) : 0;
}
function getMatch(title) {
let teabagMatch = regexTeaBag.exec(title);
if (teabagMatch !== null) {
return [
'',
eval(teabagMatch[0].replace('ф/п', '').replaceAll(' ', '').replace(',', '.').replace(/x|X|х|Х/, '*')).toString(),
'г'
];
} else {
return regex.exec(title);
}
}
function insertUnitWeight($parent) {
if (!$parent.attr('data-unit-weight-appended') && $parent.find('.change-weight').length) {
let weight = `${Math.round($parent.find('.checkbox-custom__input').attr('data-unit-step') * 1000)}г`;
$parent.find('.product-price--unit .product-price__unit').append(` (${weight})`);
$parent.attr('data-unit-weight-appended', 1);
}
}
function loadPaginatedProductsRecursively(url, finishCallback, errorCallback) {
//https://devtoolstips.org/tips/en/list-all-event-listeners/ - pull element event
$.ajax({
async: false,
url: url,
success: function(data) {
let $products = $(data).find('.catalog-list > article.catalog-item');
if ($products.length) {
$('.catalog-list').append($products);
$products.find('.b-addToCart').addClass('b-addToCart--appended').on('click', function() {
$(this).closest('article').find('.catalog-item__title a').attr('target', '_blank')[0].click();
});
}
let $nextPageBtn = $(data).find('.product-pagination__item.next');
if ($nextPageBtn.length && !$nextPageBtn.hasClass('disabled')) {
setTimeout(() => loadPaginatedProductsRecursively($nextPageBtn.find('a').attr('href'), finishCallback, errorCallback), 1000);
} else {
finishCallback();
}
},
error: function(jqXHR, textStatus, errorThrown) {
alert('Error! (see console for details)');
console.log(jqXHR, textStatus, errorThrown);
errorCallback();
}
});
}
function sortCatalogByPricePerKilo() {
$('article.banner-item').hide();
$('.catalog-list article.catalog-item').each(function() {
let title = $(this).find('.catalog-item__title').text().trim();
let match = getMatch(title);
let $priceBlock = $(this).find('> .catalog-item__bottom:not(.price_per_kilo)').first();
$(this).attr('data-lowest-price-per-kilo', getLowestPricePerKilo($priceBlock, match !== null ? getFormatedWeight(match) : 1000));
});
$('article.catalog-item', '.catalog-list').sort(function (a, b) {
var contentA = parseFloat($(a).attr('data-lowest-price-per-kilo'));
var contentB = parseFloat($(b).attr('data-lowest-price-per-kilo'));
return (contentA < contentB) ? -1 : (contentA > contentB) ? 1 : 0;
}).prependTo('.catalog-list');
}
//-------------- STYLES ---------------//
GM_addStyle(/*css*/`
.catalog-item__price-per-kilo--hidden {
display: none !important;
}
.price-per-kilo_global--hidden .price_per_kilo {
display: none !important;
}
.search-custom-price {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.search-custom-price_bottom,
.search-custom-price_top,
.search-custom-price_card {
font-weight: 600;
margin-right: 5px;
}
.search-custom-price_bottom:last-child,
.search-custom-price_top:last-child,
.search-custom-price_card:last-child {
margin-right: 0px;
}
.search-custom-price_bottom {
text-decoration: line-through;
color: var(--text-grey);
}
.search-custom-price_top {
color: var(--text-color);
}
.search-custom-price_bottom + .search-custom-price_top {
color: var(--accent-color) !important;
}
.search-custom-price_card {
width: fit-content;
padding: 2px 4px;
color: var(--accent-light);
background: #28aa4e;
border-radius: 4px;
}
.search-custom-price__price-per-kilo--text {
margin-top: -10px;
font-weight: 600;
font-size: 12px;
color: var(--text-color);
}
.product-about__price-per-kilo {
margin-top: 15px;
position: relative;
}
.product-about__price-per-kilo::before {
content: '------- Ціна за кілограм -------';
position: absolute;
top: -30px;
left: 0;
font-weight: bold;
}
.catalog-item__price-per-kilo {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-top: 50px !important
}
.catalog-item__price-per-kilo .atbcard-sale {
position: static;
}
.catalog-item__price-per-kilo::before {
content: '------- Ціна за кілограм -------';
position: absolute;
top: -15px;
left: 0;
font-weight: bold;
transform: translateY(-100%);
}
.multi-results .multi-label {
width: min-content;
}
.sort_by_price_pe_kilo {
margin-right: 15px;
color: black;
font-size: larger;
font-weight: 500;
}
.sort_by_price_pe_kilo[disabled] {
background-color: #bffbbf;
}
.b-addToCart--appended .b-addToCart__basket-btn svg {
background: red;
border-radius: 7px;
}
.b-addToCart--appended .b-addToCart__btn-wrap {
border-color: red;
}
`);