nitdoc: migrate quicksearch to jQuery.UI widget
authorAlexandre Terrasa <alexandre@moz-code.org>
Thu, 13 Feb 2014 03:25:01 +0000 (22:25 -0500)
committerAlexandre Terrasa <alexandre@moz-code.org>
Thu, 20 Feb 2014 19:11:58 +0000 (14:11 -0500)
Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

share/nitdoc/js/plugins/quicksearch.js
share/nitdoc/styles/Nitdoc.QuickSearch.css

index a3b29c1..db7d693 100644 (file)
 */
 
 /*
- * Nitdoc QuickSearch module
+ * Nitdoc QuickSearch widget
  */
 define([
        "jquery",
+       "jQueryUI",
        "plugins/utils",
        "quicksearchList",
-], function($, utils) {
-
-       var QuickSearch = {
-               rawList: this.nitdocQuickSearchRawList, // List of raw resulsts generated by nitdoc tool
-               searchField: null, // <input:text> search field
-               currentTable: null, // current search results <table>
-               currentIndex: -1, // current cursor position into search results table
-
-               // Enable QuickSearch plugin
-               enableQuickSearch: function(containerSelector) {
-                       this.searchField = $(document.createElement("input"))
-                       .attr({
-                               id: "nitdoc-qs-field",
-                               type: "text",
+], function($, ui, utils) {
+       $.widget("nitdoc.quicksearch", {
+
+               options: {
+                       list: {}, // List of raw results generated by nitdoc tool
+                       fieldNotUsedClass: "nitdoc-qs-field-notused",
+                       fieldAttrs: {
                                autocomplete: "off",
                                value: "quick search..."
-                       })
-                       .addClass("nitdoc-qs-field-notused")
-                       .keydown(function(event) {
-                               return QuickSearch.doKeyDownAction(event.keyCode);
-                       })
-                       .keyup(function(event) {
-                               QuickSearch.doKeyUpAction(event.keyCode);
-                       })
-                       .focusout(function() {
-                               if($(this).val() == "") {
-                                       $(this).addClass("nitdoc-qs-field-notused");
-                                       $(this).val("quick search...");
-                               }
-                       })
-                       .focusin(function() {
-                               if($(this).val() == "quick search...") {
-                                       $(this).removeClass("nitdoc-qs-field-notused");
-                                       $(this).val("");
-                               }
-                       });
-
-                       $(containerSelector).append(
-                               $(document.createElement("li"))
-                               .attr("id", "nitdoc-qs-li")
-                               .append(this.searchField)
-                       );
+                       },
+                       tableID: "nitdoc-qs-table",
+                       tableCSS: {
+                               "position": "absolute"
+                       },
+                       rowClass: "nitdoc-qs-row",
+                       rowCatClass: "nitdoc-qs-cat",
+                       rowSubClass: "nitdoc-qs-sub",
+                       rowActiveClass: "nitdoc-qs-active",
+                       rowOverflowClass: "nitdoc-qs-overflow",
+                       rowOverflowActive: "nitdoc-qs-overflow-active",
+                       rowNoResultClass: "nitdoc-qs-noresult",
+                       overflowUpHtml: "&#x25B2;",
+                       overflowDownHtml: "&#x25BC;",
+                       noresultText: "Sorry, there is no match, best results are:",
+                       infoClass: "nitdoc-qs-info",
+                       gotoPage: "search.html",
+                       maxSize: 10
+               },
 
-                       // Close quicksearch list on click
-                       $(document).click(function(e) {
-                               QuickSearch.closeResultsTable();
-                       });
+               _create: function() {
+                       this.element
+                               .attr(this.options.fieldAttrs)
+                               .addClass(this.options.fieldNotUsedClass)
+                               .keydown($.proxy(this._doKeyDown, this))
+                               .keyup($.proxy(this._doKeyUp, this))
+                               .focusout($.proxy(this._doFocusOut, this))
+                               .focusin($.proxy(this._doFocusIn, this));
+
+                       this._table = $("<table/>")
+                               .attr("id", this.options.tableID)
+                               .css(this.options.tableCSS)
+                               .css("min-width", this.element.outerWidth());
+                       $("body").append(this._table);
+
+                       $(document).click($.proxy(this.closeTable, this));
                },
 
-               doKeyDownAction: function(key) {
-                       switch(key) {
+               /* events */
+
+               _doKeyDown: function(event) {
+                       switch(event.keyCode) {
                                case 38: // Up
-                                       this.selectPrevResult();
+                                       this._selectPrev();
                                        return false;
                                case 40: // Down
-                                       this.selectNextResult();
+                                       this._selectNext();
                                        return false;
                                default:
                                        return true;
                         }
                },
 
-               doKeyUpAction: function(key) {
-                       switch(key) {
+               _doKeyUp: function(event) {
+                       switch(event.keyCode) {
                                case 38: // Up
                                case 40: // Down
-                               break;
-
+                                       break;
                                case 13: // Enter
-                                       this.goToResult();
-                                       return false;
-                               break;
-
+                                       this._loadResult();
+                                       break;
                                case 27: // Escape
-                                       $(this).blur();
-                                       this.closeResultsTable();
-                               break;
-
+                                       this.element.blur();
+                                       this.closeTable();
+                                       break;
                                default: // Other keys
-                                       var query = this.searchField.val();
-                                       if(!query) {
-                                               return false;
-                                       }
-                                       var results = QuickSearch.getResults(query);
-                                       this.displayResultsTable(query, results);
-                               break;
+                                       this.search();
+                                       break;
                        }
                },
 
-               // Get results corresponding to search query
-               getResults: function(query) {
+               _doFocusOut: function() {
+                       if(this.element.val() == "") {
+                               this.element.addClass(this.options.fieldNotUsedClass);
+                               this.element.val(this.options.fieldAttrs.value);
+                       }
+               },
+
+               _doFocusIn: function() {
+                       if(this.element.val() == this.options.fieldAttrs.value) {
+                               this.element.removeClass(this.options.fieldNotUsedClass);
+                               this.element.val("");
+                       }
+               },
+
+               /* Result lookup */
+
+               _getResults: function(query) {
                        var results = {};
-                       results.matches = new Array();
-                       for(var entry in this.rawList) {
+                       results.matches = [];
+                       for(var entry in this.options.list) {
                                if(!entry.startsWith(query, true)) {
                                        continue;
                                }
-                               var cat = new Object();
-                               cat.name = entry;
-                               cat.entries = this.rawList[entry];
+                               var cat = {
+                                       name: entry,
+                                       entries: this.options.list[entry]
+                               };
                                results.matches[results.matches.length] = cat;
 
                                if(entry == query) {
@@ -133,25 +141,25 @@ define([
                                        cat.rank = 1 + query.dice(entry);
                                }
                        }
-                       results.matches.sort(this.rankSorter);
+                       results.matches.sort(this._rankSorter);
                        results.partials = new Array();
                        if(results.matches.length == 0) {
-                               for(var entry in this.rawList) {
-                                       var cat = new Object();
-                                       cat.name = entry;
-                                       cat.entries = this.rawList[entry];
+                               for(var entry in this.options.list) {
+                                       var cat = {
+                                               name: entry,
+                                               entries: this.options.list[entry]
+                                       }
                                        cat.rank = query.dice(entry);
                                        if(cat.rank > 0) {
                                                results.partials[results.partials.length] = cat;
                                        }
                                }
-                               results.partials.sort(this.rankSorter);
+                               results.partials.sort(this._rankSorter);
                        }
                        return results;
                },
 
-               // Sort an array of results by rank
-               rankSorter: function(a, b){
+               _rankSorter: function(a, b){
                        if(a.rank < b.rank) {
                                return 1;
                        } else if(a.rank > b.rank) {
@@ -160,202 +168,229 @@ define([
                        return 0;
                },
 
-               // Display results in a popup table
-               displayResultsTable: function(query, results) {
-                       // Clear results table
-                       if(this.currentTable) this.currentTable.remove();
-
-                       // Build results table
-                       this.currentIndex = -1;
-                       this.currentTable = $(document.createElement("table"));
-                       this.currentTable.attr("id", "nitdoc-qs-table");
-                       this.currentTable.css("position", "absolute");
-                       this.currentTable.width(this.searchField.outerWidth());
-
-                       var maxSize = 10;
-                       var count = 0;
-                       var resultSet;
-                       if(results.matches.length == 0) {
+               /* Results table */
+
+               search: function() {
+                       var query = this.element.val();
+                       if(query) {
+                               var results = this._getResults(query);
+                               this.openTable(query, results);
+                       }
+               },
+
+               openTable: function(query, results) {
+                       this._table.empty();
+                       this._rows = [];
+                       this._index = -1;
+
+                       var resultSet = results.matches;
+                       if(resultSet.length == 0) {
                                resultSet = results.partials
-                       } else {
-                               resultSet = results.matches
                        }
+
                        for(var i in resultSet) {
                                var cat = resultSet[i];
                                var result = cat.entries[0];
-
-                               this.addResultRow(count, cat.name, result.txt, result.url, "nitdoc-qs-cat")
-                               if(count >= maxSize) {
-                                       this.currentTable.find("tbody").children().last().hide();
-                               }
-                               count++;
-
+                               this.addRow(cat.name, result.txt, result.url, this.options.rowCatClass)
                                for(var j = 1; j < cat.entries.length; j++) {
                                        var result = cat.entries[j];
-                                       this.addResultRow(count, cat.name, result.txt, result.url, "nitdoc-qs-sub")
-                                       if(count >= maxSize) {
-                                               this.currentTable.find("tr.nitdoc-qs-row").last().hide();
-                                       }
-                                       count++;
+                                       this.addRow(cat.name, result.txt, result.url, this.options.rowSubClass)
                                }
                        }
-                       if(count >= maxSize) {
-                               this.currentTable.prepend(
-                                       $(document.createElement("tr"))
-                                       .addClass("nitdoc-qs-overflow-up")
-                                       .addClass("nitdoc-qs-overflow-inactive")
-                                       .append(
-                                               $(document.createElement("td"))
-                                               .attr("colspan", 2)
-                                               .html("&#x25B2;")
-                                       )
-                                       .click( function(e) {
-                                               e.stopPropagation();
-                                               QuickSearch.selectPrevResult();
-                                       })
-                               );
-                               this.currentTable.append(
-                                       $(document.createElement("tr"))
-                                       .addClass("nitdoc-qs-overflow-down")
-                                       .addClass(count >= maxSize ? "nitdoc-qs-overflow-active" : "nitdoc-qs-overflow-inactive")
-                                       .append(
-                                               $(document.createElement("td"))
-                                               .attr("colspan", 2)
-                                               .html("&#x25BC;")
-                                       )
-                                       .click( function(e) {
-                                               e.stopPropagation();
-                                               console.log("nest");
-                                               QuickSearch.selectNextResult();
-                                       })
-                               );
+
+                       if(this._rows.length >= this.options.maxSize) {
+                               this.addOverflowUp();
+                               this.addOverflowDown();
                        }
                        if(results.matches.length == 0) {
-                               this.currentTable.prepend(
-                                       $("<tr class='nitdoc-qs-noresult'>")
-                                       .append(
-                                               $("<td colspan='2'>")
-                                               .html("Sorry, there is no match, best results are:")
-                                       )
-                               );
+                               this.addNoResultRow();
                        }
 
-                       // Initialize table
-                       $("body").append(this.currentTable);
-                       this.resizeResultsTable();
-                       if(this.currentTable.find("tr").length > 0) {
-                               this.setIndex(0);
+                       if(resultSet.length > 0) {
+                               this._setIndex(0);
                        }
+                       this._table.show();
+                       this._autosizeTable();
                },
 
-               // adds a result row to the current result table
-               addResultRow: function(index, name, txt, url, cls) {
-                       this.currentTable.append(
-                               $(document.createElement("tr"))
-                               .addClass("nitdoc-qs-row")
+               closeTable: function(target) {
+                       if(target != this.element && target != this._table) {
+                               this._table.hide();
+                       }
+               },
+
+               addRow: function(name, txt, url, cls) {
+                       var row = $("<tr/>")
+                               .addClass(this.options.rowClass)
                                .data("searchDetails", {name: name, url: url})
-                               .data("index", index)
+                               .data("index", this._rows.length)
                                .append(
-                                       $(document.createElement("td")).html(name)
-                                       .addClass(cls)
+                                       $("<td/>")
+                                               .html(name)
+                                               .addClass(cls)
                                )
                                .append(
-                                       $(document.createElement("td"))
-                                               .addClass("nitdoc-qs-info")
+                                       $("<td/>")
                                                .html(txt + "&nbsp;&raquo;")
+                                               .addClass(this.options.infoClass)
+                               )
+                               .mouseover($.proxy(this._mouseOverRow, this))
+                               .click($.proxy(this._clickRow, this))
+                       this._rows.push(row);
+                       if(this._rows.length >= this.options.maxSize) {
+                               row.hide();
+                       }
+                       this._table.append(row);
+               },
+
+               addOverflowUp: function() {
+                       this._table.prepend(
+                               $("<tr/>")
+                                       .addClass(this.options.rowOverflowClass)
+                                       .append(
+                                               $("<td/>")
+                                                       .attr("colspan", 2)
+                                                       .html(this.options.overflowUpHtml)
+                                       )
+                                       .click($.proxy(this._clickPrev, this))
+                       );
+               },
+
+               addOverflowDown: function() {
+                       this._table.append(
+                               $("<tr/>")
+                                       .addClass(this.options.rowOverflowClass)
+                                       .addClass(this.options.rowOverflowActive)
+                                       .append(
+                                               $("<td/>")
+                                                       .attr("colspan", 2)
+                                                       .html(this.options.overflowDownHtml)
+                                       )
+                                       .click($.proxy(this._clickNext, this))
+                       );
+               },
+
+               addNoResultRow: function() {
+                       this._table.prepend(
+                               $("<tr/>")
+                               .addClass(this.options.rowNoResultClass)
+                               .append(
+                                       $("<td/>")
+                                       .attr("colspan", "2")
+                                       .text(this.options.noresultText)
                                )
-                               .mouseover( function() {
-                                       QuickSearch.setIndex($(this).data("index"));
-                               })
-                               .mouseout( function() {
-                                       $(this).removeClass("nitdoc-qs-active");
-                                })
-                               .click( function() {
-                                       window.location = $(this).data("searchDetails")["url"];
-                               })
                        );
                },
 
-               // adapts result table to content
-               resizeResultsTable: function() {
-                       this.currentTable.offset({
-                               left: this.searchField.offset().left + (this.searchField.outerWidth() - this.currentTable.outerWidth()),
-                               top: this.searchField.offset().top + this.searchField.outerHeight()
+               _autosizeTable: function() {
+                       this._table.position({
+                               my: "right top",
+                               at: "right bottom",
+                               of: this.element
                        });
                },
 
-               // select row at index
-               setIndex: function(index) {
-                       $(this.currentTable.find("tr.nitdoc-qs-row")[this.currentIndex]).removeClass("nitdoc-qs-active");
-                       this.currentIndex = index;
-                       var currentRow = $(this.currentTable.find("tr.nitdoc-qs-row")[this.currentIndex]);
-                       currentRow.addClass("nitdoc-qs-active");
-                       //searchField.val(currentRow.data("searchDetails").name);
+               _hasIndex: function(index) {
+                       return index >= 0 && index < this._rows.length;
                },
 
-               hasPrev: function(index) {
+               _hasPrev: function(index) {
                        return index - 1 >= 0;
                },
 
-               hasNext: function(index) {
-                       return index + 1 < this.currentTable.find("tr.nitdoc-qs-row").length;
+               _hasNext: function(index) {
+                       return index + 1 < this._rows.length;
                },
 
-               selectPrevResult: function() {
-                       if(this.hasPrev(this.currentIndex)) {
-                               this.setIndex(this.currentIndex - 1);
-                               if(!$(this.currentTable.find("tr.nitdoc-qs-row")[this.currentIndex]).is(":visible")) {
-                                       this.currentTable.find("tr.nitdoc-qs-row:visible").last().hide();
-                                       this.currentTable.find("tr.nitdoc-qs-overflow-down").addClass("nitdoc-qs-overflow-active");
-                                       $(this.currentTable.find("tr.nitdoc-qs-row")[this.currentIndex]).show();
-                                       if(!this.hasPrev(this.currentIndex)) {
-                                               this.currentTable.find("tr.nitdoc-qs-overflow-up").removeClass("nitdoc-qs-overflow-active");
+               _setIndex: function(index) {
+                       if(this._hasIndex(this._index)) {
+                               this._rows[this._index].removeClass(this.options.rowActiveClass);
+                       }
+                       this._index = index;
+                       if(this._hasIndex(this._index)) {
+                               this._rows[this._index].addClass(this.options.rowActiveClass);
+                       }
+               },
+
+               _selectPrev: function() {
+                       if(this._hasPrev(this._index)) {
+                               this._setIndex(this._index - 1);
+                               if(!this._rows[this._index].is(":visible")) {
+                                       this._table.find("tr." + this.options.rowClass + ":visible").last().hide();
+                                       this._table.find("tr." + this.options.rowOverflowClass).addClass(this.options.rowOverflowActive);
+                                       this._rows[this._index].show();
+                                       if(!this._hasPrev(this._index)) {
+                                               this._table.find("tr." + this.options.rowOverflowClass).removeClass(this.options.rowOverflowActive);
                                        }
-                                       this.resizeResultsTable();
+                                       this._autosizeTable();
                                }
                        }
                },
 
-               selectNextResult: function() {
-                       if(this.hasNext(this.currentIndex)) {
-                               this.setIndex(this.currentIndex + 1);
-                               if(!$(this.currentTable.find("tr.nitdoc-qs-row")[this.currentIndex]).is(":visible")) {
-                                       this.currentTable.find("tr.nitdoc-qs-row:visible").first().hide();
-                                       this.currentTable.find("tr.nitdoc-qs-overflow-up").addClass("nitdoc-qs-overflow-active");
-                                       $(this.currentTable.find("tr.nitdoc-qs-row")[this.currentIndex]).show();
-                                       if(!this.hasNext(this.currentIndex)) {
-                                               this.currentTable.find("tr.nitdoc-qs-overflow-down").removeClass("nitdoc-qs-overflow-active");
+               _selectNext: function() {
+                       if(this._hasNext(this._index)) {
+                               this._setIndex(this._index + 1);
+                               if(!this._rows[this._index].is(":visible")) {
+                                       this._table.find("tr." + this.options.rowClass + ":visible").first().hide();
+                                       this._table.find("tr." + this.options.rowOverflowClass).addClass(this.options.rowOverflowActive);
+                                       this._rows[this._index].show();
+                                       if(!this._hasNext(this._index)) {
+                                               this._table.find("tr." + this.options.rowOverflowClass).removeClass(this.options.rowOverflowActive);
                                        }
-                                       this.resizeResultsTable();
+                                       this._autosizeTable();
                                }
                        }
                },
 
                // Load selected search result page
-               goToResult: function() {
-                       if(this.currentIndex > -1) {
-                               window.location = $(this.currentTable.find("tr.nitdoc-qs-row")[this.currentIndex]).data("searchDetails").url;
+               _loadResult: function() {
+                       if(this._index > -1) {
+                               window.location = this._rows[this._index].data("searchDetails").url;
                                return;
                        }
+                       if(this.element.val().length == 0) { return; }
 
-                       if(this.searchField.val().length == 0) { return; }
-
-                       window.location = "search.html#q=" + this.searchField.val();
-                       if(window.location.href.indexOf("search.html") > -1) {
+                       window.location = this.options.gotoPage + "#q=" + this.element.val();
+                       if(window.location.href.indexOf(this.options.gotoPage) > -1) {
                                location.reload();
                        }
                },
 
-               // Close the results table
-               closeResultsTable: function(target) {
-                       if(target != this.searchField && target != this.currentTable) {
-                               if(this.currentTable != null) {
-                                       this.currentTable.remove();
-                                       this.currentTable = null;
-                               }
-                       }
-               }
-       };
+               /* table events */
 
-       QuickSearch.enableQuickSearch("nav.main ul");
+               _clickNext: function(event) {
+                       event.stopPropagation();
+                       this._selectNext();
+               },
+
+               _clickPrev: function(event) {
+                       event.stopPropagation();
+                       this._selectPrev();
+               },
+
+               _clickRow: function(event) {
+                       window.location = $(event.currentTarget).data("searchDetails")["url"];
+               },
+
+               _mouseOverRow: function(event) {
+                       this._setIndex($(event.currentTarget).data("index"));
+               }
+       });
+
+       var searchField = $("<input/>")
+       .addClass("nitdoc-qs-field-notused")
+       .attr({
+               id: "nitdoc-qs-field",
+               type: "text",
+       })
+
+       $("nav.main ul").append(
+               $("<li/>")
+                       .attr("id", "nitdoc-qs-li")
+                       .append(searchField)
+       );
+
+       searchField.quicksearch({
+               list: this.nitdocQuickSearchRawList
+       });
 });
index b0a434c..42b3925 100644 (file)
@@ -55,7 +55,6 @@
        overflow: hidden;\r
        line-height: 22px;\r
        padding: 2px;\r
-       width: 25%;\r
 }\r
 \r
 #nitdoc-qs-table td.nitdoc-qs-sub {\r
@@ -65,8 +64,7 @@
 \r
 #nitdoc-qs-table td.nitdoc-qs-info {\r
        color: #0D8921;\r
-       font-size: small;\r
-       width: 75%;\r
+       font-size: smaller;\r
        text-align: right;\r
 }\r
 \r
@@ -76,8 +74,7 @@
        line-height: 15px;\r
 }\r
 \r
-#nitdoc-qs-table tr.nitdoc-qs-overflow-up td,\r
-#nitdoc-qs-table tr.nitdoc-qs-overflow-down td {\r
+#nitdoc-qs-table tr.nitdoc-qs-overflow td {\r
        text-align: center;\r
        font-size:      x-small;\r
        line-height: 10px;\r