1 /* This file is part of NIT ( http://www.nitlanguage.org ).
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
7 http://www.apache.org/licenses/LICENSE-2.0
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
15 Documentation generator for the nit language.
16 Generate API documentation in HTML format from nit source code.
20 * Nitdoc.Github comment edition module
22 * Allows user to modify source code comments directly from the Nitdoc
28 "plugins/github/loginbox",
29 "plugins/github/modalbox",
30 "plugins/github/commentbox",
32 ], function($
, Base64
, GithubAPI
, LoginBox
, ModalBox
, CommentBox
) {
33 var GithubUser
= function(login
, password
, repo
, branch
) {
35 this.password
= password
;
37 this.auth
= "Basic " + Base64
.encode(login
+ ':' + password
);
42 init
: function(upstream
, basesha1
) {
43 console
.info("Github plugin: init GitHub module (upstream: "+ upstream
+", base: " + basesha1
+ ")");
44 this.origin
= this._parseUpstream(upstream
);
46 $
("nav.main ul").append(
48 .attr("id", "nitdoc-github-li")
50 .loginbox("displayLogin")
51 .bind("loginbox_logoff", function() {
52 GithubUI
.disactivate();
54 .bind("loginbox_login", function(event
, infos
) {
55 GithubUI
._tryLoginFromCredentials(infos
);
58 // check local session
59 this._tryLoginFromLocalSession();
62 activate
: function(user
, origin
) {
63 this.openedComments
= 0;
64 this._saveSession(user
);
65 $
("#nitdoc-github-li").loginbox("displayLogout", origin
, user
);
66 this._attachCommentBoxes();
67 this._reloadComments();
69 // Prevent page unload if there is comments in editing mode
70 $
(window
).on('beforeunload', function() {
71 if(GithubUI
.openedComments
> 0){
72 return "There is uncommited modified comments. Are you sure you want to leave this page?";
77 disactivate
: function() {
78 if(this.openedComments
> 0){
79 if(!confirm('There is uncommited modified comments. Are you sure you want to leave this page?')) {
85 $
("#nitdoc-github-li").loginbox("toggle");
86 $
("#nitdoc-github-li").loginbox("displayLogin");
87 $
(window
).unbind('beforeunload');
88 //window.location.reload();
93 _checkLoginInfos
: function(infos
) {
94 if(!infos
.login ||
!infos
.password ||
!infos
.repo ||
!infos
.branch
) {
97 "Please enter your GitHub username, password, repository and branch.",
106 _tryLoginFromCredentials
: function(infos
) {
107 if(this._checkLoginInfos(infos
)) {
108 var isok
= this._tryLogin(infos
.login
, infos
.password
, infos
.repo
, infos
.branch
);
110 this.activate(this.user
, this.origin
);
112 if(isok
== "error:login") {
115 "The username, password, repo or branch you entered is incorrect.",
118 } else if(isok
== "error:sha") {
120 "Base commit not found",
121 "The provided Github repository must contain the base commit '" + UI
.origin
.sha
+ "'",
124 } else if(isok
== "error:profile") {
126 "Incomplete Github profile",
127 "Please set your public name and email in your " +
128 "<a href='https://github.com/settings/profile'>GitHub profile</a>." +
129 "<br/><br/>Your public profile informations are used to sign-off your commits.",
137 _tryLoginFromLocalSession
: function() {
138 if(localStorage
.user
) {
139 var session
= JSON
.parse(localStorage
.user
);
140 var isok
= this._tryLogin(
142 Base64
.decode(session
.password
),
147 this.activate(this.user
, this.origin
);
149 console
.debug("Github plugin: Session found but authentification failed");
150 localStorage
.clear();
153 console
.debug("Github plugin: No session found");
157 _tryLogin
: function(login
, password
, repo
, branch
) {
158 var tmpUser
= new GithubUser(login
, password
, repo
, branch
);
159 if(!GithubAPI
.login(tmpUser
)) {
160 return "error:login";
162 if(!tmpUser
.infos
.name ||
!tmpUser
.infos
.email
) {
163 return "error:profile";
165 var commit
= GithubAPI
.getCommit(tmpUser
, this.origin
.sha
);
166 if(!commit ||
!commit
.sha
) {
173 _saveSession
: function(user
) {
174 localStorage
.user
= JSON
.stringify({
176 password
: Base64
.encode(user
.password
),
180 // check local storage synchro with branch
181 if(localStorage
.base
!= this.origin
.sha
) {
182 console
.log("Base changed: cleaned cache");
183 localStorage
.requests
= "[]";
184 localStorage
.base
= this.origin
.sha
;
188 /* html decoration */
190 // Attach edit button on each comment
191 _attachCommentBoxes
: function() {
192 $
("textarea.baseComment").each(function() {
193 $
(this).commentbox();
198 $
(this).nextAll(".info:first").find(".noComment").hide()
199 $
(this).nextAll(".info:first").before(
204 $
("<div/>").addClass("nitdoc")
209 $
(this).nextAll(".info:first").prepend(
211 .addClass("nitdoc-github-editComment")
212 .css("cursor", "pointer")
213 .text((isNew ?
"add" : "edit") + " comment")
214 .click($
.proxy(GithubUI
._openCommentBox
, GithubUI
, null, $
(this)))
218 $
(this).bind("commentbox_commit", function(event
, data
) {
219 GithubUI
._saveChanges(data
);
220 $
(this).commentbox("close");
221 GithubUI
._reloadComments();
223 .bind("commentbox_preview", function(event
, data
) {
224 var converter
= new Markdown
.Converter()
225 var html
= converter
.makeHtml(data
.value
);
226 ModalBox
.open("Preview", html
, false);
228 .bind("commentbox_open", function(event
, data
) {
229 GithubUI
.openedComments
++;
230 $
(this).nextAll(".comment").hide();
232 .bind("commentbox_close", function(event
, data
) {
233 GithubUI
.openedComments
--;
234 $
(this).nextAll(".comment").show();
239 // reload comments from saved pull request
240 _reloadComments
: function() {
241 if(!localStorage
.requests
){ return; }
242 $
("p.pullRequest").remove();
243 var converter
= new Markdown
.Converter();
244 var requests
= JSON
.parse(localStorage
.requests
);
245 // Look for modified comments in page
247 if(!requests
[i]) { continue; }
248 var request
= requests
[i];
249 $
("textarea[data-comment-location=\"" + request
.location
+ "\"]").each(function () {
250 if(request
.isClosed
) {
251 var oldComment
= Base64
.decode(request
.oldComment
);
252 var htmlComment
= converter
.makeHtml(oldComment
);
253 $
(this).val(oldComment
);
255 $
(this).nextAll("div.comment:first").hide();
257 $
(this).nextAll("div.comment:first").show();
259 $
(this).nextAll("div.comment").find("div.nitdoc").empty().html(htmlComment
);
260 $
(this).nextAll("p.info").find("a.nitdoc-github-editComment").show();
262 var newComment
= Base64
.decode(request
.comment
);
263 var htmlComment
= converter
.makeHtml(newComment
);
264 $
(this).val(newComment
);
266 $
(this).nextAll("div.comment:first").hide();
268 $
(this).nextAll("div.comment:first").show();
270 $
(this).nextAll("div.comment").find("div.nitdoc").empty().html(htmlComment
);
271 GithubUI
._addPullRequestLink($
(this), request
);
272 $
(this).nextAll("p.info").find("a.nitdoc-github-editComment").hide();
278 _addPullRequestLink
: function(baseArea
, request
) {
279 baseArea
.nextAll("p.info").before(
281 .addClass("pullRequest inheritance")
282 .text("comment modified in ")
286 href
: request
.request
.html_url
,
287 title
: "Review on GitHub"
289 .text("pull request #" + request
.request
.number
)
294 .data("pullrequest-number", request
.request
.number
)
295 .addClass("nitdoc-github-update")
297 .click($
.proxy(GithubUI
._doUpdateRequest
, GithubUI
, null, baseArea
, request
))
302 .data("pullrequest-number", request
.request
.number
)
303 .addClass("nitdoc-github-cancel")
305 .click($
.proxy(GithubUI
._doCancelRequest
, GithubUI
, null, baseArea
, request
))
312 _saveChanges
: function(edit
) {
313 // if pull request update close existing pull request for the comment
315 this._closePullRequest(edit
.requestID
);
317 edit
.oldContent
= this._getFileContent(edit
.location
.path
);
318 edit
.newContent
= this._mergeComment(edit
.oldContent
, edit
.newComment
, edit
.location
);
319 edit
.request
= this._pushChanges(edit
)
321 ModalBox
.open("Unable to commit changes!", response
, true);
324 this._saveRequest(edit
);
327 // save pull request in local storage
328 _saveRequest
: function(edit
) {
330 if(localStorage
.requests
) {requests
= JSON
.parse(localStorage
.requests
)}
331 requests
[edit
.request
.number
] = {
332 request
: edit
.request
,
333 location
: edit
.location
.origin
,
334 comment
: Base64
.encode(edit
.newComment
),
335 oldComment
: Base64
.encode(edit
.oldComment
)
337 localStorage
.requests
= JSON
.stringify(requests
);
341 Creating a new pull request with the new comment take 5 steps:
342 1. get the base tree from latest commit
344 2. create a new blob with updated file content
345 3. post a new tree from base tree and blob
346 4. post the new commit with new tree
347 5. create the pull request
349 _pushChanges
: function(edit
) {
350 var baseTree
= GithubAPI
.getTree(this.user
, this.origin
.sha
);
352 ModalBox
.open("Unable to locate base tree!", baseTree
.status
+ ": " + baseTree
.statusText
, true);
355 console
.log("Base tree: " + baseTree
.url
);
356 var newBlob
= GithubAPI
.createBlob(this.user
, edit
.newContent
);
358 ModalBox
.open("Unable to create new blob!", newBlob
.status
+ ": " + newBlob
.statusText
, true);
361 console
.log("New blob: " + newBlob
.url
);
362 var newTree
= GithubAPI
.createTree(this.user
, baseTree
, edit
.location
.path
, newBlob
);
364 ModalBox
.open("Unable to create new tree!", newTree
.status
+ ": " + newTree
.statusText
, true);
367 console
.log("New tree: " + newTree
.url
);
368 var newCommit
= GithubAPI
.createCommit(this.user
, edit
.message
, baseTree
.sha
, newTree
);
370 ModalBox
.open("Unable to create new commit!", newCommit
.status
+ ": " + newCommit
.statusText
, true);
373 console
.log("New commit: " + newCommit
.url
);
374 var pullRequest
= GithubAPI
.createPullRequest(this.user
, edit
.title
, "Pull request from Nitdoc", this.origin
, newCommit
.sha
);
375 if(!pullRequest
.number
) {
376 ModalBox
.open("Unable to create pull request!", pullRequest
.status
+ ": " + pullRequest
.statusText
, true);
379 console
.log("New pull request: " + pullRequest
.url
);
383 // close previously opened pull request
384 _closePullRequest
: function(number
) {
385 var requests
= JSON
.parse(localStorage
.requests
);
386 if(!requests
[number]) {
387 ModalBox
.open("Unable to close pull request!", "Pull request " + number
+ "not found", true);
390 // close pull request
391 var res
= GithubAPI
.updatePullRequest(this.user
, "Closed from Nitdoc", "", "closed", requests
[number].request
);
393 ModalBox
.open("Unable to close pull request!", res
.status
+ ": " + res
.statusText
, true);
396 // update in localstorage
397 requests
[number].isClosed
= true;
398 localStorage
.requests
= JSON
.stringify(requests
);
403 _parseUpstream
: function(upstream
) {
404 var parts
= upstream
.split(":");
413 _getFileContent
: function(githubUrl
) {
414 var origFile
= GithubAPI
.getFile(this.user
, githubUrl
);
415 if(!origFile
.content
) {
416 ModalBox
.open("Unable to locate source file!", origFile
.status
+ ": " + origFile
.statusText
, true);
419 var base64Content
= origFile
.content
.substring(0, origFile
.content
.length
- 1)
420 return Base64
.decode(base64Content
);
423 _mergeComment
: function(fileContent
, comment
, location
) {
424 // replace comment in file content
425 var res
= new String();
426 var lines
= fileContent
.split("\n");
427 // copy lines fron 0 to lstart
428 for(var i
= 0; i
< location
.lstart
- 1; i
++) {
429 res
+= lines
[i] + "\n";
432 if(comment
&& comment
!= "") {
433 var commentLines
= comment
.split("\n");
434 for(var i
= 0; i
< commentLines
.length
; i
++) {
435 var line
= commentLines
[i];
436 var tab
= location
.tabpos
> 1 ?
"\t" : "";
437 res
+= tab
+ (line
.length
> 0 ?
"# " : "#") + line
+ "\n";
440 // copy lines fron lend to end
441 for(var i
= location
.lend
- 1; i
< lines
.length
; i
++) {
443 if(i
< lines
.length
- 1) { res
+= "\n"; }
450 _openCommentBox
: function(event
, baseArea
) {
451 baseArea
.commentbox("open", this.user
);
454 _doCancelRequest
: function(event
, baseArea
, request
) {
455 this._closePullRequest(request
.request
.number
);
456 this._reloadComments();
459 _doUpdateRequest
: function(event
, baseArea
, request
) {
460 baseArea
.commentbox("open", this.user
, request
.request
.number
);
464 // Get github plugin data
465 var upstream
= $
("body").attr("data-github-upstream");
466 var basesha1
= $
("body").attr("data-github-base-sha1");
467 if(upstream
&& basesha1
) {
468 GithubUI
.init(upstream
, basesha1
);