nitdoc: Added a function to display the edit mode of a comment
[nit.git] / share / nitdoc / scripts / js-facilities.js
1 // User
2 var userB64 = null;
3 var sessionStarted = false;
4 var editComment = 0;
5
6 /*
7 * JQuery Case Insensitive :icontains selector
8 */
9 $.expr[':'].icontains = function(obj, index, meta, stack){
10 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;
11 };
12
13 /*
14 * Quick Search global vars
15 */
16
17 // Current search results preview table
18 var currentTable = null;
19
20 //Hightlighted index in search result preview table
21 var currentIndex = -1;
22
23
24 /*
25 * Add folding and filtering facilities to class description page.
26 */
27 $(document).ready(function() {
28
29 // Hide edit tags
30 $('textarea').hide();
31 $('a[id=commitBtn]').hide();
32 $('a[id=cancelBtn]').hide();
33 // Hide Authenfication form
34 $(".popover").hide();
35 // Update display
36 updateDisplaying();
37
38 /*
39 * Highlight the spoted element
40 */
41 highlightBlock(currentAnchor());
42
43 /*
44 * Nav block folding
45 */
46
47 // Menu nav folding
48 $(".menu nav h3")
49 .prepend(
50 $(document.createElement("a"))
51 .html("-")
52 .addClass("fold")
53 )
54 .css("cursor", "pointer")
55 .click( function() {
56 if($(this).find("a.fold").html() == "+") {
57 $(this).find("a.fold").html("-");
58 } else {
59 $(this).find("a.fold").html("+");
60 }
61 $(this).nextAll().toggle();
62 })
63
64 // Insert search field
65 $("nav.main ul")
66 .append(
67 $(document.createElement("li"))
68 .append(
69 $(document.createElement("form"))
70 .append(
71 $(document.createElement("input"))
72 .attr({
73 id: "search",
74 type: "text",
75 autocomplete: "off",
76 value: "quick search..."
77 })
78 .addClass("notUsed")
79
80 // Key management
81 .keyup(function(e) {
82 switch(e.keyCode) {
83
84 // Select previous result on "Up"
85 case 38:
86 // If already on first result, focus search input
87 if(currentIndex == 0) {
88 $("#search").val($(currentTable.find("tr")[currentIndex]).data("searchDetails").name);
89 $("#search").focus();
90 // Else select previous result
91 } else if(currentIndex > 0) {
92 $(currentTable.find("tr")[currentIndex]).removeClass("activeSearchResult");
93 currentIndex--;
94 $(currentTable.find("tr")[currentIndex]).addClass("activeSearchResult");
95 $("#search").val($(currentTable.find("tr")[currentIndex]).data("searchDetails").name);
96 $("#search").focus();
97 }
98 break;
99
100 // Select next result on "Down"
101 case 40:
102 if(currentIndex < currentTable.find("tr").length - 1) {
103 $(currentTable.find("tr")[currentIndex]).removeClass("activeSearchResult");
104 currentIndex++;
105 $(currentTable.find("tr")[currentIndex]).addClass("activeSearchResult");
106 $("#search").val($(currentTable.find("tr")[currentIndex]).data("searchDetails").name);
107 $("#search").focus();
108 }
109 break;
110 // Go to url on "Enter"
111 case 13:
112 if(currentIndex > -1) {
113 window.location = $(currentTable.find("tr")[currentIndex]).data("searchDetails").url;
114 return false;
115 }
116 if($("#search").val().length == 0)
117 return false
118
119 window.location = "full-index.html#q=" + $("#search").val();
120 if(window.location.href.indexOf("full-index.html") > -1) {
121 location.reload();
122 }
123 return false;
124 break;
125
126 // Hide results preview on "Escape"
127 case 27:
128 $(this).blur();
129 if(currentTable != null) {
130 currentTable.remove();
131 currentTable = null;
132 }
133 break;
134
135 default:
136 if($("#search").val().length == 0) {
137 return false;
138 }
139
140 // Remove previous table
141 if(currentTable != null) {
142 currentTable.remove();
143 }
144
145 // Build results table
146 currentIndex = -1;
147 currentTable = $(document.createElement("table"));
148
149 // Escape regexp related characters in query
150 var query = $("#search").val();
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 query = query.replace(/\)/gi, "\\)");
159
160 var index = 0;
161 var regexp = new RegExp("^" + query, "i");
162 for(var entry in entries) {
163 if(index > 10) {
164 break;
165 }
166 var result = entry.match(regexp);
167 if(result != null && result.toString().toUpperCase() == $("#search").val().toUpperCase()) {
168 for(var i = 0; i < entries[entry].length; i++) {
169 if(index > 10) {
170 break;
171 }
172 currentTable.append(
173 $(document.createElement("tr"))
174 .data("searchDetails", {name: entry, url: entries[entry][i]["url"]})
175 .data("index", index)
176 .append($(document.createElement("td")).html(entry))
177 .append(
178 $(document.createElement("td"))
179 .addClass("entryInfo")
180 .html(entries[entry][i]["txt"] + "&nbsp;&raquo;"))
181 .mouseover( function() {
182 $(currentTable.find("tr")[currentIndex]).removeClass("activeSearchResult");
183 $(this).addClass("activeSearchResult");
184 currentIndex = $(this).data("index");
185 })
186 .mouseout( function() {
187 $(this).removeClass("activeSearchResult");
188 })
189 .click( function() {
190 window.location = $(this).data("searchDetails")["url"];
191 })
192 );
193 index++;
194 }
195 }
196 }
197
198 // Initialize table properties
199 currentTable.attr("id", "searchTable");
200 currentTable.css("position", "absolute");
201 currentTable.width($("#search").outerWidth());
202 $("header").append(currentTable);
203 currentTable.offset({left: $("#search").offset().left + ($("#search").outerWidth() - currentTable.outerWidth()), top: $("#search").offset().top + $("#search").outerHeight()});
204
205 // Preselect first entry
206 if(currentTable.find("tr").length > 0) {
207 currentIndex = 0;
208 $(currentTable.find("tr")[currentIndex]).addClass("activeSearchResult");
209 $("#search").focus();
210 }
211 break;
212 }
213 })
214 .focusout(function() {
215 if($(this).val() == "") {
216 $(this).addClass("notUsed");
217 $(this).val("quick search...");
218 }
219 })
220 .focusin(function() {
221 if($(this).val() == "quick search...") {
222 $(this).removeClass("notUsed");
223 $(this).val("");
224 }
225 })
226 )
227 .submit( function() {
228 return false;
229 })
230 )
231 );
232
233 // Close quicksearch list on click
234 $(document).click(function(e) {
235 if(e.target != $("#search")[0] && e.target != $("#searchTable")[0]) {
236 if(currentTable != null) {
237 currentTable.remove();
238 currentTable = null;
239 }
240 }
241 });
242
243 // Insert filter field
244 $("article.filterable h2, nav.filterable h3")
245 .after(
246 $(document.createElement("div"))
247 .addClass("filter")
248 .append(
249 $(document.createElement("input"))
250 .attr({
251 type: "text",
252 value: "filter..."
253 })
254 .addClass("notUsed")
255 .keyup(function() {
256 $(this).parent().parent().find("ul li:not(:icontains('" + $(this).val() + "'))").addClass("hide");
257 $(this).parent().parent().find("ul li:icontains('" + $(this).val() + "')").removeClass("hide");
258 })
259 .focusout(function() {
260 if($(this).val() == "") {
261 $(this).addClass("notUsed");
262 $(this).val("filter...");
263 }
264 })
265 .focusin(function() {
266 if($(this).val() == "filter...") {
267 $(this).removeClass("notUsed");
268 $(this).val("");
269 }
270 })
271 )
272 );
273
274 // Filter toggle between H I R in nav porperties list
275 $("nav.properties.filterable .filter")
276 .append(
277 $(document.createElement("a"))
278 .html("H")
279 .attr({
280 title: "hide inherited properties"
281 })
282 .click( function() {
283 if($(this).hasClass("hidden")) {
284 $(this).parent().parent().find("li.inherit").show();
285 } else {
286 $(this).parent().parent().find("li.inherit").hide();
287 }
288
289 $(this).toggleClass("hidden");
290 })
291 )
292 .append(
293 $(document.createElement("a"))
294 .html("R")
295 .attr({
296 title: "hide redefined properties"
297 })
298 .click( function() {
299 if($(this).hasClass("hidden")) {
300 $(this).parent().parent().find("li.redef").show();
301 } else {
302 $(this).parent().parent().find("li.redef").hide();
303 }
304
305 $(this).toggleClass("hidden");
306 })
307 )
308 .append(
309 $(document.createElement("a"))
310 .html("I")
311 .attr({
312 title: "hide introduced properties"
313 })
314 .click( function() {
315 if($(this).hasClass("hidden")) {
316 $(this).parent().parent().find("li.intro").show();
317 } else {
318 $(this).parent().parent().find("li.intro").hide();
319 }
320
321 $(this).toggleClass("hidden");
322 })
323 );
324
325 // Filter toggle between I R in
326 $("article.properties.filterable .filter, article.classes.filterable .filter")
327 .append(
328 $(document.createElement("a"))
329 .html("I")
330 .attr({
331 title: "hide introduced properties"
332 })
333 .click( function() {
334 if($(this).hasClass("hidden")) {
335 $(this).parent().parent().find("li.intro").show();
336 } else {
337 $(this).parent().parent().find("li.intro").hide();
338 }
339
340 $(this).toggleClass("hidden");
341 })
342 )
343 .append(
344 $(document.createElement("a"))
345 .html("R")
346 .attr({
347 title: "hide redefined properties"
348 })
349 .click( function() {
350 if($(this).hasClass("hidden")) {
351 $(this).parent().parent().find("li.redef").show();
352 } else {
353 $(this).parent().parent().find("li.redef").hide();
354 }
355
356 $(this).toggleClass("hidden");
357 })
358 );
359
360 /*
361 * Anchors jumps
362 */
363 $("a[href*='#']").click( function() {
364 highlightBlock($(this).attr("href").split(/#/)[1]);
365 });
366
367 //Preload filter fields with query string
368 preloadFilters();
369 // Hide Authenfication form
370 $(".popover").hide();
371 // Display Login modal
372 $("#logGitHub").click(function(){ displayLogginModal(); });
373 // Update display
374 updateDisplaying();
375 // If cookie existing the session is opened
376 if(sessionStarted){ userB64 = "Basic " + getUserPass("logginNitdoc"); }
377
378 // Sign In an github user or Log out him
379 $("#signIn").click(function(){
380 if(!sessionStarted){
381 if($('#loginGit').val() == "" || $('#passwordGit').val() == ""){ displayMessage('The comment field is empty!', 40, 45); }
382 else
383 {
384 userName = $('#loginGit').val();
385 password = $('#passwordGit').val();
386 repoName = $('#repositoryGit').val();
387 branchName = $('#branchGit').val();
388 userB64 = "Basic " + base64.encode(userName+':'+password);
389 setCookie("logginNitdoc", base64.encode(userName+':'+password+':'+repoName+':'+branchName), 1);
390 $('#loginGit').val("");
391 $('#passwordGit').val("");
392 }
393 }
394 else
395 {
396 // Delete cookie and reset settings
397 del_cookie("logginNitdoc");
398 }
399 displayLogginModal();
400 });
401
402 // Open edit file
403 $('pre[class=text_label]').click(function(){
404 // the customer is loggued ?
405 if(!sessionStarted || userName == ""){
406 // No => nothing happen
407 return;
408 }
409 else{
410 var arrayNew = $(this).text().split('\n');
411 var lNew = arrayNew.length - 1;
412 var adapt = "";
413
414 for (var i = 0; i < lNew; i++) {
415 adapt += arrayNew[i];
416 if(i < lNew-1){ adapt += "\n"; }
417 }
418 editComment += 1;
419 // hide comment
420 $(this).hide();
421 // Show edit box
422 $(this).next().show();
423 // Show cancel button
424 $(this).next().next().show();
425 // Show commit button
426 $(this).next().next().next().show();
427 // Add text in edit box
428 if($(this).next().val() == ""){ $(this).next().val(adapt); }
429 // Resize edit box
430 $(this).next().height($(this).next().prop("scrollHeight"));
431 // Select it
432 $(this).next().select();
433 preElement = $(this);
434 }
435 });
436
437 $('a[id=cancelBtn]').click(function(){
438 if(editComment > 0){ editComment -= 1; }
439 // Hide itself
440 $(this).hide();
441 // Hide commitBtn
442 $(this).next().hide();
443 // Hide Textarea
444 $(this).prev().hide();
445 // Show comment
446 $(this).prev().prev().show();
447 });
448
449 });
450
451 /* Parse current URL and return anchor name */
452 function currentAnchor() {
453 var index = document.location.hash.indexOf("#");
454 if (index >= 0) {
455 return document.location.hash.substring(index + 1);
456 }
457 return null;
458 }
459
460 /* Prealod filters field using search query */
461 function preloadFilters() {
462 // Parse URL and get query string
463 var search = currentAnchor();
464
465 if(search == null || search.indexOf("q=") == -1)
466 return;
467
468 search = search.substring(2, search.length);
469
470 if(search == "" || search == "undefined")
471 return;
472
473 $(":text").val(search);
474 $(".filter :text")
475 .removeClass("notUsed")
476 .trigger("keyup");
477
478 }
479
480 /* Hightlight the spoted block */
481 function highlightBlock(a) {
482 if(a == undefined) {
483 return;
484 }
485
486 $(".highlighted").removeClass("highlighted");
487
488 var target = $("#" + a);
489
490 if(target.is("article")) {
491 target.parent().addClass("highlighted");
492 }
493
494 target.addClass("highlighted");
495 target.show();
496 }
497
498 function displayLogginModal(){
499 if ($('.popover').is(':hidden')) { $('.popover').show(); }
500 else { $('.popover').hide(); }
501 updateDisplaying();
502 }
503
504 function updateDisplaying(){
505 if (checkCookie())
506 {
507 $('#loginGit').hide();
508 $('#passwordGit').hide();
509 $('#lbpasswordGit').hide();
510 $('#lbloginGit').hide();
511 $('#repositoryGit').hide();
512 $('#lbrepositoryGit').hide();
513 $('#lbbranchGit').hide();
514 $('#branchGit').hide();
515 $("#liGitHub").attr("class", "current");
516 $("#imgGitHub").attr("src", "resources/icons/github-icon-w.png");
517 $('#nickName').text(userName);
518 $('#githubAccount').attr("href", "https://github.com/"+userName);
519 $('#logginMessage').css({'display' : 'block'});
520 $('#logginMessage').css({'text-align' : 'center'});
521 $('.popover').css({'height' : '80px'});
522 $('#signIn').text("Sign out");
523 sessionStarted = true;
524 }
525 else
526 {
527 sessionStarted = false;
528 $('#logginMessage').css({'display' : 'none'});
529 $("#liGitHub").attr("class", "");
530 $("#imgGitHub").attr("src", "resources/icons/github-icon.png");
531 $('#loginGit').val("");
532 $('#passwordGit').val("");
533 $('#nickName').text("");
534 $('.popover').css({'height' : '280px'});
535 $('#logginMessage').css({'display' : 'none'});
536 $('#repositoryGit').val($('#repoName').attr('name'));
537 $('#branchGit').val('wikidoc');
538 $('#signIn').text("Sign In");
539 $('#loginGit').show();
540 $('#passwordGit').show();
541 $('#lbpasswordGit').show();
542 $('#lbloginGit').show();
543 $('#repositoryGit').show();
544 $('#lbrepositoryGit').show();
545 $('#lbbranchGit').show();
546 $('#branchGit').show();
547 }
548 }
549
550 function setCookie(c_name, value, exdays)
551 {
552 var exdate=new Date();
553 exdate.setDate(exdate.getDate() + exdays);
554 var c_value=escape(value) + ((exdays==null) ? "" : "; expires="+exdate.toUTCString());
555 document.cookie=c_name + "=" + c_value;
556 }
557
558 function del_cookie(c_name)
559 {
560 document.cookie = c_name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
561 }
562
563 function getCookie(c_name)
564 {
565 var c_value = document.cookie;
566 var c_start = c_value.indexOf(" " + c_name + "=");
567 if (c_start == -1) { c_start = c_value.indexOf(c_name + "="); }
568 if (c_start == -1) { c_value = null; }
569 else
570 {
571 c_start = c_value.indexOf("=", c_start) + 1;
572 var c_end = c_value.indexOf(";", c_start);
573 if (c_end == -1) { c_end = c_value.length; }
574 c_value = unescape(c_value.substring(c_start,c_end));
575 }
576 return c_value;
577 }
578
579 function getUserPass(c_name){
580 var cookie = base64.decode(getCookie(c_name));
581 return base64.encode(cookie.split(':')[0] + ':' + cookie.split(':')[1]);
582 }
583
584 function checkCookie()
585 {
586 var cookie=getCookie("logginNitdoc");
587 if (cookie!=null && cookie!="")
588 {
589 cookie = base64.decode(cookie);
590 userName = cookie.split(':')[0];
591 repoName = cookie.split(':')[2];
592 branchName = cookie.split(':')[3];
593 return true;
594 }
595 else { return false; }
596 }
597
598
599 /*
600 * Base64
601 */
602 base64 = {};
603 base64.PADCHAR = '=';
604 base64.ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
605 base64.getbyte64 = function(s,i) {
606 // This is oddly fast, except on Chrome/V8.
607 // Minimal or no improvement in performance by using a
608 // object with properties mapping chars to value (eg. 'A': 0)
609 var idx = base64.ALPHA.indexOf(s.charAt(i));
610 if (idx == -1) {
611 throw "Cannot decode base64";
612 }
613 return idx;
614 }
615
616 base64.decode = function(s) {
617 // convert to string
618 s = "" + s;
619 var getbyte64 = base64.getbyte64;
620 var pads, i, b10;
621 var imax = s.length
622 if (imax == 0) {
623 return s;
624 }
625
626 if (imax % 4 != 0) {
627 throw "Cannot decode base64";
628 }
629
630 pads = 0
631 if (s.charAt(imax -1) == base64.PADCHAR) {
632 pads = 1;
633 if (s.charAt(imax -2) == base64.PADCHAR) {
634 pads = 2;
635 }
636 // either way, we want to ignore this last block
637 imax -= 4;
638 }
639
640 var x = [];
641 for (i = 0; i < imax; i += 4) {
642 b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) |
643 (getbyte64(s,i+2) << 6) | getbyte64(s,i+3);
644 x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff, b10 & 0xff));
645 }
646
647 switch (pads) {
648 case 1:
649 b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) | (getbyte64(s,i+2) << 6)
650 x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff));
651 break;
652 case 2:
653 b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12);
654 x.push(String.fromCharCode(b10 >> 16));
655 break;
656 }
657 return x.join('');
658 }
659
660 base64.getbyte = function(s,i) {
661 var x = s.charCodeAt(i);
662 if (x > 255) {
663 throw "INVALID_CHARACTER_ERR: DOM Exception 5";
664 }
665 return x;
666 }
667
668
669 base64.encode = function(s) {
670 if (arguments.length != 1) {
671 throw "SyntaxError: Not enough arguments";
672 }
673 var padchar = base64.PADCHAR;
674 var alpha = base64.ALPHA;
675 var getbyte = base64.getbyte;
676
677 var i, b10;
678 var x = [];
679
680 // convert to string
681 s = "" + s;
682
683 var imax = s.length - s.length % 3;
684
685 if (s.length == 0) {
686 return s;
687 }
688 for (i = 0; i < imax; i += 3) {
689 b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8) | getbyte(s,i+2);
690 x.push(alpha.charAt(b10 >> 18));
691 x.push(alpha.charAt((b10 >> 12) & 0x3F));
692 x.push(alpha.charAt((b10 >> 6) & 0x3f));
693 x.push(alpha.charAt(b10 & 0x3f));
694 }
695 switch (s.length - imax) {
696 case 1:
697 b10 = getbyte(s,i) << 16;
698 x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
699 padchar + padchar);
700 break;
701 case 2:
702 b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8);
703 x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
704 alpha.charAt((b10 >> 6) & 0x3f) + padchar);
705 break;
706 }
707 return x.join('');
708 }
709
710 $.fn.spin = function(opts) {
711 this.each(function() {
712 var $this = $(this),
713 data = $this.data();
714
715 if (data.spinner) {
716 data.spinner.stop();
717 delete data.spinner;
718 }
719 if (opts !== false) {
720 data.spinner = new Spinner($.extend({color: $this.css('color')}, opts)).spin(this);
721 }
722 });
723 return this;
724 };