Browse Source
enable lazy-loading of gitgraph.js (#9036)
enable lazy-loading of gitgraph.js (#9036)
- 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.lunny/display_deleted_branch2
committed by
Lauris BH
23 changed files with 1334 additions and 676 deletions
-
1.eslintignore
-
1.eslintrc
-
1047package-lock.json
-
4package.json
-
2public/js/gitgraph.js
-
1public/js/gitgraph.js.map
-
2public/js/index.js
-
2public/js/index.js.map
-
3public/vendor/VERSIONS
-
2public/vendor/librejs.html
-
435public/vendor/plugins/gitgraph/gitgraph.js
-
1routers/repo/commit.go
-
4templates/base/footer.tmpl
-
5templates/base/head.tmpl
-
3templates/pwa/serviceworker_js.tmpl
-
15web_src/js/draw.js
-
16web_src/js/gitGraph.js
-
3web_src/js/index.js
-
12web_src/js/publicPath.js
-
0web_src/vendor/gitgraph.js/LICENSE
-
5web_src/vendor/gitgraph.js/gitgraph.custom.css
-
419web_src/vendor/gitgraph.js/gitgraph.custom.js
-
27webpack.config.js
@ -1 +1,2 @@ |
|||
/public/js/semantic.dropdown.custom.js |
|||
/web_src/js/vendor/** |
1047
package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2
public/js/gitgraph.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1
public/js/gitgraph.js.map
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2
public/js/index.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2
public/js/index.js.map
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -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
|
@ -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); |
|||
}); |
@ -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(/\/[^/]*$/, '')}/`; |
|||
} |
@ -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
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue