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