はてなasinページに商品情報を表示

はてなブックマークレットの「asin記法を表示」をベースにGreasemonkey化して、さらに自分の好みの書式で表示するようにしてみたもの。ISBN-13対応版。

HatenaAsinInfo.user.js

// ==UserScript==
// @name           Hatena Asin Info
// @namespace      http://d.hatena.ne.jp/nozom/
// @include        http://d.hatena.ne.jp/asin/*
// ==/UserScript==

(function(){
    var preferISBN13 = true;

    function getNodesFromXPath(aXPath, aContextNode) {
        const XULNS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
        const XHTMLNS = 'http://www.w3.org/1999/xhtml';
        const XLinkNS = 'http://www.w3.org/1999/xlink';

        if (aXPath) {
            aXPath = String(aXPath);
        } else {
            throw 'ERROR: blank XPath expression';
        }
        if (aContextNode) {
            try {
                if (!(gContextNode instanceof Node))
                    throw '';
            } catch(e) {
                throw 'ERROR: invalid context node';
            }
        }

        const type = XPathResult.ORDERED_NODE_ITERATOR_TYPE;
        const xmlDoc  = aContextNode ? aContextNode.ownerDocument : document ;
        const context = aContextNode || xmlDoc.documentElement;
        const resolver = {
            lookupNamespaceURI : function(aPrefix) {
                switch (aPrefix) {
                    case 'xul':
                        return XULNS;
                    case 'html':
                    case 'xhtml':
                        return XHTMLNS;
                    case 'xlink':
                        return XLinkNS;
                    default:
                        return '';
                }
            }
        };

        try {
            var expression = xmlDoc.createExpression(aXPath, resolver);
            return expression.evaluate(context, type, null);
        } catch(e) {
            return {
                snapshotLength : 0,
                snapshotItem : function() { return null; }
            };
        }
    }

    function $X(aXPath, aContextNode) {
        var result = getNodesFromXPath(aXPath, aContextNode);
        switch (result.resultType) {
            case XPathResult.STRING_TYPE : return result.stringValue;
            case XPathResult.NUMBER_TYPE : return result.numberValue;
            case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
            case XPathResult.ORDERED_NODE_ITERATOR_TYPE: {
                var ret = [];
                var node;
                while (node = result.iterateNext()) {
                    ret.push(node);
                }
                return ret;
            }
            case XPathResult.ORDERED_NODE_SNAPSHOT_TYPE: {
                var ret = [];
                for (var i = 0, len = result.snapshotLength; i < len ; i++) {
                    ret.push(result.snapshotItem(i));
                }
                return ret;
            }
        }
    }

    function ISBN10_calc_checkdigit(str) {
        str = str.replace(/-/g,'');
        var digit = 0;
        for (var i = 0; i < 9; i++) {
            digit += parseInt(str.charAt(i)) * (10 - i);
        }
        var d = (11 - (digit % 11)) % 11;
        return (d == 10) ? 'X' : ('' + d);
    }

    function ISBN13_calc_checkdigit(str) {
        str = str.replace(/-/g,'');
        var odd = 0;
        var even = 0;
        for (var i = 0; i < 12; i += 2) {
            even += parseInt(str.charAt(i));
            odd += parseInt(str.charAt(i + 1));
        }
        // var d = (10 - (even + odd * 3) % 10) % 10;
        var d = 9 - (even + odd * 3 + 9) % 10;
        return '' + d;
    }

    function isISBN10(isbn) {
        isbn = isbn.replace(/-/g,'').toUpperCase();
        if (isbn.match(/^[0-9]{9}[0-9X]$/)) {
            return isbn.charAt(9) == ISBN10_calc_checkdigit(isbn);
        }
        return false;
    }

    function isISBN13(isbn) {
        isbn = isbn.replace(/-/g,'');
        if (isbn.match(/^[0-9]{13}$/)) {
            return isbn.charAt(12) == ISBN13_calc_checkdigit(isbn);
        }
        return false;
    }

    function isISBN(isbn) {
        if (isISBN10(isbn)) return 10;
        if (isISBN13(isbn)) return 13;
        return false;
    }

    function ISBN10to13(isbn10) {
        const prefix = '978-';

        var str = isbn10.replace(/-/g,'').substr(0, 9);
        var d = ISBN13_calc_checkdigit(prefix + str);

        return prefix + str + d;
    }

    function div(s) {
        var d = document.createElement('div');
        with (d.style) {
            border = '1px solid #5279E7';
            // margin = '10 5% 10 5%';
            marginBottom = '20px';
            padding = '2px';
        }
        d.appendChild(s);
        return d;
    }

    function textfield(value) {
        var node = document.createElement('input');
        node.type = 'text';
        node.size = 150;
        node.value = value;
        node.style.border = 'none';
        node.addEventListener('focus', function(e) { node.select(); }, true);
        return node;
    }

    if (window.location.href.match(/\/asin\/([^\/]+)/)) {
        var asin = RegExp.$1;

        if ($X("//form/ul").length == 0) return;

        var nodes = $X("//form/ul/li");
        var authors = [];
        for (var i = 0; i < nodes.length; i++) {
            var li = nodes[i];
            if (li.textContent.match(/^([^:]+):/)) {
                var key = RegExp.$1;

                if ((key == '作者') ||
                    (key == 'アーティスト')) {
                    var anodes = li.getElementsByTagName('a');
                    for (var j = 0; j < anodes.length; j++) {
                        authors.push(anodes[j].textContent);
                    }
                }
            }
        }

        var authors_sep = (authors.length > 0)
                          ? ' / ' + authors.join(', ') + ' '
                          : ' ';

        var asin_prefix = isISBN(asin) ? 'ISBN:' : 'ASIN:';

        if (preferISBN13 && isISBN10(asin)) asin = ISBN10to13(asin);

        var h1 = document.getElementsByTagName('h1')[0];
        if (h1) {
            var title = h1.textContent;
            var d = div(textfield(title + authors_sep + asin_prefix + asin));
            h1.parentNode.insertBefore(d, h1.nextSibling);
        }
    }
})();