nitdoc: Adds a way to hide all edit tags
[nit.git] / share / nitdoc / scripts / js-facilities.js
1 // User
2 var userB64 = null;
3 var sessionStarted = false;
4
5 /*
6 * JQuery Case Insensitive :icontains selector
7 */
8 $.expr[':'].icontains = function(obj, index, meta, stack){
9 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;
10 };
11
12 /*
13 * Quick Search global vars
14 */
15
16 // Current search results preview table
17 var currentTable = null;
18
19 //Hightlighted index in search result preview table
20 var currentIndex = -1;
21
22
23 /*
24 * Add folding and filtering facilities to class description page.
25 */
26 $(document).ready(function() {
27
28 // Hide edit tags
29 $('textarea').hide();
30 $('a[id=commitBtn]').hide();
31 $('a[id=cancelBtn]').hide();
32 // Hide Authenfication form
33 $(".popover").hide();
34 // Update display
35 updateDisplaying();
36
37 /*
38 * Highlight the spoted element
39 */
40 highlightBlock(currentAnchor());
41
42 /*
43 * Nav block folding
44 */
45
46 // Menu nav folding
47 $(".menu nav h3")
48 .prepend(
49 $(document.createElement("a"))
50 .html("-")
51 .addClass("fold")
52 )
53 .css("cursor", "pointer")
54 .click( function() {
55 if($(this).find("a.fold").html() == "+") {
56 $(this).find("a.fold").html("-");
57 } else {
58 $(this).find("a.fold").html("+");
59 }
60 $(this).nextAll().toggle();
61 })
62
63 // Insert search field
64 $("nav.main ul")
65 .append(
66 $(document.createElement("li"))
67 .append(
68 $(document.createElement("form"))
69 .append(
70 $(document.createElement("input"))
71 .attr({
72 id: "search",
73 type: "text",
74 autocomplete: "off",
75 value: "quick search..."
76 })
77 .addClass("notUsed")
78
79 // Key management
80 .keyup(function(e) {
81 switch(e.keyCode) {
82
83 // Select previous result on "Up"
84 case 38:
85 // If already on first result, focus search input
86 if(currentIndex == 0) {
87 $("#search").val($(currentTable.find("tr")[currentIndex]).data("searchDetails").name);
88 $("#search").focus();
89 // Else select previous result
90 } else if(currentIndex > 0) {
91 $(currentTable.find("tr")[currentIndex]).removeClass("activeSearchResult");
92 currentIndex--;
93 $(currentTable.find("tr")[currentIndex]).addClass("activeSearchResult");
94 $("#search").val($(currentTable.find("tr")[currentIndex]).data("searchDetails").name);
95 $("#search").focus();
96 }
97 break;
98
99 // Select next result on "Down"
100 case 40:
101 if(currentIndex < currentTable.find("tr").length - 1) {
102 $(currentTable.find("tr")[currentIndex]).removeClass("activeSearchResult");
103 currentIndex++;
104 $(currentTable.find("tr")[currentIndex]).addClass("activeSearchResult");
105 $("#search").val($(currentTable.find("tr")[currentIndex]).data("searchDetails").name);
106 $("#search").focus();
107 }
108 break;
109 // Go to url on "Enter"
110 case 13:
111 if(currentIndex > -1) {
112 window.location = $(currentTable.find("tr")[currentIndex]).data("searchDetails").url;
113 return false;
114 }
115 if($("#search").val().length == 0)
116 return false
117
118 window.location = "full-index.html#q=" + $("#search").val();
119 if(window.location.href.indexOf("full-index.html") > -1) {
120 location.reload();
121 }
122 return false;
123 break;
124
125 // Hide results preview on "Escape"
126 case 27:
127 $(this).blur();
128 if(currentTable != null) {
129 currentTable.remove();
130 currentTable = null;
131 }
132 break;
133
134 default:
135 if($("#search").val().length == 0) {
136 return false;
137 }
138
139 // Remove previous table
140 if(currentTable != null) {
141 currentTable.remove();
142 }
143
144 // Build results table
145 currentIndex = -1;
146 currentTable = $(document.createElement("table"));
147
148 // Escape regexp related characters in query
149 var query = $("#search").val();
150 query = query.replace(/\[/gi, "\\[");
151 query = query.replace(/\|/gi, "\\|");
152 query = query.replace(/\*/gi, "\\*");
153 query = query.replace(/\+/gi, "\\+");
154 query = query.replace(/\\/gi, "\\\\");
155 query = query.replace(/\?/gi, "\\?");
156 query = query.replace(/\(/gi, "\\(");
157 query = query.replace(/\)/gi, "\\)");
158
159 var index = 0;
160 var regexp = new RegExp("^" + query, "i");
161 for(var entry in entries) {
162 if(index > 10) {
163 break;
164 }
165 var result = entry.match(regexp);
166 if(result != null && result.toString().toUpperCase() == $("#search").val().toUpperCase()) {
167 for(var i = 0; i < entries[entry].length; i++) {
168 if(index > 10) {
169 break;
170 }
171 currentTable.append(
172 $(document.createElement("tr"))
173 .data("searchDetails", {name: entry, url: entries[entry][i]["url"]})
174 .data("index", index)
175 .append($(document.createElement("td")).html(entry))
176 .append(
177 $(document.createElement("td"))
178 .addClass("entryInfo")
179 .html(entries[entry][i]["txt"] + "&nbsp;&raquo;"))
180 .mouseover( function() {
181 $(currentTable.find("tr")[currentIndex]).removeClass("activeSearchResult");
182 $(this).addClass("activeSearchResult");
183 currentIndex = $(this).data("index");
184 })
185 .mouseout( function() {
186 $(this).removeClass("activeSearchResult");
187 })
188 .click( function() {
189 window.location = $(this).data("searchDetails")["url"];
190 })
191 );
192 index++;
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 /*
360 * Anchors jumps
361 */
362 $("a[href*='#']").click( function() {
363 highlightBlock($(this).attr("href").split(/#/)[1]);
364 });
365
366 //Preload filter fields with query string
367 preloadFilters();
368 // Hide Authenfication form
369 $(".popover").hide();
370 // Display Login modal
371 $("#logGitHub").click(function(){ displayLogginModal(); });
372 // Update display
373 updateDisplaying();
374 // If cookie existing the session is opened
375 if(sessionStarted){ userB64 = "Basic " + getUserPass("logginNitdoc"); }
376
377 // Sign In an github user or Log out him
378 $("#signIn").click(function(){
379 if(!sessionStarted){
380 if($('#loginGit').val() == "" || $('#passwordGit').val() == ""){ displayMessage('The comment field is empty!', 40, 45); }
381 else
382 {
383 userName = $('#loginGit').val();
384 password = $('#passwordGit').val();
385 repoName = $('#repositoryGit').val();
386 branchName = $('#branchGit').val();
387 userB64 = "Basic " + base64.encode(userName+':'+password);
388 setCookie("logginNitdoc", base64.encode(userName+':'+password+':'+repoName+':'+branchName), 1);
389 $('#loginGit').val("");
390 $('#passwordGit').val("");
391 }
392 }
393 else
394 {
395 // Delete cookie and reset settings
396 del_cookie("logginNitdoc");
397 }
398 displayLogginModal();
399 });
400 });
401
402 /* Parse current URL and return anchor name */
403 function currentAnchor() {
404 var index = document.location.hash.indexOf("#");
405 if (index >= 0) {
406 return document.location.hash.substring(index + 1);
407 }
408 return null;
409 }
410
411 /* Prealod filters field using search query */
412 function preloadFilters() {
413 // Parse URL and get query string
414 var search = currentAnchor();
415
416 if(search == null || search.indexOf("q=") == -1)
417 return;
418
419 search = search.substring(2, search.length);
420
421 if(search == "" || search == "undefined")
422 return;
423
424 $(":text").val(search);
425 $(".filter :text")
426 .removeClass("notUsed")
427 .trigger("keyup");
428
429 }
430
431 /* Hightlight the spoted block */
432 function highlightBlock(a) {
433 if(a == undefined) {
434 return;
435 }
436
437 $(".highlighted").removeClass("highlighted");
438
439 var target = $("#" + a);
440
441 if(target.is("article")) {
442 target.parent().addClass("highlighted");
443 }
444
445 target.addClass("highlighted");
446 target.show();
447 }
448
449 function displayLogginModal(){
450 if ($('.popover').is(':hidden')) { $('.popover').show(); }
451 else { $('.popover').hide(); }
452 updateDisplaying();
453 }
454
455 function updateDisplaying(){
456 if (checkCookie())
457 {
458 $('#loginGit').hide();
459 $('#passwordGit').hide();
460 $('#lbpasswordGit').hide();
461 $('#lbloginGit').hide();
462 $('#repositoryGit').hide();
463 $('#lbrepositoryGit').hide();
464 $('#lbbranchGit').hide();
465 $('#branchGit').hide();
466 $("#liGitHub").attr("class", "current");
467 $("#imgGitHub").attr("src", "resources/icons/github-icon-w.png");
468 $('#nickName').text(userName);
469 $('#githubAccount').attr("href", "https://github.com/"+userName);
470 $('#logginMessage').css({'display' : 'block'});
471 $('#logginMessage').css({'text-align' : 'center'});
472 $('.popover').css({'height' : '80px'});
473 $('#signIn').text("Sign out");
474 sessionStarted = true;
475 }
476 else
477 {
478 sessionStarted = false;
479 $('#logginMessage').css({'display' : 'none'});
480 $("#liGitHub").attr("class", "");
481 $("#imgGitHub").attr("src", "resources/icons/github-icon.png");
482 $('#loginGit').val("");
483 $('#passwordGit').val("");
484 $('#nickName').text("");
485 $('.popover').css({'height' : '280px'});
486 $('#logginMessage').css({'display' : 'none'});
487 $('#repositoryGit').val($('#repoName').attr('name'));
488 $('#branchGit').val('wikidoc');
489 $('#signIn').text("Sign In");
490 $('#loginGit').show();
491 $('#passwordGit').show();
492 $('#lbpasswordGit').show();
493 $('#lbloginGit').show();
494 $('#repositoryGit').show();
495 $('#lbrepositoryGit').show();
496 $('#lbbranchGit').show();
497 $('#branchGit').show();
498 }
499 }
500
501 function setCookie(c_name, value, exdays)
502 {
503 var exdate=new Date();
504 exdate.setDate(exdate.getDate() + exdays);
505 var c_value=escape(value) + ((exdays==null) ? "" : "; expires="+exdate.toUTCString());
506 document.cookie=c_name + "=" + c_value;
507 }
508
509 function del_cookie(c_name)
510 {
511 document.cookie = c_name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
512 }
513
514 function getCookie(c_name)
515 {
516 var c_value = document.cookie;
517 var c_start = c_value.indexOf(" " + c_name + "=");
518 if (c_start == -1) { c_start = c_value.indexOf(c_name + "="); }
519 if (c_start == -1) { c_value = null; }
520 else
521 {
522 c_start = c_value.indexOf("=", c_start) + 1;
523 var c_end = c_value.indexOf(";", c_start);
524 if (c_end == -1) { c_end = c_value.length; }
525 c_value = unescape(c_value.substring(c_start,c_end));
526 }
527 return c_value;
528 }
529
530 function getUserPass(c_name){
531 var cookie = base64.decode(getCookie(c_name));
532 return base64.encode(cookie.split(':')[0] + ':' + cookie.split(':')[1]);
533 }
534
535 function checkCookie()
536 {
537 var cookie=getCookie("logginNitdoc");
538 if (cookie!=null && cookie!="")
539 {
540 cookie = base64.decode(cookie);
541 userName = cookie.split(':')[0];
542 repoName = cookie.split(':')[2];
543 branchName = cookie.split(':')[3];
544 return true;
545 }
546 else { return false; }
547 }
548
549
550 /*
551 * Base64
552 */
553 base64 = {};
554 base64.PADCHAR = '=';
555 base64.ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
556 base64.getbyte64 = function(s,i) {
557 // This is oddly fast, except on Chrome/V8.
558 // Minimal or no improvement in performance by using a
559 // object with properties mapping chars to value (eg. 'A': 0)
560 var idx = base64.ALPHA.indexOf(s.charAt(i));
561 if (idx == -1) {
562 throw "Cannot decode base64";
563 }
564 return idx;
565 }
566
567 base64.decode = function(s) {
568 // convert to string
569 s = "" + s;
570 var getbyte64 = base64.getbyte64;
571 var pads, i, b10;
572 var imax = s.length
573 if (imax == 0) {
574 return s;
575 }
576
577 if (imax % 4 != 0) {
578 throw "Cannot decode base64";
579 }
580
581 pads = 0
582 if (s.charAt(imax -1) == base64.PADCHAR) {
583 pads = 1;
584 if (s.charAt(imax -2) == base64.PADCHAR) {
585 pads = 2;
586 }
587 // either way, we want to ignore this last block
588 imax -= 4;
589 }
590
591 var x = [];
592 for (i = 0; i < imax; i += 4) {
593 b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) |
594 (getbyte64(s,i+2) << 6) | getbyte64(s,i+3);
595 x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff, b10 & 0xff));
596 }
597
598 switch (pads) {
599 case 1:
600 b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) | (getbyte64(s,i+2) << 6)
601 x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff));
602 break;
603 case 2:
604 b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12);
605 x.push(String.fromCharCode(b10 >> 16));
606 break;
607 }
608 return x.join('');
609 }
610
611 base64.getbyte = function(s,i) {
612 var x = s.charCodeAt(i);
613 if (x > 255) {
614 throw "INVALID_CHARACTER_ERR: DOM Exception 5";
615 }
616 return x;
617 }
618
619
620 base64.encode = function(s) {
621 if (arguments.length != 1) {
622 throw "SyntaxError: Not enough arguments";
623 }
624 var padchar = base64.PADCHAR;
625 var alpha = base64.ALPHA;
626 var getbyte = base64.getbyte;
627
628 var i, b10;
629 var x = [];
630
631 // convert to string
632 s = "" + s;
633
634 var imax = s.length - s.length % 3;
635
636 if (s.length == 0) {
637 return s;
638 }
639 for (i = 0; i < imax; i += 3) {
640 b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8) | getbyte(s,i+2);
641 x.push(alpha.charAt(b10 >> 18));
642 x.push(alpha.charAt((b10 >> 12) & 0x3F));
643 x.push(alpha.charAt((b10 >> 6) & 0x3f));
644 x.push(alpha.charAt(b10 & 0x3f));
645 }
646 switch (s.length - imax) {
647 case 1:
648 b10 = getbyte(s,i) << 16;
649 x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
650 padchar + padchar);
651 break;
652 case 2:
653 b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8);
654 x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
655 alpha.charAt((b10 >> 6) & 0x3f) + padchar);
656 break;
657 }
658 return x.join('');
659 }
660
661 $.fn.spin = function(opts) {
662 this.each(function() {
663 var $this = $(this),
664 data = $this.data();
665
666 if (data.spinner) {
667 data.spinner.stop();
668 delete data.spinner;
669 }
670 if (opts !== false) {
671 data.spinner = new Spinner($.extend({color: $this.css('color')}, opts)).spin(this);
672 }
673 });
674 return this;
675 };