DongleSize2Metric

Convert the toy size chart information from imperial to metric system from some famous adult toy makers

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         DongleSize2Metric
// @namespace    https://github.com/yossi99/DongleSize2Metric
// @version      v0.0.4-beta
// @description  Convert the toy size chart information from imperial to metric system from some famous adult toy makers
// @author       Yossi99
// @match        https://twintailcreations.com/products/*
// @match        https://bad-dragon.com/products/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=bad-dragon.com
// @license      MIT
// ==/UserScript==


const SupportedDomains = Object.freeze({
    TwinTail: 'twintailcreations.com',
    BadDragon: 'bad-dragon.com'
});
const patchAttribute = 'patched';

(function() {
    const domain = window.location.host
    const supDomains = Object.values(SupportedDomains);
    if (!supDomains.find(i => i === domain)) {
        console.error('Domain not supported');
        return;
    }
    const domainMap = {
        [SupportedDomains.TwinTail]: PatchTwinTailSizeTable,
        [SupportedDomains.BadDragon]: PatchBadDragonSizeTable
    }
    const observer = new MutationObserver((event) => {
        domainMap[domain]();
    });
    observer.observe(document.querySelector('body'), {childList: true, subtree: true});
})();

function inches2CmFormated(string) { return `${(+string * 2.54).toFixed(2)} cm`;}

function PatchBadDragonSizeTable() {
    const toySizeTable = [".table.sizing-chart__table", "table.sizing-chart"]
      .map((i) => document.querySelector(i))
      .find((i) => i);
    if (!toySizeTable) {
        console.warn("Toy size table not found");
        return false;
    }
    const tableRows = toySizeTable.querySelectorAll('tbody tr')
    for (let row of tableRows) {
        const header = row.querySelector('th');
        (header ? patchHeader(header) : console.warn ('Header not found'));
        const rowData = [...row.querySelectorAll(`td:not([${patchAttribute}])`)]
        rowData.filter(i => !Number.isNaN(+i.textContent))
            ?.forEach(i => patchElement(i));
    }
    return true;

    function patchElement(element) {
        if (!(element instanceof Element)) {
            return;
        }
        element.textContent = inches2CmFormated(element.textContent);
        element.setAttribute(patchAttribute, true);
    }
    function patchHeader(header) {
        if (!(header instanceof Element)) {
            return;
        }
        const headerExp = /(?:\w+| )+ \([\w]+\)$/s;
        if (!headerExp.test(header.textContent)) {
            return;
        }
        header.textContent = header.textContent.replace(/\([\w]+\)$/s, '(cm)');
    }
}

function PatchTwinTailSizeTable() {
    const chartBtnSelector = [
        '.ProductForm__Label > button',
        '[data-action=open-modal].ProductForm__LabelLink'
    ].find(i => document.querySelector(i));
    if (!chartBtnSelector) {
        console.warn('chart button not found');
        return;
    }
    const chartBtn = document.querySelector(chartBtnSelector);
    const chartControlName = chartBtn.getAttribute('aria-controls');
    if (!chartControlName) {
        console.error('Chart control name not found');
        return false;
    }
    const toySizeTable = document.querySelector(`[id=${chartControlName}]`)?.querySelector('table');
    if (!toySizeTable) {
        console.warn('Toy size table not found!');
        return false;
    }
    const sizeRegExp = /((?:\d*\.)?\d+)(?:"|”)?(?:$|(?="|”))/s;
    // Get rows and skip header (toy name)
    const tableRows = [...(document.querySelectorAll('tr') ?? [])].slice(1);
    for (let row of tableRows) {
        // Skip category header
        const rowData = [...(row.querySelectorAll(`td:not([${patchAttribute}])`) ?? [])].slice(1);
        rowData.filter(i => sizeRegExp.test(i.textContent))
            ?.forEach(i => patchElement(i));
    }
    return true;

    function patchElement(element) {
        if (!(element instanceof Element)) {
            return;
        }
        const inches = +(sizeRegExp.exec(element.textContent)[1]);
        if (Number.isNaN(inches)) {
            console.error("Failed to extract size from: ", element);
            return;
        }
        element.textContent = element.textContent.replace(sizeRegExp, inches2CmFormated(inches));
        element.setAttribute(patchAttribute, true);
    }
}