1e932adeccec268b75f553411377dec0de22b2ce
[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 $(this).addClass("editComment");
453 var baseComment = $(this).parent().prev();
454 var location = ui.parseLocation(baseComment.attr("data-comment-location"));
455 location.lend = location.lstart;
456 var locString = "../" + location.path + ":" + location.lstart + "," + location.tabpos + "--" + location.lend + ",0";
457 baseComment.attr("data-comment-location", locString);
458 $(this).html("<a class='editComment noComment'>add comment</a> for ");
459 });
460 $('.description div.comment').each(function() {
461 var p = $(this).next();
462 p.prepend("<span class='editComment'><a class='editComment'>edit comment</a> for </span>")
463 });
464 $('a.editComment').each(function() {
465 $(this).css("cursor", "pointer")
466 $(this).click(function() {
467 $(this).parent().hide();
468 if(!$(this).hasClass("noComment")) {
469 $(this).parent().parent().prev().hide();
470 ui.openCommentBox($(this).parent().parent().prev().prev());
471 } else {
472 ui.openCommentBox($(this).parent().parent().prev());
473 }
474 });
475 });
476
477 // load comment from current branch
478 this.reloadComments();
479 }
480
481 this.openModalBox = function(title, msg, isError) {
482 $('#fade').show();
483 $('#modal')
484 .empty()
485 .append($('<a class="close"><img src="resources/icons/close.png" class="btnClose" title="Close" alt="Close"/></a>').click(function() {ui.closeModalBox()}))
486 .append("<h3>" + title + "</h3>")
487 .append("<div>" + msg + "</div>")
488 .append(
489 $("<div class='buttonArea'>")
490 .append($("<button>Ok</button>").click(function() {ui.closeModalBox()}))
491 )
492 .show()
493 .css("top", "50%")
494 .css("margin-top", -($('#modal').outerHeight() / 2) + "px")
495 .css("left", "50%")
496 .css("margin-left", -($('#modal').outerWidth() / 2) + "px");
497 if(isError) {
498 $("#modal h3").addClass("error");
499 }
500 }
501
502 this.closeModalBox = function() {
503 $('#fade , #modal').hide();
504 }
505
506 this.openCommentBox = function(baseArea) {
507 this.openedComments += 1;
508 // get text and format it
509 var formated = "";
510 var len = 1;
511 var commentLines = baseArea.text().split('\n');
512 for (var i = 0; i < commentLines.length; i++) {
513 formated += commentLines[i];
514 if(i < commentLines.length - 2){ formated += "\n"; }
515 }
516 len = commentLines.length - 1;
517
518 // create comment box
519 var tarea = $("<textarea rows='" + len + "'>" + formated + "</textarea>");
520 var width = width = baseArea.parent().innerWidth() - 13;
521 tarea.css("width", width + "px");
522 tarea.css("display", "block");
523 tarea.keyup(function(event) {
524 $(event.target).css("height", (event.target.value.split(/\r|\n/).length * 16) + "px");
525 var baseComment = $(event.target).parents("div.description").find("textarea.baseComment").text();
526 if ($(event.target).val() != baseComment) {
527 $(event.target).parent().find("button.commit").removeAttr("disabled");
528 } else {
529 $(event.target).parent().find("button.commit").attr("disabled", "disabled");
530 }
531 });
532 tarea.keydown(function(event) {
533 if(event.keyCode == 13){
534 $(event.target).css("height", ($(event.target).outerHeight() + 6) + "px");
535 }
536 });
537 var commentBox = $("<div class='commentBox'></div>")
538 .attr("data-comment-namespace", baseArea.attr("data-comment-namespace"))
539 .attr("data-comment-location", baseArea.attr("data-comment-location"))
540 .append(tarea)
541 .append(
542 $("<a class='preview'>preview</a>")
543 .click(function() {
544 var converter = new Markdown.Converter()
545 var html = converter.makeHtml(tarea.val());
546 ui.openModalBox("Preview", html, false);
547 })
548 )
549 .append(
550 $("<button class='commit'>Commit</button>")
551 .click(function() {
552 ui.openCommitBox($(this).parent());
553 })
554 )
555 .append(
556 $("<button class='cancel'>Cancel</button>")
557 .click(function() {ui.closeCommentBox($(this).parent())})
558 );
559 if(!baseArea.text()) {
560 commentBox.addClass("newComment");
561 }
562 baseArea.after(commentBox);
563 tarea.trigger("keyup");
564 }
565
566 this.closeCommentBox = function(commentBox) {
567 this.openedComments -= 1;
568 var target = commentBox.next();
569 if(!commentBox.hasClass("newComment")) {
570 target.show();
571 target = target.next();
572 }
573 target.find("span.editComment").show();
574 commentBox.remove();
575 }
576
577 this.openCommitBox = function(commentBox) {
578 $('#fade').show();
579 $('#modal')
580 .empty()
581 .append($('<a class="close"><img src="resources/icons/close.png" class="btnClose" title="Close" alt="Close"/></a>').click(function() {ui.closeModalBox()}))
582 .append("<h3>Commit changes</h3><br/>")
583 .append("<label for='message'>Message:</label><br/>")
584 .append("<textarea id='message'>Wikidoc: " + (commentBox.hasClass("newComment") ? "added" : "modified") + " comment for " + commentBox.attr("data-comment-namespace") + "</textarea><br/>")
585 .append("<input id='signOff' type='checkbox' value='Signed-off-by: " + githubAPI.getSignedOff() + "'/>")
586 .change(function(e) {
587 if ($(e.target).is(':checked')) {
588 $("#commitBtn").removeAttr("disabled");
589 } else {
590 $("#commitBtn").attr("disabled", "disabled");
591 }
592 })
593 .append("<label for='signOff'> Signed-off-by: " + githubAPI.getSignedOff() + "</label>")
594 .append(
595 $("<div class='buttonArea'>")
596 .append(
597 $("<button id='commitBtn' disabled='disabled' class='github'><img src='resources/icons/github-icon.png'/>Commit</button>")
598 .mousedown(function() {
599 $(this).text("Commiting...");
600 })
601 .mouseup(function() {
602 ui.commit($(this).parent().parent(), commentBox)
603 })
604 )
605 )
606 .show()
607 .css("top", "50%")
608 .css("margin-top", -($('#modal').outerHeight() / 2) + "px")
609 .css("left", "50%")
610 .css("margin-left", -($('#modal').outerWidth() / 2) + "px");
611 }
612
613
614 this.commit = function(commitBox, commentBox) {
615 // get comments datas
616 var location = this.parseLocation(commentBox.attr("data-comment-location"));
617 var comment = commentBox.find("textarea").val();
618
619 // get file content from github
620 var origFile = githubAPI.getFile(location.path, $('#github-head').val());
621 if(!origFile.content) {
622 this.openModalBox("Unable to locate source file!", origFile.status + ": " + origFile.statusText);
623 return;
624 }
625 var base64Content = origFile.content.substring(0, origFile.content.length - 1)
626 var fileContent = Base64.decode(base64Content);
627
628 // commit
629 var newContent = this.mergeComment(fileContent, comment, location);
630 var message = commitBox.find("#message").val() + "\n\n" + commitBox.find("#signOff").val();
631 var response = this.pushComment($('#github-base').val(), $('#github-head').val(), location.path, newContent, message)
632 if(!response) {
633 // abort procedure
634 return;
635 }
636
637 // save pull request in cookie
638 var requests = [];
639 if(!!localStorage.requests) {requests = JSON.parse(localStorage.requests)}
640 requests[requests.length] = {
641 request: response,
642 location: commentBox.attr("data-comment-location"),
643 comment: Base64.encode(comment)
644 };
645 localStorage.requests = JSON.stringify(requests);
646 // close boxes
647 this.closeModalBox()
648 this.closeCommentBox(commentBox);
649 // reload comments
650 this.reloadComments();
651 }
652
653 /*
654 Creating a new pull request with the new comment take 5 steps:
655 1. get the base tree from latest commit
656 2. create a new blob with updated file content
657 3. post a new tree from base tree and blob
658 4. post the new commit with new tree
659 5. create the pull request
660 */
661 this.pushComment = function(base, branch, path, content, message) {
662 var baseTree = githubAPI.getTree(localStorage.latestCommit);
663 if(!baseTree.sha) {
664 this.openModalBox("Unable to locate base tree!", baseTree.status + ": " + baseTree.statusText, true);
665 return false;
666 }
667 console.log("Base tree: " + baseTree.url);
668 var newBlob = githubAPI.createBlob(content);
669 if(!newBlob.sha) {
670 this.openModalBox("Unable to create new blob!", newBlob.status + ": " + newBlob.statusText, true);
671 return false;
672 }
673 console.log("New blob: " + newBlob.url);
674 var newTree = githubAPI.createTree(baseTree, path, newBlob);
675 if(!newTree.sha) {
676 this.openModalBox("Unable to create new tree!", newTree.status + ": " + newTree.statusText, true);
677 return false;
678 }
679 console.log("New tree: " + newTree.url);
680 var newCommit = githubAPI.createCommit(message, localStorage.latestCommit, newTree);
681 if(!newCommit.sha) {
682 this.openModalBox("Unable to create new commit!", newCommit.status + ": " + newCommit.statusText, true);
683 return false;
684 }
685 console.log("New commit: " + newCommit.url);
686 var pullRequest = githubAPI.createPullRequest(message.split("\n\n")[0], message, base, newCommit.sha);
687 if(!pullRequest.number) {
688 this.openModalBox("Unable to create pull request!", pullRequest.status + ": " + pullRequest.statusText, true);
689 return false;
690 }
691 console.log("New pull request: " + pullRequest.url);
692 return pullRequest;
693 }
694
695 this.reloadComments = function() {
696 if(!localStorage.requests){ return; }
697 var requests = JSON.parse(localStorage.requests);
698 var converter = new Markdown.Converter();
699 // Look for modified comments in page
700 for(i in requests) {
701 var request = requests[i];
702 $("textarea[data-comment-location=\"" + request.location + "\"]").each(function () {
703 var div = $(this).next();
704 if(request.isClosed) {
705 if(div.is("div.comment.newComment")) {
706 // hide empty comment
707 div.next().remove();
708 div.next().find("span.noComment").show();
709 div.remove();
710 } else if(div.is("div.comment.locked")) {
711 // unlock comment
712 div.removeClass("locked");
713 div.css("cursor", "pointer")
714 div.click(function() {
715 ui.openCommentBox(div.prev());
716 });
717 div.next().remove();
718 }
719 } else {
720 // create div for the new coment
721 if(!div.is("div.comment")) {
722 $(this).after("<div class='comment newComment'></div>");
723 div = $(this).next();
724 }
725 // lock modified comment
726 if(!div.hasClass("locked")) {
727 // convert modified comment to markdown
728 div.empty()
729 div.append(converter.makeHtml(Base64.decode(request.comment)));
730 // lock click
731 div.css("cursor", "auto");
732 div.unbind("click");
733 div.addClass("locked");
734 div.after(
735 $("<p class='locked inheritance'>")
736 .text("comment modified in ")
737 .append("<a href='"+ request.request.html_url +"' target='_blank' title='Review on GitHub'>pull request #"+ request.request.number +"</a>")
738 .append(" ")
739 .append(
740 $("<a data-pullrequest-number='"+ request.request.number +"' class='cancel'>cancel</a>")
741 .click(function (){
742 ui.closePullRequest($(this).attr("data-pullrequest-number"));
743 })
744 )
745 );
746 }
747 // hide "add comment" link
748 if(div.hasClass("newComment")) {
749 div.next().next().find("span.noComment").hide();
750 }
751 }
752
753 });
754 }
755 }
756
757 this.closePullRequest = function(number) {
758 // close pull request
759 var res = githubAPI.updatePullRequest("Canceled from Wikidoc", "", "closed", number);
760 if(!res.id) {
761 this.openModalBox("Unable to close pull request!", res.status + ": " + res.statusText, true);
762 return false;
763 }
764 // remove from localstorage
765 var requests = JSON.parse(localStorage.requests);
766 for(i in requests) {
767 if(requests[i].request.number == number) {
768 requests[i].isClosed = true;
769 }
770 }
771 localStorage.requests = JSON.stringify(requests);
772 ui.reloadComments()
773 }
774
775 /* Utility */
776
777 // Extract infos from string location "../lib/standard/collection/array.nit:457,1--458,0"
778 this.parseLocation = function(location) {
779 var parts = location.split(":");
780 var loc = new Object();
781 loc.path = parts[0].substr(3, parts[0].length);
782 loc.lstart = parseInt(parts[1].split("--")[0].split(",")[0]);
783 loc.tabpos = parseInt(parts[1].split("--")[0].split(",")[1]);
784 loc.lend = parseInt(parts[1].split("--")[1].split(",")[0]);
785 return loc;
786 }
787
788 // Meld modified comment into file content
789 this.mergeComment = function(fileContent, comment, location) {
790 // replace comment in file content
791 var res = new String();
792 var lines = fileContent.split("\n");
793 // copy lines fron 0 to lstart
794 for(var i = 0; i < location.lstart - 1; i++) {
795 res += lines[i] + "\n";
796 }
797 // set comment
798 if(comment && comment != "") {
799 var commentLines = comment.split("\n");
800 for(var i = 0; i < commentLines.length; i++) {
801 var line = commentLines[i];
802 var tab = location.tabpos > 1 ? "\t" : "";
803 res += tab + (line.length > 0 ? "# " : "#") + line + "\n";
804 }
805 }
806 // copy lines fron lend to end
807 for(var i = location.lend - 1; i < lines.length; i++) {
808 res += lines[i];
809 if(i < lines.length - 1) { res += "\n"; }
810 }
811 return res;
812 }
813
814 }
815 var ui;
816