ni_nitdoc: quicksearch is now case sensitive
[nit.git] / share / ni_nitdoc / scripts / js-facilities.js
1 /*
2 * JQuery Case Insensitive :icontains selector
3 */
4 $.expr[':'].icontains = function(obj, index, meta, stack){
5 return (obj.textContent.replace(/\[[0-9]+\]/g, "") || obj.innerText.replace(/\[[0-9]+\]/g, "") || jQuery(obj).text().replace(/\[[0-9]+\]/g, "") || '').toLowerCase().indexOf(meta[3].toLowerCase()) >= 0;
6 };
7 /*
8 * Quick Search global vars
9 */
10
11 // Current search results preview table
12 var currentTable = null;
13
14 //Hightlighted index in search result preview table
15 var currentIndex = -1;
16
17 /*
18 * Add folding and filtering facilities to class description page.
19 */
20 $(document).ready(function() {
21
22 /*
23 * Nav block folding
24 */
25
26 // Menu nav folding
27 $(".menu nav h3")
28 .prepend(
29 $(document.createElement("a"))
30 .html("-")
31 .addClass("fold")
32 )
33 .css("cursor", "pointer")
34 .click( function() {
35 if($(this).find("a.fold").html() == "+") {
36 $(this).find("a.fold").html("-");
37 } else {
38 $(this).find("a.fold").html("+");
39 }
40 $(this).nextAll().toggle();
41 })
42
43 // Insert search field
44 $("nav.main ul")
45 .append(
46 $(document.createElement("li"))
47 .append(
48 $(document.createElement("form"))
49 .append(
50 $(document.createElement("input"))
51 .attr({
52 id: "search",
53 type: "text",
54 autocomplete: "off",
55 value: "quick search..."
56 })
57 .addClass("notUsed")
58
59 // Key management
60 .keyup(function(e) {
61 switch(e.keyCode) {
62 // Select previous result on "Up"
63 case 38:
64 // If already on first result, focus search input
65 if(currentIndex == 0) {
66 $("#search").val($(currentTable.find("tr")[currentIndex]).data("searchDetails").name);
67 $("#search").focus();
68 // Else select previous result
69 } else if(currentIndex > 0) {
70 $(currentTable.find("tr")[currentIndex]).removeClass("activeSearchResult");
71 currentIndex--;
72 $(currentTable.find("tr")[currentIndex]).addClass("activeSearchResult");
73 $("#search").val($(currentTable.find("tr")[currentIndex]).data("searchDetails").name);
74 $("#search").focus();
75 }
76 break;
77
78 // Select next result on "Down"
79 case 40:
80 if(currentIndex < currentTable.find("tr").length - 1) {
81 if($(currentTable.find("tr")[currentIndex + 1]).hasClass("overflow")) {
82 break;
83 }
84 $(currentTable.find("tr")[currentIndex]).removeClass("activeSearchResult");
85 currentIndex++;
86 $(currentTable.find("tr")[currentIndex]).addClass("activeSearchResult");
87 $("#search").val($(currentTable.find("tr")[currentIndex]).data("searchDetails").name);
88 $("#search").focus();
89 }
90 break;
91 // Go to url on "Enter"
92 case 13:
93 if(currentIndex > -1) {
94 window.location = $(currentTable.find("tr")[currentIndex]).data("searchDetails").url;
95 return false;
96 }
97 if($("#search").val().length == 0)
98 return false
99
100 window.location = "search.html#q=" + $("#search").val();
101 if(window.location.href.indexOf("search.html") > -1) {
102 location.reload();
103 }
104 return false;
105 break;
106
107 // Hide results preview on "Escape"
108 case 27:
109 $(this).blur();
110 if(currentTable != null) {
111 currentTable.remove();
112 currentTable = null;
113 }
114 break;
115
116 default:
117 if($("#search").val().length == 0) {
118 return false;
119 }
120
121 // Remove previous table
122 if(currentTable != null) {
123 currentTable.remove();
124 }
125
126 // Build results table
127 currentIndex = -1;
128 currentTable = $(document.createElement("table"));
129
130 // Escape regexp related characters in query
131 var origQuery = $("#search").val();
132 var query = origQuery;
133 query = query.replace(/\\/gi, "\\\\");
134 query = query.replace(/\[/gi, "\\[");
135 query = query.replace(/\|/gi, "\\|");
136 query = query.replace(/\*/gi, "\\*");
137 query = query.replace(/\+/gi, "\\+");
138 query = query.replace(/\?/gi, "\\?");
139 query = query.replace(/\(/gi, "\\(");
140 query = query.replace(/\)/gi, "\\)");
141 query = query.replace(/&/gi, "&&");
142
143 var index = 0;
144 var regexp = new RegExp("^" + query);
145 var overflow = 0;
146 for(var entry in entries) {
147 var result = entry.match(regexp);
148 if(result != null) {
149 for(var i = 0; i < entries[entry].length; i++) {
150 if(index > 10) {
151 overflow++;
152 break;
153 }
154 currentTable.append(
155 $(document.createElement("tr"))
156 .data("searchDetails", {name: entry, url: entries[entry][i]["url"]})
157 .data("index", index)
158 .append($(document.createElement("td")).html(entry))
159 .append(
160 $(document.createElement("td"))
161 .addClass("entryInfo")
162 .html(entries[entry][i]["txt"] + "&nbsp;&raquo;"))
163 .mouseover( function() {
164 $(currentTable.find("tr")[currentIndex]).removeClass("activeSearchResult");
165 $(this).addClass("activeSearchResult");
166 currentIndex = $(this).data("index");
167 })
168 .mouseout( function() {
169 $(this).removeClass("activeSearchResult");
170 })
171 .click( function() {
172 window.location = $(this).data("searchDetails")["url"];
173 })
174 );
175 index++;
176 }
177 }
178 }
179 if(overflow > 0) {
180 currentTable.append(
181 $("<tr class='overflow'>")
182 .append(
183 $("<td colspan='2'>")
184 .append(
185 $("<a href='#' title='Show all results' data-query='"+ origQuery +"'>" + overflow + " more results for '" + origQuery + "'</a>")
186 .click(function() {
187 window.location = "search.html#q=" + $(this).attr("data-query");
188 if(window.location.href.indexOf("search.html") > -1) {
189 location.reload();
190 }
191 })
192 )
193 )
194 );
195 }
196
197 // Initialize table properties
198 currentTable.attr("id", "searchTable");
199 currentTable.css("position", "absolute");
200 currentTable.width($("#search").outerWidth());
201 $("header").append(currentTable);
202 currentTable.offset({left: $("#search").offset().left + ($("#search").outerWidth() - currentTable.outerWidth()), top: $("#search").offset().top + $("#search").outerHeight()});
203
204 // Preselect first entry
205 if(currentTable.find("tr").length > 0) {
206 currentIndex = 0;
207 $(currentTable.find("tr")[currentIndex]).addClass("activeSearchResult");
208 $("#search").focus();
209 }
210 break;
211 }
212 })
213 .focusout(function() {
214 if($(this).val() == "") {
215 $(this).addClass("notUsed");
216 $(this).val("quick search...");
217 }
218 })
219 .focusin(function() {
220 if($(this).val() == "quick search...") {
221 $(this).removeClass("notUsed");
222 $(this).val("");
223 }
224 })
225 )
226 .submit( function() {
227 return false;
228 })
229 )
230 );
231
232 // Close quicksearch list on click
233 $(document).click(function(e) {
234 if(e.target != $("#search")[0] && e.target != $("#searchTable")[0]) {
235 if(currentTable != null) {
236 currentTable.remove();
237 currentTable = null;
238 }
239 }
240 });
241
242 // Insert filter field
243 $("article.filterable h2, nav.filterable h3")
244 .after(
245 $(document.createElement("div"))
246 .addClass("filter")
247 .append(
248 $(document.createElement("input"))
249 .attr({
250 type: "text",
251 value: "filter..."
252 })
253 .addClass("notUsed")
254 .keyup(function() {
255 $(this).parent().parent().find("ul li:not(:icontains('" + $(this).val() + "'))").addClass("hide");
256 $(this).parent().parent().find("ul li:icontains('" + $(this).val() + "')").removeClass("hide");
257 })
258 .focusout(function() {
259 if($(this).val() == "") {
260 $(this).addClass("notUsed");
261 $(this).val("filter...");
262 }
263 })
264 .focusin(function() {
265 if($(this).val() == "filter...") {
266 $(this).removeClass("notUsed");
267 $(this).val("");
268 }
269 })
270 )
271 );
272
273 // Filter toggle between H I R in nav porperties list
274 $("nav.properties.filterable .filter")
275 .append(
276 $(document.createElement("a"))
277 .html("H")
278 .attr({
279 title: "hide inherited properties"
280 })
281 .click( function() {
282 if($(this).hasClass("hidden")) {
283 $(this).parent().parent().find("li.inherit").show();
284 } else {
285 $(this).parent().parent().find("li.inherit").hide();
286 }
287
288 $(this).toggleClass("hidden");
289 })
290 )
291 .append(
292 $(document.createElement("a"))
293 .html("R")
294 .attr({
295 title: "hide redefined properties"
296 })
297 .click( function() {
298 if($(this).hasClass("hidden")) {
299 $(this).parent().parent().find("li.redef").show();
300 } else {
301 $(this).parent().parent().find("li.redef").hide();
302 }
303
304 $(this).toggleClass("hidden");
305 })
306 )
307 .append(
308 $(document.createElement("a"))
309 .html("I")
310 .attr({
311 title: "hide introduced properties"
312 })
313 .click( function() {
314 if($(this).hasClass("hidden")) {
315 $(this).parent().parent().find("li.intro").show();
316 } else {
317 $(this).parent().parent().find("li.intro").hide();
318 }
319
320 $(this).toggleClass("hidden");
321 })
322 );
323
324 // Filter toggle between I R in
325 $("article.properties.filterable .filter, article.classes.filterable .filter")
326 .append(
327 $(document.createElement("a"))
328 .html("I")
329 .attr({
330 title: "hide introduced properties"
331 })
332 .click( function() {
333 if($(this).hasClass("hidden")) {
334 $(this).parent().parent().find("li.intro").show();
335 } else {
336 $(this).parent().parent().find("li.intro").hide();
337 }
338
339 $(this).toggleClass("hidden");
340 })
341 )
342 .append(
343 $(document.createElement("a"))
344 .html("R")
345 .attr({
346 title: "hide redefined properties"
347 })
348 .click( function() {
349 if($(this).hasClass("hidden")) {
350 $(this).parent().parent().find("li.redef").show();
351 } else {
352 $(this).parent().parent().find("li.redef").hide();
353 }
354
355 $(this).toggleClass("hidden");
356 })
357 );
358
359 //Preload filter fields with query string
360 preloadFilters();
361
362 //Copy to clipboard utility on signatures
363 $(".signature").each(function(){
364 $(this).append(
365 $("<button class='copyButton' data-clipboard-text='" + $(this).attr("data-untyped-signature") + "'></button>")
366 .append($("<img src='./resources/icons/copy.png' />"))
367 )
368 });
369
370 var clip = new ZeroClipboard($(".copyButton"), {
371 moviePath: "./ZeroClipboard.swf"
372 } );
373 });
374
375 /* Parse current URL and return anchor name */
376 function currentAnchor() {
377 var index = document.location.hash.indexOf("#");
378 if (index >= 0) {
379 return document.location.hash.substring(index + 1);
380 }
381 return null;
382 }
383
384 /* Prealod filters field using search query */
385 function preloadFilters() {
386 // Parse URL and get query string
387 var search = currentAnchor();
388
389 if(search == null || search.indexOf("q=") == -1)
390 return;
391
392 search = search.substring(2, search.length);
393
394 if(search == "" || search == "undefined")
395 return;
396
397 $(":text").val(search);
398 $(".filter :text")
399 .removeClass("notUsed")
400 .trigger("keyup");
401
402 }
403