Amazon_Keepa_Sakura_Button

Add links to Keepa and Sakura Checker to the Amazon.co.jp product screen.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu betiği yüklemek için bir betik yöneticisi eklentisi yüklemeniz gerekecektir.

(Zaten bir betik yöneticim var, hadi yükleyelim!)

Advertisement:

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

Advertisement:

// ==UserScript==
// @name            Amazon_Keepa_Sakura_Button
// @name:ja         Amazonの商品画面に価格履歴とサクラチェックのボタンを追加
// @namespace       https://greasyfork.org/users/1324207
// @match           https://www.amazon.co.jp/dp/*
// @match           https://www.amazon.co.jp/*/dp/*
// @match           https://www.amazon.co.jp/gp/product/*
// @match           https://www.amazon.co.jp/exec/obidos/ASIN/*
// @match           https://www.amazon.co.jp/o/ASIN/*
// @match           https://www.amazon.co.jp/gp/aw/d/*
// @version         1.5.0
// @author          Lark8037
// @description     Add links to Keepa and Sakura Checker to the Amazon.co.jp product screen.
// @description:ja  Amazonの商品画面にKeepaとサクラチェッカーへのリンクを追加します。
// @run-at          document-idle
// @license         MIT
// @noframes
// @icon            https://www.amazon.co.jp/favicon.ico
// ==/UserScript==

((d, l, h, w) => {
    'use strict';

    const CID = 'checker-links',
        SID = 'checker-style',
        DA = 'data-asin',
        SEC = 'div.a-section',
        K = 'https://keepa.com/#!product/5-',
        S = 'https://sakura-checker.jp/search/',
        T = '" target="_blank" rel="noopener noreferrer">',
        RE = /[A-Z0-9]{10}/i,
        QE = /(?:^|[?&])asin=([A-Z0-9]{10})(?=&|$)/i;

    let cur = '',
        lastForm = '',
        lastUrl = '',
        timer = 0,
        gen = 0;

    const norm = v => {
        v = v && RE.exec(v);
        return v ? v[0].toUpperCase() : '';
    };

    function formASIN() {
        let e = d.getElementById('ASIN'),
            n,
            i,
            v;

        if ((v = e && norm(e.value))) return v;

        n = d.getElementsByName('ASIN');
        for (i = 0; i < n.length; i++) {
            if ((v = norm(n[i].value))) return v;
        }

        n = d.getElementsByName('ASIN.0');
        for (i = 0; i < n.length; i++) {
            if ((v = norm(n[i].value))) return v;
        }

        return '';
    }

    function urlASIN() {
        const m = QE.exec(l.search);
        return m ? m[1].toUpperCase() : norm(l.pathname);
    }

    function getASIN() {
        const f = formASIN(),
            u = urlASIN(),
            r = cur
                ? f && f !== lastForm
                    ? f
                    : u && u !== lastUrl
                        ? u
                        : f === cur || u === cur
                            ? cur
                            : f || u
                : f || u;

        lastForm = f;
        lastUrl = u;

        return r || '';
    }

    function style() {
        d.getElementById(SID) || (d.head || d.documentElement).insertAdjacentHTML(
            'beforeend',
            `<style id="${SID}">
#checker-links{contain:content}
#checker-links>a,.checker>a{display:block;border:0;height:4ex;line-height:4ex;margin-bottom:1.2ex;width:100%;text-align:center;color:#000;border-radius:10em;text-decoration:none;font-size:1em}
#checker-links>.price-history-link,.checker>.price-history-link{background:deepskyblue}
#checker-links>.price-history-link:hover,.checker>.price-history-link:hover{background:dodgerblue}
#checker-links>.sakura-checker-link,.checker>.sakura-checker-link{background:deeppink}
#checker-links>.sakura-checker-link:hover,.checker>.sakura-checker-link:hover{background:crimson}
@media(max-width:768px){#checker-links>a,.checker>a{height:5.5ex;line-height:5.5ex}}
</style>`
        );
    }

    const links = a =>
        '<a class="price-history-link" href="' + K + a + T + '価格履歴</a>' +
        '<a class="sakura-checker-link" href="' + S + a + '/' + T + 'サクラチェック</a>';

    function target() {
        return d.getElementById('buyNow') ||
            d.getElementById('add-to-cart-button') ||
            d.getElementById('buybox')?.getElementsByClassName('a-button-stack')[0] ||
            d.getElementById('add-to-cart-button-ubb') ||
            d.getElementById('buybox-see-all-buying-choices') ||
            d.getElementById('buybox-see-all-buying-choices-announce') ||
            d.getElementById('rcx-subscribe-submit-button-announce') ||
            d.getElementById('dealsAccordionRow') ||
            d.getElementById('outOfStock');
    }

    function setLinks(c, a) {
        const x = c.children;

        c.setAttribute(DA, a);

        if (x.length > 1) {
            x[0].href = K + a;
            x[1].href = S + a + '/';
        } else {
            c.innerHTML = links(a);
        }
    }

    function place(c) {
        const e = target(),
            p = e?.closest?.(SEC) || e?.parentNode;

        if (p?.parentNode && c.previousElementSibling !== p) {
            p.parentNode.insertBefore(c, p.nextSibling);
        }

        return !!p;
    }

    function draw(a) {
        const c = d.getElementById(CID);
        let e,
            p;

        if (c) {
            if (c.getAttribute(DA) !== a) {
                setLinks(c, a);
                place(c);
            }

            cur = a;
            return 1;
        }

        if (!(e = target()) || !(p = e.closest?.(SEC) || e.parentNode)) return 0;

        style();

        p.insertAdjacentHTML(
            'afterend',
            '<div id="' + CID + '" class="checker" ' + DA + '="' + a + '">' + links(a) + '</div>'
        );

        cur = a;
        return 1;
    }

    function sync() {
        const a = getASIN();
        return a ? draw(a) : 0;
    }

    function pulse() {
        let i = 0;
        const g = ++gen,
            f = () => {
                if (g !== gen) return;

                sync();

                timer = ++i < 7
                    ? w.setTimeout(f, i < 2 ? 64 : i < 3 ? 160 : i < 4 ? 400 : i < 5 ? 900 : 1800)
                    : 0;
            };

        if (timer) w.clearTimeout(timer);
        timer = w.setTimeout(f, 0);
    }

    function boot(n, t) {
        if (!sync() && n) {
            w.setTimeout(() => {
                boot(n - 1, t < 1024 ? t << 1 : t);
            }, t);
        }
    }

    function hook(name) {
        const fn = h[name];

        if (typeof fn !== 'function') return;

        h[name] = function () {
            const r = fn.apply(this, arguments);
            pulse();
            return r;
        };
    }

    hook('pushState');
    hook('replaceState');

    w.addEventListener('popstate', pulse, true);
    d.addEventListener('click', pulse, true);
    d.addEventListener('change', pulse, true);

    boot(16, 32);
})(document, location, history, window);