// ==UserScript==
// @name        search word highlighter
// @namespace   http://inasphere.net/
// @description Google検索結果から遷移したページで検索ワードをハイライト表示する
// @include     *
// @author      inamenai
// @license     MIT License
// @version     0.2
// ==/UserScript==

// 配布場所
//     http://d.hatena.ne.jp/inamenai/20090225/p1
// 変更履歴
//     2009/02/26 0.2 エスケープされていた文字がHTMLタグに戻ってしまう現象を修正
//     2009/02/25 0.1 リリース

(function() {

    // リファラがgoogleでありかつ現在ページがgoogleでない場合のみ動作させる
    var GOOGLE_URL_REG_EXP = /http:\/\/.*google\.co\.jp\//;
    if (!GOOGLE_URL_REG_EXP.test(document.referrer) || GOOGLE_URL_REG_EXP.test(document.location.href)) {
        return;
    }
    // リファラから検索クエリが正常に取得できなかったら終了
    var SEARCH_QUERY_REG_EXP = /q=[A-Za-z0-9%\+]+/;
    var queryArray = SEARCH_QUERY_REG_EXP.exec(document.referrer);
    if (queryArray == null || queryArray[0] == null || queryArray[0] == "") {
        return;
    }

    // 検索ワードごとの配列に分解
    var query = decodeURIComponent(queryArray[0].substring(2));
    var wordArray = query.split(/[\s\+]/);

    // ハイライトの対象としないHTMLタグの定義
    var IGNORE_TAG_REG_EXP = /textarea|option|style|script/i;

    // 全検索ワードごとにループ
    for (var i = 0; i < wordArray.length; i++) {

        // body以下の全エレメントノードを取得してエレメントノードごとにループ
        // ハイライト用の<span>タグ追加によってツリー構造は変化するのでループのたびに再取得する
        var elementNodeArray = Array.slice(document.getElementsByTagName("body")[0].getElementsByTagName("*"));
        for (var j = 0; j < elementNodeArray.length; j++) {

            // エレメントノードの持つ子ノードごとにループ
            var childNodeArray = Array.slice(elementNodeArray[j].childNodes);
            for (var k = 0; k < childNodeArray.length; k++) {

                var targetNode = childNodeArray[k];

                // 処理対象はテキストノードのみ
                if (targetNode.nodeType != 3) {
                    continue;
                }

                // 親ノードがハイライト対象外のタグの場合は除外
                if (IGNORE_TAG_REG_EXP.test(targetNode.parentNode.tagName)) {
                    continue;
                }

                // テキスト内容を取得
                var text = targetNode.nodeValue;

                // ソース上でエスケープされていた部分が特殊文字に戻った状態で取得されるので再エスケープ
                text = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');

                // 検索ワードが見つからなければ何もしない
                var wordRegExp = new RegExp(wordArray[i], "gi");
                if (!wordRegExp.test(text)) {
                    continue;
                }

                // 適当なエレメントノードを作ってinnerHTMLにタグ混じりのテキストを放り込む
                // 自動的にノードツリーに変換されるのでそれを取得
                var tmpNode = document.createElement("span");
                tmpNode.innerHTML = text.replace(wordRegExp, "<span class=\"swh_word" + (i % 10 + 1) + "\">$&</span>");
                var childrenArray = Array.slice(tmpNode.childNodes);

                // 作成された子ノードごとにループ
                for (var l = 0; l < childrenArray.length; l++) {

                    // 子ノードを元のノードの位置にinsert
                    targetNode.parentNode.insertBefore(childrenArray[l], targetNode);
                }

                // 元のノードを削除
                targetNode.parentNode.removeChild(targetNode);
            }
        }
    }

    // ハイライト用スタイルを追加
    GM_addStyle(
        "span.swh_word1 {font-weight: bold; color: #000000; background-color: #ffff66;} " +
        "span.swh_word2 {font-weight: bold; color: #000000; background-color: #a0ffff;} " +
        "span.swh_word3 {font-weight: bold; color: #000000; background-color: #99ff99;} " +
        "span.swh_word4 {font-weight: bold; color: #000000; background-color: #ff9999;} " +
        "span.swh_word5 {font-weight: bold; color: #000000; background-color: #ff66ff;} " +
        "span.swh_word6 {font-weight: bold; color: #ffffff; background-color: #880000;} " +
        "span.swh_word7 {font-weight: bold; color: #ffffff; background-color: #00aa00;} " +
        "span.swh_word8 {font-weight: bold; color: #ffffff; background-color: #886800;} " +
        "span.swh_word9 {font-weight: bold; color: #ffffff; background-color: #004699;} " +
        "span.swh_word10 {font-weight: bold; color: #ffffff; background-color: #990099;}"
    );
})();
