a3b29c17bb2f723e32c87117188ce349f2222752
[nit.git] / share / nitdoc / js / plugins / quicksearch.js
1 /* This file is part of NIT ( http://www.nitlanguage.org ).
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14
15 Documentation generator for the nit language.
16 Generate API documentation in HTML format from nit source code.
17 */
18
19 /*
20 * Nitdoc QuickSearch module
21 */
22 define([
23 "jquery",
24 "plugins/utils",
25 "quicksearchList",
26 ], function($, utils) {
27
28 var QuickSearch = {
29 rawList: this.nitdocQuickSearchRawList, // List of raw resulsts generated by nitdoc tool
30 searchField: null, // <input:text> search field
31 currentTable: null, // current search results <table>
32 currentIndex: -1, // current cursor position into search results table
33
34 // Enable QuickSearch plugin
35 enableQuickSearch: function(containerSelector) {
36 this.searchField = $(document.createElement("input"))
37 .attr({
38 id: "nitdoc-qs-field",
39 type: "text",
40 autocomplete: "off",
41 value: "quick search..."
42 })
43 .addClass("nitdoc-qs-field-notused")
44 .keydown(function(event) {
45 return QuickSearch.doKeyDownAction(event.keyCode);
46 })
47 .keyup(function(event) {
48 QuickSearch.doKeyUpAction(event.keyCode);
49 })
50 .focusout(function() {
51 if($(this).val() == "") {
52 $(this).addClass("nitdoc-qs-field-notused");
53 $(this).val("quick search...");
54 }
55 })
56 .focusin(function() {
57 if($(this).val() == "quick search...") {
58 $(this).removeClass("nitdoc-qs-field-notused");
59 $(this).val("");
60 }
61 });
62
63 $(containerSelector).append(
64 $(document.createElement("li"))
65 .attr("id", "nitdoc-qs-li")
66 .append(this.searchField)
67 );
68
69 // Close quicksearch list on click
70 $(document).click(function(e) {
71 QuickSearch.closeResultsTable();
72 });
73 },
74
75 doKeyDownAction: function(key) {
76 switch(key) {
77 case 38: // Up
78 this.selectPrevResult();
79 return false;
80 case 40: // Down
81 this.selectNextResult();
82 return false;
83 default:
84 return true;
85 }
86 },
87
88 doKeyUpAction: function(key) {
89 switch(key) {
90 case 38: // Up
91 case 40: // Down
92 break;
93
94 case 13: // Enter
95 this.goToResult();
96 return false;
97 break;
98
99 case 27: // Escape
100 $(this).blur();
101 this.closeResultsTable();
102 break;
103
104 default: // Other keys
105 var query = this.searchField.val();
106 if(!query) {
107 return false;
108 }
109 var results = QuickSearch.getResults(query);
110 this.displayResultsTable(query, results);
111 break;
112 }
113 },
114
115 // Get results corresponding to search query
116 getResults: function(query) {
117 var results = {};
118 results.matches = new Array();
119 for(var entry in this.rawList) {
120 if(!entry.startsWith(query, true)) {
121 continue;
122 }
123 var cat = new Object();
124 cat.name = entry;
125 cat.entries = this.rawList[entry];
126 results.matches[results.matches.length] = cat;
127
128 if(entry == query) {
129 cat.rank = 3;
130 } else if(entry.toUpperCase() == query.toUpperCase()) {
131 cat.rank = 2;
132 } else {
133 cat.rank = 1 + query.dice(entry);
134 }
135 }
136 results.matches.sort(this.rankSorter);
137 results.partials = new Array();
138 if(results.matches.length == 0) {
139 for(var entry in this.rawList) {
140 var cat = new Object();
141 cat.name = entry;
142 cat.entries = this.rawList[entry];
143 cat.rank = query.dice(entry);
144 if(cat.rank > 0) {
145 results.partials[results.partials.length] = cat;
146 }
147 }
148 results.partials.sort(this.rankSorter);
149 }
150 return results;
151 },
152
153 // Sort an array of results by rank
154 rankSorter: function(a, b){
155 if(a.rank < b.rank) {
156 return 1;
157 } else if(a.rank > b.rank) {
158 return -1;
159 }
160 return 0;
161 },
162
163 // Display results in a popup table
164 displayResultsTable: function(query, results) {
165 // Clear results table
166 if(this.currentTable) this.currentTable.remove();
167
168 // Build results table
169 this.currentIndex = -1;
170 this.currentTable = $(document.createElement("table"));
171 this.currentTable.attr("id", "nitdoc-qs-table");
172 this.currentTable.css("position", "absolute");
173 this.currentTable.width(this.searchField.outerWidth());
174
175 var maxSize = 10;
176 var count = 0;
177 var resultSet;
178 if(results.matches.length == 0) {
179 resultSet = results.partials
180 } else {
181 resultSet = results.matches
182 }
183 for(var i in resultSet) {
184 var cat = resultSet[i];
185 var result = cat.entries[0];
186
187 this.addResultRow(count, cat.name, result.txt, result.url, "nitdoc-qs-cat")
188 if(count >= maxSize) {
189 this.currentTable.find("tbody").children().last().hide();
190 }
191 count++;
192
193 for(var j = 1; j < cat.entries.length; j++) {
194 var result = cat.entries[j];
195 this.addResultRow(count, cat.name, result.txt, result.url, "nitdoc-qs-sub")
196 if(count >= maxSize) {
197 this.currentTable.find("tr.nitdoc-qs-row").last().hide();
198 }
199 count++;
200 }
201 }
202 if(count >= maxSize) {
203 this.currentTable.prepend(
204 $(document.createElement("tr"))
205 .addClass("nitdoc-qs-overflow-up")
206 .addClass("nitdoc-qs-overflow-inactive")
207 .append(
208 $(document.createElement("td"))
209 .attr("colspan", 2)
210 .html("&#x25B2;")
211 )
212 .click( function(e) {
213 e.stopPropagation();
214 QuickSearch.selectPrevResult();
215 })
216 );
217 this.currentTable.append(
218 $(document.createElement("tr"))
219 .addClass("nitdoc-qs-overflow-down")
220 .addClass(count >= maxSize ? "nitdoc-qs-overflow-active" : "nitdoc-qs-overflow-inactive")
221 .append(
222 $(document.createElement("td"))
223 .attr("colspan", 2)
224 .html("&#x25BC;")
225 )
226 .click( function(e) {
227 e.stopPropagation();
228 console.log("nest");
229 QuickSearch.selectNextResult();
230 })
231 );
232 }
233 if(results.matches.length == 0) {
234 this.currentTable.prepend(
235 $("<tr class='nitdoc-qs-noresult'>")
236 .append(
237 $("<td colspan='2'>")
238 .html("Sorry, there is no match, best results are:")
239 )
240 );
241 }
242
243 // Initialize table
244 $("body").append(this.currentTable);
245 this.resizeResultsTable();
246 if(this.currentTable.find("tr").length > 0) {
247 this.setIndex(0);
248 }
249 },
250
251 // adds a result row to the current result table
252 addResultRow: function(index, name, txt, url, cls) {
253 this.currentTable.append(
254 $(document.createElement("tr"))
255 .addClass("nitdoc-qs-row")
256 .data("searchDetails", {name: name, url: url})
257 .data("index", index)
258 .append(
259 $(document.createElement("td")).html(name)
260 .addClass(cls)
261 )
262 .append(
263 $(document.createElement("td"))
264 .addClass("nitdoc-qs-info")
265 .html(txt + "&nbsp;&raquo;")
266 )
267 .mouseover( function() {
268 QuickSearch.setIndex($(this).data("index"));
269 })
270 .mouseout( function() {
271 $(this).removeClass("nitdoc-qs-active");
272 })
273 .click( function() {
274 window.location = $(this).data("searchDetails")["url"];
275 })
276 );
277 },
278
279 // adapts result table to content
280 resizeResultsTable: function() {
281 this.currentTable.offset({
282 left: this.searchField.offset().left + (this.searchField.outerWidth() - this.currentTable.outerWidth()),
283 top: this.searchField.offset().top + this.searchField.outerHeight()
284 });
285 },
286
287 // select row at index
288 setIndex: function(index) {
289 $(this.currentTable.find("tr.nitdoc-qs-row")[this.currentIndex]).removeClass("nitdoc-qs-active");
290 this.currentIndex = index;
291 var currentRow = $(this.currentTable.find("tr.nitdoc-qs-row")[this.currentIndex]);
292 currentRow.addClass("nitdoc-qs-active");
293 //searchField.val(currentRow.data("searchDetails").name);
294 },
295
296 hasPrev: function(index) {
297 return index - 1 >= 0;
298 },
299
300 hasNext: function(index) {
301 return index + 1 < this.currentTable.find("tr.nitdoc-qs-row").length;
302 },
303
304 selectPrevResult: function() {
305 if(this.hasPrev(this.currentIndex)) {
306 this.setIndex(this.currentIndex - 1);
307 if(!$(this.currentTable.find("tr.nitdoc-qs-row")[this.currentIndex]).is(":visible")) {
308 this.currentTable.find("tr.nitdoc-qs-row:visible").last().hide();
309 this.currentTable.find("tr.nitdoc-qs-overflow-down").addClass("nitdoc-qs-overflow-active");
310 $(this.currentTable.find("tr.nitdoc-qs-row")[this.currentIndex]).show();
311 if(!this.hasPrev(this.currentIndex)) {
312 this.currentTable.find("tr.nitdoc-qs-overflow-up").removeClass("nitdoc-qs-overflow-active");
313 }
314 this.resizeResultsTable();
315 }
316 }
317 },
318
319 selectNextResult: function() {
320 if(this.hasNext(this.currentIndex)) {
321 this.setIndex(this.currentIndex + 1);
322 if(!$(this.currentTable.find("tr.nitdoc-qs-row")[this.currentIndex]).is(":visible")) {
323 this.currentTable.find("tr.nitdoc-qs-row:visible").first().hide();
324 this.currentTable.find("tr.nitdoc-qs-overflow-up").addClass("nitdoc-qs-overflow-active");
325 $(this.currentTable.find("tr.nitdoc-qs-row")[this.currentIndex]).show();
326 if(!this.hasNext(this.currentIndex)) {
327 this.currentTable.find("tr.nitdoc-qs-overflow-down").removeClass("nitdoc-qs-overflow-active");
328 }
329 this.resizeResultsTable();
330 }
331 }
332 },
333
334 // Load selected search result page
335 goToResult: function() {
336 if(this.currentIndex > -1) {
337 window.location = $(this.currentTable.find("tr.nitdoc-qs-row")[this.currentIndex]).data("searchDetails").url;
338 return;
339 }
340
341 if(this.searchField.val().length == 0) { return; }
342
343 window.location = "search.html#q=" + this.searchField.val();
344 if(window.location.href.indexOf("search.html") > -1) {
345 location.reload();
346 }
347 },
348
349 // Close the results table
350 closeResultsTable: function(target) {
351 if(target != this.searchField && target != this.currentTable) {
352 if(this.currentTable != null) {
353 this.currentTable.remove();
354 this.currentTable = null;
355 }
356 }
357 }
358 };
359
360 QuickSearch.enableQuickSearch("nav.main ul");
361 });