DongleSize2Metric

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

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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);
    }
}