- moved gitgraph.js to web_src and made it importable and es6-compatible - created new webpack chunk for gitgraph - enabled CSS loader in webpack - enabled async/await syntax via regenerator-runtime - added script to ensure webpack chunks are loaded correctly - disable terser's comment extraction to prevent .LICENCE files gitgraph.js has many issues: 1. it is incompatible with ES6 because of strict-mode violations 1. it does not export anything 1. it's css has weird styles like for `body` 1. it is not available on npm I fixed points 1-3 in our version so it's now loadable in webpack. We should eventually consider alternatives.tags/v1.21.12.1
| @@ -1 +1,2 @@ | |||||
| /public/js/semantic.dropdown.custom.js | /public/js/semantic.dropdown.custom.js | ||||
| /web_src/js/vendor/** | |||||
| @@ -14,6 +14,7 @@ env: | |||||
| node: true | node: true | ||||
| globals: | globals: | ||||
| __webpack_public_path__: true | |||||
| Clipboard: false | Clipboard: false | ||||
| CodeMirror: false | CodeMirror: false | ||||
| Dropzone: false | Dropzone: false | ||||
| @@ -6,16 +6,20 @@ | |||||
| }, | }, | ||||
| "devDependencies": { | "devDependencies": { | ||||
| "@babel/core": "7.7.2", | "@babel/core": "7.7.2", | ||||
| "@babel/plugin-transform-runtime": "7.6.2", | |||||
| "@babel/preset-env": "7.7.1", | "@babel/preset-env": "7.7.1", | ||||
| "@babel/runtime": "7.7.2", | |||||
| "autoprefixer": "9.7.1", | "autoprefixer": "9.7.1", | ||||
| "babel-loader": "8.0.6", | "babel-loader": "8.0.6", | ||||
| "core-js": "3.4.1", | "core-js": "3.4.1", | ||||
| "css-loader": "3.2.0", | |||||
| "eslint": "6.6.0", | "eslint": "6.6.0", | ||||
| "eslint-config-airbnb-base": "14.0.0", | "eslint-config-airbnb-base": "14.0.0", | ||||
| "eslint-plugin-import": "2.18.2", | "eslint-plugin-import": "2.18.2", | ||||
| "less": "3.10.3", | "less": "3.10.3", | ||||
| "less-plugin-clean-css": "1.5.1", | "less-plugin-clean-css": "1.5.1", | ||||
| "postcss-cli": "6.1.3", | "postcss-cli": "6.1.3", | ||||
| "style-loader": "1.0.0", | |||||
| "stylelint": "11.1.1", | "stylelint": "11.1.1", | ||||
| "stylelint-config-standard": "19.0.0", | "stylelint-config-standard": "19.0.0", | ||||
| "terser-webpack-plugin": "2.2.1", | "terser-webpack-plugin": "2.2.1", | ||||
| @@ -17,9 +17,6 @@ Version: 2.3.1 | |||||
| File(s): /vendor/plugins/clipboard/clipboard.min.js | File(s): /vendor/plugins/clipboard/clipboard.min.js | ||||
| Version: 1.5.9 | Version: 1.5.9 | ||||
| File(s): /vendor/plugins/gitgraph/gitgraph.js | |||||
| Version: 745f604212e2abfe2f0a59169ea530857b46625c | |||||
| File(s): /vendor/plugins/vue/vue.min.js | File(s): /vendor/plugins/vue/vue.min.js | ||||
| Version: 2.1.10 | Version: 2.1.10 | ||||
| @@ -46,7 +46,7 @@ | |||||
| <td><a href="https://github.com/zenorocha/clipboard.js/archive/v1.5.9.tar.gz">clipboard-1.5.9.tar.gz</a></td> | <td><a href="https://github.com/zenorocha/clipboard.js/archive/v1.5.9.tar.gz">clipboard-1.5.9.tar.gz</a></td> | ||||
| </tr> | </tr> | ||||
| <tr> | <tr> | ||||
| <td><a href="./plugins/gitgraph/gitgraph.js">gitgraph.js</a></td> | |||||
| <td><a href="../js/gitgraph.js">gitgraph.js</a></td> | |||||
| <td><a href="https://github.com/bluef/gitgraph.js/blob/master/LICENSE">BSD 3-Clause</a></td> | <td><a href="https://github.com/bluef/gitgraph.js/blob/master/LICENSE">BSD 3-Clause</a></td> | ||||
| <td><a href="https://github.com/bluef/gitgraph.js">gitgraph.js-latest</a></td> | <td><a href="https://github.com/bluef/gitgraph.js">gitgraph.js-latest</a></td> | ||||
| </tr> | </tr> | ||||
| @@ -1,435 +0,0 @@ | |||||
| /* | |||||
| * @license magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt BSD 3-Clause | |||||
| * Copyright (c) 2011, Terrence Lee <kill889@gmail.com> | |||||
| * All rights reserved. | |||||
| * | |||||
| * Redistribution and use in source and binary forms, with or without | |||||
| * modification, are permitted provided that the following conditions are met: | |||||
| * * Redistributions of source code must retain the above copyright | |||||
| * notice, this list of conditions and the following disclaimer. | |||||
| * * Redistributions in binary form must reproduce the above copyright | |||||
| * notice, this list of conditions and the following disclaimer in the | |||||
| * documentation and/or other materials provided with the distribution. | |||||
| * * Neither the name of the <organization> nor the | |||||
| * names of its contributors may be used to endorse or promote products | |||||
| * derived from this software without specific prior written permission. | |||||
| * | |||||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |||||
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||||
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||||
| * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |||||
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||||
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||||
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||||
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
| */ | |||||
| var gitGraph = function (canvas, rawGraphList, config) { | |||||
| if (!canvas.getContext) { | |||||
| return; | |||||
| } | |||||
| if (typeof config === "undefined") { | |||||
| config = { | |||||
| unitSize: 20, | |||||
| lineWidth: 3, | |||||
| nodeRadius: 4 | |||||
| }; | |||||
| } | |||||
| var flows = []; | |||||
| var graphList = []; | |||||
| var ctx = canvas.getContext("2d"); | |||||
| var devicePixelRatio = window.devicePixelRatio || 1; | |||||
| var backingStoreRatio = ctx.webkitBackingStorePixelRatio || | |||||
| ctx.mozBackingStorePixelRatio || | |||||
| ctx.msBackingStorePixelRatio || | |||||
| ctx.oBackingStorePixelRatio || | |||||
| ctx.backingStorePixelRatio || 1; | |||||
| var ratio = devicePixelRatio / backingStoreRatio; | |||||
| var init = function () { | |||||
| var maxWidth = 0; | |||||
| var i; | |||||
| var l = rawGraphList.length; | |||||
| var row; | |||||
| var midStr; | |||||
| for (i = 0; i < l; i++) { | |||||
| midStr = rawGraphList[i].replace(/\s+/g, " ").replace(/^\s+|\s+$/g, ""); | |||||
| maxWidth = Math.max(midStr.replace(/(\_|\s)/g, "").length, maxWidth); | |||||
| row = midStr.split(""); | |||||
| graphList.unshift(row); | |||||
| } | |||||
| var width = maxWidth * config.unitSize; | |||||
| var height = graphList.length * config.unitSize; | |||||
| canvas.width = width * ratio; | |||||
| canvas.height = height * ratio; | |||||
| canvas.style.width = width + 'px'; | |||||
| canvas.style.height = height + 'px'; | |||||
| ctx.lineWidth = config.lineWidth; | |||||
| ctx.lineJoin = "round"; | |||||
| ctx.lineCap = "round"; | |||||
| ctx.scale(ratio, ratio); | |||||
| }; | |||||
| var genRandomStr = function () { | |||||
| var chars = "0123456789ABCDEF"; | |||||
| var stringLength = 6; | |||||
| var randomString = '', rnum, i; | |||||
| for (i = 0; i < stringLength; i++) { | |||||
| rnum = Math.floor(Math.random() * chars.length); | |||||
| randomString += chars.substring(rnum, rnum + 1); | |||||
| } | |||||
| return randomString; | |||||
| }; | |||||
| var findFlow = function (id) { | |||||
| var i = flows.length; | |||||
| while (i-- && flows[i].id !== id) {} | |||||
| return i; | |||||
| }; | |||||
| var findColomn = function (symbol, row) { | |||||
| var i = row.length; | |||||
| while (i-- && row[i] !== symbol) {} | |||||
| return i; | |||||
| }; | |||||
| var findBranchOut = function (row) { | |||||
| if (!row) { | |||||
| return -1 | |||||
| } | |||||
| var i = row.length; | |||||
| while (i-- && | |||||
| !(row[i - 1] && row[i] === "/" && row[i - 1] === "|") && | |||||
| !(row[i - 2] && row[i] === "_" && row[i - 2] === "|")) {} | |||||
| return i; | |||||
| }; | |||||
| var findLineBreak = function (row) { | |||||
| if (!row) { | |||||
| return -1 | |||||
| } | |||||
| var i = row.length; | |||||
| while (i-- && | |||||
| !(row[i - 1] && row[i - 2] && row[i] === " " && row[i - 1] === "|" && row[i - 2] === "_")) {} | |||||
| return i; | |||||
| }; | |||||
| var genNewFlow = function () { | |||||
| var newId; | |||||
| do { | |||||
| newId = genRandomStr(); | |||||
| } while (findFlow(newId) !== -1); | |||||
| return {id:newId, color:"#" + newId}; | |||||
| }; | |||||
| //Draw methods | |||||
| var drawLine = function (moveX, moveY, lineX, lineY, color) { | |||||
| ctx.strokeStyle = color; | |||||
| ctx.beginPath(); | |||||
| ctx.moveTo(moveX, moveY); | |||||
| ctx.lineTo(lineX, lineY); | |||||
| ctx.stroke(); | |||||
| }; | |||||
| var drawLineRight = function (x, y, color) { | |||||
| drawLine(x, y + config.unitSize / 2, x + config.unitSize, y + config.unitSize / 2, color); | |||||
| }; | |||||
| var drawLineUp = function (x, y, color) { | |||||
| drawLine(x, y + config.unitSize / 2, x, y - config.unitSize / 2, color); | |||||
| }; | |||||
| var drawNode = function (x, y, color) { | |||||
| ctx.strokeStyle = color; | |||||
| drawLineUp(x, y, color); | |||||
| ctx.beginPath(); | |||||
| ctx.arc(x, y, config.nodeRadius, 0, Math.PI * 2, true); | |||||
| ctx.fill(); | |||||
| }; | |||||
| var drawLineIn = function (x, y, color) { | |||||
| drawLine(x + config.unitSize, y + config.unitSize / 2, x, y - config.unitSize / 2, color); | |||||
| }; | |||||
| var drawLineOut = function (x, y, color) { | |||||
| drawLine(x, y + config.unitSize / 2, x + config.unitSize, y - config.unitSize / 2, color); | |||||
| }; | |||||
| var draw = function (graphList) { | |||||
| var colomn, colomnIndex, prevColomn, condenseIndex, breakIndex = -1; | |||||
| var x, y; | |||||
| var color; | |||||
| var nodePos; | |||||
| var tempFlow; | |||||
| var prevRowLength = 0; | |||||
| var flowSwapPos = -1; | |||||
| var lastLinePos; | |||||
| var i, l; | |||||
| var condenseCurrentLength, condensePrevLength = 0, condenseNextLength = 0; | |||||
| var inlineIntersect = false; | |||||
| //initiate color array for first row | |||||
| for (i = 0, l = graphList[0].length; i < l; i++) { | |||||
| if (graphList[0][i] !== "_" && graphList[0][i] !== " ") { | |||||
| flows.push(genNewFlow()); | |||||
| } | |||||
| } | |||||
| y = (canvas.height / ratio) - config.unitSize * 0.5; | |||||
| //iterate | |||||
| for (i = 0, l = graphList.length; i < l; i++) { | |||||
| x = config.unitSize * 0.5; | |||||
| currentRow = graphList[i]; | |||||
| nextRow = graphList[i + 1]; | |||||
| prevRow = graphList[i - 1]; | |||||
| flowSwapPos = -1; | |||||
| condenseCurrentLength = currentRow.filter(function (val) { | |||||
| return (val !== " " && val !== "_") | |||||
| }).length; | |||||
| if (nextRow) { | |||||
| condenseNextLength = nextRow.filter(function (val) { | |||||
| return (val !== " " && val !== "_") | |||||
| }).length; | |||||
| } else { | |||||
| condenseNextLength = 0; | |||||
| } | |||||
| //pre process begin | |||||
| //use last row for analysing | |||||
| if (prevRow) { | |||||
| if (!inlineIntersect) { | |||||
| //intersect might happen | |||||
| for (colomnIndex = 0; colomnIndex < prevRowLength; colomnIndex++) { | |||||
| if (prevRow[colomnIndex + 1] && | |||||
| (prevRow[colomnIndex] === "/" && prevRow[colomnIndex + 1] === "|") || | |||||
| ((prevRow[colomnIndex] === "_" && prevRow[colomnIndex + 1] === "|") && | |||||
| (prevRow[colomnIndex + 2] === "/"))) { | |||||
| flowSwapPos = colomnIndex; | |||||
| //swap two flow | |||||
| tempFlow = {id:flows[flowSwapPos].id, color:flows[flowSwapPos].color}; | |||||
| flows[flowSwapPos].id = flows[flowSwapPos + 1].id; | |||||
| flows[flowSwapPos].color = flows[flowSwapPos + 1].color; | |||||
| flows[flowSwapPos + 1].id = tempFlow.id; | |||||
| flows[flowSwapPos + 1].color = tempFlow.color; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (condensePrevLength < condenseCurrentLength && | |||||
| ((nodePos = findColomn("*", currentRow)) !== -1 && | |||||
| (findColomn("_", currentRow) === -1))) { | |||||
| flows.splice(nodePos - 1, 0, genNewFlow()); | |||||
| } | |||||
| if (prevRowLength > currentRow.length && | |||||
| (nodePos = findColomn("*", prevRow)) !== -1) { | |||||
| if (findColomn("_", currentRow) === -1 && | |||||
| findColomn("/", currentRow) === -1 && | |||||
| findColomn("\\", currentRow) === -1) { | |||||
| flows.splice(nodePos + 1, 1); | |||||
| } | |||||
| } | |||||
| } //done with the previous row | |||||
| prevRowLength = currentRow.length; //store for next round | |||||
| colomnIndex = 0; //reset index | |||||
| condenseIndex = 0; | |||||
| condensePrevLength = 0; | |||||
| breakIndex = -1; //reset break index | |||||
| while (colomnIndex < currentRow.length) { | |||||
| colomn = currentRow[colomnIndex]; | |||||
| if (colomn !== " " && colomn !== "_") { | |||||
| ++condensePrevLength; | |||||
| } | |||||
| //check and fix line break in next row | |||||
| if (colomn === "/" && currentRow[colomnIndex - 1] && currentRow[colomnIndex - 1] === "|") { | |||||
| if ((breakIndex = findLineBreak(nextRow)) !== -1) { | |||||
| nextRow.splice(breakIndex, 1); | |||||
| } | |||||
| } | |||||
| //if line break found replace all '/' with '|' after breakIndex in previous row | |||||
| if (breakIndex !== - 1 && colomn === "/" && colomnIndex > breakIndex) { | |||||
| currentRow[colomnIndex] = "|"; | |||||
| colomn = "|"; | |||||
| } | |||||
| if (colomn === " " && | |||||
| currentRow[colomnIndex + 1] && | |||||
| currentRow[colomnIndex + 1] === "_" && | |||||
| currentRow[colomnIndex - 1] && | |||||
| currentRow[colomnIndex - 1] === "|") { | |||||
| currentRow.splice(colomnIndex, 1); | |||||
| currentRow[colomnIndex] = "/"; | |||||
| colomn = "/"; | |||||
| } | |||||
| //create new flow only when no intersect happened | |||||
| if (flowSwapPos === -1 && | |||||
| colomn === "/" && | |||||
| currentRow[colomnIndex - 1] && | |||||
| currentRow[colomnIndex - 1] === "|") { | |||||
| flows.splice(condenseIndex, 0, genNewFlow()); | |||||
| } | |||||
| //change \ and / to | when it's in the last position of the whole row | |||||
| if (colomn === "/" || colomn === "\\") { | |||||
| if (!(colomn === "/" && findBranchOut(nextRow) === -1)) { | |||||
| if ((lastLinePos = Math.max(findColomn("|", currentRow), | |||||
| findColomn("*", currentRow))) !== -1 && | |||||
| (lastLinePos < colomnIndex - 1)) { | |||||
| while (currentRow[++lastLinePos] === " ") {} | |||||
| if (lastLinePos === colomnIndex) { | |||||
| currentRow[colomnIndex] = "|"; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| if (colomn === "*" && | |||||
| prevRow && | |||||
| prevRow[condenseIndex + 1] === "\\") { | |||||
| flows.splice(condenseIndex + 1, 1); | |||||
| } | |||||
| if (colomn !== " ") { | |||||
| ++condenseIndex; | |||||
| } | |||||
| ++colomnIndex; | |||||
| } | |||||
| condenseCurrentLength = currentRow.filter(function (val) { | |||||
| return (val !== " " && val !== "_") | |||||
| }).length; | |||||
| //do some clean up | |||||
| if (flows.length > condenseCurrentLength) { | |||||
| flows.splice(condenseCurrentLength, flows.length - condenseCurrentLength); | |||||
| } | |||||
| colomnIndex = 0; | |||||
| //a little inline analysis and draw process | |||||
| while (colomnIndex < currentRow.length) { | |||||
| colomn = currentRow[colomnIndex]; | |||||
| prevColomn = currentRow[colomnIndex - 1]; | |||||
| if (currentRow[colomnIndex] === " ") { | |||||
| currentRow.splice(colomnIndex, 1); | |||||
| x += config.unitSize; | |||||
| continue; | |||||
| } | |||||
| //inline interset | |||||
| if ((colomn === "_" || colomn === "/") && | |||||
| currentRow[colomnIndex - 1] === "|" && | |||||
| currentRow[colomnIndex - 2] === "_") { | |||||
| inlineIntersect = true; | |||||
| tempFlow = flows.splice(colomnIndex - 2, 1)[0]; | |||||
| flows.splice(colomnIndex - 1, 0, tempFlow); | |||||
| currentRow.splice(colomnIndex - 2, 1); | |||||
| colomnIndex = colomnIndex - 1; | |||||
| } else { | |||||
| inlineIntersect = false; | |||||
| } | |||||
| color = flows[colomnIndex].color; | |||||
| switch (colomn) { | |||||
| case "_" : | |||||
| drawLineRight(x, y, color); | |||||
| x += config.unitSize; | |||||
| break; | |||||
| case "*" : | |||||
| drawNode(x, y, color); | |||||
| break; | |||||
| case "|" : | |||||
| drawLineUp(x, y, color); | |||||
| break; | |||||
| case "/" : | |||||
| if (prevColomn && | |||||
| (prevColomn === "/" || | |||||
| prevColomn === " ")) { | |||||
| x -= config.unitSize; | |||||
| } | |||||
| drawLineOut(x, y, color); | |||||
| x += config.unitSize; | |||||
| break; | |||||
| case "\\" : | |||||
| drawLineIn(x, y, color); | |||||
| break; | |||||
| } | |||||
| ++colomnIndex; | |||||
| } | |||||
| y -= config.unitSize; | |||||
| } | |||||
| }; | |||||
| init(); | |||||
| draw(graphList); | |||||
| }; | |||||
| // @end-license | |||||
| @@ -111,7 +111,6 @@ func Graph(ctx *context.Context) { | |||||
| ctx.Data["Reponame"] = ctx.Repo.Repository.Name | ctx.Data["Reponame"] = ctx.Repo.Repository.Name | ||||
| ctx.Data["CommitCount"] = commitsCount | ctx.Data["CommitCount"] = commitsCount | ||||
| ctx.Data["Branch"] = ctx.Repo.BranchName | ctx.Data["Branch"] = ctx.Repo.BranchName | ||||
| ctx.Data["RequireGitGraph"] = true | |||||
| ctx.Data["Page"] = context.NewPagination(int(allCommitsCount), setting.UI.GraphMaxCommitNum, page, 5) | ctx.Data["Page"] = context.NewPagination(int(allCommitsCount), setting.UI.GraphMaxCommitNum, page, 5) | ||||
| ctx.HTML(200, tplGraph) | ctx.HTML(200, tplGraph) | ||||
| } | } | ||||
| @@ -23,10 +23,6 @@ | |||||
| CodeMirror.modeURL = "{{StaticUrlPrefix}}/vendor/plugins/codemirror/mode/%N/%N.js"; | CodeMirror.modeURL = "{{StaticUrlPrefix}}/vendor/plugins/codemirror/mode/%N/%N.js"; | ||||
| </script> | </script> | ||||
| {{end}} | {{end}} | ||||
| {{if .RequireGitGraph}} | |||||
| <!-- graph --> | |||||
| <script src="{{StaticUrlPrefix}}/vendor/plugins/gitgraph/gitgraph.js"></script> | |||||
| {{end}} | |||||
| <!-- Third-party libraries --> | <!-- Third-party libraries --> | ||||
| {{if .RequireHighlightJS}} | {{if .RequireHighlightJS}} | ||||
| @@ -87,11 +87,6 @@ | |||||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css"> | <link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css"> | ||||
| {{end}} | {{end}} | ||||
| {{if .RequireGitGraph}} | |||||
| <!-- graph --> | |||||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/gitgraph/gitgraph.css"> | |||||
| {{end}} | |||||
| {{if .RequireTribute}} | {{if .RequireTribute}} | ||||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.css"> | <link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.css"> | ||||
| {{end}} | {{end}} | ||||
| @@ -6,9 +6,9 @@ var urlsToCache = [ | |||||
| '{{StaticUrlPrefix}}/vendor/plugins/jquery-migrate/jquery-migrate.min.js?v=3.0.1', | '{{StaticUrlPrefix}}/vendor/plugins/jquery-migrate/jquery-migrate.min.js?v=3.0.1', | ||||
| '{{StaticUrlPrefix}}/vendor/plugins/semantic/semantic.min.js', | '{{StaticUrlPrefix}}/vendor/plugins/semantic/semantic.min.js', | ||||
| '{{StaticUrlPrefix}}/js/index.js?v={{MD5 AppVer}}', | '{{StaticUrlPrefix}}/js/index.js?v={{MD5 AppVer}}', | ||||
| '{{StaticUrlPrefix}}/js/gitgraph.js?v={{MD5 AppVer}}', | |||||
| '{{StaticUrlPrefix}}/js/semantic.dropdown.custom.js?v={{MD5 AppVer}}', | '{{StaticUrlPrefix}}/js/semantic.dropdown.custom.js?v={{MD5 AppVer}}', | ||||
| '{{StaticUrlPrefix}}/vendor/plugins/clipboard/clipboard.min.js', | '{{StaticUrlPrefix}}/vendor/plugins/clipboard/clipboard.min.js', | ||||
| '{{StaticUrlPrefix}}/vendor/plugins/gitgraph/gitgraph.js', | |||||
| '{{StaticUrlPrefix}}/vendor/plugins/vue/vue.min.js', | '{{StaticUrlPrefix}}/vendor/plugins/vue/vue.min.js', | ||||
| '{{StaticUrlPrefix}}/vendor/plugins/emojify/emojify.custom.js', | '{{StaticUrlPrefix}}/vendor/plugins/emojify/emojify.custom.js', | ||||
| '{{StaticUrlPrefix}}/vendor/plugins/cssrelpreload/loadCSS.min.js', | '{{StaticUrlPrefix}}/vendor/plugins/cssrelpreload/loadCSS.min.js', | ||||
| @@ -25,7 +25,6 @@ var urlsToCache = [ | |||||
| '{{StaticUrlPrefix}}/vendor/assets/font-awesome/css/font-awesome.min.css', | '{{StaticUrlPrefix}}/vendor/assets/font-awesome/css/font-awesome.min.css', | ||||
| '{{StaticUrlPrefix}}/vendor/assets/octicons/octicons.min.css', | '{{StaticUrlPrefix}}/vendor/assets/octicons/octicons.min.css', | ||||
| '{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css', | '{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css', | ||||
| '{{StaticUrlPrefix}}/vendor/plugins/gitgraph/gitgraph.css', | |||||
| '{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.css', | '{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.css', | ||||
| '{{StaticUrlPrefix}}/vendor/plugins/semantic/semantic.min.css', | '{{StaticUrlPrefix}}/vendor/plugins/semantic/semantic.min.css', | ||||
| '{{StaticUrlPrefix}}/css/index.css?v={{MD5 AppVer}}', | '{{StaticUrlPrefix}}/css/index.css?v={{MD5 AppVer}}', | ||||
| @@ -1,15 +0,0 @@ | |||||
| /* globals gitGraph */ | |||||
| $(() => { | |||||
| const graphList = []; | |||||
| if (!document.getElementById('graph-canvas')) { | |||||
| return; | |||||
| } | |||||
| $('#graph-raw-list li span.node-relation').each(function () { | |||||
| graphList.push($(this).text()); | |||||
| }); | |||||
| gitGraph(document.getElementById('graph-canvas'), graphList); | |||||
| }); | |||||
| @@ -0,0 +1,16 @@ | |||||
| $(async () => { | |||||
| const graphCanvas = document.getElementById('graph-canvas'); | |||||
| if (!graphCanvas) return; | |||||
| const [{ default: gitGraph }] = await Promise.all([ | |||||
| import(/* webpackChunkName: "gitgraph" */'../vendor/gitgraph.js/gitgraph.custom.js'), | |||||
| import(/* webpackChunkName: "gitgraph" */'../vendor/gitgraph.js/gitgraph.custom.css'), | |||||
| ]); | |||||
| const graphList = []; | |||||
| $('#graph-raw-list li span.node-relation').each(function () { | |||||
| graphList.push($(this).text()); | |||||
| }); | |||||
| gitGraph(graphCanvas, graphList); | |||||
| }); | |||||
| @@ -2,6 +2,9 @@ | |||||
| /* exported timeAddManual, toggleStopwatch, cancelStopwatch, initHeatmap */ | /* exported timeAddManual, toggleStopwatch, cancelStopwatch, initHeatmap */ | ||||
| /* exported toggleDeadlineForm, setDeadline, updateDeadline, deleteDependencyModal, cancelCodeComment, onOAuthLoginClick */ | /* exported toggleDeadlineForm, setDeadline, updateDeadline, deleteDependencyModal, cancelCodeComment, onOAuthLoginClick */ | ||||
| import './publicPath'; | |||||
| import './gitGraph'; | |||||
| function htmlEncode(text) { | function htmlEncode(text) { | ||||
| return jQuery('<div />').text(text).html(); | return jQuery('<div />').text(text).html(); | ||||
| } | } | ||||
| @@ -0,0 +1,12 @@ | |||||
| /* This sets up webpack's chunk loading to load resources from the same | |||||
| directory where it loaded index.js from. This file must be imported | |||||
| before any lazy-loading is being attempted. */ | |||||
| if (document.currentScript && document.currentScript.src) { | |||||
| const url = new URL(document.currentScript.src); | |||||
| __webpack_public_path__ = `${url.pathname.replace(/\/[^/]*$/, '')}/`; | |||||
| } else { | |||||
| // compat: IE11 | |||||
| const script = document.querySelector('script[src*="/index.js"]'); | |||||
| __webpack_public_path__ = `${script.getAttribute('src').replace(/\/[^/]*$/, '')}/`; | |||||
| } | |||||
| @@ -1,6 +1,3 @@ | |||||
| body {font:13.34px/1.4 helvetica,arial,freesans,clean,sans-serif;} | |||||
| em {font-style:normal;} | |||||
| #git-graph-container, #rel-container {float:left;} | #git-graph-container, #rel-container {float:left;} | ||||
| #rel-container {max-width:30%; overflow-x:auto;} | #rel-container {max-width:30%; overflow-x:auto;} | ||||
| #git-graph-container {overflow-x:auto; width:100%} | #git-graph-container {overflow-x:auto; width:100%} | ||||
| @@ -13,4 +10,4 @@ em {font-style:normal;} | |||||
| #git-graph-container li a em {color:#BB0000;border-bottom:1px dotted #BBBBBB;text-decoration:none;font-style:normal;} | #git-graph-container li a em {color:#BB0000;border-bottom:1px dotted #BBBBBB;text-decoration:none;font-style:normal;} | ||||
| #rev-container {width:100%} | #rev-container {width:100%} | ||||
| #rev-list {margin:0;padding:0 5px 0 5px;min-width:95%} | #rev-list {margin:0;padding:0 5px 0 5px;min-width:95%} | ||||
| #graph-raw-list {margin:0px;} | |||||
| #graph-raw-list {margin:0px;} | |||||
| @@ -0,0 +1,419 @@ | |||||
| /* | |||||
| * @license magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt BSD 3-Clause | |||||
| * Copyright (c) 2011, Terrence Lee <kill889@gmail.com> | |||||
| * All rights reserved. | |||||
| * | |||||
| * Redistribution and use in source and binary forms, with or without | |||||
| * modification, are permitted provided that the following conditions are met: | |||||
| * * Redistributions of source code must retain the above copyright | |||||
| * notice, this list of conditions and the following disclaimer. | |||||
| * * Redistributions in binary form must reproduce the above copyright | |||||
| * notice, this list of conditions and the following disclaimer in the | |||||
| * documentation and/or other materials provided with the distribution. | |||||
| * * Neither the name of the <organization> nor the | |||||
| * names of its contributors may be used to endorse or promote products | |||||
| * derived from this software without specific prior written permission. | |||||
| * | |||||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |||||
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||||
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||||
| * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | |||||
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||||
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||||
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||||
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
| */ | |||||
| export default function gitGraph(canvas, rawGraphList, config) { | |||||
| if (!canvas.getContext) { | |||||
| return; | |||||
| } | |||||
| if (typeof config === 'undefined') { | |||||
| config = { | |||||
| unitSize: 20, | |||||
| lineWidth: 3, | |||||
| nodeRadius: 4 | |||||
| }; | |||||
| } | |||||
| const flows = []; | |||||
| const graphList = []; | |||||
| const ctx = canvas.getContext('2d'); | |||||
| const devicePixelRatio = window.devicePixelRatio || 1; | |||||
| const backingStoreRatio = ctx.webkitBackingStorePixelRatio | |||||
| || ctx.mozBackingStorePixelRatio | |||||
| || ctx.msBackingStorePixelRatio | |||||
| || ctx.oBackingStorePixelRatio | |||||
| || ctx.backingStorePixelRatio || 1; | |||||
| const ratio = devicePixelRatio / backingStoreRatio; | |||||
| const init = function () { | |||||
| let maxWidth = 0; | |||||
| let i; | |||||
| const l = rawGraphList.length; | |||||
| let row; | |||||
| let midStr; | |||||
| for (i = 0; i < l; i++) { | |||||
| midStr = rawGraphList[i].replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, ''); | |||||
| maxWidth = Math.max(midStr.replace(/(_|\s)/g, '').length, maxWidth); | |||||
| row = midStr.split(''); | |||||
| graphList.unshift(row); | |||||
| } | |||||
| const width = maxWidth * config.unitSize; | |||||
| const height = graphList.length * config.unitSize; | |||||
| canvas.width = width * ratio; | |||||
| canvas.height = height * ratio; | |||||
| canvas.style.width = `${width}px`; | |||||
| canvas.style.height = `${height}px`; | |||||
| ctx.lineWidth = config.lineWidth; | |||||
| ctx.lineJoin = 'round'; | |||||
| ctx.lineCap = 'round'; | |||||
| ctx.scale(ratio, ratio); | |||||
| }; | |||||
| const genRandomStr = function () { | |||||
| const chars = '0123456789ABCDEF'; | |||||
| const stringLength = 6; | |||||
| let randomString = '', rnum, i; | |||||
| for (i = 0; i < stringLength; i++) { | |||||
| rnum = Math.floor(Math.random() * chars.length); | |||||
| randomString += chars.substring(rnum, rnum + 1); | |||||
| } | |||||
| return randomString; | |||||
| }; | |||||
| const findFlow = function (id) { | |||||
| let i = flows.length; | |||||
| while (i-- && flows[i].id !== id); | |||||
| return i; | |||||
| }; | |||||
| const findColomn = function (symbol, row) { | |||||
| let i = row.length; | |||||
| while (i-- && row[i] !== symbol); | |||||
| return i; | |||||
| }; | |||||
| const findBranchOut = function (row) { | |||||
| if (!row) { | |||||
| return -1; | |||||
| } | |||||
| let i = row.length; | |||||
| while (i-- | |||||
| && !(row[i - 1] && row[i] === '/' && row[i - 1] === '|') | |||||
| && !(row[i - 2] && row[i] === '_' && row[i - 2] === '|')); | |||||
| return i; | |||||
| }; | |||||
| const findLineBreak = function (row) { | |||||
| if (!row) { | |||||
| return -1; | |||||
| } | |||||
| let i = row.length; | |||||
| while (i-- | |||||
| && !(row[i - 1] && row[i - 2] && row[i] === ' ' && row[i - 1] === '|' && row[i - 2] === '_')); | |||||
| return i; | |||||
| }; | |||||
| const genNewFlow = function () { | |||||
| let newId; | |||||
| do { | |||||
| newId = genRandomStr(); | |||||
| } while (findFlow(newId) !== -1); | |||||
| return { id: newId, color: `#${newId}` }; | |||||
| }; | |||||
| // Draw methods | |||||
| const drawLine = function (moveX, moveY, lineX, lineY, color) { | |||||
| ctx.strokeStyle = color; | |||||
| ctx.beginPath(); | |||||
| ctx.moveTo(moveX, moveY); | |||||
| ctx.lineTo(lineX, lineY); | |||||
| ctx.stroke(); | |||||
| }; | |||||
| const drawLineRight = function (x, y, color) { | |||||
| drawLine(x, y + config.unitSize / 2, x + config.unitSize, y + config.unitSize / 2, color); | |||||
| }; | |||||
| const drawLineUp = function (x, y, color) { | |||||
| drawLine(x, y + config.unitSize / 2, x, y - config.unitSize / 2, color); | |||||
| }; | |||||
| const drawNode = function (x, y, color) { | |||||
| ctx.strokeStyle = color; | |||||
| drawLineUp(x, y, color); | |||||
| ctx.beginPath(); | |||||
| ctx.arc(x, y, config.nodeRadius, 0, Math.PI * 2, true); | |||||
| ctx.fill(); | |||||
| }; | |||||
| const drawLineIn = function (x, y, color) { | |||||
| drawLine(x + config.unitSize, y + config.unitSize / 2, x, y - config.unitSize / 2, color); | |||||
| }; | |||||
| const drawLineOut = function (x, y, color) { | |||||
| drawLine(x, y + config.unitSize / 2, x + config.unitSize, y - config.unitSize / 2, color); | |||||
| }; | |||||
| const draw = function (graphList) { | |||||
| let colomn, colomnIndex, prevColomn, condenseIndex, breakIndex = -1; | |||||
| let x, y; | |||||
| let color; | |||||
| let nodePos; | |||||
| let tempFlow; | |||||
| let prevRowLength = 0; | |||||
| let flowSwapPos = -1; | |||||
| let lastLinePos; | |||||
| let i, l; | |||||
| let condenseCurrentLength, condensePrevLength = 0; | |||||
| let inlineIntersect = false; | |||||
| // initiate color array for first row | |||||
| for (i = 0, l = graphList[0].length; i < l; i++) { | |||||
| if (graphList[0][i] !== '_' && graphList[0][i] !== ' ') { | |||||
| flows.push(genNewFlow()); | |||||
| } | |||||
| } | |||||
| y = (canvas.height / ratio) - config.unitSize * 0.5; | |||||
| // iterate | |||||
| for (i = 0, l = graphList.length; i < l; i++) { | |||||
| x = config.unitSize * 0.5; | |||||
| const currentRow = graphList[i]; | |||||
| const nextRow = graphList[i + 1]; | |||||
| const prevRow = graphList[i - 1]; | |||||
| flowSwapPos = -1; | |||||
| condenseCurrentLength = currentRow.filter((val) => { | |||||
| return (val !== ' ' && val !== '_'); | |||||
| }).length; | |||||
| // pre process begin | |||||
| // use last row for analysing | |||||
| if (prevRow) { | |||||
| if (!inlineIntersect) { | |||||
| // intersect might happen | |||||
| for (colomnIndex = 0; colomnIndex < prevRowLength; colomnIndex++) { | |||||
| if (prevRow[colomnIndex + 1] | |||||
| && (prevRow[colomnIndex] === '/' && prevRow[colomnIndex + 1] === '|') | |||||
| || ((prevRow[colomnIndex] === '_' && prevRow[colomnIndex + 1] === '|') | |||||
| && (prevRow[colomnIndex + 2] === '/'))) { | |||||
| flowSwapPos = colomnIndex; | |||||
| // swap two flow | |||||
| tempFlow = { id: flows[flowSwapPos].id, color: flows[flowSwapPos].color }; | |||||
| flows[flowSwapPos].id = flows[flowSwapPos + 1].id; | |||||
| flows[flowSwapPos].color = flows[flowSwapPos + 1].color; | |||||
| flows[flowSwapPos + 1].id = tempFlow.id; | |||||
| flows[flowSwapPos + 1].color = tempFlow.color; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (condensePrevLength < condenseCurrentLength | |||||
| && ((nodePos = findColomn('*', currentRow)) !== -1 | |||||
| && (findColomn('_', currentRow) === -1))) { | |||||
| flows.splice(nodePos - 1, 0, genNewFlow()); | |||||
| } | |||||
| if (prevRowLength > currentRow.length | |||||
| && (nodePos = findColomn('*', prevRow)) !== -1) { | |||||
| if (findColomn('_', currentRow) === -1 | |||||
| && findColomn('/', currentRow) === -1 | |||||
| && findColomn('\\', currentRow) === -1) { | |||||
| flows.splice(nodePos + 1, 1); | |||||
| } | |||||
| } | |||||
| } // done with the previous row | |||||
| prevRowLength = currentRow.length; // store for next round | |||||
| colomnIndex = 0; // reset index | |||||
| condenseIndex = 0; | |||||
| condensePrevLength = 0; | |||||
| breakIndex = -1; // reset break index | |||||
| while (colomnIndex < currentRow.length) { | |||||
| colomn = currentRow[colomnIndex]; | |||||
| if (colomn !== ' ' && colomn !== '_') { | |||||
| ++condensePrevLength; | |||||
| } | |||||
| // check and fix line break in next row | |||||
| if (colomn === '/' && currentRow[colomnIndex - 1] && currentRow[colomnIndex - 1] === '|') { | |||||
| if ((breakIndex = findLineBreak(nextRow)) !== -1) { | |||||
| nextRow.splice(breakIndex, 1); | |||||
| } | |||||
| } | |||||
| // if line break found replace all '/' with '|' after breakIndex in previous row | |||||
| if (breakIndex !== -1 && colomn === '/' && colomnIndex > breakIndex) { | |||||
| currentRow[colomnIndex] = '|'; | |||||
| colomn = '|'; | |||||
| } | |||||
| if (colomn === ' ' | |||||
| && currentRow[colomnIndex + 1] | |||||
| && currentRow[colomnIndex + 1] === '_' | |||||
| && currentRow[colomnIndex - 1] | |||||
| && currentRow[colomnIndex - 1] === '|') { | |||||
| currentRow.splice(colomnIndex, 1); | |||||
| currentRow[colomnIndex] = '/'; | |||||
| colomn = '/'; | |||||
| } | |||||
| // create new flow only when no intersect happened | |||||
| if (flowSwapPos === -1 | |||||
| && colomn === '/' | |||||
| && currentRow[colomnIndex - 1] | |||||
| && currentRow[colomnIndex - 1] === '|') { | |||||
| flows.splice(condenseIndex, 0, genNewFlow()); | |||||
| } | |||||
| // change \ and / to | when it's in the last position of the whole row | |||||
| if (colomn === '/' || colomn === '\\') { | |||||
| if (!(colomn === '/' && findBranchOut(nextRow) === -1)) { | |||||
| if ((lastLinePos = Math.max(findColomn('|', currentRow), | |||||
| findColomn('*', currentRow))) !== -1 | |||||
| && (lastLinePos < colomnIndex - 1)) { | |||||
| while (currentRow[++lastLinePos] === ' '); | |||||
| if (lastLinePos === colomnIndex) { | |||||
| currentRow[colomnIndex] = '|'; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| if (colomn === '*' | |||||
| && prevRow | |||||
| && prevRow[condenseIndex + 1] === '\\') { | |||||
| flows.splice(condenseIndex + 1, 1); | |||||
| } | |||||
| if (colomn !== ' ') { | |||||
| ++condenseIndex; | |||||
| } | |||||
| ++colomnIndex; | |||||
| } | |||||
| condenseCurrentLength = currentRow.filter((val) => { | |||||
| return (val !== ' ' && val !== '_'); | |||||
| }).length; | |||||
| // do some clean up | |||||
| if (flows.length > condenseCurrentLength) { | |||||
| flows.splice(condenseCurrentLength, flows.length - condenseCurrentLength); | |||||
| } | |||||
| colomnIndex = 0; | |||||
| // a little inline analysis and draw process | |||||
| while (colomnIndex < currentRow.length) { | |||||
| colomn = currentRow[colomnIndex]; | |||||
| prevColomn = currentRow[colomnIndex - 1]; | |||||
| if (currentRow[colomnIndex] === ' ') { | |||||
| currentRow.splice(colomnIndex, 1); | |||||
| x += config.unitSize; | |||||
| continue; | |||||
| } | |||||
| // inline interset | |||||
| if ((colomn === '_' || colomn === '/') | |||||
| && currentRow[colomnIndex - 1] === '|' | |||||
| && currentRow[colomnIndex - 2] === '_') { | |||||
| inlineIntersect = true; | |||||
| tempFlow = flows.splice(colomnIndex - 2, 1)[0]; | |||||
| flows.splice(colomnIndex - 1, 0, tempFlow); | |||||
| currentRow.splice(colomnIndex - 2, 1); | |||||
| colomnIndex -= 1; | |||||
| } else { | |||||
| inlineIntersect = false; | |||||
| } | |||||
| color = flows[colomnIndex].color; | |||||
| switch (colomn) { | |||||
| case '_': | |||||
| drawLineRight(x, y, color); | |||||
| x += config.unitSize; | |||||
| break; | |||||
| case '*': | |||||
| drawNode(x, y, color); | |||||
| break; | |||||
| case '|': | |||||
| drawLineUp(x, y, color); | |||||
| break; | |||||
| case '/': | |||||
| if (prevColomn | |||||
| && (prevColomn === '/' | |||||
| || prevColomn === ' ')) { | |||||
| x -= config.unitSize; | |||||
| } | |||||
| drawLineOut(x, y, color); | |||||
| x += config.unitSize; | |||||
| break; | |||||
| case '\\': | |||||
| drawLineIn(x, y, color); | |||||
| break; | |||||
| } | |||||
| ++colomnIndex; | |||||
| } | |||||
| y -= config.unitSize; | |||||
| } | |||||
| }; | |||||
| init(); | |||||
| draw(graphList); | |||||
| } | |||||
| // @end-license | |||||
| @@ -4,17 +4,24 @@ const TerserPlugin = require('terser-webpack-plugin'); | |||||
| module.exports = { | module.exports = { | ||||
| mode: 'production', | mode: 'production', | ||||
| entry: { | entry: { | ||||
| index: ['./web_src/js/index', './web_src/js/draw'] | |||||
| index: ['./web_src/js/index'] | |||||
| }, | }, | ||||
| devtool: 'source-map', | devtool: 'source-map', | ||||
| output: { | output: { | ||||
| path: path.resolve(__dirname, 'public/js'), | path: path.resolve(__dirname, 'public/js'), | ||||
| filename: 'index.js' | |||||
| filename: 'index.js', | |||||
| chunkFilename: '[name].js', | |||||
| }, | }, | ||||
| optimization: { | optimization: { | ||||
| minimize: true, | minimize: true, | ||||
| minimizer: [new TerserPlugin({ | minimizer: [new TerserPlugin({ | ||||
| sourceMap: true, | sourceMap: true, | ||||
| extractComments: false, | |||||
| terserOptions: { | |||||
| output: { | |||||
| comments: false, | |||||
| }, | |||||
| }, | |||||
| })], | })], | ||||
| }, | }, | ||||
| module: { | module: { | ||||
| @@ -33,10 +40,22 @@ module.exports = { | |||||
| corejs: 3, | corejs: 3, | ||||
| } | } | ||||
| ] | ] | ||||
| ] | |||||
| ], | |||||
| plugins: [ | |||||
| [ | |||||
| '@babel/plugin-transform-runtime', | |||||
| { | |||||
| regenerator: true, | |||||
| } | |||||
| ] | |||||
| ], | |||||
| } | } | ||||
| } | } | ||||
| } | |||||
| }, | |||||
| { | |||||
| test: /\.css$/i, | |||||
| use: ['style-loader', 'css-loader'], | |||||
| }, | |||||
| ] | ] | ||||
| } | } | ||||
| }; | }; | ||||