+++ /dev/null
-/* This file is part of NIT ( http://www.nitlanguage.org ).\r
-\r
- Licensed under the Apache License, Version 2.0 (the "License");\r
- you may not use this file except in compliance with the License.\r
- You may obtain a copy of the License at\r
-\r
- http://www.apache.org/licenses/LICENSE-2.0\r
-\r
- Unless required by applicable law or agreed to in writing, software\r
- distributed under the License is distributed on an "AS IS" BASIS,\r
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- See the License for the specific language governing permissions and\r
- limitations under the License.\r
-\r
- Documentation generator for the nit language.\r
- Generate API documentation in HTML format from nit source code.\r
-*/\r
-\r
-/*\r
- * Nitdoc.Github comment edition module\r
- *\r
- * Allows user to modify source code comments directly from the Nitdoc\r
- */\r
-define([\r
- "jquery",\r
- "github-api",\r
- "highlight",\r
- "marked",\r
- "nit",\r
- "plugins/modalbox",\r
- "plugins/github/loginbox",\r
- "plugins/github/commentbox",\r
- "utils"\r
-], function($, GithubAPI, hljs, marked) {\r
- var GithubUser = function(login, password, repo, branch) {\r
- this.login = login;\r
- this.password = password;\r
- this.repo = repo;\r
- this.auth = "Basic " + (login + ':' + password).base64Encode();\r
- this.branch = branch;\r
- }\r
-\r
- var GithubUI = {\r
- init: function(upstream, basesha1) {\r
- console.info("Github plugin: init GitHub module (upstream: "+ upstream +", base: " + basesha1 + ")");\r
- this.origin = this._parseUpstream(upstream);\r
- this._initMarked();\r
- // Add github menu\r
- $("#topmenu>.container-fluid").append(\r
- $("<a/>")\r
- .attr({\r
- "id": "nitdoc-github-li",\r
- "type": "button",\r
- "class": "navbar-btn navbar-right btn-link",\r
- "href": "#",\r
- "data-container": "body",\r
- "data-toggle": "popover",\r
- "data-placement": "bottom",\r
- "data-content": "bottom",\r
- "data-html": "true",\r
- })\r
- .loginbox()\r
- //.loginbox("displayLogin")\r
- .bind("loginbox_logoff", function() {\r
- GithubUI.disactivate();\r
- })\r
- .bind("loginbox_login", function(event, infos) {\r
- GithubUI._tryLoginFromCredentials(infos);\r
- })\r
- );\r
- // check local session\r
- this._tryLoginFromLocalSession();\r
- },\r
-\r
- activate: function(user, origin) {\r
- this.openedComments = 0;\r
- this._saveSession(user);\r
- $("#nitdoc-github-li").loginbox("displayLogout", origin, user);\r
- this._attachCommentBoxes();\r
- this._reloadComments();\r
-\r
- // Prevent page unload if there is comments in editing mode\r
- $(window).on('beforeunload', function() {\r
- if(GithubUI.openedComments > 0){\r
- return "There is uncommited modified comments. Are you sure you want to leave this page?";\r
- }\r
- });\r
- },\r
-\r
- disactivate: function() {\r
- if(this.openedComments > 0){\r
- if(!confirm('There is uncommited modified comments. Are you sure you want to leave this page?')) {\r
- return false;\r
- }\r
- }\r
-\r
- localStorage.clear();\r
- $("#nitdoc-github-li").loginbox("toggle");\r
- $("#nitdoc-github-li").loginbox("displayLogin");\r
- $(window).unbind('beforeunload');\r
- //window.location.reload();\r
- },\r
-\r
- /* login */\r
-\r
- _checkLoginInfos: function(infos) {\r
- if(!infos.login || !infos.password || !infos.repo || !infos.branch) {\r
- $("<p/>")\r
- .text("Please enter your GitHub username, password, repository and branch.")\r
- .modalbox({\r
- title: "Sign in error",\r
- isError: true\r
- })\r
- .modalbox("open");\r
- return false;\r
- } else {\r
- return true;\r
- }\r
- },\r
-\r
- _tryLoginFromCredentials: function(infos) {\r
- if(this._checkLoginInfos(infos)) {\r
- var isok = this._tryLogin(infos.login, infos.password, infos.repo, infos.branch);\r
- if(isok === true) {\r
- this.activate(this.user, this.origin);\r
- } else {\r
- if(isok == "error:login") {\r
- $("<p/>")\r
- .text("The username, password, repo or branch you entered is incorrect.")\r
- .modalbox({\r
- title: "Github sign in error",\r
- isError: true\r
- })\r
- .modalbox("open");\r
- } else if(isok == "error:sha") {\r
- $("<p/>")\r
- .text("The provided Github repository must contain the base commit '" + this.origin.sha + "'.")\r
- .modalbox({\r
- title: "Github base commit error",\r
- isError: true\r
- })\r
- .modalbox("open");\r
- } else if(isok == "error:profile") {\r
- $("<p/>")\r
- .text("Please set your public name and email in your " +\r
- "<a href='https://github.com/settings/profile'>GitHub profile</a>." +\r
- "<br/><br/>Your public profile informations are used to sign-off your commits.")\r
- .modalbox({\r
- title: "Github profile error",\r
- isError: true\r
- })\r
- .modalbox("open");\r
- }\r
- }\r
- }\r
- },\r
-\r
- _tryLoginFromLocalSession: function() {\r
- if(localStorage.user) {\r
- var session = JSON.parse(localStorage.user);\r
- var isok = this._tryLogin(\r
- session.login,\r
- session.password.base64Decode(),\r
- session.repo,\r
- session.branch\r
- );\r
- if(isok === true) {\r
- this.activate(this.user, this.origin);\r
- } else {\r
- console.debug("Github plugin: Session found but authentification failed");\r
- localStorage.clear();\r
- }\r
- } else {\r
- console.debug("Github plugin: No session found");\r
- }\r
- },\r
-\r
- _tryLogin: function(login, password, repo, branch) {\r
- var tmpUser = new GithubUser(login, password, repo, branch);\r
- if(!GithubAPI.login(tmpUser)) {\r
- return "error:login";\r
- }\r
- if(!tmpUser.infos.name || !tmpUser.infos.email) {\r
- return "error:profile";\r
- }\r
- var commit = GithubAPI.getCommit(tmpUser, this.origin.sha);\r
- if(!commit || !commit.sha) {\r
- return "error:sha";\r
- }\r
- this.user = tmpUser;\r
- return true;\r
- },\r
-\r
- _saveSession: function(user) {\r
- localStorage.user = JSON.stringify({\r
- login: user.login,\r
- password: user.password.base64Encode(),\r
- repo: user.repo,\r
- branch: user.branch,\r
- });\r
- // check local storage synchro with branch\r
- if(localStorage.base != this.origin.sha) {\r
- console.log("Base changed: cleaned cache");\r
- localStorage.requests = "[]";\r
- localStorage.base = this.origin.sha;\r
- }\r
- },\r
-\r
- /* html decoration */\r
-\r
- // Attach edit button on each comment\r
- _attachCommentBoxes: function() {\r
- $("textarea.baseComment").each(function() {\r
- $(this).commentbox();\r
-\r
- var isNew = false;\r
- if(!$(this).val()) {\r
- isNew = true;\r
- $(this).nextAll(".info:first").find(".noComment").hide()\r
- $(this).nextAll(".info:first").before(\r
- $("<div/>")\r
- .hide()\r
- .addClass("comment")\r
- .append(\r
- $("<div/>").addClass("nitdoc")\r
- )\r
- )\r
- }\r
-\r
- $(this).nextAll(".info:first").prepend(\r
- $("<a/>")\r
- .addClass("nitdoc-github-editComment")\r
- .css("cursor", "pointer")\r
- .text((isNew ? "add" : "edit") + " comment")\r
- .click($.proxy(GithubUI._openCommentBox, GithubUI, null, $(this)))\r
- .after(" for ")\r
- )\r
-\r
- $(this).bind("commentbox_commit", function(event, data) {\r
- GithubUI._saveChanges(data);\r
- $(this).commentbox("close");\r
- GithubUI._reloadComments();\r
- })\r
- .bind("commentbox_preview", function(event, data) {\r
- $("<div/>")\r
- .append($("<h4/>").text("Comment:"))\r
- .append(\r
- $("<div/>")\r
- .addClass("description")\r
- .append(\r
- $("<div/>")\r
- .addClass("comment")\r
- .append(\r
- $("<div/>")\r
- .addClass("nitdoc")\r
- .html(marked(data.value))\r
- )\r
- )\r
- )\r
- .append($("<h4/>").text("Message:"))\r
- .append(\r
- $("<div/>")\r
- .addClass("description")\r
- .append(\r
- $("<div/>")\r
- .addClass("comment")\r
- .append(\r
- $("<div/>").html(marked(data.message))\r
- )\r
- )\r
- )\r
- .modalbox({\r
- title: "Preview comment",\r
- css: {"min-width": "500px"}\r
- })\r
- .modalbox("open");\r
- })\r
- .bind("commentbox_open", function(event, data) {\r
- GithubUI.openedComments++;\r
- $(this).nextAll(".comment").hide();\r
- })\r
- .bind("commentbox_close", function(event, data) {\r
- GithubUI.openedComments--;\r
- $(this).nextAll(".comment").show();\r
- });\r
- });\r
- },\r
-\r
- // reload comments from saved pull request\r
- _reloadComments: function() {\r
- if(!localStorage.requests){ return; }\r
- $("p.pullRequest").remove();\r
- var requests = JSON.parse(localStorage.requests);\r
- // Look for modified comments in page\r
- for(i in requests) {\r
- if(!requests[i]) { continue; }\r
- var request = requests[i];\r
- $("textarea[data-comment-location=\"" + request.location + "\"]").each(function () {\r
- if(request.isClosed) {\r
- var oldComment = request.oldComment.base64Decode();\r
- var htmlComment = marked(oldComment);\r
- $(this).val(oldComment);\r
- if(!$(this).val()) {\r
- $(this).nextAll("div.comment:first").hide();\r
- } else {\r
- $(this).nextAll("div.comment:first").show();\r
- }\r
- $(this).nextAll("div.comment").find("div.nitdoc").empty().html(htmlComment);\r
- $(this).nextAll("p.info").find("a.nitdoc-github-editComment").show();\r
- } else {\r
- var newComment = request.comment.base64Decode();\r
- var htmlComment = marked(newComment);\r
- $(this).val(newComment);\r
- if(!$(this).val()) {\r
- $(this).nextAll("div.comment:first").hide();\r
- } else {\r
- $(this).nextAll("div.comment:first").show();\r
- }\r
- $(this).nextAll("div.comment").find("div.nitdoc").empty().html(htmlComment);\r
- GithubUI._addPullRequestLink($(this), request);\r
- $(this).nextAll("p.info").find("a.nitdoc-github-editComment").hide();\r
- }\r
- });\r
- }\r
- },\r
-\r
- _addPullRequestLink: function(baseArea, request) {\r
- baseArea.nextAll("p.info").before(\r
- $("<p/>")\r
- .addClass("pullRequest inheritance")\r
- .text("comment modified in ")\r
- .append(\r
- $("<a/>")\r
- .attr({\r
- href: request.request.html_url,\r
- title: "Review on GitHub"\r
- })\r
- .text("pull request #" + request.request.number)\r
- )\r
- .append(" ")\r
- .append(\r
- $("<a/>")\r
- .data("pullrequest-number", request.request.number)\r
- .addClass("nitdoc-github-update")\r
- .text("update")\r
- .click($.proxy(GithubUI._doUpdateRequest, GithubUI, null, baseArea, request))\r
- )\r
- .append(" ")\r
- .append(\r
- $("<a/>")\r
- .data("pullrequest-number", request.request.number)\r
- .addClass("nitdoc-github-cancel")\r
- .text("cancel")\r
- .click($.proxy(GithubUI._doCancelRequest, GithubUI, null, baseArea, request))\r
- )\r
- );\r
- },\r
-\r
- /* github calls */\r
-\r
- _saveChanges: function(edit) {\r
- // if pull request update close existing pull request for the comment\r
- if(edit.requestID) {\r
- this._closePullRequest(edit.requestID);\r
- }\r
- edit.oldContent = this._getFileContent(edit.location.path);\r
- edit.newContent = this._mergeComment(edit.oldContent, edit.newComment, edit.location);\r
- edit.request = this._pushChanges(edit)\r
- if(!edit.request) {\r
- $("<p/>")\r
- .text("Unable to commit changes.<br/>" + response)\r
- .modalbox({\r
- title: "Github commit error",\r
- isError: true\r
- })\r
- .modalbox("open");\r
- return;\r
- }\r
- this._saveRequest(edit);\r
- },\r
-\r
- // save pull request in local storage\r
- _saveRequest: function(edit) {\r
- var requests = {};\r
- if(localStorage.requests) {requests = JSON.parse(localStorage.requests)}\r
- requests[edit.request.number] = {\r
- request: edit.request,\r
- location: edit.location.origin,\r
- comment: edit.newComment.base64Encode(),\r
- oldComment: edit.oldComment.base64Encode()\r
- };\r
- localStorage.requests = JSON.stringify(requests);\r
- },\r
-\r
- /*\r
- Creating a new pull request with the new comment take 5 steps:\r
- 1. get the base tree from latest commit\r
-\r
- 2. create a new blob with updated file content\r
- 3. post a new tree from base tree and blob\r
- 4. post the new commit with new tree\r
- 5. create the pull request\r
- */\r
- _pushChanges: function(edit) {\r
- var baseTree = GithubAPI.getTree(this.user, this.origin.sha);\r
- if(!baseTree.sha) {\r
- $("<p/>")\r
- .text("Unable to locate base tree.<br/>" + baseTree.status + ": " + baseTree.statusText)\r
- .modalbox({\r
- title: "Github commit error",\r
- isError: true\r
- })\r
- .modalbox("open");\r
- return false;\r
- }\r
- console.log("Base tree: " + baseTree.url);\r
- var newBlob = GithubAPI.createBlob(this.user, edit.newContent);\r
- if(!newBlob.sha) {\r
- $("<p/>")\r
- .text("Unable to create new blob.<br/>" + newBlob.status + ": " + newBlob.statusText)\r
- .modalbox({\r
- title: "Github commit error",\r
- isError: true\r
- })\r
- .modalbox("open");\r
- return false;\r
- }\r
- console.log("New blob: " + newBlob.url);\r
- var newTree = GithubAPI.createTree(this.user, baseTree, edit.location.path, newBlob);\r
- if(!newTree.sha) {\r
- $("<p/>")\r
- .text("Unable to create new tree.<br/>" + newTree.status + ": " + newTree.statusText)\r
- .modalbox({\r
- title: "Github commit error",\r
- isError: true\r
- })\r
- .modalbox("open");\r
- return false;\r
- }\r
- console.log("New tree: " + newTree.url);\r
- var newCommit = GithubAPI.createCommit(this.user, edit.message, baseTree.sha, newTree);\r
- if(!newCommit.sha) {\r
- $("<p/>")\r
- .text("Unable to create new commit.<br/>" + newCommit.status + ": " + newCommit.statusText)\r
- .modalbox({\r
- title: "Github commit error",\r
- isError: true\r
- })\r
- .modalbox("open");\r
- return false;\r
- }\r
- console.log("New commit: " + newCommit.url);\r
- var pullRequest = GithubAPI.createPullRequest(this.user, edit.title, "Pull request from Nitdoc", this.origin, newCommit.sha);\r
- if(!pullRequest.number) {\r
- $("<p/>")\r
- .text("Unable to create pull request.<br/>" + pullRequest.status + ": " + pullRequest.statusText)\r
- .modalbox({\r
- title: "Github commit error",\r
- isError: true\r
- })\r
- .modalbox("open");\r
- return false;\r
- }\r
- console.log("New pull request: " + pullRequest.url);\r
- return pullRequest;\r
- },\r
-\r
- // close previously opened pull request\r
- _closePullRequest: function(number) {\r
- var requests = JSON.parse(localStorage.requests);\r
- if(!requests[number]) {\r
- $("<p/>")\r
- .text("Unable to close pull request.<br/>" + "Pull request " + number + "not found")\r
- .modalbox({\r
- title: "Github commit error",\r
- isError: true\r
- })\r
- .modalbox("open");\r
- return false;\r
- }\r
- // close pull request\r
- var res = GithubAPI.updatePullRequest(this.user, "Closed from Nitdoc", "", "closed", requests[number].request);\r
- if(!res.id) {\r
- $("<p/>")\r
- .text("Unable to close pull request.<br/>" + res.status + ": " + res.statusText)\r
- .modalbox({\r
- title: "Github commit error",\r
- isError: true\r
- })\r
- .modalbox("open");\r
- return false;\r
- }\r
- // update in localstorage\r
- requests[number].isClosed = true;\r
- localStorage.requests = JSON.stringify(requests);\r
- },\r
-\r
- /* internals */\r
-\r
- _initMarked: function() {\r
- var renderer = new marked.Renderer();\r
- renderer.code = function(code) {\r
- return '<pre class="nitcode hljs">' + hljs.highlight('nit', code).value + '</pre>';\r
- }\r
- renderer.codespan = function(code) {\r
- return '<code class="nitcode hljs">' + hljs.highlight('nit', code).value + '</code>';\r
- }\r
- marked.setOptions({\r
- renderer: renderer,\r
- gfm: true,\r
- tables: true,\r
- breaks: true,\r
- pedantic: false,\r
- sanitize: true,\r
- smartLists: true,\r
- smartypants: false\r
- });\r
- },\r
-\r
- _parseUpstream: function(upstream) {\r
- var parts = upstream.split(":");\r
- return {\r
- user: parts[0],\r
- repo: parts[1],\r
- branch: parts[2],\r
- sha: basesha1\r
- };\r
- },\r
-\r
- _getFileContent: function(githubUrl) {\r
- var origFile = GithubAPI.getFile(this.user, githubUrl);\r
- if(!origFile.content) {\r
- $("<p/>")\r
- .text("Unable to locate source file.<br/>" + origFile.status + ": " + origFile.statusText)\r
- .modalbox({\r
- title: "Github commit error",\r
- isError: true\r
- })\r
- .modalbox("open");\r
- return;\r
- }\r
- var base64Content = origFile.content.substring(0, origFile.content.length - 1)\r
- return base64Content.base64Decode();\r
- },\r
-\r
- _mergeComment: function(fileContent, comment, location) {\r
- // replace comment in file content\r
- var res = new String();\r
- var lines = fileContent.split("\n");\r
- // copy lines fron 0 to lstart\r
- for(var i = 0; i < location.lstart - 1; i++) {\r
- res += lines[i] + "\n";\r
- }\r
- // set comment\r
- if(comment && comment != "") {\r
- var commentLines = comment.split("\n");\r
- for(var i = 0; i < commentLines.length; i++) {\r
- var line = commentLines[i];\r
- var tab = location.tabpos > 1 ? "\t" : "";\r
- res += tab + (line.length > 0 ? "# " : "#") + line + "\n";\r
- }\r
- }\r
- // copy lines fron lend to end\r
- for(var i = location.lend - 1; i < lines.length; i++) {\r
- res += lines[i];\r
- if(i < lines.length - 1) { res += "\n"; }\r
- }\r
- return res;\r
- },\r
-\r
- /* events */\r
-\r
- _openCommentBox: function(event, baseArea) {\r
- baseArea.commentbox("open", this.user);\r
- },\r
-\r
- _doCancelRequest: function(event, baseArea, request) {\r
- this._closePullRequest(request.request.number);\r
- this._reloadComments();\r
- },\r
-\r
- _doUpdateRequest: function(event, baseArea, request) {\r
- baseArea.commentbox("open", this.user, request.request.number);\r
- },\r
- }\r
-\r
- // Get github plugin data\r
- var upstream = $("body").attr("data-github-upstream");\r
- var basesha1 = $("body").attr("data-github-base-sha1");\r
- if(upstream && basesha1) {\r
- GithubUI.init(upstream, basesha1);\r
- }\r
-});\r