/*
 * jQuerySearchBox.js
 *
 * Copyright (c) 2010 Tomohiro Okuwaki (http://www.tinybeans.net/blog/)
 *
 * Since  : 2010-07-29
 * Update : 2010-08-27
 * Version: 0.05
 * Comment: 
 * 
 * Maybe... jQuery 1.3.x later 
 * 
 */
(function($){
    $.fn.jQuerySearchBox = function(options){
        var op = $.extend({
            searchBoxCreate: true,
            searchTextID   : "search_text",
            searchBtnID    : "search_submit",
            searchBtn      : "Search",
            closeBtn       : "Close",
            loadingImgPath : "/jQuerySearchBox/loading.gif",
            searchDataPath : "/jQuerySearchBox/search_data.js",
            searchTagsClass: "search_tags",
            resultID       : "search_result",
            resultBox      : "<div id='search_result_box'/>",
            resultBoxID    : "search_result_box",
            resultWraperID : "",
            resultBoxInsert: "prepend", // prepend,append,html
            cookie         : false,
            cache          : false,
            limitFields    : null
        },options || {});

        // Function //////////
        function makeKeywords(str){
            var esc = str.match(/".*?"/g);
            if(esc){
                for(var i=0; i<esc.length; i++){
                    esc[i] = esc[i].replace(/[ 　]/g,"%20").replace(/"/g,"");
                }
                str = str.replace(/".*?"/g, "") + " " + esc.join(" ");
            }
            str = str.replace(/([\/\\\.\*\+\?\|\(\)\[\]\{\}\$\^])/g,"\\$1");
            str = str.replace(/( +|　+)/g, " ");
            str = str.replace(/^( |　)|( |　)$/g, "");
            return str.split(" ");
        }
        function setCookie(key, val, days){
            var cookie = escape(key) + "=" + escape(val);
            if(days != null){
                var expires = new Date();
                expires.setDate(expires.getDate() + days);
                cookie += ";expires=" + expires.toGMTString();
            }
            document.cookie = cookie;
        }
        function getCookie(key) {
            if(document.cookie){
                var cookies = document.cookie.split(";");
                for(var i=0; i<cookies.length; i++){
                    var cookie = cookies[i].replace(/\s/g,"").split("=");
                    if(cookie[0] == escape(key)){
                        return unescape(cookie[1]);
                    }
                }
            }
            return "";
        }
        function resultMsg(num, key, id, lim) {
            var find = (num > 0) ? "が" + num + " 件見つかりました。" : "は見つかりませんでした。";
            var field = (lim != "") ? lim + "に": "";
            var keyTag = new Array(),
                keyStr = new Array(),
                resTag = "",
                resStr = "",
                j = 0, k = 0;
            for(var i=0; i<key.length; i++){
                if(key[i].match(/^tag=/)){
                    keyTag[j] = key[i];
                    j++;
                } else {
                    keyStr[k] = key[i];
                    k++;
                }
            }
            if(keyTag.length){
                keyTag = keyTag.join(", ").replace(/%20/g," ");
                resTag = "タグに「" + keyTag + "」";
            }
            if(keyStr.length){
                keyStr = keyStr.join(", ").replace(/%20/g," ");
                resStr = field + "「" + keyStr + "」";
            }
            return "<p id='" + id + "'>" + resTag + resStr + find + "</p>";
        }
        
        // Init //////////
        var self = $(this);
        var clicked = 0;
        var resultID  = "#" + op.resultID;
        var searchTxt = (op.searchBoxCreate) ? $("<p id='inputbox'><input id='"+ op.searchTextID +"' type='text' /></p>") : "";
        var searchBtn = (op.searchBoxCreate) ? $("<p id='searchbtn'><a id='"+ op.searchBtnID +"' href='javascript:document.searchform.submit()'><img src='/img/btn_search.gif' alt='検索' width='40' height='32' class='on' /></a>") : "";
        if(op.cookie){
            if(searchTxt == ""){
                $("#" + op.searchTextID).val(getCookie("jsb_text"));
            } else {
                searchTxt.val(getCookie("jsb_text"));
            }
        }
        var loadImg     = $("<img id='search_loading' src='" + op.loadingImgPath + "' alt='loading' />").hide();
        var resultBox   = $(op.resultBox).hide();
        var resultBoxID = "#" + op.resultBoxID;
        var closeTarget = (op.resultWraperID != "") ? op.resultWraperID : op.resultBoxID;
        var closeBtn    = $("<p id='search_result_close'><a href='#'>"+ op.closeBtn +"</a></p>");
        var hiddenID    = op.searchTextID +"_hidden";
        var hidden      = $("#" + op.searchTextID).after("<input id='"+ hiddenID +"' type='hidden' />")

        var select = "", limitFieldVal = "", limitFieldTxt = "";

        // 検索対象を選択するセレクトボックス
        if(op.limitFields){
            select = $("<select id='limit_fields'/>");
            select.append("<option value='' selected='selected'>すべての項目</option>");
            for(var key in op.limitFields){
                var option = $("<option/>").val(key).text(op.limitFields[key]);
                select.append(option);
            }
        }
        
        // Setting //////////
        self.append(searchTxt,searchBtn,loadImg,select);
        var searchSubmit = $("#" + op.searchBtnID);
        
        // search_data.js読み込み完了前の処理
        function unread(e){
            e.preventDefault();
            loadImg.show();
            clicked++;
        }
        searchSubmit.click(unread);
        
        // Main //////////
        $.ajax({
            type: "GET",
            cache: op.cache,
            dataType: "json",
            url: op.searchDataPath,
            error: function(){
                $("#" + op.searchBtnID).click(function(){
                    $(resultBoxID).html("<strong>データを取得できませんでした。</strong>");
                });
            },
            success: function(data){
                searchSubmit.unbind("click", unread);
                searchSubmit.click(function(e){
                    e.preventDefault();
                    clicked = 0;
                    loadImg.show();
                    
                    // 検索フィールドの限定
                    if(op.limitFields){
                        $("#limit_fields").find(":selected").each(function(){
                            limitFieldVal = $(this).val();
                            limitFieldTxt = $(this).text();
                        });
                    }

                    var searchTags = 0,
                        resultTags = "",
                        keywordTags = new Array();
                    
                    var text = $("#" + op.searchTextID).val();
                    var keywords = makeKeywords(text);

                    if(op.cookie) setCookie("jsb_text", text, 1);

                    // data.item の各要素(obj)の数だけループ
                    var result = $.grep(data.item, function(obj, i){
                    
                        var matched = 0;
                        
                        // キーワード配列(keywords)の数だけループ
                        for(var i=0; i<keywords.length; i++){

                            // タグ検索
                            if(keywords[i].match(/^tag=/)){
                                searchTags++;
                                keywordTags[i] = keywords[i].replace(/^tag=/,"");
                                var keyword = new RegExp("," + keywordTags[i] + ",","");
                                if(keyword.test(obj["tag"])){
                                    matched++;
                                }
                            // フィールド限定検索
                            } else if(limitFieldVal != "") {
                                var keyword = new RegExp(keywords[i],"i");
                                if(keyword.test(obj[limitFieldVal])){
                                    matched++;
                                }
                            // キーワード検索
                            } else {
                                var keyword = new RegExp(keywords[i],"i");
                                for(var key in obj){
                                    if(keyword.test(obj[key])){
                                        matched++;
                                        break;
                                    }
                                }
                            }
                        }
                        
                        // マッチした数がキーワードの数と同じ場合は検索結果配列に格納
                        return (matched == keywords.length) ? true : false;
                    }, false);

                    // キーワード検索結果の処理
                    var ul = $("<ul/>");
                    for(var j=0; j<result.length; j++){
                        var a = $("<a/>").attr("href",result[j]["url"]).text(result[j]["title"].replace(/%20/g," "));
                        var li = $("<li/>").append(a);
						var resultbody = result[j]["body"].replace(/%20/g," ");
						if (resultbody != "") {
							li.append("<br />" + resultbody.substr(0,100) + "...");
						}
                        ul.append(li);
                        if(searchTags > 0){
                            resultTags += result[j]["tag"];
                        }
                    }
                    // タグ検索結果の絞り込みタグの処理
                    var dlTags = $("<dl id='search_result_tags'/>"),
                        dtTags = $("<dt>絞り込みタグ：</dt>"),
                        ddTags = $("<dd/>"),
                        ulTags = $("<ul/>"),
                        andTags = 0;
                    if(searchTags > 0){
                        for(var i=0; i<keywordTags.length; i++){
                            var reg = new RegExp("," + keywordTags[i] + ",","g");
                            resultTags = resultTags.replace(reg,",");
                        }
                        resultTags = resultTags.replace(/,+/g,",").replace(/^,|,$/g,"").split(",");
                        resultTags.sort();
                        // 隣接する重複要素を除去
                        for (var i=1; i<resultTags.length; i++) {
                            if(resultTags[i] === resultTags[i-1]) {
                                resultTags.splice(i--, 1);
                            }
                        }
                        for(var i=0; i<resultTags.length; i++){
                            var a = $("<a/>")
                                    .attr({"href":"#","rel":"and"})
                                    .addClass(op.searchTagsClass)
                                    .text(resultTags[i].replace(/%20/g," "));
                            var li = $("<li/>").append(a);
                            ulTags.append(li);
                        }
                        if(resultTags[0] != "" && resultTags.length > 0){
                            andTags++;
                            ddTags.append(ulTags);
                            dlTags.append(dtTags,ddTags)
                        }
                    }

                    // 検索結果表示ボックスを挿入など
                    switch(op.resultBoxInsert){
                        case "prepend": $(resultID).prepend(resultBox); break;
                        case "append" : $(resultID).append(resultBox); break;
                        default       : $(resultID).html(resultBox);
                    }
                    
                    if(result.length > 0 && searchTags > 0 && andTags > 0){
                        $(resultBoxID)
                            .html(resultMsg(result.length, keywords, "search_result_msg", limitFieldTxt))
                            .append(dlTags,ul,closeBtn)
                            .show();
                    } else if(result.length > 0){
                        $(resultBoxID)
                            .html(resultMsg(result.length, keywords, "search_result_msg", limitFieldTxt))
                            .append(ul,closeBtn)
                            .show();
                    } else {
                        $(resultBoxID)
                            .html(resultMsg(result.length, keywords, "search_result_msg", limitFieldTxt))
                            .append(closeBtn)
                            .show();
                    }
                    if(op.resultWraperID != "") {
                        $("#" + op.resultWraperID).show();
                    }
                    $("#search_result_close").click(function(){
                        $("#" + closeTarget).slideUp();
                    });
                    loadImg.hide();
                }); // click
                
                if(clicked != 0){
                    searchSubmit.click();
                }
                $("#" + op.searchTextID).keydown(function(e){  
                    if(e.keyCode=="13"){
                        e.preventDefault();
                        $("#" + op.searchBtnID).click();
                    }
                });
                $("." + op.searchTagsClass).live("click", function(e){
                    e.preventDefault();
                    var tagName = $(this).text();
                        tagName = (tagName.match(/[ 　]/g)) ? "\"" + "tag=" + tagName + "\"" : "tag=" + tagName;
                    var keyword = $("#" + op.searchTextID).val() ? $("#" + op.searchTextID).val() + " " : "";
                        keyword = ($(this).attr('rel') == "and") ? keyword : "";
                    $("#" + op.searchTextID).val(keyword + tagName);
                    $("#" + op.searchBtnID).click();
                });
            } // success
        }); // ajax
    }
})(jQuery);

