src/doc/api: add links to renderer code
[nit.git] / share / nitdoc / js / plugins / quicksearch.js
index a3b29c1..f8cd520 100644 (file)
 */
 
 /*
- * Nitdoc QuickSearch module
+ * Nitdoc QuickSearch widget
  */
-define([
-       "jquery",
-       "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",
-                               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)
-                       );
-
-                       // Close quicksearch list on click
-                       $(document).click(function(e) {
-                               QuickSearch.closeResultsTable();
-                       });
-               },
+$.widget("nitdoc.quicksearch", {
 
-               doKeyDownAction: function(key) {
-                       switch(key) {
-                               case 38: // Up
-                                       this.selectPrevResult();
-                                       return false;
-                               case 40: // Down
-                                       this.selectNextResult();
-                                       return false;
-                               default:
-                                       return true;
-                        }
+       options: {
+               list: {}, // List of raw results generated by nitdoc tool
+               fieldAttrs: {
+                       autocomplete: "off",
                },
-
-               doKeyUpAction: function(key) {
-                       switch(key) {
-                               case 38: // Up
-                               case 40: // Down
+               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
+       },
+
+       _create: function() {
+               // set widget options
+               this.element.attr(this.options.fieldAttrs);
+               // event dispatch
+               this._on(this.element, {
+                       "keydown": this._doKeyDown,
+                       "keyup": this._doKeyUp,
+                       "input": this._doInput
+               });
+               // add result table element once
+               this._table = $("<table/>")
+                       .attr("id", this.options.tableID)
+                       .css(this.options.tableCSS)
+                       .css("min-width", this.element.outerWidth());
+               $("body").append(this._table);
+               // make table disappear when a click occurs outside
+               $(document).click($.proxy(this.closeTable, this));
+       },
+
+       /* events */
+
+       _doKeyDown: function(event) {
+               switch(event.keyCode) {
+                       case 38: // Up
+                               this._selectPrev();
+                               return false;
+                       case 40: // Down
+                               this._selectNext();
+                               return false;
+                       default:
+                               return true;
+                }
+       },
+
+       _doKeyUp: function(event) {
+               switch(event.keyCode) {
+                       case 38: // Up
+                       case 40: // Down
                                break;
+                       case 13: // Enter
+                               this._loadResult();
+                               return false;
+                       case 27: // Escape
+                               this.element.blur();
+                               this.closeTable();
+                               return true;
+                       default: // Other keys
+                               return true;
+               }
+       },
 
-                               case 13: // Enter
-                                       this.goToResult();
-                                       return false;
-                               break;
+       _doInput: function(event) {
+               Utils.delayEvent($.proxy(this.search, this));
+       },
 
-                               case 27: // Escape
-                                       $(this).blur();
-                                       this.closeResultsTable();
-                               break;
+       /* Result lookup */
 
-                               default: // Other keys
-                                       var query = this.searchField.val();
-                                       if(!query) {
-                                               return false;
-                                       }
-                                       var results = QuickSearch.getResults(query);
-                                       this.displayResultsTable(query, results);
-                               break;
+       _getResults: function(query) {
+               var results = {};
+               results.matches = [];
+               for(var entry in this.options.list) {
+                       if(!entry.startsWith(query, true)) {
+                               continue;
                        }
-               },
-
-               // Get results corresponding to search query
-               getResults: function(query) {
-                       var results = {};
-                       results.matches = new Array();
-                       for(var entry in this.rawList) {
-                               if(!entry.startsWith(query, true)) {
-                                       continue;
-                               }
-                               var cat = new Object();
-                               cat.name = entry;
-                               cat.entries = this.rawList[entry];
-                               results.matches[results.matches.length] = cat;
-
-                               if(entry == query) {
-                                       cat.rank = 3;
-                               } else if(entry.toUpperCase() == query.toUpperCase()) {
-                                       cat.rank = 2;
-                               } else {
-                                       cat.rank = 1 + query.dice(entry);
-                               }
+                       var cat = {
+                               name: entry,
+                               entries: this.options.list[entry]
+                       };
+                       results.matches[results.matches.length] = cat;
+
+                       if(entry == query) {
+                               cat.rank = 3;
+                       } else if(entry.toUpperCase() == query.toUpperCase()) {
+                               cat.rank = 2;
+                       } else {
+                               cat.rank = 1 + query.dice(entry);
                        }
-                       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];
-                                       cat.rank = query.dice(entry);
-                                       if(cat.rank > 0) {
-                                               results.partials[results.partials.length] = cat;
-                                       }
+               }
+               results.matches.sort(this._rankSorter);
+               results.partials = new Array();
+               if(results.matches.length == 0) {
+                       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);
                        }
-                       return results;
-               },
+                       results.partials.sort(this._rankSorter);
+               }
+               return results;
+       },
+
+       _rankSorter: function(a, b){
+               if(a.rank < b.rank) {
+                       return 1;
+               } else if(a.rank > b.rank) {
+                       return -1;
+               }
+               return 0;
+       },
 
-               // Sort an array of results by rank
-               rankSorter: function(a, b){
-                       if(a.rank < b.rank) {
-                               return 1;
-                       } else if(a.rank > b.rank) {
-                               return -1;
-                       }
-                       return 0;
-               },
+       /* Results table */
 
-               // 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) {
-                               resultSet = results.partials
-                       } else {
-                               resultSet = results.matches
-                       }
-                       for(var i in resultSet) {
-                               var cat = resultSet[i];
-                               var result = cat.entries[0];
+       search: function() {
+               var query = this.element.val();
+               if(query) {
+                       var results = this._getResults(query);
+                       this.openTable(query, results);
+               }
+       },
 
-                               this.addResultRow(count, cat.name, result.txt, result.url, "nitdoc-qs-cat")
-                               if(count >= maxSize) {
-                                       this.currentTable.find("tbody").children().last().hide();
-                               }
-                               count++;
-
-                               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++;
-                               }
-                       }
-                       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(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:")
-                                       )
-                               );
-                       }
+       openTable: function(query, results) {
+               this._table.empty();
+               this._rows = [];
+               this._index = -1;
 
-                       // Initialize table
-                       $("body").append(this.currentTable);
-                       this.resizeResultsTable();
-                       if(this.currentTable.find("tr").length > 0) {
-                               this.setIndex(0);
+               var resultSet = results.matches;
+               if(resultSet.length == 0) {
+                       resultSet = results.partials
+               }
+
+               for(var i in resultSet) {
+                       var cat = resultSet[i];
+                       var result = cat.entries[0];
+                       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.addRow(cat.name, result.txt, result.url, this.options.rowSubClass)
                        }
-               },
+               }
 
-               // 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")
-                               .data("searchDetails", {name: name, url: url})
-                               .data("index", index)
-                               .append(
-                                       $(document.createElement("td")).html(name)
+               if(this._rows.length >= this.options.maxSize) {
+                       this.addOverflowUp();
+                       this.addOverflowDown();
+               }
+               if(results.matches.length == 0) {
+                       this.addNoResultRow();
+               }
+
+               if(resultSet.length > 0) {
+                       this._setIndex(0);
+               }
+               this._table.show();
+               this._autosizeTable();
+       },
+
+       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", this._rows.length)
+                       .append(
+                               $("<td/>")
+                                       .html(name)
                                        .addClass(cls)
+                       )
+                       .append(
+                               $("<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(
-                                       $(document.createElement("td"))
-                                               .addClass("nitdoc-qs-info")
-                                               .html(txt + "&nbsp;&raquo;")
+                                       $("<td/>")
+                                               .attr("colspan", 2)
+                                               .html(this.options.overflowDownHtml)
                                )
-                               .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()
-                       });
-               },
-
-               // 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);
-               },
-
-               hasPrev: function(index) {
-                       return index - 1 >= 0;
-               },
-
-               hasNext: function(index) {
-                       return index + 1 < this.currentTable.find("tr.nitdoc-qs-row").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");
-                                       }
-                                       this.resizeResultsTable();
+                               .click($.proxy(this._clickNext, this))
+               );
+       },
+
+       addNoResultRow: function() {
+               this._table.prepend(
+                       $("<tr/>")
+                       .addClass(this.options.rowNoResultClass)
+                       .append(
+                               $("<td/>")
+                               .attr("colspan", "2")
+                               .text(this.options.noresultText)
+                       )
+               );
+       },
+
+       _autosizeTable: function() {
+               this._table.position({
+                       my: "right top",
+                       at: "right bottom",
+                       of: this.element
+               });
+       },
+
+       _hasIndex: function(index) {
+               return index >= 0 && index < this._rows.length;
+       },
+
+       _hasPrev: function(index) {
+               return index - 1 >= 0;
+       },
+
+       _hasNext: function(index) {
+               return index + 1 < this._rows.length;
+       },
+
+       _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._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");
-                                       }
-                                       this.resizeResultsTable();
+               }
+       },
+
+       _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._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;
-                               return;
-                       }
+       // Load selected search result page
+       _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 = this.options.gotoPage + "#q=" + this.element.val();
+               if(window.location.href.indexOf(this.options.gotoPage) > -1) {
+                       location.reload();
+               }
+       },
 
-                       window.location = "search.html#q=" + this.searchField.val();
-                       if(window.location.href.indexOf("search.html") > -1) {
-                               location.reload();
-                       }
-               },
+       /* table events */
 
-               // Close the results table
-               closeResultsTable: function(target) {
-                       if(target != this.searchField && target != this.currentTable) {
-                               if(this.currentTable != null) {
-                                       this.currentTable.remove();
-                                       this.currentTable = null;
-                               }
-                       }
-               }
-       };
+       _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"));
+       }
+});
 
-       QuickSearch.enableQuickSearch("nav.main ul");
+var searchField = $("<input/>")
+.addClass("form-control input-sm")
+.attr({
+       id: "nitdoc-qs-field",
+       type: "text",
+       placeholder: "Search..."
+})
+
+$("#topmenu-collapse").append(
+       $("<div>")
+       .addClass("navbar-form navbar-right")
+       .append(
+               $("<div>")
+               .addClass("form-group")
+               .append(searchField)
+       )
+);
+
+searchField.quicksearch({
+       list: this.nitdocQuickSearchRawList
 });