nitdoc: Adds action to commit button when we are in "edit mode".
[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 // Activate edit mode
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 // Disable the edit mode
438 $('a[id=cancelBtn]').click(function(){
439 if(editComment > 0){ editComment -= 1; }
440 // Hide itself
441 $(this).hide();
442 // Hide commitBtn
443 $(this).next().hide();
444 // Hide Textarea
445 $(this).prev().hide();
446 // Show comment
447 $(this).prev().prev().show();
448 });
449
450 // Display commit form
451 $('a[id=commitBtn]').click(function(){
452 updateComment = $(this).prev().prev().val();
453 commentType = $(this).prev().prev().prev().attr('type');
454
455 if(updateComment == ""){ displayMessage('The comment field is empty!', 40, 45); }
456 else{
457 if(!sessionStarted){
458 displayMessage("You need to be loggued before commit something", 45, 40);
459 displayLogginModal();
460 return;
461 }
462 $('#commitMessage').val('New commit');
463 pathFile = $(this).prev().prev().prev().attr('tag');
464 $('#modal').show().prepend('<a class="close"><img src="resources/icons/close.png" class="btn_close" title="Close" alt="Close" /></a>');
465 $('body').append('<div id="fade"></div>');
466 $('#fade').css({'filter' : 'alpha(opacity=80)'}).fadeIn();
467 }
468 });
469
470 // Close commit form
471 $('.btn_close').click(function(){
472 $(this).hide();
473 $(this).next().hide();
474 if(editComment > 0){ editComment -= 1; }
475 });
476
477 //Close Popups and Fade Layer
478 $('body').on('click', 'a.close, #fade', function() {
479 if(editComment > 0){ editComment -= 1; }
480 $('#fade , #modal').fadeOut(function() {
481 $('#fade, a.close').remove();
482 });
483 $('#modalQuestion').hide();
484 });
485
486 });
487
488 /* Parse current URL and return anchor name */
489 function currentAnchor() {
490 var index = document.location.hash.indexOf("#");
491 if (index >= 0) {
492 return document.location.hash.substring(index + 1);
493 }
494 return null;
495 }
496
497 /* Prealod filters field using search query */
498 function preloadFilters() {
499 // Parse URL and get query string
500 var search = currentAnchor();
501
502 if(search == null || search.indexOf("q=") == -1)
503 return;
504
505 search = search.substring(2, search.length);
506
507 if(search == "" || search == "undefined")
508 return;
509
510 $(":text").val(search);
511 $(".filter :text")
512 .removeClass("notUsed")
513 .trigger("keyup");
514
515 }
516
517 /* Hightlight the spoted block */
518 function highlightBlock(a) {
519 if(a == undefined) {
520 return;
521 }
522
523 $(".highlighted").removeClass("highlighted");
524
525 var target = $("#" + a);
526
527 if(target.is("article")) {
528 target.parent().addClass("highlighted");
529 }
530
531 target.addClass("highlighted");
532 target.show();
533 }
534
535 function displayLogginModal(){
536 if ($('.popover').is(':hidden')) { $('.popover').show(); }
537 else { $('.popover').hide(); }
538 updateDisplaying();
539 }
540
541 function updateDisplaying(){
542 if (checkCookie())
543 {
544 $('#loginGit').hide();
545 $('#passwordGit').hide();
546 $('#lbpasswordGit').hide();
547 $('#lbloginGit').hide();
548 $('#repositoryGit').hide();
549 $('#lbrepositoryGit').hide();
550 $('#lbbranchGit').hide();
551 $('#branchGit').hide();
552 $("#liGitHub").attr("class", "current");
553 $("#imgGitHub").attr("src", "resources/icons/github-icon-w.png");
554 $('#nickName').text(userName);
555 $('#githubAccount').attr("href", "https://github.com/"+userName);
556 $('#logginMessage').css({'display' : 'block'});
557 $('#logginMessage').css({'text-align' : 'center'});
558 $('.popover').css({'height' : '80px'});
559 $('#signIn').text("Sign out");
560 sessionStarted = true;
561 }
562 else
563 {
564 sessionStarted = false;
565 $('#logginMessage').css({'display' : 'none'});
566 $("#liGitHub").attr("class", "");
567 $("#imgGitHub").attr("src", "resources/icons/github-icon.png");
568 $('#loginGit').val("");
569 $('#passwordGit').val("");
570 $('#nickName').text("");
571 $('.popover').css({'height' : '280px'});
572 $('#logginMessage').css({'display' : 'none'});
573 $('#repositoryGit').val($('#repoName').attr('name'));
574 $('#branchGit').val('wikidoc');
575 $('#signIn').text("Sign In");
576 $('#loginGit').show();
577 $('#passwordGit').show();
578 $('#lbpasswordGit').show();
579 $('#lbloginGit').show();
580 $('#repositoryGit').show();
581 $('#lbrepositoryGit').show();
582 $('#lbbranchGit').show();
583 $('#branchGit').show();
584 }
585 }
586
587 function setCookie(c_name, value, exdays)
588 {
589 var exdate=new Date();
590 exdate.setDate(exdate.getDate() + exdays);
591 var c_value=escape(value) + ((exdays==null) ? "" : "; expires="+exdate.toUTCString());
592 document.cookie=c_name + "=" + c_value;
593 }
594
595 function del_cookie(c_name)
596 {
597 document.cookie = c_name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
598 }
599
600 function getCookie(c_name)
601 {
602 var c_value = document.cookie;
603 var c_start = c_value.indexOf(" " + c_name + "=");
604 if (c_start == -1) { c_start = c_value.indexOf(c_name + "="); }
605 if (c_start == -1) { c_value = null; }
606 else
607 {
608 c_start = c_value.indexOf("=", c_start) + 1;
609 var c_end = c_value.indexOf(";", c_start);
610 if (c_end == -1) { c_end = c_value.length; }
611 c_value = unescape(c_value.substring(c_start,c_end));
612 }
613 return c_value;
614 }
615
616 function getUserPass(c_name){
617 var cookie = base64.decode(getCookie(c_name));
618 return base64.encode(cookie.split(':')[0] + ':' + cookie.split(':')[1]);
619 }
620
621 function checkCookie()
622 {
623 var cookie=getCookie("logginNitdoc");
624 if (cookie!=null && cookie!="")
625 {
626 cookie = base64.decode(cookie);
627 userName = cookie.split(':')[0];
628 repoName = cookie.split(':')[2];
629 branchName = cookie.split(':')[3];
630 return true;
631 }
632 else { return false; }
633 }
634
635
636 /*
637 * Base64
638 */
639 base64 = {};
640 base64.PADCHAR = '=';
641 base64.ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
642 base64.getbyte64 = function(s,i) {
643 // This is oddly fast, except on Chrome/V8.
644 // Minimal or no improvement in performance by using a
645 // object with properties mapping chars to value (eg. 'A': 0)
646 var idx = base64.ALPHA.indexOf(s.charAt(i));
647 if (idx == -1) {
648 throw "Cannot decode base64";
649 }
650 return idx;
651 }
652
653 base64.decode = function(s) {
654 // convert to string
655 s = "" + s;
656 var getbyte64 = base64.getbyte64;
657 var pads, i, b10;
658 var imax = s.length
659 if (imax == 0) {
660 return s;
661 }
662
663 if (imax % 4 != 0) {
664 throw "Cannot decode base64";
665 }
666
667 pads = 0
668 if (s.charAt(imax -1) == base64.PADCHAR) {
669 pads = 1;
670 if (s.charAt(imax -2) == base64.PADCHAR) {
671 pads = 2;
672 }
673 // either way, we want to ignore this last block
674 imax -= 4;
675 }
676
677 var x = [];
678 for (i = 0; i < imax; i += 4) {
679 b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) |
680 (getbyte64(s,i+2) << 6) | getbyte64(s,i+3);
681 x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff, b10 & 0xff));
682 }
683
684 switch (pads) {
685 case 1:
686 b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) | (getbyte64(s,i+2) << 6)
687 x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff));
688 break;
689 case 2:
690 b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12);
691 x.push(String.fromCharCode(b10 >> 16));
692 break;
693 }
694 return x.join('');
695 }
696
697 base64.getbyte = function(s,i) {
698 var x = s.charCodeAt(i);
699 if (x > 255) {
700 throw "INVALID_CHARACTER_ERR: DOM Exception 5";
701 }
702 return x;
703 }
704
705
706 base64.encode = function(s) {
707 if (arguments.length != 1) {
708 throw "SyntaxError: Not enough arguments";
709 }
710 var padchar = base64.PADCHAR;
711 var alpha = base64.ALPHA;
712 var getbyte = base64.getbyte;
713
714 var i, b10;
715 var x = [];
716
717 // convert to string
718 s = "" + s;
719
720 var imax = s.length - s.length % 3;
721
722 if (s.length == 0) {
723 return s;
724 }
725 for (i = 0; i < imax; i += 3) {
726 b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8) | getbyte(s,i+2);
727 x.push(alpha.charAt(b10 >> 18));
728 x.push(alpha.charAt((b10 >> 12) & 0x3F));
729 x.push(alpha.charAt((b10 >> 6) & 0x3f));
730 x.push(alpha.charAt(b10 & 0x3f));
731 }
732 switch (s.length - imax) {
733 case 1:
734 b10 = getbyte(s,i) << 16;
735 x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
736 padchar + padchar);
737 break;
738 case 2:
739 b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8);
740 x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
741 alpha.charAt((b10 >> 6) & 0x3f) + padchar);
742 break;
743 }
744 return x.join('');
745 }
746
747 $.fn.spin = function(opts) {
748 this.each(function() {
749 var $this = $(this),
750 data = $this.data();
751
752 if (data.spinner) {
753 data.spinner.stop();
754 delete data.spinner;
755 }
756 if (opts !== false) {
757 data.spinner = new Spinner($.extend({color: $this.css('color')}, opts)).spin(this);
758 }
759 });
760 return this;
761 };