PageSourceSearch

https://sanpomania.com/commons/vendor/tinybeans/flexibleSearch/flexibleSearch.min.js

js sanpomania.com collected 2026-10-07 03:27:22 UTC 45,852 bytes, 1,109 lines download raw bytes

1/*
2 * flexibleSearch.js
3 *
4 * Copyright (c) Tomohiro Okuwaki / bit part LLC (http://bit-part.net/)
5 *
6 * Since  : 2010-11-12
7 * Update : 2017-06-24
8 * Version: 2.3.1
9 * Comment: Please use this with Movable Type :)
10 *
11 * You have to include "mustache.js" before "flexibleSearch.js".
12 * Maybe... jQuery 1.7.x later
13 *
14-*/
15
16(function ($) {
17    $.fn.flexibleSearch = function (options) {
18        var op = $.extend({}, $.fn.flexibleSearch.defaults, options);
19        var $this = this;
20
21        // =======================================================================
22        //  Search Form HTML <start>
23        //
24
25        if (op.searchFormCreation) {
26
27            // Advanced Form HTML <start>
28            var advancedFormTmpl = [];
29            var advancedFormHTML = "";
30            if (op.advancedFormObj !== null) {
31                // input:hidden
32                if ("hidden" in op.advancedFormObj) {
33                    advancedFormTmpl.push([
34                        '<div class="fs-advanced-hidden" style="display:none;">',
35                        '{{#hidden}}',
36                            '<input type="hidden"',
37                                '{{#id}} id="{{id}}"{{/id}}',
38                                '{{#name}} name="{{name}}"{{/name}}',
39                                '{{#value}} value="{{value}}"{{/value}}>',
40                        '{{/hidden}}',
41                        '</div>'
42                    ].join(""));
43                }
44
45                // input:text
46                if ("text" in op.advancedFormObj) {
47                    advancedFormTmpl.push([
48                        '<div class="fs-advanced-text">',
49                        '{{#text}}',
50                            '{{#label}}',
51                            '<label{{#id}} id="{{id}}-label"{{/id}}{{#name}} for="{{name}}"{{/name}} class="fs-text{{#name}} fs-{{name}}{{/name}}">',
52                            '{{/label}}',
53                                '<input type="text"',
54                                    '{{#id}} id="{{id}}"{{/id}}',
55                                    '{{#name}} name="{{name}}"{{/name}}',
56                                    '{{#value}} value="{{value}}"{{/value}}',
57                                    '{{#placeholder}} placeholder="{{placeholder}}"{{/placeholder}}>',
58                            '{{#label}}',
59                            '{{label}}</label>',
60                            '{{/label}}',
61                        '{{/text}}',
62                        '</div>'
63                    ].join(""));
64                }
65
66                // input:checkbox
67                if ("checkbox" in op.advancedFormObj) {
68                    advancedFormTmpl.push([
69                        '<div class="fs-advanced-checkbox">',
70                        '{{#checkbox}}',
71                            '{{#label}}',
72                            '<label{{#id}} id="{{id}}-label"{{/id}}{{#name}} for="{{name}}"{{/name}} class="fs-checkbox{{#name}} fs-{{name}}{{/name}}">',
73                            '{{/label}}',
74                                '<input type="checkbox"',
75                                    '{{#id}} id="{{id}}"{{/id}}',
76                                    '{{#name}} name="{{name}}"{{/name}}',
77                                    '{{#value}} value="{{value}}"{{/value}}',
78                                    '{{#checked}} checked="checked"{{/checked}}>',
79                            '{{#label}}',
80                            '{{label}}</label>',
81                            '{{/label}}',
82                        '{{/checkbox}}',
83                        '</div>'
84                    ].join(""));
85                }
86
87                // input:radio
88                if ("radio" in op.advancedFormObj) {
89                    advancedFormTmpl.push([
90                        '<div class="fs-advanced-radio">',
91                        '{{#radio}}',
92                            '{{#label}}',
93                            '<label{{#id}} id="{{id}}-label"{{/id}}{{#name}} for="{{name}}"{{/name}} class="fs-radio{{#name}} fs-{{name}}{{/name}}">',
94                            '{{/label}}',
95                                '<input type="radio"',
96                                    '{{#id}} id="{{id}}"{{/id}}',
97                                    '{{#name}} name="{{name}}"{{/name}}',
98                                    '{{#value}} value="{{value}}"{{/value}}',
99                                    '{{#checked}} checked="checked"{{/checked}}>',
100                            '{{#label}}',
101                            '{{label}}</label>',
102                            '{{/label}}',
103                        '{{/radio}}',
104                        '</div>'
105                    ].join(""));
106                }
107
108                // Make HTML of hidden, text, checkbox and radio
109                advancedFormHTML = Mustache.render(advancedFormTmpl.join(""), op.advancedFormObj);
110
111                // input:select
112                if ("select" in op.advancedFormObj) {
113                    var advancedFormSelectObj = {
114                        "selects": op.advancedFormObj.select,
115                        "options": function () {
116                            var optionObj = this.option;
117                            var optionSet = [];
118                            for (var i = 0, l = optionObj.length; i < l; i++) {
119                                var slctd = optionObj[i].selected ? " selected": "";
120                                optionSet.push('<option value="' + optionObj[i].value + '"' + slctd + '>
120' + optionObj[i].label +'</option>');
121                            }
122                            return optionSet.join("");
123                        }
124                    };
125                    var advancedFormSelectTmpl = [
126                        '<div class="fs-advanced-select">',
127                        '{{#selects}}',
128                            '<select{{#id}} id="{{id}}"{{/id}}{{#name}} name="{{name}}"{{/name}}{{#size}} size="{{size}}"{{/size}}{{#multiple}} multiple{{/multiple}} class="fs-select{{#name}} fs-{{name}}{{/name}}">',
129                                '{{{options}}}',
130                            '</select>',
131                        '{{/selects}}',
132                        '</div>'
133                    ].join("");
134
135                    // Make HTML of select
136                    advancedFormHTML += Mustache.render(advancedFormSelectTmpl, advancedFormSelectObj);
137                }
138            }
139            // Advanced Form HTML </end>
140
141            // Search Form <start>
142            //
143            // * {{action}} (= op.searchFormAction)
144            // * {{type}} (= op.searchFormInputType)
145            // * {{placeholder}} (= op.searchFormInputPlaceholder)
146            // * {{submitBtnText}} (= op.searchFormSubmitBtnText)
147            var searchFormObj = {
148                action: op.searchFormAction,
149                type: op.searchFormInputType,
150                placeholder: op.searchFormInputPlaceholder,
151                submitBtnText: op.searchFormSubmitBtnText
152            };
153            var searchFormHTML = "";
154            if (op.limit !== null && typeof op.limit === 'number') {
155                op.paginateCount = op.limit;
156            }
157            if (op.searchFormHTML !== null) {
158                searchFormHTML = op.searchFormHTML;
159            }
160            else {
161                var searchFormTmpl = [
162                    '<form action="{{action}}" method="GET">',
163                        '<input type="hidden" name="offset" value="0">',
164                        '<input type="hidden" name="limit" value="' + op.paginateCount + '">',
165                        '<input type="{{type}}" name="search" placeholder="{{placeholder}}" class="fs-text fs-search">',
166                        '<input type="submit" value="{{submitBtnText}}" class="fs-btn fs-submit">',
167                        advancedFormHTML, // Advanced Form
168                    '</form>'
169                ].join("");
170
171                // Make search form HTML
172                searchFormHTML = Mustache.render(searchFormTmpl, searchFormObj);
173            }
174            // Search Form </end>
175
176            // Insert into DOM
177            $this[0].innerHTML = searchFormHTML;
178        }
179
180        //
181        //  Search Form HTML </end>
182        // -----------------------------------------------------------------------
183
184        // =======================================================================
185        //  Result Block Template <start>
186        //
187
188        // Search Result Loading Image <start>
189        var resultLoadingHTML = "";
190        if (op.loadingImgHTML !== null) {
191            resultLoadingHTML = op.loadingImgHTML;
192        }
193        else if (op.loadingImgPath) {
194            resultLoadingHTML = '<span class="fs-loading"><img src="' + op.loadingImgPath + '" alt=""></span>';
195        }
196        // Search Result Loading Image </end>
197
198        // Search Result Message <start>
199        //
200        // * {{keywords}}
201        // * {{count}}
202        // * {{firstPage}}
203        // * {{lastPage}}
204        // * {{currentPage}}
205        var resultMsgTmpl = "";
206        if (op.resultMsgTmpl !== null) {
207            resultMsgTmpl = op.resultMsgTmpl;
208        }
209        else {
210            resultMsgTmpl = [
211                '<div{{#id}} id="{{id}}"{{/id}}{{#classname}} class="{{classname}}"{{/classname}}>',
212                    '<p>',
213                        '{{#keywords}}「{{keywords}}」が {{/keywords}}',
214                        '{{#count}}{{count}} 件見つかりました。{{/count}}',
215                        '{{^count}}見つかりませんでした。{{/count}}',
216                        '{{#count}}({{lastPage}} ページ中 {{currentPage}} ページ目を表示){{/count}}',
217                        // '({{firstPage}}〜{{lastPage}} ページ中 {{currentPage}} ページ目を表示)',
218                    '</p>',
219                '</div>'
220            ].join("");
221        }
222        // Search Result Message </end>
223
224        // Search Result Item <start>
225        //
226        // * keys used in your JSON
227        var resultItemTmpl = "";
228        if (op.resultItemTmpl !== null) {
229            resultItemTmpl = op.resultItemTmpl;
230        }
231        else {
232            resultItemTmpl = [
233                '<div id="' + op.resultBlockId + '-items">',
234                    '<ul>',
235                    '{{#items}}',
236                        '<li>{{&title}}</li>',
237                    '{{/items}}',
238                    '</ul>',
239                '</div>'
240            ].join("");
241        }
242        // Search Result Item </end>
243
244        // Paginate <start>
245        //
246        // * {{paginateId}}
247        // * {{&current}}
248        // * {{.}}
249        var paginateTmpl = "";
250        if (op.paginateTmpl !== null) {
251            paginateTmpl = op.paginateTmpl;
252        }
253        else {
254            paginateTmpl = [
255                '<div{{#id}} id="{{id}}"{{/id}}{{#classname}} class="{{classname}}"{{/classname}}>',
256                    '<ul>',
257
258                        '{{#showTurnPage}}',
259                        '{{#exceptFirst}}',
260                        '<li class="fs-prev"><span><a class="fs-prev-link fs-turn-page-link" href="#" title="{{prevPageText}}">{{prevPageText}}</a></span></li>',
261                        '{{/exceptFirst}}',
262                        '{{/showTurnPage}}',
263
264                        '{{#page}}',
265                        '{{#checkRange}}',
266                        '<li class="{{current}}"{{#hidePageNumber}} style="display:none;"{{/hidePageNumber}}><span><a class="fs-page-link {{currentLink}}" href="#" title="{{pageNumber}}">{{pageNumber}}</a></span></li>',
267                        '{{/checkRange}}',
268                        '{{/page}}',
269
270                        '{{#showTurnPage}}',
271                        '{{#exceptLast}}',
272                        '<li class="fs-next"><span><a class="fs-next-link fs-turn-page-link" href="#" title="{{nextPageText}}">{{nextPageText}}</a></span></li>',
273                        '{{/exceptLast}}',
274                        '{{/showTurnPage}}',
275
276                    '</ul>',
277                '</div>'
278            ].join("");
279        }
280        // Paginate </end>
281
282        //
283        //  Result Block Template </end>
284        // -----------------------------------------------------------------------
285
286        // =======================================================================
287        //  Get parameters and serialize parameters <start>
288        //
289
290        // Serialize parameters
291        $this.find("form").on("submit", function (e) {
292            e.preventDefault();
293            var url = $(this).attr("action") || location.href.replace(/\?.*/, "");
294            var params = $(this).serializeArray();
295            var postParams = [];
296            for (var i = -1, n = params.length; ++i < n;) {
297                if (params[i].name === "search") {
298                    postParams.push({name:"search",value:$.trim(params[i].value.replace(/[ | ]+/g, " "))});
299                }else if (params[i].name == "cate[]" || params[i].name == "site[]") {
300                }else{
301                    postParams.push(params[i]);
302                }
303            }
304            var cates=[];
305            $("#catelistUl").children().each(function(index) {
306                cateLoop(this,cates);
307            });
308            postParams.push({name:"cate[]",value:cates.join(",")});
309            cates=[];
310            $("#sitelistUl").children().each(function(index) {
311                cateLoop(this,cates);
312            });
313            postParams.push({name:"site[]",value:cates.join(",")});
314            postParams = op.submitAction(postParams);
315            var serializeParams = $.param(postParams);
316            if (serializeParams) {
317                url = url + "?" + serializeParams;
318            }
319            location.href = url;
320            return false;
321        });
322
323        var paramStr = decodeURIComponent(location.search.replace(/^\?/, ""));
324
325        // Set initial parameters
326        if (paramStr === '' && op.initialParameter !== null && typeof op.initialParameter === 'string') {
327            paramStr = decodeURIComponent(op.initialParameter.replace(/^\?/, ""));
328        }
329
330        //
331        //  Get parameters and serialize parameters </end>
332        // -----------------------------------------------------------------------
333
334        // =======================================================================
335        //  Preload search data files <start>
336        //
337
338        if (paramStr === "") {
339            switch (typeof op.searchDataPathPreload) {
340                case "string":
341                    $.ajax({
342                        type: "GET",
343                        cache: true,
344                        dataType: "json",
345                        url: op.searchDataPathPreload
346                    });
347                    break;
348                case "object":
349                    if (op.searchDataPathPreload === null || op.searchDataPathPreload === "") {
350                        break;
351                    }
352                    if (op.searchDataPathPreload.length) {
353                        for (var i = -1, n = op.searchDataPathPreload.length; ++i < n;) {
354                            $.ajax({
355                                type: "GET",
356                                cache: true,
357                                dataType: "json",
358                                url: op.searchDataPathPreload[i]
359                            });
360                        }
361                    }
362                    else {
363                        for (var key in op.searchDataPathPreload) {
364                            $.ajax({
365                                type: "GET",
366                                cache: true,
367                                dataType: "json",
368                                url: op.searchDataPathPreload[key]
369                            });
370                        }
371                    }
372                    break;
373            }
374            return false;
375        }
376
377        //
378        //  Preload search data files </end>
379        // -----------------------------------------------------------------------
380
381        // Search Result Loading Image
382        if (resultLoadingHTML) {
383            document.getElementById(op.resultBlockId).innerHTML = resultLoadingHTML;
384        }
385
386        // Set values to Search Form
387        var searchWords = [];
388        var paramAry = paramStr.split(/&|%26/);
389        var paramObj = {};
390        var paramExistArry = [];
391        var advancedSearchObj = {};
392        var limit = (op.limit !== null && typeof op.limit === 'number') ? op.limit: 10;
393        var offset = 0;
394        var sortBy = "";
395        var sortOrder = "";
396        var sortType = "";
397        var jsonPath = "";
398        var dataId = "";
399        var api = false;
400        var excludeParams = ["search", "dataId", "offset", "limit", "sortBy", "sortOrder", "sortType"];
401        if (op.simplePaginate === true) {
402            excludeParams.push("page");
403        }
404        if (op.excludeParams !== null) {
405            $.merge(excludeParams, op.excludeParams.toLowerCase().split(","));
406        }
407
408        for (var i = -1, n = paramAry.length; ++i < n;) {
409            var param = paramAry[i].split("=");
410            var key = param[0];
411            var value = param[1] || "";
412            paramObj[key] = value;
413            // Set "advancedSearchObj" and "searchWords"
414            switch (key) {
415                case "search":
416                    value = (value === "+") ? "" : value; // If value is " ", it is "+" on URL.
417                    searchWords = value.split(/\+| |%20/);
418                    break;
419                case "offset":
420                    offset = value;
421                    break;
422                case "limit":
423                    limit = (op.limit !== null && typeof op.limit === 'number') ? op.limit: value;
424                    break;
425                case "dataId":
426                    dataId = value;
427                    break;
428                case "sortBy":
429                    sortBy = value.toLowerCase();
430                    break;
431                case "sortOrder":
432                    sortOrder = value.toLowerCase();
433                    break;
434                case "sortType":
435                    sortType = value.toLowerCase();
436                    break;
437            }
438
439            // Restore search condition
440            if (key === "offset" || key === "limit") {
441                continue;
442            }
443            if (op.simplePaginate === true && key === "page") {
444                continue;
445            }
446            if(key.indexOf("[]") > -1){
447              if(value==""){
448                paramObj[key] = [];
449              }else{
450                $this.find("[name='" + key + "']").each(function () {
451                  values  = value.split(",");
452                  var type = $(this).attr('type');
453                  if (values.indexOf($(this).val()) > -1) {
454                    $(this).prop("checked", true);
455                    $(this).change();
456                  }
457                  paramObj[key] = values;
458                });
459              }
460                
461            }else{
462            $this.find("[name='" + key + "']").each(function () {
463                var tagname = this.tagName.toLowerCase();
464                switch (tagname) {
465                    case "input":
466                        var type = $(this).attr('type');
467                        if (type === "checkbox" && $(this).val() === value) {
468                            $(this).prop("checked", true);
469                        }
470                        else if (type === "radio" && $(this).val() === value) {
471                            $(this).prop("checked", true);
472                        }
473                        else if (type === "text" || type === "search" || type === "hidden") {
474                            if (key === "search") {
475                                $(this).val(value.replace(/\+/g," "));
476                            } else {
477                                $(this).val(value);
478                            }
479                        }
480                    break;
481                    case "select":
482                        $(this).find("option").each(function () {
483                            if ($(this).val() === value) {
484                                $(this).prop("selected", true);
485                            }
486                            else if (value === "" && $(this).val() === " ") {
487                                $(this).prop("selected", true);
488                            }
489                        });
490                    break;
491                }
492            });
493            }
494
495            // Set advancedSearchObj. This object is used in original search.
496            if (value !== "") {
497                value = value.replace(/\+/g, " ");
498                if ($.inArray(key, excludeParams) !== -1) {
499                    continue;
500                }
501                if ($.inArray(key, paramExistArry) !== -1) {
502                    advancedSearchObj[key] += "," + value;
503                }
504                else {
505                    advancedSearchObj[key] = value;
506                }
507            }
508            paramExistArry.push(key);
509        } // for
510
511        // For simple paginate option
512        if (op.simplePaginate === true && typeof paramObj["page"] !== 'undefined') {
513            if (/all/i.test(paramObj["page"])) {
514                offset = 0;
515                limit = 100000000;
516            }
517            else {
518                offset = (paramObj["page"] - 1) * limit;
519            }
520        }
521
522        // Set paramKeyCount
523        var paramKeyCount = 0;
524        for (var key in advancedSearchObj) {
525            paramKeyCount++;
526        }
527
528        // =======================================================================
529        //  Set JSON path and using Data API <start>
530        //
531
532        // Set jsonPath
533        switch (typeof op.searchDataPath) {
534            case "string":
535                jsonPath = op.searchDataPath;
536            break;
537            case "object":
538                if (dataId === "") {
539                    window.alert("dataId is required.");
540                    return;
541                }
542                else {
543                    if (op.dataApiDataIds !== null && $.inArray(dataId, op.dataApiDataIds.split(",")) !== -1) {
544                        api = true;
545                        if (op.dataApiParams !== null) {
546                            paramStr += (paramStr !== "") ? "&" + $.param(op.dataApiParams): $.param(op.dataApiParams);
547                        }
548                    }
549                    jsonPath = op.searchDataPath[dataId];
550                }
551            break;
552        }
553
554        //
555        //  Set JSON path and using Data API </end>
556        // -----------------------------------------------------------------------
557
558        // =======================================================================
559        //  Search <start>
560        //
561        if (api) {
562            jsonPath += "?" + paramStr;
563        }
564        $.ajax({
565            type: "GET",
566            cache: op.cache,
567            dataType: "json",
568            url: jsonPath,
569            error: function (jqXHR, textStatus, errorThrown) {
570                op.ajaxError(jqXHR, textStatus, errorThrown);
571            },
572            success: function (response) {
573                var resultJSON = {};
574                if (api) {
575                    // Data API
576                    resultJSON = response;
577                }
578                else {
579                    // Original JSON
580                    // Clone the items
581                    var cloneItems = $.grep(response.items, function () {
582                        return true;
583                    });
584                    // Advanced Search
585                    cloneItems = $.grep(cloneItems, function (item, i) {
586                        return jsonAdvancedSearch (item, advancedSearchObj, paramKeyCount, "like", op.advancedSearchCond);
587                    });
588
589                    // Search by keywords
590                    cloneItems = $.grep(cloneItems, function (item, i) {
591                        return jsonKeywordsSearch (item, searchWords);
592                    });
593                
594                    // 事前加工
595                    if (op.modifyCloneJSON !== null && typeof op.modifyCloneJSON === "function") {
596                        cloneItems = op.modifyCloneJSON(cloneItems);
597                    }
598                    // Do custom search
599                    if (op.customSearch !== null && typeof op.customSearch === "function") {
600                        cloneItems = $.grep(cloneItems, function (item, i) {
601                            return op.customSearch(item, paramObj);
602                        });
603                    }
604
605                    // Set resultJSON
606                    var limitIdx = Number(limit) + Number(offset);
607                    resultJSON.totalResults = cloneItems.length;
608
609                    // Sort
610                    if (resultJSON.totalResults !== 0 && sortBy !== "" && sortBy in cloneItems[0]) {
611                        if (sortOrder !== "ascend") {
612                            sortOrder = "descend";
613                        }
614                        if (sortType !== "numeric") {
615                            sortType = "string";
616                        }
617                        objectSort(cloneItems, sortBy, sortOrder, sortType);
618                    }
619
620                    // Custom Sort
621                    if (op.customSort !== null && typeof op.customSort === "function") {
622                        cloneItems = op.customSort(cloneItems, paramObj);
623                    }
624
625                    resultJSON.items = $.grep(cloneItems, function (item, i) {
626                        if (i < offset) {
627                            return false;
628                        }
629                        else if (i >= limitIdx) {
630                            return false;
631                        }
632                        else {
633                            return true;
634                        }
635                    });
636                }
637                // Paginate
638                var currentPage = Math.ceil(offset / limit) + 1;
639                var realMaxPageCount = Math.ceil(resultJSON.totalResults / limit);
640                var maxPageCount = op.maxPageCount ? op.maxPageCount : 100;
641                var forwordRange = ((maxPageCount % 2) == 0) ? (maxPageCount / 2) - 1 : Math.floor(maxPageCount / 2);
642                var backwardRange = ((maxPageCount % 2) == 0) ? maxPageCount / 2 : Math.floor(maxPageCount / 2);
643                var startPage, lastPage;
644                if (realMaxPageCount <= maxPageCount) {
645                    startPage = 1;
646                    lastPage = realMaxPageCount;
647                }
648                else {
649                    startPage = currentPage - forwordRange;
650                    lastPage = currentPage + backwardRange;
651                    if (startPage < 1) {
652                        startPage = 1;
653                        lastPage = maxPageCount;
654                    }
655                    else {
656                      if (lastPage > realMaxPageCount) {
657                          backwardRange = realMaxPageCount - currentPage;
658                          lastPage = realMaxPageCount;
659                          forwordRange = maxPageCount - backwardRange;
660                          startPage = currentPage - forwordRange;
661                      }
662                    }
663                }
664                var pageList = [];
665                for (var i = 0, n = realMaxPageCount; ++i <= n;) {
666                    pageList.push({pageNumber: i});
667                }
668                var paginateJSON = {
669                    id: op.paginateId,
670                    classname: op.paginateClassName,
671                    page: pageList,
672                    hidePageNumber: op.hidePageNumber,
673                    showTurnPage: op.showTurnPage,
674                    prevPageText: op.prevPageText,
675                    nextPageText: op.nextPageText,
676                    isFirst: function(){
677                        return currentPage === 1;
678                    },
679                    isLast: function(){
680                        if (!pageList.length) {
681                            return true;
682                        }
683                        return currentPage === pageList.length;
684                    },
685                    exceptFirst: function(){
686                        return currentPage !== 1;
687                    },
688                    exceptLast: function(){
689                        return pageList.length > 0 && currentPage !== pageList.length;
690                    },
691                    checkRange: function(){
692                        return this.pageNumber >= startPage && this.pageNumber <= lastPage;
693                    },
694                    current: function () {
695                        if (this.pageNumber === currentPage) {
696                            return 'fs-current';
697                        }
698                        else if (this.pageNumber === (currentPage - 1)) {
699                            return "fs-current-prev";
700                        }
701                        else if (this.pageNumber === (currentPage + 1)) {
702                            return "fs-current-next";
703                        }
704                        else {
705                            return "";
706                        }
707                    },
708                    isCurrent: function(){
709                      return this.pageNumber === currentPage;
710                    },
711                    lastPage: function () {
712                        return paginateJSON.page.length;
713                    },
714                    currentLink: function () {
715                        if (this.pageNumber === currentPage) {
716                            return 'fs-current-link';
717                        }
718                        else if (this.pageNumber === (currentPage - 1)) {
719                            return "fs-current-prev-link";
720                        }
721                        else if (this.pageNumber === (currentPage + 1)) {
722                            return "fs-current-next-link";
723                        }
724                        else {
725                            return "";
726                        }
727                    },
728                    currentCountFrom: function () {
729                        return offset - 0 + 1;
730                    },
731                    currentCountTo: function () {
732                        var num = offset - 0 + limit;
733                        var res = num < resultJSON.totalResults ? num : resultJSON.totalResults;
734                        return res - 0;
735                    },
736                    totalResults: resultJSON.totalResults,
737                    count: resultJSON.totalResults,
738                    currentPage: currentPage
739                };
740                var paginateHTML = Mustache.render(paginateTmpl, paginateJSON);
741
742                // Result message
743                var resultMsgObj = {
744                    id: op.resultMsgId ? op.resultMsgId : '',
745                    classname: op.resultMsgClassName ? op.resultMsgClassName : '',
746                    keywords: searchWords.join(', '),
747                    keywordArray: searchWords.join('') !== '' ? searchWords : null,
748                    count: resultJSON.totalResults,
749                    metaTitle: document.title,
750                    firstPage: function () {
751                        return paginateJSON.page[0].pageNumber;
752                    },
753                    lastPage: function () {
754                        return paginateJSON.page.length;
755                    },
756                    currentPage: currentPage
757                };
758                var resultMsgHTML = Mustache.render(resultMsgTmpl, resultMsgObj);
759
760                // Set the meta title
761                if (op.resultMetaTitleRewrite) {
762                    var resultMetaTitleTmpl = (op.resultMetaTitleTmpl) ? op.resultMetaTitleTmpl : [
763                            // '{{#keywords}}{{keywords}}{{/keywords}}',
764                            '{{#keywordArray}}{{.}} {{/keywordArray}}',
765                            '{{#count}} {{count}}ä»¶{{/count}}',
766                            '{{#count}} {{currentPage}}/{{lastPage}}{{/count}}',
767                            '{{#metaTitle}} | {{metaTitle}}{{/metaTitle}}'
768                        ].join("");
769                    var resultMetaTitleHTML = Mustache.render(resultMetaTitleTmpl, resultMsgObj);
770                    document.title = resultMetaTitleHTML;
771                }
772
773                // Show result
774                if (op.modifyResultJSON !== null && typeof op.modifyResultJSON === "function") {
775                    resultJSON = op.modifyResultJSON(resultJSON);
776                }
777                var resultItemHTML = Mustache.render(resultItemTmpl, resultJSON);
778
779                // Callback
780                if (op.modifyResultMsgHTML !== null && typeof op.modifyResultMsgHTML === "function") {
781                    resultMsgHTML = op.modifyResultMsgHTML(resultMsgHTML);
782                }
783                if (op.modifyResultItemHTML !== null && typeof op.modifyResultItemHTML === "function") {
784                    resultItemHTML = op.modifyResultItemHTML(resultItemHTML);
785                }
786                if (op.modifyPaginateHTML !== null && typeof op.modifyPaginateHTML === "function") {
787                    paginateHTML = op.modifyPaginateHTML(paginateHTML);
788                }
789
790                // Search Result Block HTML
791                var resultAllHTML = '';
792
793                // Add result message HTML
794                if (op.resultMsgInsertMethods === null) {
795                    resultAllHTML += resultMsgHTML;
796                }
797                // Add result items HTML
798                resultAllHTML += resultItemHTML;
799                // Add paginate HTML
800                if (realMaxPageCount === 1 && op.hideOnePagePaginate === true) {
801                    // nothing to do
802                }
803                else if (op.paginateInsertMethods === null) {
804                    resultAllHTML += paginateHTML;
805                }
806
807                // Add all of result HTML to DOM
808                document.getElementById(op.resultBlockId).innerHTML = resultAllHTML;
809
810                // Add result message HTML to DOM
811                if (op.resultMsgInsertMethods !== null) {
812                    for (var i = 0, l = op.resultMsgInsertMethods.length; i < l; i++) {
813                        $(op.resultMsgInsertMethods[i].selector)[op.resultMsgInsertMethods[i].method](resultMsgHTML);
814                    }
815                }
816                // Add paginate HTML to DOM
817                if (realMaxPageCount === 1 && op.hideOnePagePaginate === true) {
818                    // nothing to do
819                }
820                else if (op.paginateInsertMethods !== null) {
821                    for (var i = 0, l = op.paginateInsertMethods.length; i < l; i++) {
822                        $(op.paginateInsertMethods[i].selector)[op.paginateInsertMethods[i].method](paginateHTML);
823                    }
824                }
825
826                // Callback
827                if (op.resultComplete !== null && typeof op.resultComplete === "function") {
828                    op.resultComplete(resultJSON.totalResults);
829                }
830
831                // Bind pageLink() to paginate link
832                var paginateSelector = op.paginateId ? "#" + op.paginateId : "." + op.paginateClassName;
833                $(paginateSelector)
834                    .on("click", "a.fs-page-link", function (e) {
835                        e.preventDefault();
836                        var page = $(this).attr("title");
837                        var offset = (Number(page) - 1) * Number(limit);
838                        var url = location.href.replace(/\?.*/g, '');
839                        var query = location.search ? location.search.replace(/^\?/, '') : op.initialParameter;
840                        if (op.simplePaginate === true) {
841                            if (query.indexOf('page=') === -1) {
842                                query += query ? '&page=' + page : 'page=' + page;
843                            }
844                            else {
845                                query = query.replace(/page=[0-9]+/, 'page=' + page);
846                            }
847                            query = query.replace(/&?offset=[0-9]+/g, '');
848                        }
849                        else {
850                            if (query.indexOf('offset=') === -1) {
851                                query += query ? '&offset=' + offset : 'offset=' + offset;
852                            }
853                            else {
854                                query = query.replace(/offset=[0-9]+/, 'offset=' + offset);
855                            }
856                            query = query.replace(/&?offset=0/, '');
857                        }
858                        if (query) {
859                            url += "?" + query.replace(/^&+/, '');
860                        }
861                        location.href = url;
862                    })
863                    .on("click", "a.fs-turn-page-link", function (e) {
864                        e.preventDefault();
865                        if ($(this).hasClass('fs-prev-link')) {
866                            $(e.delegateTarget).find('.fs-current-prev-link').trigger('click');
867                        }
868                        else if ($(this).hasClass('fs-next-link')) {
869                            $(e.delegateTarget).find('.fs-current-next-link').trigger('click');
870                        }
871                    });
872            } // success
873        }); // ajax
874
875        //
876        //  Search </end>
877        // -----------------------------------------------------------------------
878
879        // =======================================================================
880        //  Functions <start>
881        //
882function cateLoop(li,cates){
883                if($(li).children("input").is(':checked')){
884                  cates.push($(li).children("input").val());
885                }else{
886                if($(li).children("ul").length >0){
887                   $(li).children("ul").children("li").each(function(index) {
888                     cateLoop(this,cates);
889                   });
890                }
891                }
892
893}
894
895        function jsonAdvancedSearch (obj, advancedSearchObj, paramKeyCount, matchType, cond) {
896            var matched = 0;
897            if (matchType === "like") {
898                for (var key in advancedSearchObj) {
899                    var valueArray = advancedSearchObj[key].split(",");
900                    var valueArrayLength = valueArray.length;
901                    var _matched = 0;
902                    for (var i = -1, n = valueArrayLength; ++i < n;) {
903                        var reg = new RegExp(valueArray[i].replace(/([.*+?^=!:${}()|[\]\/\\])/g, "\\$1"), "i");
904                        if (typeof obj[key] === "undefined" || typeof obj[key] === "string" && reg.test(obj[key])) {
905                            if (cond === 'AND' || cond === 'and') {
906                                _matched++;
907                            }
908                            else {
909                                matched++;
910                                break;
911                            }
912                        }
913                        // else if (obj[key] && typeof obj[key] === "object" && obj[key].length) {
914                        //     for (var i = -1, n = obj[key].length; ++i < n;) {
915                        //         if (reg.test(obj[key])) return true;
916                        //     }
917                        // }
918                    }
919                    if (cond === 'AND' || cond === 'and') {
920                        if (_matched === valueArrayLength) {
921                            matched++;
922                        }
923                    }
924                }
925                return matched === paramKeyCount;
926            }
927            else {
928                for (var key in advancedSearchObj) {
929                    if (obj[key] && typeof obj[key] === "string" && obj[key] !== advancedSearchObj[key]) {
930                        return false;
931                    }
932                    // else if (obj[key] && typeof obj[key] === "object" && obj[key].length) {
933                    //     for (var i = -1, n = obj[key].length; ++i < n;) {
934                    //         return advancedSearchObj[key] === obj[key][i];
935                    //     }
936                    // }
937                }
938            }
939            return matched;
940        }
941
942        function jsonKeywordsSearch (obj, keywordsArray) {
943            var keywordsCount = keywordsArray.length;
944            var keywordsMutchCount = 0;
945            for (var i = -1; ++i < keywordsCount;) {
946                var reg = new RegExp(keywordsArray[i].replace(/([.*+?^=!:${}()|[\]\/\\])/g, "\\$1"), "i");
947                // if (reg.test(obj[key])) keyMatch++;
948                for (var key in obj) {
949                    if (reg.test(obj[key])) {
950                        keywordsMutchCount++;
951                        break;
952                    }
953                }
954            }
955            return (keywordsCount === keywordsMutchCount);
956        }
957
958        // Sort object.
959        // @paran {Array} array JSON
960        // @paran {String} sortBy Sort by this property's value.
961        // @paran {String} sortOrder ascend or descend.
962        // @paran {String} sortType numeric or string.
963        function objectSort (array, sortBy, sortOrder, sortType) {
964            if (!sortBy && typeof sortBy !== "string") {
965                return;
966            }
967            sortOrder = (sortOrder === 'ascend') ? -1 : 1;
968            array.sort(function(obj1, obj2){
969                var v1 = obj1[sortBy];
970                var v2 = obj2[sortBy];
971                if (sortType === 'numeric') {
972                    v1 = v1 - 0;
973                    v2 = v2 - 0;
974                }
975                else if (sortType === 'string') {
976                    v1 = '' + v1;
977                    v2 = '' + v2;
978                }
979                if (v1 < v2) {
980                    return 1 * sortOrder;
981                }
982                if (v1 > v2) {
983                    return -1 * sortOrder;
984                }
985                return 0;
986            });
987        }
988
989        //
990        //  Functions </end>
991        // -----------------------------------------------------------------------
992    };
993    $.fn.flexibleSearch.defaults = {
994        initialParameter: null,
995        // The limit parameter is overwritten and locked as this value.
996        limit: null,
997        // If you want to use 'page' parameter without 'limit' and 'offset', set true to this option.
998        simplePaginate: false,
999        // Path
1000        searchDataPath: "/flexibleSearch/search.json",
1001        searchDataPathPreload: null,
1002
1003        // Data API
1004        dataApiDataIds: null,
1005        dataApiParams: null,
1006
1007        // Performance
1008        cache:  true, // I recommend "true".
1009
1010        // Search Form
1011        searchFormCreation: true,
1012        searchFormHTML: null,
1013        searchFormAction: "",
1014        searchFormInputType: "search",
1015        searchFormInputPlaceholder: "Search words",
1016        searchFormSubmitBtnText: "Search",
1017
1018        // Advanced Search Form
1019        advancedFormObj: null,
1020        advancedSearchCond: 'OR', // 'AND'
1021
1022        // Result Block
1023        loadingImgPath: "/flexibleSearch/loading.gif",
1024        loadingImgHTML: null,
1025
1026        resultBlockId: "fs-result",
1027        resultItemTmpl: null,
1028
1029        resultMsgId: null,
1030        resultMsgClassName: "fs-result-msg",
1031        resultMsgTmpl: null,
1032
1033        resultMetaTitleRewrite: true,
1034        resultMetaTitleTmpl: null,
1035
1036        // You can set an array including plane object which has two properties,
1037        // method property and selector property.
1038        // e.g.
1039        //     resultMsgInsertMethods: [
1040        //         {
1041        //             "selector": "foo",
1042        //             "method": "append"
1043        //         }
1044        //     ],
1045        resultMsgInsertMethods: null,
1046
1047        // Paginate
1048        paginateId: null,
1049        paginateClassName: "fs-paginate",
1050        paginateTmpl: null,
1051        paginateCount: 10,
1052        hidePageNumber: false,
1053        showTurnPage: true,
1054        prevPageText: 'Prev',
1055        nextPageText: 'Next',
1056        maxPageCount: 10,
1057        // If you want to hide one page pagination, set true.
1058        hideOnePagePaginate: false,
1059        // You can set an array including plane object which has two properties,
1060        // method property and selector property.
1061        // e.g.
1062        //     paginateInsertMethods: [
1063        //         {
1064        //             "selector": "foo",
1065        //             "method": "append"
1066        //         }
1067        //     ],
1068        paginateInsertMethods: null,
1069
1070        // Submit
1071        submitAction: function (paramArray) {
1072            return paramArray;
1073        },
1074
1075        // Ajax
1076        ajaxError: function (jqXHR, textStatus, errorThrown) {
1077            window.alert(textStatus);
1078        },
1079
1080        // Callbacks
1081
1082        // you can search in your logic.
1083        // e.g.
1084        //     customSearch: function(item, paramObj){
1085        //         // item : Each item in items
1086        //         // paramObj : Plane object of parameters
1087        //         // The item is removed when return false
1088        //         return true or false;
1089        //     },
1090        customSearch: null,
1091
1092        // You can modify the search result JSON.
1093        // e.g.
1094        //     modifyResultJSON = function(json){
1095        //         json["fullName"] = function(){
1096        //             return this.firstName + " " + this.lastName;
1097        //         };
1098        //         return json;
1099        //     },
1100        modifyResultJSON: null,
1101        modifyResultMsgHTML: null,
1102        modifyResultItemHTML: null,
1103        modifyPaginateHTML: null,
1104        resultComplete: null,
1105
1106        excludeParams: null,
1107        dummy: false
1108    };
1109})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.