6ab4f39bfe5d65f2b5b335759c8d96d6d4e5df9d
[nit.git] / share / ni_nitdoc / scripts / github.js
1 $(document).ready(function() {
2 // set ui elements
3 ui = new GitHubUI();
4 ui.init();
5
6 // check cookie at startup
7 sessionCookie = new SessionCookie("nitdoc_github_session");
8 var session = sessionCookie.getSession();
9 //checkCookie()
10 if(session) {
11 githubAPI = new GitHubAPI(session.user, session.password, session.repo)
12 ui.activate();
13 console.log("Session started from cookie (head: "+ $("body").attr("data-github-head") +", head: "+ $("body").attr("data-github-base") +")");
14
15 } else {
16 console.log("No cookie found");
17 }
18 });
19
20 // Check if a comment is editing
21 window.onbeforeunload = function() {
22 if(ui.openedComments > 0){
23 return 'Are you sure you want to leave this page?';
24 }
25 };
26
27 /* GitHub API */
28
29 function GitHubAPI(login, password, repo) {
30 this.login = login;
31 this.password = password;
32 this.repo = repo;
33 this.auth = "Basic " + Base64.encode(login + ':' + password);
34
35 /* GitHub Account */
36
37 // try to login to github API
38 this.tryLogin = function() {
39 var res = false;
40 $.ajax({
41 beforeSend: function (xhr) {
42 xhr.setRequestHeader ("Authorization", githubAPI.auth);
43 },
44 type: "GET",
45 url: "https://api.github.com/repos/" + this.login+ "/" + this.repo,
46 async: false,
47 dataType: 'json',
48 success: function() {
49 res = true;
50 }
51 });
52 return res;
53 }
54
55 this.getUserInfos = function() {
56 var res = false;
57 $.ajax({
58 beforeSend: function (xhr) {
59 xhr.setRequestHeader ("Authorization", githubAPI.auth);
60 },
61 type: "GET",
62 url: "https://api.github.com/users/" + this.login,
63 async: false,
64 dataType: 'json',
65 success: function(response) {
66 res = response;
67 },
68 error: function(response) {
69 res = response;
70 }
71 });
72 return res;
73 }
74
75 this.getSignedOff = function() {
76 var infos = this.getUserInfos();
77 return infos.name + " <" + infos.email + ">";
78 }
79
80 /* GitHub Repos */
81
82 this.getFile = function(path, branch){
83 var res = false;
84 $.ajax({
85 type: "GET",
86 url: "https://api.github.com/repos/" + this.login + "/" + this.repo + "/contents/" + path,
87 data: {
88 ref: branch
89 },
90 async: false,
91 dataType: 'json',
92 success: function(response) {
93 res = response;
94 },
95 error: function(response) {
96 res = response;
97 }
98 });
99 return res;
100 }
101
102 /* GitHub commits */
103
104 // get the latest commit on `branchName`
105 this.getLastCommit = function(branchName) {
106 var res = false;
107 $.ajax({
108 beforeSend: function (xhr) {
109 xhr.setRequestHeader ("Authorization", githubAPI.auth);
110 },
111 type: "GET",
112 url: "https://api.github.com/repos/" + this.login + "/" + this.repo + "/git/refs/heads/" + branchName,
113 async: false,
114 dataType: 'json',
115 success: function(response) {
116 res = response.object;
117 },
118 error: function(response) {
119 res = response;
120 }
121 });
122 return res;
123 }
124
125 // get the base tree for commit
126 this.getTree = function(sha) {
127 var res = false;
128 $.ajax({
129 beforeSend: function (xhr) {
130 xhr.setRequestHeader ("Authorization", githubAPI.auth);
131 },
132 type: "GET",
133 url: "https://api.github.com/repos/" + this.login + "/" + this.repo + "/git/trees/" + sha + "?recursive=1",
134 async: false,
135 dataType: 'json',
136 success: function(response) {
137 res = response;
138 },
139 error: function(response) {
140 res = response;
141 }
142 });
143 return res;
144 }
145
146 // create a new blob
147 this.createBlob = function(content) {
148 var res = false;
149 $.ajax({
150 beforeSend: function (xhr) {
151 xhr.setRequestHeader ("Authorization", githubAPI.auth);
152 },
153 type: "POST",
154 url: "https://api.github.com/repos/" + this.login + "/" + this.repo + "/git/blobs",
155 async: false,
156 dataType: 'json',
157 data: JSON.stringify({
158 content: Base64.encode(content),
159 encoding: "base64"
160 }),
161 success: function(response) {
162 res = response;
163 },
164 error: function(response) {
165 res = response;
166 }
167 });
168 return res;
169 }
170
171 // create a new tree from a base tree
172 this.createTree = function(baseTree, path, blob) {
173 var res = false;
174 $.ajax({
175 beforeSend: function (xhr) {
176 xhr.setRequestHeader ("Authorization", githubAPI.auth);
177 },
178 type: "POST",
179 url: "https://api.github.com/repos/" + this.login + "/" + this.repo + "/git/trees",
180 data: JSON.stringify({
181 base_tree: baseTree.sha,
182 tree: [{
183 path: path,
184 mode: 100644, // file (blob)
185 type: "blob",
186 sha: blob.sha
187 }]
188 }),
189 async: false,
190 dataType: 'json',
191 success: function(response) {
192 res = response;
193 },
194 error: function(response) {
195 res = response;
196 }
197 });
198 return res;
199 }
200
201 // create a new commit
202 this.createCommit = function(message, parentCommit, tree) {
203 var res = false;
204 $.ajax({
205 beforeSend: function (xhr) {
206 xhr.setRequestHeader ("Authorization", githubAPI.auth);
207 },
208 type: "POST",
209 url: "https://api.github.com/repos/" + this.login + "/" + this.repo + "/git/commits",
210 data: JSON.stringify({
211 message: message,
212 parents: parentCommit,
213 tree: tree.sha,
214 }),
215 async: false,
216 dataType: 'json',
217 success: function(response) {
218 res = response;
219 },
220 error: function(response) {
221 res = response;
222 }
223 });
224 return res;
225 }
226
227 // create a pull request
228 this.createPullRequest = function(title, body, base, head) {
229 var res = false;
230 $.ajax({
231 beforeSend: function (xhr) {
232 xhr.setRequestHeader ("Authorization", githubAPI.auth);
233 },
234 type: "POST",
235 url: "https://api.github.com/repos/" + this.login + "/" + this.repo + "/pulls",
236 data: JSON.stringify({
237 title: title,
238 body: body,
239 base: base,
240 head: head
241 }),
242 async: false,
243 dataType: 'json',
244 success: function(response) {
245 res = response;
246 },
247 error: function(response) {
248 res = response;
249 }
250 });
251 return res;
252 }
253
254 // update a pull request
255 this.updatePullRequest = function(title, body, state, number) {
256 var res = false;
257 $.ajax({
258 beforeSend: function (xhr) {
259 xhr.setRequestHeader ("Authorization", githubAPI.auth);
260 },
261 type: "PATCH",
262 url: "https://api.github.com/repos/" + this.login + "/" + this.repo + "/pulls/" + number,
263 data: JSON.stringify({
264 title: title,
265 body: body,
266 state: state
267 }),
268 async: false,
269 dataType: 'json',
270 success: function(response) {
271 res = response;
272 },
273 error: function(response) {
274 res = response;
275 }
276 });
277 return res;
278 }
279 }
280 var githubAPI;
281
282 /* GitHub cookie management */
283
284 function SessionCookie(cookieName) {
285 this.cookieName = cookieName
286
287 this.setSession = function (user, password, repo) {
288 var value = Base64.encode(JSON.stringify({
289 user: user,
290 password: password,
291 repo: repo
292 }));
293 var exdate = new Date();
294 exdate.setDate(exdate.getDate() + 1);
295 document.cookie = this.cookieName + "=" + value + "; expires=" + exdate.toUTCString();
296 }
297
298 this.delSession = function() {
299 document.cookie = this.cookieName + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
300 }
301
302 this.getCookieDatas = function() {
303 var c_name = this.cookieName;
304 var c_value = document.cookie;
305 var c_start = c_value.indexOf(" " + c_name + "=");
306 if (c_start == -1) { c_start = c_value.indexOf(c_name + "="); }
307 if (c_start == -1) {
308 c_value = null;
309 } else {
310 c_start = c_value.indexOf("=", c_start) + 1;
311 var c_end = c_value.indexOf(";", c_start);
312 if (c_end == -1) { c_end = c_value.length; }
313 c_value = unescape(c_value.substring(c_start,c_end));
314 }
315 return c_value;
316 }
317
318 this.getSession = function() {
319 var cookie = this.getCookieDatas();
320 if (!!cookie) {
321 return JSON.parse(Base64.decode(cookie));
322 }
323 return false;
324 }
325 }
326 var sessionCookie;
327
328 /* GitHub login box */
329
330 function LoginBox() {
331 // Add login box
332 $("nav.main ul").append(
333 $("<li id='liGitHub'></li>")
334 .append(
335 $("<a class='btn' id='logGitHub'><img id='imgGitHub' src='resources/icons/github-icon.png' alt='GitHub'/></a>")
336 .click(function() { ui.loginBox.toggle() })
337 )
338 .append(
339 " <div id='loginBox' style='display: none;'>" +
340 " <div class='arrow'>&nbsp;</div>" +
341 " <h3>Github Sign In</h3>" +
342 " <div id='signedIn' style='display: none'>" +
343 " <label id='logginMessage'>Hello " +
344 " <a id='githubAccount'><strong id='nickName'></strong></a>!" +
345 " </label>" +
346 " <label for='github-repo'>Repo</label>" +
347 " <input id='github-repo' disabled='disabled' type='text'/>" +
348 " <label for='github-head'>Head</label>" +
349 " <input id='github-head' disabled='disabled' type='text'/>" +
350 " <label for='github-base'>Base</label>" +
351 " <input id='github-base' disabled='disabled' type='text'/>" +
352 " <button class='signIn github'><img src='resources/icons/github-icon.png'/>Sign Off</button>" +
353 " </div>" +
354 " <div id='signedOff'>" +
355 " <form>" +
356 " <label for='loginGit'>Username</label>" +
357 " <input id='loginGit' type='text'/>" +
358 " <label for='passwordGit'>Password</label>" +
359 " <input id='passwordGit' type='password'/>" +
360 " <label for='repositoryGit'>Repository</label>" +
361 " <input id='repositoryGit' type='text'/>" +
362 " <button class='signIn github'><img src='resources/icons/github-icon.png'/>Sign In</button>" +
363 " </form>" +
364 " </div>" +
365 " </div>" +
366 " </div>"
367 )
368 );
369
370 // Login with github user or logout current session
371 $("#loginBox .signIn").click(function(){
372 if($('#signedIn').is(':hidden')){
373 if(!$('#loginGit').val() || !$('#passwordGit').val() || !$('#repositoryGit').val()) {
374 ui.openModalBox("Login incorrect!", "Please enter your username, password and repository.", true);
375 } else {
376 githubAPI = new GitHubAPI($('#loginGit').val(), $('#passwordGit').val(), $('#repositoryGit').val());
377 if(githubAPI.tryLogin()) {
378 // open session and set cookie
379 sessionCookie.setSession(githubAPI.login, githubAPI.password, githubAPI.repo);
380 ui.activate();
381 } else {
382 githubAPI = false;
383 ui.openModalBox("Login incorrect!", "Your login information was incorrect!", true);
384 }
385 }
386 } else {
387 ui.disactivate();
388 ui.loginBox.toggle();
389 }
390 return false;
391 });
392
393 this.toggle = function() {
394 if ($('#loginBox').is(':hidden')) {
395 $('#loginBox').show();
396 if (!$('#loginGit').is(':hidden')) { $('#loginGit').focus(); }
397 } else {
398 $('#loginBox').hide();
399 }
400 }
401 }
402
403 /* Comment edition UI */
404
405 function GitHubUI() {
406 this.loginBox = new LoginBox();
407 this.openedComments = 0;
408
409 this.init = function() {
410 $("body").append("<div id='modal'></div>");
411 $('body').append('<div id="fade"></div>');
412 }
413
414 this.disactivate = function() {
415 // close session and purge cookie
416 sessionCookie.delSession();
417 localStorage.clear();
418 window.location.reload();
419 }
420
421 this.activate = function() {
422 // get lastest commit
423 var latest = githubAPI.getLastCommit($("body").attr("data-github-head"));
424 if(!latest || !latest.sha) {
425 this.openModalBox("Head branch not found!", latest.status + ": " + latest.statusText, true)
426 return;
427 }
428 if(localStorage.latestCommit != latest.sha) {
429 console.log("Latest commit changed: cleaned cache");
430 localStorage.requests = "[]";
431 localStorage.latestCommit = latest.sha;
432 }
433 console.log("Latest commit sha: " + localStorage.latestCommit);
434
435 // reload loginBox
436 $('#signedOff').hide();
437 $('#signedIn').show();
438 $("#imgGitHub").attr("src", "resources/icons/github-icon-w.png");
439 $("#liGitHub").addClass("current");
440
441 // login form values
442 $('#nickName').text(githubAPI.login);
443 $('#githubAccount').attr("href", "https://github.com/" + githubAPI.login);
444 $('#github-repo').val(githubAPI.repo);
445 $('#github-base').val($("body").attr("data-github-base"));
446 $('#github-head').val($("body").attr("data-github-head"));
447
448 // Activate edit mode
449
450 // Add hidden <pre> to empty commits
451 $("span.noComment").each(function() {
452 var baseComment = $(this).parent().prev();
453 var location = ui.parseLocation(baseComment.attr("data-comment-location"));
454 location.lend = location.lstart;
455 var locString = "../" + location.path + ":" + location.lstart + "," + location.tabpos + "--" + location.lend + ",0";
456 baseComment.attr("data-comment-location", locString);
457 $(this).html("<a class='noComment'>add comment</a> for ");
458 });
459 $('span.noComment a').each(function() {
460 $(this).css("cursor", "pointer")
461 $(this).click(function() {
462 $(this).parent().hide();
463 ui.openCommentBox($(this).parent().parent().prev());
464 });
465 });
466 $('.description div.comment').each(function() {
467 $(this).css("cursor", "pointer")
468 $(this).click(function() {
469 ui.openCommentBox($(this).prev());
470 $(this).hide();
471 });
472 });
473
474 // load comment from current branch
475 this.reloadComments();
476 }
477
478 this.openModalBox = function(title, msg, isError) {
479 $('#fade').show();
480 $('#modal')
481 .empty()
482 .append($('<a class="close"><img src="resources/icons/close.png" class="btnClose" title="Close" alt="Close"/></a>').click(function() {ui.closeModalBox()}))
483 .append("<h3>" + title + "</h3>")
484 .append("<div>" + msg + "</div>")
485 .append(
486 $("<div class='buttonArea'>")
487 .append($("<button>Ok</button>").click(function() {ui.closeModalBox()}))
488 )
489 .show()
490 .css("top", "50%")
491 .css("margin-top", -($('#modal').outerHeight() / 2) + "px")
492 .css("left", "50%")
493 .css("margin-left", -($('#modal').outerWidth() / 2) + "px");
494 if(isError) {
495 $("#modal h3").addClass("error");
496 }
497 }
498
499 this.closeModalBox = function() {
500 $('#fade , #modal').hide();
501 }
502
503 this.openCommentBox = function(baseArea) {
504 console.log(baseArea);
505 this.openedComments += 1;
506 // get text and format it
507 var formated = "";
508 var len = 1;
509 var commentLines = baseArea.text().split('\n');
510 for (var i = 0; i < commentLines.length; i++) {
511 formated += commentLines[i];
512 if(i < commentLines.length - 2){ formated += "\n"; }
513 }
514 len = commentLines.length - 1;
515
516 // create comment box
517 var tarea = $("<textarea rows='" + len + "'>" + formated + "</textarea>");
518 var width = width = baseArea.parent().innerWidth() - 13;
519 tarea.css("width", width + "px");
520 tarea.css("display", "block");
521 tarea.keyup(function(event) {
522 $(event.target).css("height", (event.target.value.split(/\r|\n/).length * 16) + "px");
523 var baseComment = $(event.target).parents("div.description").find("textarea.baseComment").text();
524 if ($(event.target).val() != baseComment) {
525 $(event.target).parent().find("button.commit").removeAttr("disabled");
526 } else {
527 $(event.target).parent().find("button.commit").attr("disabled", "disabled");
528 }
529 });
530 tarea.keydown(function(event) {
531 if(event.keyCode == 13){
532 $(event.target).css("height", ($(event.target).outerHeight() + 6) + "px");
533 }
534 });
535 var commentBox = $("<div class='commentBox'></div>")
536 .attr("data-comment-namespace", baseArea.attr("data-comment-namespace"))
537 .attr("data-comment-location", baseArea.attr("data-comment-location"))
538 .append(tarea)
539 .append(
540 $("<a class='preview'>preview</a>")
541 .click(function() {
542 var converter = new Markdown.Converter()
543 var html = converter.makeHtml(tarea.val());
544 ui.openModalBox("Preview", html, false);
545 })
546 )
547 .append(
548 $("<button class='commit'>Commit</button>")
549 .click(function() {
550 ui.openCommitBox($(this).parent());
551 })
552 )
553 .append(
554 $("<button class='cancel'>Cancel</button>")
555 .click(function() {ui.closeCommentBox($(this).parent())})
556 );
557 if(!baseArea.text()) {
558 commentBox.addClass("newComment");
559 }
560 baseArea.after(commentBox);
561 tarea.trigger("keyup");
562 }
563
564 this.closeCommentBox = function(commentBox) {
565 this.openedComments -= 1;
566 if(!!commentBox.parent().find(".baseComment").text()) {
567 commentBox.parent().find("div.comment").show();
568 } else if(commentBox.hasClass("newComment")) {
569 commentBox.next().find("span.noComment").show();
570 }
571 commentBox.remove();
572 }
573
574 this.openCommitBox = function(commentBox) {
575 $('#fade').show();
576 $('#modal')
577 .empty()
578 .append($('<a class="close"><img src="resources/icons/close.png" class="btnClose" title="Close" alt="Close"/></a>').click(function() {ui.closeModalBox()}))
579 .append("<h3>Commit changes</h3><br/>")
580 .append("<label for='message'>Message:</label><br/>")
581 .append("<textarea id='message'>Wikidoc: " + (commentBox.hasClass("newComment") ? "added" : "modified") + " comment for " + commentBox.attr("data-comment-namespace") + "</textarea><br/>")
582 .append("<input id='signOff' type='checkbox' value='Signed-off-by: " + githubAPI.getSignedOff() + "'/>")
583 .change(function(e) {
584 if ($(e.target).is(':checked')) {
585 $("#commitBtn").removeAttr("disabled");
586 } else {
587 $("#commitBtn").attr("disabled", "disabled");
588 }
589 })
590 .append("<label for='signOff'> Signed-off-by: " + githubAPI.getSignedOff() + "</label>")
591 .append(
592 $("<div class='buttonArea'>")
593 .append(
594 $("<button id='commitBtn' disabled='disabled' class='github'><img src='resources/icons/github-icon.png'/>Commit</button>")
595 .mousedown(function() {
596 $(this).text("Commiting...");
597 })
598 .mouseup(function() {
599 ui.commit($(this).parent().parent(), commentBox)
600 })
601 )
602 )
603 .show()
604 .css("top", "50%")
605 .css("margin-top", -($('#modal').outerHeight() / 2) + "px")
606 .css("left", "50%")
607 .css("margin-left", -($('#modal').outerWidth() / 2) + "px");
608 }
609
610
611 this.commit = function(commitBox, commentBox) {
612 // get comments datas
613 var location = this.parseLocation(commentBox.attr("data-comment-location"));
614 var comment = commentBox.find("textarea").val();
615
616 // get file content from github
617 var origFile = githubAPI.getFile(location.path, $('#github-head').val());
618 if(!origFile.content) {
619 this.openModalBox("Unable to locate source file!", origFile.status + ": " + origFile.statusText);
620 return;
621 }
622 var base64Content = origFile.content.substring(0, origFile.content.length - 1)
623 var fileContent = Base64.decode(base64Content);
624
625 // commit
626 var newContent = this.mergeComment(fileContent, comment, location);
627 var message = commitBox.find("#message").val() + "\n\n" + commitBox.find("#signOff").val();
628 var response = this.pushComment($('#github-base').val(), $('#github-head').val(), location.path, newContent, message)
629 if(!response) {
630 // abort procedure
631 return;
632 }
633
634 // save pull request in cookie
635 var requests = [];
636 if(!!localStorage.requests) {requests = JSON.parse(localStorage.requests)}
637 requests[requests.length] = {
638 request: response,
639 location: commentBox.attr("data-comment-location"),
640 comment: Base64.encode(comment)
641 };
642 localStorage.requests = JSON.stringify(requests);
643 // close boxes
644 this.closeModalBox()
645 this.closeCommentBox(commentBox);
646 // reload comments
647 this.reloadComments();
648 }
649
650 /*
651 Creating a new pull request with the new comment take 5 steps:
652 1. get the base tree from latest commit
653 2. create a new blob with updated file content
654 3. post a new tree from base tree and blob
655 4. post the new commit with new tree
656 5. create the pull request
657 */
658 this.pushComment = function(base, branch, path, content, message) {
659 var baseTree = githubAPI.getTree(localStorage.latestCommit);
660 if(!baseTree.sha) {
661 this.openModalBox("Unable to locate base tree!", baseTree.status + ": " + baseTree.statusText, true);
662 return false;
663 }
664 console.log("Base tree: " + baseTree.url);
665 var newBlob = githubAPI.createBlob(content);
666 if(!newBlob.sha) {
667 this.openModalBox("Unable to create new blob!", newBlob.status + ": " + newBlob.statusText, true);
668 return false;
669 }
670 console.log("New blob: " + newBlob.url);
671 var newTree = githubAPI.createTree(baseTree, path, newBlob);
672 if(!newTree.sha) {
673 this.openModalBox("Unable to create new tree!", newTree.status + ": " + newTree.statusText, true);
674 return false;
675 }
676 console.log("New tree: " + newTree.url);
677 var newCommit = githubAPI.createCommit(message, localStorage.latestCommit, newTree);
678 if(!newCommit.sha) {
679 this.openModalBox("Unable to create new commit!", newCommit.status + ": " + newCommit.statusText, true);
680 return false;
681 }
682 console.log("New commit: " + newCommit.url);
683 var pullRequest = githubAPI.createPullRequest(message.split("\n\n")[0], message, base, newCommit.sha);
684 if(!pullRequest.number) {
685 this.openModalBox("Unable to create pull request!", pullRequest.status + ": " + pullRequest.statusText, true);
686 return false;
687 }
688 console.log("New pull request: " + pullRequest.url);
689 return pullRequest;
690 }
691
692 this.reloadComments = function() {
693 if(!localStorage.requests){ return; }
694 var requests = JSON.parse(localStorage.requests);
695 var converter = new Markdown.Converter();
696 // Look for modified comments in page
697 for(i in requests) {
698 var request = requests[i];
699 $("textarea[data-comment-location=\"" + request.location + "\"]").each(function () {
700 var div = $(this).next();
701 if(request.isClosed) {
702 if(div.is("div.comment.newComment")) {
703 // hide empty comment
704 div.next().remove();
705 div.next().find("span.noComment").show();
706 div.remove();
707 } else if(div.is("div.comment.locked")) {
708 // unlock comment
709 div.removeClass("locked");
710 div.css("cursor", "pointer")
711 div.click(function() {
712 ui.openCommentBox(div.prev());
713 });
714 div.next().remove();
715 }
716 } else {
717 // create div for the new coment
718 if(!div.is("div.comment")) {
719 $(this).after("<div class='comment newComment'></div>");
720 div = $(this).next();
721 }
722 // lock modified comment
723 if(!div.hasClass("locked")) {
724 // convert modified comment to markdown
725 div.empty()
726 div.append(converter.makeHtml(Base64.decode(request.comment)));
727 // lock click
728 div.css("cursor", "auto");
729 div.unbind("click");
730 div.addClass("locked");
731 div.after(
732 $("<p class='locked inheritance'>")
733 .text("comment modified in ")
734 .append("<a href='"+ request.request.html_url +"' target='_blank' title='Review on GitHub'>pull request #"+ request.request.number +"</a>")
735 .append(" ")
736 .append(
737 $("<a data-pullrequest-number='"+ request.request.number +"' class='cancel'>cancel</a>")
738 .click(function (){
739 ui.closePullRequest($(this).attr("data-pullrequest-number"));
740 })
741 )
742 );
743 }
744 // hide "add comment" link
745 if(div.hasClass("newComment")) {
746 div.next().next().find("span.noComment").hide();
747 }
748 }
749
750 });
751 }
752 }
753
754 this.closePullRequest = function(number) {
755 // close pull request
756 var res = githubAPI.updatePullRequest("Canceled from Wikidoc", "", "closed", number);
757 if(!res.id) {
758 this.openModalBox("Unable to close pull request!", res.status + ": " + res.statusText, true);
759 return false;
760 }
761 // remove from localstorage
762 var requests = JSON.parse(localStorage.requests);
763 for(i in requests) {
764 if(requests[i].request.number == number) {
765 requests[i].isClosed = true;
766 }
767 }
768 localStorage.requests = JSON.stringify(requests);
769 ui.reloadComments()
770 }
771
772 /* Utility */
773
774 // Extract infos from string location "../lib/standard/collection/array.nit:457,1--458,0"
775 this.parseLocation = function(location) {
776 var parts = location.split(":");
777 var loc = new Object();
778 loc.path = parts[0].substr(3, parts[0].length);
779 loc.lstart = parseInt(parts[1].split("--")[0].split(",")[0]);
780 loc.tabpos = parseInt(parts[1].split("--")[0].split(",")[1]);
781 loc.lend = parseInt(parts[1].split("--")[1].split(",")[0]);
782 return loc;
783 }
784
785 // Meld modified comment into file content
786 this.mergeComment = function(fileContent, comment, location) {
787 // replace comment in file content
788 var res = new String();
789 var lines = fileContent.split("\n");
790 // copy lines fron 0 to lstart
791 for(var i = 0; i < location.lstart - 1; i++) {
792 res += lines[i] + "\n";
793 }
794 // set comment
795 if(comment && comment != "") {
796 var commentLines = comment.split("\n");
797 for(var i = 0; i < commentLines.length; i++) {
798 var line = commentLines[i];
799 var tab = location.tabpos > 1 ? "\t" : "";
800 res += tab + "# " + line + "\n";
801 }
802 }
803 // copy lines fron lend to end
804 for(var i = location.lend - 1; i < lines.length; i++) {
805 res += lines[i];
806 if(i < lines.length - 1) { res += "\n"; }
807 }
808 return res;
809 }
810
811 }
812 var ui;
813