ni_nitdoc: fixed broken search with special caracters in JS
[nit.git] / share / nitdoc / scripts / js-facilities.js
1 // User
2 var userB64 = null;
3 var userName = "";
4 var password = "";
5 var sessionStarted = false;
6 var editComment = 0;
7 var currentfileContent = '';
8 var originalFileContent = '';
9 var addNewComment = false;
10 var commentLineStart;
11 var commentLineEnd;
12
13 // SHA GitHub
14 var shaLastCommit = "";
15 var shaBaseTree;
16 var shaNewTree;
17 var shaNewCommit;
18 var shaBlob;
19 var shaMaster;
20 var repoExist = false;
21 var branchExist = false;
22 var githubRepo;
23 var loginProcess = false;
24 var signedOff = '';
25 var userEmail = '';
26 var commitMessage = '';
27 var numComment = '';
28 var showcomment = false;
29
30 // Spinner vars
31 var opts = {
32 lines: 11, // The number of lines to draw
33 length: 7, // The length of each line
34 width: 4, // The line thickness
35 radius: 10, // The radius of the inner circle
36 corners: 1, // Corner roundness (0..1)
37 rotate: 0, // The rotation offset
38 color: '#FFF', // #rgb or #rrggbb
39 speed: 1, // Rounds per second
40 trail: 60, // Afterglow percentage
41 shadow: false, // Whether to render a shadow
42 hwaccel: false, // Whether to use hardware acceleration
43 className: 'spinner', // The CSS class to assign to the spinner
44 zIndex: 99999, // The z-index (defaults to 2000000000)
45 top: '300', // Top position relative to parent in px
46 left: 'auto' // Left position relative to parent in px
47 };
48 var targetSpinner = document.getElementById('waitCommit');
49 var spinner = new Spinner(opts).spin(targetSpinner);
50
51 /*
52 * JQuery Case Insensitive :icontains selector
53 */
54 $.expr[':'].icontains = function(obj, index, meta, stack){
55 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;
56 };
57
58 /*
59 * Quick Search global vars
60 */
61
62 // Current search results preview table
63 var currentTable = null;
64
65 //Hightlighted index in search result preview table
66 var currentIndex = -1;
67
68 // Check if a comment is editing
69 window.onbeforeunload = function() {
70 if(editComment > 0){
71 return 'Are you sure you want to leave this page?';
72 }
73 };
74
75 /*
76 * Add folding and filtering facilities to class description page.
77 */
78 $(document).ready(function() {
79
80 // Hide edit tags
81 $('textarea').hide();
82 $('a[id=commitBtn]').hide();
83 $('a[id=cancelBtn]').hide();
84 // Hide Authenfication form
85 $(".popover").hide();
86 // Update display
87 updateDisplaying();
88 /*
89 * Highlight the spoted element
90 */
91 highlightBlock(currentAnchor());
92
93 /*
94 * Nav block folding
95 */
96
97 // Menu nav folding
98 $(".menu nav h3")
99 .prepend(
100 $(document.createElement("a"))
101 .html("-")
102 .addClass("fold")
103 )
104 .css("cursor", "pointer")
105 .click( function() {
106 if($(this).find("a.fold").html() == "+") {
107 $(this).find("a.fold").html("-");
108 } else {
109 $(this).find("a.fold").html("+");
110 }
111 $(this).nextAll().toggle();
112 })
113
114 // Insert search field
115 $("nav.main ul")
116 .append(
117 $(document.createElement("li"))
118 .append(
119 $(document.createElement("form"))
120 .append(
121 $(document.createElement("input"))
122 .attr({
123 id: "search",
124 type: "text",
125 autocomplete: "off",
126 value: "quick search..."
127 })
128 .addClass("notUsed")
129
130 // Key management
131 .keyup(function(e) {
132 switch(e.keyCode) {
133
134 // Select previous result on "Up"
135 case 38:
136 // If already on first result, focus search input
137 if(currentIndex == 0) {
138 $("#search").val($(currentTable.find("tr")[currentIndex]).data("searchDetails").name);
139 $("#search").focus();
140 // Else select previous result
141 } else if(currentIndex > 0) {
142 $(currentTable.find("tr")[currentIndex]).removeClass("activeSearchResult");
143 currentIndex--;
144 $(currentTable.find("tr")[currentIndex]).addClass("activeSearchResult");
145 $("#search").val($(currentTable.find("tr")[currentIndex]).data("searchDetails").name);
146 $("#search").focus();
147 }
148 break;
149
150 // Select next result on "Down"
151 case 40:
152 if(currentIndex < currentTable.find("tr").length - 1) {
153 $(currentTable.find("tr")[currentIndex]).removeClass("activeSearchResult");
154 currentIndex++;
155 $(currentTable.find("tr")[currentIndex]).addClass("activeSearchResult");
156 $("#search").val($(currentTable.find("tr")[currentIndex]).data("searchDetails").name);
157 $("#search").focus();
158 }
159 break;
160 // Go to url on "Enter"
161 case 13:
162 if(currentIndex > -1) {
163 window.location = $(currentTable.find("tr")[currentIndex]).data("searchDetails").url;
164 return false;
165 }
166 if($("#search").val().length == 0)
167 return false
168
169 window.location = "full-index.html#q=" + $("#search").val();
170 if(window.location.href.indexOf("full-index.html") > -1) {
171 location.reload();
172 }
173 return false;
174 break;
175
176 // Hide results preview on "Escape"
177 case 27:
178 $(this).blur();
179 if(currentTable != null) {
180 currentTable.remove();
181 currentTable = null;
182 }
183 break;
184
185 default:
186 if($("#search").val().length == 0) {
187 return false;
188 }
189
190 // Remove previous table
191 if(currentTable != null) {
192 currentTable.remove();
193 }
194
195 // Build results table
196 currentIndex = -1;
197 currentTable = $(document.createElement("table"));
198
199 // Escape regexp related characters in query
200 var query = $("#search").val();
201 query = query.replace(/\\/gi, "\\\\");
202 query = query.replace(/\[/gi, "\\[");
203 query = query.replace(/\|/gi, "\\|");
204 query = query.replace(/\*/gi, "\\*");
205 query = query.replace(/\+/gi, "\\+");
206 query = query.replace(/\?/gi, "\\?");
207 query = query.replace(/\(/gi, "\\(");
208 query = query.replace(/\)/gi, "\\)");
209 query = query.replace(/&/gi, "&&");
210 query = query.replace(/>/gi, "&gt;");
211 query = query.replace(/</gi, "&lt;");
212
213 var index = 0;
214 var regexp = new RegExp("^" + query, "i");
215 for(var entry in entries) {
216 if(index > 10) {
217 break;
218 }
219 var result = entry.match(regexp);
220 if(result != null) {
221 for(var i = 0; i < entries[entry].length; i++) {
222 if(index > 10) {
223 break;
224 }
225 currentTable.append(
226 $(document.createElement("tr"))
227 .data("searchDetails", {name: entry, url: entries[entry][i]["url"]})
228 .data("index", index)
229 .append($(document.createElement("td")).html(entry))
230 .append(
231 $(document.createElement("td"))
232 .addClass("entryInfo")
233 .html(entries[entry][i]["txt"] + "&nbsp;&raquo;"))
234 .mouseover( function() {
235 $(currentTable.find("tr")[currentIndex]).removeClass("activeSearchResult");
236 $(this).addClass("activeSearchResult");
237 currentIndex = $(this).data("index");
238 })
239 .mouseout( function() {
240 $(this).removeClass("activeSearchResult");
241 })
242 .click( function() {
243 window.location = $(this).data("searchDetails")["url"];
244 })
245 );
246 index++;
247 }
248 }
249 }
250
251 // Initialize table properties
252 currentTable.attr("id", "searchTable");
253 currentTable.css("position", "absolute");
254 currentTable.width($("#search").outerWidth());
255 $("header").append(currentTable);
256 currentTable.offset({left: $("#search").offset().left + ($("#search").outerWidth() - currentTable.outerWidth()), top: $("#search").offset().top + $("#search").outerHeight()});
257
258 // Preselect first entry
259 if(currentTable.find("tr").length > 0) {
260 currentIndex = 0;
261 $(currentTable.find("tr")[currentIndex]).addClass("activeSearchResult");
262 $("#search").focus();
263 }
264 break;
265 }
266 })
267 .focusout(function() {
268 if($(this).val() == "") {
269 $(this).addClass("notUsed");
270 $(this).val("quick search...");
271 }
272 })
273 .focusin(function() {
274 if($(this).val() == "quick search...") {
275 $(this).removeClass("notUsed");
276 $(this).val("");
277 }
278 })
279 )
280 .submit( function() {
281 return false;
282 })
283 )
284 );
285
286 // Close quicksearch list on click
287 $(document).click(function(e) {
288 if(e.target != $("#search")[0] && e.target != $("#searchTable")[0]) {
289 if(currentTable != null) {
290 currentTable.remove();
291 currentTable = null;
292 }
293 }
294 });
295
296 // Insert filter field
297 $("article.filterable h2, nav.filterable h3")
298 .after(
299 $(document.createElement("div"))
300 .addClass("filter")
301 .append(
302 $(document.createElement("input"))
303 .attr({
304 type: "text",
305 value: "filter..."
306 })
307 .addClass("notUsed")
308 .keyup(function() {
309 $(this).parent().parent().find("ul li:not(:icontains('" + $(this).val() + "'))").addClass("hide");
310 $(this).parent().parent().find("ul li:icontains('" + $(this).val() + "')").removeClass("hide");
311 })
312 .focusout(function() {
313 if($(this).val() == "") {
314 $(this).addClass("notUsed");
315 $(this).val("filter...");
316 }
317 })
318 .focusin(function() {
319 if($(this).val() == "filter...") {
320 $(this).removeClass("notUsed");
321 $(this).val("");
322 }
323 })
324 )
325 );
326
327 // Filter toggle between H I R in nav porperties list
328 $("nav.properties.filterable .filter")
329 .append(
330 $(document.createElement("a"))
331 .html("H")
332 .attr({
333 title: "hide inherited properties"
334 })
335 .click( function() {
336 if($(this).hasClass("hidden")) {
337 $(this).parent().parent().find("li.inherit").show();
338 } else {
339 $(this).parent().parent().find("li.inherit").hide();
340 }
341
342 $(this).toggleClass("hidden");
343 })
344 )
345 .append(
346 $(document.createElement("a"))
347 .html("R")
348 .attr({
349 title: "hide redefined properties"
350 })
351 .click( function() {
352 if($(this).hasClass("hidden")) {
353 $(this).parent().parent().find("li.redef").show();
354 } else {
355 $(this).parent().parent().find("li.redef").hide();
356 }
357
358 $(this).toggleClass("hidden");
359 })
360 )
361 .append(
362 $(document.createElement("a"))
363 .html("I")
364 .attr({
365 title: "hide introduced properties"
366 })
367 .click( function() {
368 if($(this).hasClass("hidden")) {
369 $(this).parent().parent().find("li.intro").show();
370 } else {
371 $(this).parent().parent().find("li.intro").hide();
372 }
373
374 $(this).toggleClass("hidden");
375 })
376 );
377
378 // Filter toggle between I R in
379 $("article.properties.filterable .filter, article.classes.filterable .filter")
380 .append(
381 $(document.createElement("a"))
382 .html("I")
383 .attr({
384 title: "hide introduced properties"
385 })
386 .click( function() {
387 if($(this).hasClass("hidden")) {
388 $(this).parent().parent().find("li.intro").show();
389 } else {
390 $(this).parent().parent().find("li.intro").hide();
391 }
392
393 $(this).toggleClass("hidden");
394 })
395 )
396 .append(
397 $(document.createElement("a"))
398 .html("R")
399 .attr({
400 title: "hide redefined properties"
401 })
402 .click( function() {
403 if($(this).hasClass("hidden")) {
404 $(this).parent().parent().find("li.redef").show();
405 } else {
406 $(this).parent().parent().find("li.redef").hide();
407 }
408
409 $(this).toggleClass("hidden");
410 })
411 );
412
413 /*
414 * Anchors jumps
415 */
416 $("a[href*='#']").click( function() {
417 highlightBlock($(this).attr("href").split(/#/)[1]);
418 });
419
420 //Preload filter fields with query string
421 preloadFilters();
422 // Hide Authenfication form
423 $(".popover").hide();
424 // Display Login modal
425 $("#logGitHub").click(function(){ displayLogginModal(); });
426 // Update display
427 updateDisplaying();
428 // If cookie existing the session is opened
429 if(sessionStarted){ userB64 = "Basic " + getUserPass("logginNitdoc"); }
430
431 // Sign In an github user or Log out him
432 $("#signIn").click(function(){
433 if(!sessionStarted){
434 if($('#loginGit').val() == "" || $('#passwordGit').val() == ""){ displayMessage('Please informed login/password field!', 40, 45); }
435 else
436 {
437 userName = $('#loginGit').val();
438 password = $('#passwordGit').val();
439 githubRepo = $('#repositoryGit').val();
440 branchName = $('#branchGit').val();
441 userB64 = "Basic " + base64.encode(userName+':'+password);
442 if(checkSignIn()){
443 // Check if repo exist
444 isRepoExisting();
445 if(repoExist){
446 $.when(isBranchExisting()).done(function(){
447 loginProcess = true;
448 if(branchExist){
449 setCookie("logginNitdoc", base64.encode(userName+':'+password+':'+githubRepo+':'+branchName), 1);
450 $('#loginGit').val("");
451 $('#passwordGit').val("");
452 reloadComment();
453 }
454 });
455 }
456 }
457 }
458 }
459 else
460 {
461 // Delete cookie and reset settings
462 del_cookie("logginNitdoc");
463 closeAllCommentInEdtiting();
464 }
465 displayLogginModal();
466 });
467
468 // Activate edit mode
469 $('pre[class=text_label]').click(function(){
470 // the customer is loggued ?
471 if(!sessionStarted || userName == ""){
472 // No => nothing happen
473 return;
474 }
475 else{
476 numComment = $(this).attr('title');
477 var arrayNew = $(this).text().split('\n');
478 var lNew = arrayNew.length - 1;
479 var adapt = "";
480
481 for (var i = 0; i < lNew; i++) {
482 adapt += arrayNew[i];
483 if(i < lNew-1){ adapt += "\n"; }
484 }
485 editComment += 1;
486 getCommentOfFunction($(this));
487 // hide comment
488 $(this).hide();
489 // Show edit box
490 $(this).next().show();
491 // Show cancel button
492 $(this).next().next().show();
493 // Show commit button
494 $(this).next().next().next().show();
495 // Add text in edit box
496 if($(this).next().val() == "" || $(this).next().val() != adapt){ $(this).next().val(adapt); }
497 // Resize edit box
498 $(this).next().height($(this).next().prop("scrollHeight"));
499 resizeTextarea($(this).next());
500 // Select it
501 $(this).next().select();
502 preElement = $(this);
503 }
504 });
505
506 // Disable the edit mode
507 $('a[id=cancelBtn]').click(function(){
508 $(this).parent().prev().children('#lblDiffCommit').text("");
509 showcomment = false;
510 closeEditing($(this));
511 });
512
513 // Display commit form
514 $('a[id=commitBtn]').click(function(){
515 updateComment = $(this).prev().prev().val();
516 commentType = $(this).prev().prev().prev().attr('type');
517
518 if(updateComment == ""){ displayMessage('The comment field is empty!', 40, 45); }
519 else{
520 if(!sessionStarted){
521 displayMessage("You need to be loggued before commit something", 45, 40);
522 displayLogginModal();
523 return;
524 }
525
526 // Create the commit message
527 commitMessage = 'Wikidoc: modified comment in ' + $(this).parent().prev().prev().html().split(' ')[1];
528 $('#commitMessage').text(commitMessage);
529 $('#commitMessage').css({'display': 'block'});
530 pathFile = $(this).prev().prev().prev().attr('tag');
531 $('#modal').show().prepend('<a class="close"><img src="resources/icons/close.png" class="btn_close" title="Close" alt="Close" /></a>');
532 $('body').append('<div id="fade"></div>');
533 $('#fade').css({'filter' : 'alpha(opacity=80)'}).fadeIn();
534 }
535 });
536
537 // Close commit form
538 $('.btn_close').click(function(){
539 $(this).hide();
540 $(this).next().hide();
541 if(editComment > 0){ editComment -= 1; }
542 $('#chkSignedOff').attr('checked', false);
543 removeSignedOff();
544 });
545
546 //Close Popups and Fade Layer
547 $('body').on('click', 'a.close, #fade', function() {
548 if(editComment > 0){ editComment -= 1; }
549 $('#fade , #modal').fadeOut(function() {
550 $('#fade, a.close').remove();
551 });
552 $('#modalQuestion').hide();
553 $('#chkSignedOff').attr('checked', false);
554 removeSignedOff();
555 });
556
557 $('#loginAction').click(function(){
558 var text;
559 var url;
560 var line;
561 // Look if the customer is logged
562 if(!sessionStarted){
563 displayMessage("You need to be loggued before commit something", 100, 40);
564 $('.popover').show();
565 return;
566 }
567 else{ userB64 = "Basic " + getUserPass("logginNitdoc"); }
568 // Check if repo exist
569 isRepoExisting();
570 if(repoExist){
571 isBranchExisting();
572 if(branchExist){
573 editComment -= 1;
574 commitMessage = $('#commitMessage').val().replace(/\r?\n/g, '\\n').replace(/\t/g, '\\t').replace(/\"/g,'\\"');
575 if(commitMessage == ""){ commitMessage = "New commit";}
576 if(sessionStarted){
577 if ($.trim(updateComment) == ''){ this.value = (this.defaultValue ? this.defaultValue : ''); }
578 else{
579 displaySpinner();
580 startCommitProcess();
581 }
582 }
583 $('#modal, #modalQuestion').fadeOut(function() {
584 $('#login').val("");
585 $('#password').val("");
586 $('textarea').hide();
587 $('textarea').prev().show();
588 });
589 $('a[id=cancelBtn]').hide();
590 $('a[id=commitBtn]').hide();
591 $('a[id=lblDiffCommit]').text("");
592 showcomment = false;
593 // Re-load all comment
594 reloadComment();
595 }
596 }
597 else{ editComment -= 1; }
598 $('#chkSignedOff').attr('checked', false);
599 });
600
601 // Cancel creating branch
602 $('#btnCancelBranch').click(function(){
603 editComment -= 1;
604 $('#modalQuestion').hide();
605 $('#fade , #modal').fadeOut(function() { $('#fade, a.close').remove(); });
606 return;
607 });
608
609 // Create new branch and continu
610 $('#btnCreateBranch').click(function(){
611 $('#modalQuestion').hide();
612 if($('#btnCreateBranch').text() != 'Ok'){
613 // Create the branch
614 createBranch();
615 commitMessage = $('#commitMessage').val().replace(/\r?\n/g, '\\n').replace(/\t/g, '\\t').replace(/\"/g,'\\"');
616 if(commitMessage == ""){ commitMessage = "New commit"; }
617 if(userB64 != ""){
618 if(loginProcess){
619 setCookie("logginNitdoc", base64.encode(userName+':'+password+':'+githubRepo+':'+branchName), 1);
620 $('#loginGit').val("");
621 $('#passwordGit').val("");
622 loginProcess = false;
623 displayLogginModal();
624 }
625 else{
626 if ($.trim(updateComment) == ''){ this.value = (this.defaultValue ? this.defaultValue : ''); }
627 else{ startCommitProcess(); }
628 }
629 }
630 }
631 else
632 {
633 $('#fade , #modalQuestion, #modal').fadeOut(function() { $('#fade, a.close').remove(); });
634 }
635 });
636
637 $('a[class=newComment]').click(function(){
638 addNewComment = true;
639 editComment += 1;
640 // hide comment
641 $(this).hide();
642 // Show edit box
643 $(this).next().show();
644 // Show cancel button
645 $(this).next().next().show();
646 // Show commit button
647 $(this).next().next().next().show();
648 // Resize edit box
649 $(this).next().height($(this).next().prop("scrollHeight"));
650 resizeTextarea($(this).next());
651 // Select it
652 $(this).next().select();
653 preElement = $(this);
654 });
655
656 $("#dropBranches").change(function () {
657 $("#dropBranches option:selected").each(function () {
658 if(branchName != $(this).text()){
659 branchName = $(this).text();
660 }
661 });
662 $.when(updateCookie(userName, password, githubRepo, branchName)).done(function(){
663 closeAllCommentInEdtiting();
664 reloadComment();
665 });
666 });
667
668 $("pre").hover(
669 function () {
670 if(sessionStarted == true){
671 $(this).css({'cursor' : 'hand'});
672 }
673 else{
674 $(this).css({'cursor' : ''});
675 }
676 },
677 function () {
678 if(sessionStarted == true){
679 $(this).css({'cursor' : 'pointer'});
680 }
681 else{
682 $(this).css({'cursor' : ''});
683 }
684 }
685 );
686
687 $('#chkSignedOff').click(function(){
688 if($(this).is(':checked')){ addSignedOff(); }
689 else{ removeSignedOff(); }
690 })
691
692 $('a[id=lblDiffCommit]').click(function(){
693 showComment($(this));
694 });
695 });
696
697 /* Parse current URL and return anchor name */
698 function currentAnchor() {
699 var index = document.location.hash.indexOf("#");
700 if (index >= 0) {
701 return document.location.hash.substring(index + 1);
702 }
703 return null;
704 }
705
706 /* Prealod filters field using search query */
707 function preloadFilters() {
708 // Parse URL and get query string
709 var search = currentAnchor();
710
711 if(search == null || search.indexOf("q=") == -1)
712 return;
713
714 search = search.substring(2, search.length);
715
716 if(search == "" || search == "undefined")
717 return;
718
719 $(":text").val(search);
720 $(".filter :text")
721 .removeClass("notUsed")
722 .trigger("keyup");
723
724 }
725
726 /* Hightlight the spoted block */
727 function highlightBlock(a) {
728 if(a == undefined) {
729 return;
730 }
731
732 $(".highlighted").removeClass("highlighted");
733
734 var target = $("#" + a);
735
736 if(target.is("article")) {
737 target.parent().addClass("highlighted");
738 }
739
740 target.addClass("highlighted");
741 target.show();
742 }
743
744 // Init process to commit the new comment
745 function startCommitProcess()
746 {
747 if($('#chkSignedOff').is(':checked')){
748 var numL = preElement.attr("title");
749 commentLineStart = numL.split('-')[0] - 1;
750 if(addNewComment) { commentLineStart++; }
751 commentLineEnd = (commentLineStart + preElement.text().split('\n').length) - 1;
752 state = true;
753 replaceComment(updateComment, currentfileContent);
754 getLastCommit();
755 getBaseTree();
756 editComment = false;
757 }
758 else{
759 displayMessage('Please sign this commit', 40, 40);
760 }
761 }
762
763 function displayLogginModal(){
764 if ($('.popover').is(':hidden')) {
765 if(sessionStarted){ getListBranches(); }
766 $('.popover').show();
767 }
768 else { $('.popover').hide(); }
769 updateDisplaying();
770 }
771
772 function updateDisplaying(){
773 if (checkCookie())
774 {
775 userB64 = "Basic " + getUserPass("logginNitdoc");
776 $('#loginGit').hide();
777 $('#passwordGit').hide();
778 $('#lbpasswordGit').hide();
779 $('#lbloginGit').hide();
780 $('#repositoryGit').hide();
781 $('#lbrepositoryGit').hide();
782 $('#lbbranchGit').hide();
783 $('#branchGit').hide();
784 $('#listBranches').show();
785 $('#divGitHubRepoDisplay').show();
786 $("#liGitHub").attr("class", "current");
787 $("#imgGitHub").attr("src", "resources/icons/github-icon-w.png");
788 $('#nickName').text(userName);
789 $('#githubAccount').attr("href", "https://github.com/"+userName);
790 $('#logginMessage').css({'display' : 'block'});
791 $('#logginMessage').css({'text-align' : 'center'});
792 $('.popover').css({'height' : '190px'});
793 $('#signIn').text("Sign out");
794 $('#githubRepoDisplay').text(githubRepo);
795 sessionStarted = true;
796 reloadComment();
797 }
798 else
799 {
800 sessionStarted = false;
801 $('#logginMessage').css({'display' : 'none'});
802 $("#liGitHub").attr("class", "");
803 $("#imgGitHub").attr("src", "resources/icons/github-icon.png");
804 $('#loginGit').val("");
805 $('#passwordGit').val("");
806 $('#nickName').text("");
807 $('.popover').css({'height' : '325px'});
808 $('#logginMessage').css({'display' : 'none'});
809 $('#repositoryGit').val($('#repoName').attr('name'));
810 $('#branchGit').val('wikidoc');
811 $('#signIn').text("Sign In");
812 $('#loginGit').show();
813 $('#passwordGit').show();
814 $('#lbpasswordGit').show();
815 $('#lbloginGit').show();
816 $('#repositoryGit').show();
817 $('#lbrepositoryGit').show();
818 $('#lbbranchGit').show();
819 $('#branchGit').show();
820 $('#listBranches').hide();
821 $('#divGitHubRepoDisplay').hide();
822 }
823 }
824
825 function setCookie(c_name, value, exdays)
826 {
827 var exdate=new Date();
828 exdate.setDate(exdate.getDate() + exdays);
829 var c_value=escape(value) + ((exdays==null) ? "" : "; expires="+exdate.toUTCString());
830 document.cookie=c_name + "=" + c_value;
831 }
832
833 function del_cookie(c_name)
834 {
835 document.cookie = c_name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
836 }
837
838 function updateCookie(user, pwd, repo, branch){
839 if(checkCookie()){
840 branchName = branch;
841 setCookie("logginNitdoc", base64.encode(user+':'+pwd+':'+repo+':'+branch), 1);
842 }
843 }
844
845 function getCookie(c_name)
846 {
847 var c_value = document.cookie;
848 var c_start = c_value.indexOf(" " + c_name + "=");
849 if (c_start == -1) { c_start = c_value.indexOf(c_name + "="); }
850 if (c_start == -1) { c_value = null; }
851 else
852 {
853 c_start = c_value.indexOf("=", c_start) + 1;
854 var c_end = c_value.indexOf(";", c_start);
855 if (c_end == -1) { c_end = c_value.length; }
856 c_value = unescape(c_value.substring(c_start,c_end));
857 }
858 return c_value;
859 }
860
861 function getUserPass(c_name){
862 var cookie = base64.decode(getCookie(c_name));
863 return base64.encode(cookie.split(':')[0] + ':' + cookie.split(':')[1]);
864 }
865
866 function checkCookie()
867 {
868 var cookie=getCookie("logginNitdoc");
869 if (cookie!=null && cookie!="")
870 {
871 cookie = base64.decode(cookie);
872 userName = cookie.split(':')[0];
873 password = cookie.split(':')[1];
874 githubRepo = cookie.split(':')[2];
875 branchName = cookie.split(':')[3];
876 return true;
877 }
878 else { return false; }
879 }
880
881
882 /*
883 * Base64
884 */
885 base64 = {};
886 base64.PADCHAR = '=';
887 base64.ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
888 base64.getbyte64 = function(s,i) {
889 // This is oddly fast, except on Chrome/V8.
890 // Minimal or no improvement in performance by using a
891 // object with properties mapping chars to value (eg. 'A': 0)
892 var idx = base64.ALPHA.indexOf(s.charAt(i));
893 if (idx == -1) {
894 throw "Cannot decode base64";
895 }
896 return idx;
897 }
898
899 base64.decode = function(s) {
900 // convert to string
901 s = "" + s;
902 var getbyte64 = base64.getbyte64;
903 var pads, i, b10;
904 var imax = s.length
905 if (imax == 0) {
906 return s;
907 }
908
909 if (imax % 4 != 0) {
910 throw "Cannot decode base64";
911 }
912
913 pads = 0
914 if (s.charAt(imax -1) == base64.PADCHAR) {
915 pads = 1;
916 if (s.charAt(imax -2) == base64.PADCHAR) {
917 pads = 2;
918 }
919 // either way, we want to ignore this last block
920 imax -= 4;
921 }
922
923 var x = [];
924 for (i = 0; i < imax; i += 4) {
925 b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) |
926 (getbyte64(s,i+2) << 6) | getbyte64(s,i+3);
927 x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff, b10 & 0xff));
928 }
929
930 switch (pads) {
931 case 1:
932 b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12) | (getbyte64(s,i+2) << 6)
933 x.push(String.fromCharCode(b10 >> 16, (b10 >> 8) & 0xff));
934 break;
935 case 2:
936 b10 = (getbyte64(s,i) << 18) | (getbyte64(s,i+1) << 12);
937 x.push(String.fromCharCode(b10 >> 16));
938 break;
939 }
940 return x.join('');
941 }
942
943 base64.getbyte = function(s,i) {
944 var x = s.charCodeAt(i);
945 if (x > 255) {
946 throw "INVALID_CHARACTER_ERR: DOM Exception 5";
947 }
948 return x;
949 }
950
951
952 base64.encode = function(s) {
953 if (arguments.length != 1) {
954 throw "SyntaxError: Not enough arguments";
955 }
956 var padchar = base64.PADCHAR;
957 var alpha = base64.ALPHA;
958 var getbyte = base64.getbyte;
959
960 var i, b10;
961 var x = [];
962
963 // convert to string
964 s = "" + s;
965
966 var imax = s.length - s.length % 3;
967
968 if (s.length == 0) {
969 return s;
970 }
971 for (i = 0; i < imax; i += 3) {
972 b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8) | getbyte(s,i+2);
973 x.push(alpha.charAt(b10 >> 18));
974 x.push(alpha.charAt((b10 >> 12) & 0x3F));
975 x.push(alpha.charAt((b10 >> 6) & 0x3f));
976 x.push(alpha.charAt(b10 & 0x3f));
977 }
978 switch (s.length - imax) {
979 case 1:
980 b10 = getbyte(s,i) << 16;
981 x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
982 padchar + padchar);
983 break;
984 case 2:
985 b10 = (getbyte(s,i) << 16) | (getbyte(s,i+1) << 8);
986 x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
987 alpha.charAt((b10 >> 6) & 0x3f) + padchar);
988 break;
989 }
990 return x.join('');
991 }
992
993
994
995 function getLastCommit()
996 {
997 var urlHead = '';
998 if(sessionStarted){ urlHead = "https://api.github.com/repos/"+userName+"/"+githubRepo+"/git/refs/heads/"+branchName;}
999 else{
1000 // TODO: get url of the original repo.
1001 return;
1002 }
1003
1004 $.ajax({
1005 beforeSend: function (xhr) {
1006 if (userB64 != ""){ xhr.setRequestHeader ("Authorization", userB64); }
1007 },
1008 type: "GET",
1009 url: urlHead,
1010 dataType:"json",
1011 async: false,
1012 success: function(success)
1013 {
1014 shaLastCommit = success.object.sha;
1015 }
1016 });
1017 }
1018
1019 function getBaseTree()
1020 {
1021 $.ajax({
1022 beforeSend: function (xhr) {
1023 if (userB64 != ""){ xhr.setRequestHeader ("Authorization", userB64); }
1024 },
1025 type: "GET",
1026 url: "https://api.github.com/repos/"+userName+"/"+githubRepo+"/git/commits/" + shaLastCommit,
1027 dataType:"json",
1028 async: false,
1029 success: function(success)
1030 {
1031 shaBaseTree = success.tree.sha;
1032 if (state){ setBlob(); }
1033 else{ return; }
1034 },
1035 error: function(){
1036 return;
1037 }
1038 });
1039 }
1040
1041 function setNewTree()
1042 {
1043 $.ajax({
1044 beforeSend: function (xhr) { xhr.setRequestHeader ("Authorization", userB64); },
1045 type: "POST",
1046 url: "https://api.github.com/repos/"+userName+"/"+githubRepo+"/git/trees",
1047 async: false,
1048 dataType:'json',
1049 data:'{ "base_tree" : "'+shaBaseTree+'", '+
1050 '"tree":[{ '+
1051 '"path":"'+ pathFile +'",'+
1052 '"mode":"100644",'+
1053 '"type":"blob",'+
1054 '"sha": "'+ shaBlob +'"'+
1055 '}] '+
1056 '}',
1057 success: function(success)
1058 { // si l'appel a bien fonctionné
1059 shaNewTree = success.sha;
1060 setNewCommit();
1061 },
1062 error: function(){
1063 return;
1064 }
1065 });
1066 }
1067
1068 function setNewCommit()
1069 {
1070 addSignedOff();
1071 $.ajax({
1072 beforeSend: function (xhr) { xhr.setRequestHeader ("Authorization", userB64); },
1073 type: "POST",
1074 url: "https://api.github.com/repos/"+userName+"/"+githubRepo+"/git/commits",
1075 async: false,
1076 dataType:'json',
1077 data:'{ "message" : "'+ commitMessage +'", '+
1078 '"parents" :"'+shaLastCommit+'",'+
1079 '"tree": "'+shaNewTree+'"'+
1080 '}',
1081 success: function(success)
1082 {
1083 shaNewCommit = success.sha;
1084 commit();
1085 },
1086 error: function(){
1087 return;
1088 }
1089 });
1090 }
1091
1092 //Create a commit
1093 function commit()
1094 {
1095 $.ajax({
1096 beforeSend: function (xhr) { xhr.setRequestHeader ("Authorization", userB64); },
1097 type: "POST",
1098 url: "https://api.github.com/repos/"+userName+"/"+githubRepo+"/git/refs/heads/"+branchName,
1099 dataType:'json',
1100 data:'{ "sha" : "'+shaNewCommit+'", '+
1101 '"force" :"true"'+
1102 '}',
1103 success: function(success) { displayMessage('Commit created successfully', 40, 40); },
1104 error:function(error){ displayMessage('Error ' + error.object.message, 40, 40); }
1105 });
1106 }
1107
1108 // Create a blob
1109 function setBlob()
1110 {
1111 $.ajax({
1112 beforeSend: function (xhr) { xhr.setRequestHeader ("Authorization", userB64); },
1113 type: "POST",
1114 url: "https://api.github.com/repos/"+userName+"/"+githubRepo+"/git/blobs",
1115 async: false,
1116 dataType:'json',
1117 data:'{ "content" : "'+text.replace(/\r?\n/g, '\\n').replace(/\t/g, '\\t').replace(/\"/g,'\\"')+'", '+
1118 '"encoding" :"utf-8"'+
1119 '}',
1120 success: function(success)
1121 {
1122 shaBlob = success.sha;
1123 setNewTree();
1124 },
1125 error:function(error){
1126 displayMessage('Error : Problem parsing JSON', 40, 40);
1127 return;
1128 }
1129 });
1130 }
1131
1132 // Display file content
1133 function getFileContent(urlFile, newComment)
1134 {
1135 $.ajax({
1136 beforeSend: function (xhr) {
1137 xhr.setRequestHeader ("Accept", "application/vnd.github-blob.raw");
1138 if (userB64 != ""){ xhr.setRequestHeader ("Authorization", userB64); }
1139 },
1140 type: "GET",
1141 url: urlFile,
1142 async:false,
1143 success: function(success)
1144 {
1145 state = true;
1146 replaceComment(newComment, success);
1147 }
1148 });
1149 }
1150
1151 function replaceComment(newComment, fileContent){
1152 var arrayNew = newComment.split('\n');
1153 var lNew = arrayNew.length;
1154 text = "";
1155 var lines = fileContent.split("\n");
1156 for (var i = 0; i < lines.length; i++) {
1157 if(i == commentLineStart){
1158 if(addNewComment){
1159 for(var indexLine=0; indexLine < lines[i+1].length; indexxLine++){
1160 if(lines[i+1].substr(indexLine,1) == "\t" || lines[i+1].substr(indexLine,1) == "#"){ text += lines[i+1].substr(indexLine,1); }
1161 else{ break;}
1162 }
1163 text += lines[i] + "\n";
1164 }
1165 // We change the comment
1166 for(var j = 0; j < lNew; j++){
1167 if(commentType == 1){ text += "\t# " + arrayNew[j] + "\n"; }
1168 else{
1169 if(arrayNew[j] == ""){ text += "#"+"\n"; }
1170 else{ text += "# " + arrayNew[j] + "\n"; }
1171 }
1172 }
1173 }
1174 else if(i < commentLineStart || i >= commentLineEnd){
1175 if(i == lines.length-1){ text += lines[i]; }
1176 else{ text += lines[i] + "\n"; }
1177 }
1178 }
1179 if(addNewComment){
1180 addNewComment = false;
1181 }
1182 }
1183
1184 function getCommentLastCommit(path, origin){
1185 var urlRaw;
1186 var bkBranch = '';
1187 if(origin){// We want to get the original file
1188 bkBranch = branchName;
1189 branchName = "master";
1190 }
1191 getLastCommit();
1192 if(shaLastCommit != ""){
1193 if (checkCookie()) {
1194 urlRaw="https://rawgithub.com/"+ userName +"/"+ githubRepo +"/" + shaLastCommit + "/" + path;
1195 $.ajax({
1196 type: "GET",
1197 url: urlRaw,
1198 async: false,
1199 success: function(success)
1200 {
1201 if(origin){ originalFileContent = success; }
1202 else{ currentfileContent = success; }
1203 }
1204 });
1205 }
1206 }
1207 if(origin){ branchName = bkBranch; }
1208 }
1209
1210 function displayMessage(msg, widthDiv, margModal){
1211 spinner.stop();
1212 $('#modal').hide();
1213 $('#btnCancelBranch').hide();
1214 $('#modalQuestion').show().prepend('<a class="close"><img src="resources/icons/close.png" class="btnCloseQuestion" title="Close" alt="Close" /></a>');
1215 $('#txtQuestion').text(msg);
1216 $('#btnCreateBranch').text("Ok");
1217 var xModal = $('#modalQuestion').css('width').split('px')[0];
1218 var yModal = $('#modalQuestion').css('height').split('px')[0];
1219 var x = $(document).width/2 - xModal/2;
1220 var y = $(document).height/2 - yModal/2;
1221 var xBtnBranch = $('#btnCreateBranch').css('width').split('px')[0];
1222 $('#modalQuestion').css({'left' : x, 'top' : y});
1223 $('#modalQuestion').show();
1224 $('#btnCreateBranch').css('margin-left', xModal/2 - xBtnBranch);
1225 $('body').append('<div id="fade"></div>');
1226 $('#fade').css({'filter' : 'alpha(opacity=80)'}).fadeIn();
1227 }
1228
1229 function displaySpinner(){
1230 spinner.spin(targetSpinner);
1231 $("#waitCommit").show();
1232 }
1233
1234 // Check if the repo already exist
1235 function isRepoExisting(){
1236 $.ajax({
1237 beforeSend: function (xhr) {
1238 if (userB64 != "") { xhr.setRequestHeader ("Authorization", userB64); }
1239 },
1240 type: "GET",
1241 url: "https://api.github.com/repos/"+userName+"/"+githubRepo,
1242 async:false,
1243 dataType:'json',
1244 success: function(){ repoExist = true; },
1245 error: function()
1246 {
1247 displayMessage('Repo not found !', 35, 45);
1248 repoExist = false;
1249 }
1250 });
1251 }
1252
1253 // Check if the branch already exist
1254 function isBranchExisting(){
1255 $.ajax({
1256 beforeSend: function (xhr) {
1257 if (userB64 != "") { xhr.setRequestHeader ("Authorization", userB64); }
1258 },
1259 type: "GET",
1260 url: "https://api.github.com/repos/"+userName+"/"+githubRepo+"/git/refs/heads/"+branchName,
1261 async:false,
1262 dataType:'json',
1263 success: function(){ branchExist = true; },
1264 error: function()
1265 {
1266 branchExist = false;
1267 editComment -= 1;
1268 $('#modal').hide();
1269 $('#txtQuestion').text("Are you sure you want to create that branch ?");
1270 $('#btnCancelBranch').show();
1271 $('#btnCreateBranch').text("Yes");
1272 $('#modalQuestion').show();
1273 $('#modalQuestion').show().prepend('<a class="close"><img src="resources/icons/close.png" class="btnCloseQuestion" title="Close" alt="Close" /></a>');
1274 $('body').append('<div id="fade"></div>');
1275 $('#fade').css({'filter' : 'alpha(opacity=80)'}).fadeIn();
1276 }
1277 });
1278 }
1279
1280 function getMasterSha()
1281 {
1282 $.ajax({
1283 beforeSend: function (xhr) {
1284 if (userB64 != ""){ xhr.setRequestHeader ("Authorization", userB64); }
1285 },
1286 type: "GET",
1287 url: "https://api.github.com/repos/"+userName+"/"+githubRepo+"/git/refs/heads/master",
1288 dataType:"json",
1289 async: false,
1290 success: function(success) { shaMaster = success.object.sha; }
1291 });
1292 }
1293
1294 function createBranch(){
1295
1296 getMasterSha();
1297
1298 $.ajax({
1299 beforeSend: function (xhr) { xhr.setRequestHeader ("Authorization", userB64); },
1300 type: "POST",
1301 url: "https://api.github.com/repos/"+userName+"/"+githubRepo+"/git/refs",
1302 data:'{ "ref" : "refs/heads/'+branchName+'",'+
1303 '"sha" : "'+shaMaster+'"'+
1304 '}',
1305 success: function(){ return; },
1306 error: function(){
1307 editComment -= 1;
1308 displayMessage('Impossible to create the new branch : ' + branchName, 40, 40);
1309 }
1310 });
1311 }
1312
1313 $.fn.spin = function(opts) {
1314 this.each(function() {
1315 var $this = $(this),
1316 data = $this.data();
1317
1318 if (data.spinner) {
1319 data.spinner.stop();
1320 delete data.spinner;
1321 }
1322 if (opts !== false) {
1323 data.spinner = new Spinner($.extend({color: $this.css('color')}, opts)).spin(this);
1324 }
1325 });
1326 return this;
1327 };
1328
1329 function reloadComment(){
1330 var path = $('pre[class=text_label]').attr('tag');
1331 $.when(getCommentLastCommit(path, false)).done(function(){
1332 if(sessionStarted){ getCommentLastCommit(path, true); }
1333 $('pre[class=text_label]').each(function(){ getCommentOfFunction($(this)); });
1334 });
1335 }
1336
1337 function getCommentOfFunction(element){
1338 var textC = "";
1339 var numL = element.attr("title");
1340 if(numL != null){
1341 commentLineStart = numL-1;
1342 commentLineEnd = element.attr('name').split(numL)[1].split('-')[1]-1;
1343 var lines = currentfileContent.split("\n");
1344 for (var i = 0; i < lines.length; i++) {
1345 if(i >= commentLineStart-1 && i <= commentLineEnd+1){
1346 if (lines[i].substr(1,1) == "#"){ textC += lines[i].substr(3,lines[i].length) + "\n";}
1347 else if(lines[i].substr(0,1) == '#'){ textC += lines[i].substr(2,lines[i].length) + "\n"; }
1348 }
1349 }
1350 if(textC != element.text){element.text(textC);}
1351 if (textC != "" && editComment > 0){
1352 var originContent = originalFileContent.split("\n");
1353 var origin = '';
1354 var lblDiff = element.parent().prev().children('#lblDiffCommit');
1355 var preSave = element.parent().children('#preSave');
1356 for (var i = 0; i < originContent.length; i++) {
1357 if(i >= commentLineStart-1 && i <= commentLineEnd+1){
1358 if (originContent[i].substr(1,1) == "#"){ origin += originContent[i].substr(3,originContent[i].length) + "\n";}
1359 else if(originContent[i].substr(0,1) == '#'){ origin += originContent[i].substr(2,originContent[i].length) + "\n"; }
1360 }
1361 }
1362 if(textC != origin && numL == numComment){
1363 // The comment is different compare to the original
1364 if(showcomment == false){ lblDiff.text("Show original comment"); }
1365 preSave.text(origin);
1366 }
1367 else if (numL == numComment){ lblDiff.text(""); }
1368 }
1369 }
1370 }
1371
1372 // Get list of branches
1373 function getListBranches()
1374 {
1375 cleanListBranches();
1376 $.ajax({
1377 beforeSend: function (xhr) {
1378 if ($("#login").val() != ""){ xhr.setRequestHeader ("Authorization", userB64); }
1379 },
1380 type: "GET",
1381 url: "https://api.github.com/repos/"+userName+"/"+githubRepo+"/branches",
1382 async:false,
1383 dataType:'json',
1384 success: function(success)
1385 {
1386 for(var branch in success) {
1387 var selected = '';
1388 if(branchName == success[branch].name){
1389 selected = 'selected';
1390 }
1391 $('#dropBranches').append('<option value="" '+ selected +'>' + success[branch].name + '</option>');
1392 }
1393 }
1394 });
1395 }
1396
1397 // Delete all option in the list
1398 function cleanListBranches(){
1399 $('#dropBranches').children("option").remove();
1400 }
1401
1402 function closeAllCommentInEdtiting(){
1403 $('a[id=cancelBtn]').each(function(){
1404 closeEditing($(this));
1405 });
1406 }
1407
1408 function closeEditing(tag){
1409 if(editComment > 0){ editComment -= 1; }
1410 // Hide itself
1411 tag.hide();
1412 // Hide commitBtn
1413 tag.next().hide();
1414 // Hide Textarea
1415 tag.prev().hide();
1416 // Show comment
1417 tag.prev().prev().show();
1418 }
1419
1420 function checkSignIn(){
1421 var response = false;
1422 $.ajax({
1423 beforeSend: function (xhr) {
1424 if ($("#login").val() != ""){ xhr.setRequestHeader ("Authorization", userB64); }
1425 },
1426 type: "GET",
1427 url: "https://api.github.com/repos/"+userName+"/"+githubRepo,
1428 async:false,
1429 dataType:'json',
1430 success: function(success)
1431 {
1432 getUserInfo();
1433 response = true;
1434 displayMessage('You are now logged in');
1435 },
1436 error: function()
1437 {
1438 displayMessage('Error : Wrong username or password');
1439 response = false;
1440 }
1441 });
1442 return response;
1443 }
1444
1445 function getUserInfo(){
1446 $.ajax({
1447 beforeSend: function (xhr) {
1448 if ($("#login").val() != ""){ xhr.setRequestHeader ("Authorization", userB64); }
1449 },
1450 type: "GET",
1451 url: "https://api.github.com/user/emails",
1452 async:false,
1453 dataType:'json',
1454 success: function(success)
1455 {
1456 userEmail = success;
1457 }
1458 });
1459 }
1460
1461 function getSignedOff(){
1462 $.ajax({
1463 beforeSend: function (xhr) {
1464 if ($("#login").val() != ""){ xhr.setRequestHeader ("Authorization", userB64); }
1465 },
1466 type: "GET",
1467 url: "https://api.github.com/users/"+userName,
1468 async:false,
1469 dataType:'json',
1470 success: function(success)
1471 {
1472 signedOff = success.name;
1473 }
1474 });
1475 }
1476
1477 function addSignedOff(){
1478 $.when(getUserInfo()).done(function(){
1479 $.when(getSignedOff()).done(function(){
1480 $('#commitMessage').val($('#commitMessage').val() + "\n\nSigned-off-by: "+signedOff+" <"+userEmail+">");
1481 });
1482 });
1483 resizeTextarea($('#commitMessage'));
1484 }
1485
1486 function removeSignedOff(){
1487 $('#commitMessage').val(commitMessage);
1488 resizeTextarea($('#commitMessage'));
1489 }
1490
1491 function resizeTextarea(element){
1492 var nLines = element.val().split('\n').length + 1;
1493 element.attr('rows', nLines);
1494 }
1495
1496 function showComment(element){
1497 // Display the original comment
1498 if (showcomment == true){
1499 showcomment = false;
1500 element.text("Show original comment");
1501 }
1502 else{
1503 // Show the comment updated in user's repo
1504 showcomment = true;
1505 element.text("Comment changed in "+githubRepo+" / "+branchName);
1506 }
1507 var parent = element.parent().next(".description");
1508 var textarea = parent.children('#fileContent');
1509 var text = textarea.val();
1510 var preSave = parent.children('#preSave');
1511 textarea.val(preSave.text());
1512 preSave.text(text);
1513 // Resize edit box
1514 textarea.height(textarea.prop("scrollHeight"));
1515 resizeTextarea(textarea);
1516 }