summaryrefslogtreecommitdiffstats
path: root/web_src/js
diff options
context:
space:
mode:
Diffstat (limited to 'web_src/js')
-rw-r--r--web_src/js/features/gitgraph.js563
-rw-r--r--web_src/js/vendor/gitgraph.js432
2 files changed, 559 insertions, 436 deletions
diff --git a/web_src/js/features/gitgraph.js b/web_src/js/features/gitgraph.js
index c0006a2596..3e6b27436d 100644
--- a/web_src/js/features/gitgraph.js
+++ b/web_src/js/features/gitgraph.js
@@ -1,13 +1,568 @@
+// Although inspired by the https://github.com/bluef/gitgraph.js/blob/master/gitgraph.js
+// this has been completely rewritten with almost no remaining code
+
+// GitGraphCanvas is a canvas for drawing gitgraphs on to
+class GitGraphCanvas {
+ constructor(canvas, widthUnits, heightUnits, config) {
+ this.ctx = canvas.getContext('2d');
+
+ const width = widthUnits * config.unitSize;
+ this.height = heightUnits * config.unitSize;
+
+ const ratio = window.devicePixelRatio || 1;
+
+ canvas.width = width * ratio;
+ canvas.height = this.height * ratio;
+
+ canvas.style.width = `${width}px`;
+ canvas.style.height = `${this.height}px`;
+
+ this.ctx.lineWidth = config.lineWidth;
+ this.ctx.lineJoin = 'round';
+ this.ctx.lineCap = 'round';
+
+ this.ctx.scale(ratio, ratio);
+ this.config = config;
+ }
+ drawLine(moveX, moveY, lineX, lineY, color) {
+ this.ctx.strokeStyle = color;
+ this.ctx.beginPath();
+ this.ctx.moveTo(moveX, moveY);
+ this.ctx.lineTo(lineX, lineY);
+ this.ctx.stroke();
+ }
+ drawLineRight(x, y, color) {
+ this.drawLine(
+ x - 0.5 * this.config.unitSize,
+ y + this.config.unitSize / 2,
+ x + 0.5 * this.config.unitSize,
+ y + this.config.unitSize / 2,
+ color
+ );
+ }
+ drawLineUp(x, y, color) {
+ this.drawLine(
+ x,
+ y + this.config.unitSize / 2,
+ x,
+ y - this.config.unitSize / 2,
+ color
+ );
+ }
+ drawNode(x, y, color) {
+ this.ctx.strokeStyle = color;
+
+ this.drawLineUp(x, y, color);
+
+ this.ctx.beginPath();
+ this.ctx.arc(x, y, this.config.nodeRadius, 0, Math.PI * 2, true);
+ this.ctx.fillStyle = color;
+ this.ctx.fill();
+ }
+ drawLineIn(x, y, color) {
+ this.drawLine(
+ x + 0.5 * this.config.unitSize,
+ y + this.config.unitSize / 2,
+ x - 0.5 * this.config.unitSize,
+ y - this.config.unitSize / 2,
+ color
+ );
+ }
+ drawLineOut(x, y, color) {
+ this.drawLine(
+ x - 0.5 * this.config.unitSize,
+ y + this.config.unitSize / 2,
+ x + 0.5 * this.config.unitSize,
+ y - this.config.unitSize / 2,
+ color
+ );
+ }
+ drawSymbol(symbol, columnNumber, rowNumber, color) {
+ const y = this.height - this.config.unitSize * (rowNumber + 0.5);
+ const x = this.config.unitSize * 0.5 * (columnNumber + 1);
+ switch (symbol) {
+ case '-':
+ if (columnNumber % 2 === 1) {
+ this.drawLineRight(x, y, color);
+ }
+ break;
+ case '_':
+ this.drawLineRight(x, y, color);
+ break;
+ case '*':
+ this.drawNode(x, y, color);
+ break;
+ case '|':
+ this.drawLineUp(x, y, color);
+ break;
+ case '/':
+ this.drawLineOut(x, y, color);
+ break;
+ case '\\':
+ this.drawLineIn(x, y, color);
+ break;
+ case '.':
+ case ' ':
+ break;
+ default:
+ console.error('Unknown symbol', symbol, color);
+ }
+ }
+}
+
+class GitGraph {
+ constructor(canvas, rawRows, config) {
+ this.rows = [];
+ let maxWidth = 0;
+
+ for (let i = 0; i < rawRows.length; i++) {
+ const rowStr = rawRows[i];
+ maxWidth = Math.max(rowStr.replace(/([_\s.-])/g, '').length, maxWidth);
+
+ const rowArray = rowStr.split('');
+
+ this.rows.unshift(rowArray);
+ }
+
+ this.currentFlows = [];
+ this.previousFlows = [];
+
+ this.gitGraphCanvas = new GitGraphCanvas(
+ canvas,
+ maxWidth,
+ this.rows.length,
+ config
+ );
+ }
+
+ generateNewFlow(column) {
+ let newId;
+
+ do {
+ newId = generateRandomColorString();
+ } while (this.hasFlow(newId, column));
+
+ return {id: newId, color: `#${newId}`};
+ }
+
+ hasFlow(id, column) {
+ // We want to find the flow with the current ID
+ // Possible flows are those in the currentFlows
+ // Or flows in previousFlows[column-2:...]
+ for (
+ let idx = column - 2 < 0 ? 0 : column - 2;
+ idx < this.previousFlows.length;
+ idx++
+ ) {
+ if (this.previousFlows[idx] && this.previousFlows[idx].id === id) {
+ return true;
+ }
+ }
+ for (let idx = 0; idx < this.currentFlows.length; idx++) {
+ if (this.currentFlows[idx] && this.currentFlows[idx].id === id) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ takePreviousFlow(column) {
+ if (column < this.previousFlows.length && this.previousFlows[column]) {
+ const flow = this.previousFlows[column];
+ this.previousFlows[column] = null;
+ return flow;
+ }
+ return this.generateNewFlow(column);
+ }
+
+ draw() {
+ if (this.rows.length === 0) {
+ return;
+ }
+
+ this.currentFlows = new Array(this.rows[0].length);
+
+ // Generate flows for the first row - I do not believe that this can contain '_', '-', '.'
+ for (let column = 0; column < this.rows[0].length; column++) {
+ if (this.rows[0][column] === ' ') {
+ continue;
+ }
+ this.currentFlows[column] = this.generateNewFlow(column);
+ }
+
+ // Draw the first row
+ for (let column = 0; column < this.rows[0].length; column++) {
+ const symbol = this.rows[0][column];
+ const color = this.currentFlows[column] ? this.currentFlows[column].color : '';
+ this.gitGraphCanvas.drawSymbol(symbol, column, 0, color);
+ }
+
+ for (let row = 1; row < this.rows.length; row++) {
+ // Done previous row - step up the row
+ const currentRow = this.rows[row];
+ const previousRow = this.rows[row - 1];
+
+ this.previousFlows = this.currentFlows;
+ this.currentFlows = new Array(currentRow.length);
+
+ // Set flows for this row
+ for (let column = 0; column < currentRow.length; column++) {
+ column = this.setFlowFor(column, currentRow, previousRow);
+ }
+
+ // Draw this row
+ for (let column = 0; column < currentRow.length; column++) {
+ const symbol = currentRow[column];
+ const color = this.currentFlows[column] ? this.currentFlows[column].color : '';
+ this.gitGraphCanvas.drawSymbol(symbol, column, row, color);
+ }
+ }
+ }
+
+ setFlowFor(column, currentRow, previousRow) {
+ const symbol = currentRow[column];
+ switch (symbol) {
+ case '|':
+ case '*':
+ return this.setUpFlow(column, currentRow, previousRow);
+ case '/':
+ return this.setOutFlow(column, currentRow, previousRow);
+ case '\\':
+ return this.setInFlow(column, currentRow, previousRow);
+ case '_':
+ return this.setRightFlow(column, currentRow, previousRow);
+ case '-':
+ return this.setLeftFlow(column, currentRow, previousRow);
+ case ' ':
+ // In space no one can hear you flow ... (?)
+ return column;
+ default:
+ // Unexpected so let's generate a new flow and wait for bug-reports
+ this.currentFlows[column] = this.generateNewFlow(column);
+ return column;
+ }
+ }
+
+ // setUpFlow handles '|' or '*' - returns the last column that was set
+ // generally we prefer to take the left most flow from the previous row
+ setUpFlow(column, currentRow, previousRow) {
+ // If ' |/' or ' |_'
+ // '/|' '/|' -> Take the '|' flow directly beneath us
+ if (
+ column + 1 < currentRow.length &&
+ (currentRow[column + 1] === '/' || currentRow[column + 1] === '_') &&
+ column < previousRow.length &&
+ (previousRow[column] === '|' || previousRow[column] === '*') &&
+ previousRow[column - 1] === '/'
+ ) {
+ this.currentFlows[column] = this.takePreviousFlow(column);
+ return column;
+ }
+
+ // If ' |/' or ' |_'
+ // '/ ' '/ ' -> Take the '/' flow from the preceding column
+ if (
+ column + 1 < currentRow.length &&
+ (currentRow[column + 1] === '/' || currentRow[column + 1] === '_') &&
+ column - 1 < previousRow.length &&
+ previousRow[column - 1] === '/'
+ ) {
+ this.currentFlows[column] = this.takePreviousFlow(column - 1);
+ return column;
+ }
+
+ // If ' |'
+ // '/' -> Take the '/' flow - (we always prefer the left-most flow)
+ if (
+ column > 0 &&
+ column - 1 < previousRow.length &&
+ previousRow[column - 1] === '/'
+ ) {
+ this.currentFlows[column] = this.takePreviousFlow(column - 1);
+ return column;
+ }
+
+ // If '|' OR '|' take the '|' flow
+ // '|' '*'
+ if (
+ column < previousRow.length &&
+ (previousRow[column] === '|' || previousRow[column] === '*')
+ ) {
+ this.currentFlows[column] = this.takePreviousFlow(column);
+ return column;
+ }
+
+ // If '| ' keep the '\' flow
+ // ' \'
+ if (column + 1 < previousRow.length && previousRow[column + 1] === '\\') {
+ this.currentFlows[column] = this.takePreviousFlow(column + 1);
+ return column;
+ }
+
+ // Otherwise just create a new flow - probably this is an error...
+ this.currentFlows[column] = this.generateNewFlow(column);
+ return column;
+ }
+
+ // setOutFlow handles '/' - returns the last column that was set
+ // generally we prefer to take the left most flow from the previous row
+ setOutFlow(column, currentRow, previousRow) {
+ // If '_/' -> keep the '_' flow
+ if (column > 0 && currentRow[column - 1] === '_') {
+ this.currentFlows[column] = this.currentFlows[column - 1];
+ return column;
+ }
+
+ // If '_|/' -> keep the '_' flow
+ if (
+ column > 1 &&
+ (currentRow[column - 1] === '|' || currentRow[column - 1] === '*') &&
+ currentRow[column - 2] === '_'
+ ) {
+ this.currentFlows[column] = this.currentFlows[column - 2];
+ return column;
+ }
+
+ // If '|/'
+ // '/' -> take the '/' flow (if it is still available)
+ if (
+ column > 1 &&
+ currentRow[column - 1] === '|' &&
+ column - 2 < previousRow.length &&
+ previousRow[column - 2] === '/'
+ ) {
+ this.currentFlows[column] = this.takePreviousFlow(column - 2);
+ return column;
+ }
+
+ // If ' /'
+ // '/' -> take the '/' flow, but transform the symbol to '|' due to our spacing
+ // This should only happen if there are 3 '/' - in a row so we don't need to be cleverer here
+ if (
+ column > 0 &&
+ currentRow[column - 1] === ' ' &&
+ column - 1 < previousRow.length &&
+ previousRow[column - 1] === '/'
+ ) {
+ this.currentFlows[column] = this.takePreviousFlow(column - 1);
+ currentRow[column] = '|';
+ return column;
+ }
+
+ // If ' /'
+ // '|' -> take the '|' flow
+ if (
+ column > 0 &&
+ currentRow[column - 1] === ' ' &&
+ column - 1 < previousRow.length &&
+ (previousRow[column - 1] === '|' || previousRow[column - 1] === '*')
+ ) {
+ this.currentFlows[column] = this.takePreviousFlow(column - 1);
+ return column;
+ }
+
+ // If '/' <- Not sure this ever happens... but take the '\' flow
+ // '\'
+ if (column < previousRow.length && previousRow[column] === '\\') {
+ this.currentFlows[column] = this.takePreviousFlow(column);
+ return column;
+ }
+
+ // Otherwise just generate a new flow and wait for bug-reports...
+ this.currentFlows[column] = this.generateNewFlow(column);
+ return column;
+ }
+
+ // setInFlow handles '\' - returns the last column that was set
+ // generally we prefer to take the left most flow from the previous row
+ setInFlow(column, currentRow, previousRow) {
+ // If '\?'
+ // '/?' -> take the '/' flow
+ if (column < previousRow.length && previousRow[column] === '/') {
+ this.currentFlows[column] = this.takePreviousFlow(column);
+ return column;
+ }
+
+ // If '\?'
+ // ' \' -> take the '\' flow and reassign to '|'
+ // This should only happen if there are 3 '\' - in a row so we don't need to be cleverer here
+ if (column + 1 < previousRow.length && previousRow[column + 1] === '\\') {
+ this.currentFlows[column] = this.takePreviousFlow(column + 1);
+ currentRow[column] = '|';
+ return column;
+ }
+
+ // If '\?'
+ // ' |' -> take the '|' flow
+ if (
+ column + 1 < previousRow.length &&
+ (previousRow[column + 1] === '|' || previousRow[column + 1] === '*')
+ ) {
+ this.currentFlows[column] = this.takePreviousFlow(column + 1);
+ return column;
+ }
+
+ // Otherwise just generate a new flow and wait for bug-reports if we're wrong...
+ this.currentFlows[column] = this.generateNewFlow(column);
+ return column;
+ }
+
+ // setRightFlow handles '_' - returns the last column that was set
+ // generally we prefer to take the left most flow from the previous row
+ setRightFlow(column, currentRow, previousRow) {
+ // if '__' keep the '_' flow
+ if (column > 0 && currentRow[column - 1] === '_') {
+ this.currentFlows[column] = this.currentFlows[column - 1];
+ return column;
+ }
+
+ // if '_|_' -> keep the '_' flow
+ if (
+ column > 1 &&
+ currentRow[column - 1] === '|' &&
+ currentRow[column - 2] === '_'
+ ) {
+ this.currentFlows[column] = this.currentFlows[column - 2];
+ return column;
+ }
+
+ // if ' _' -> take the '/' flow
+ // '/ '
+ if (
+ column > 0 &&
+ column - 1 < previousRow.length &&
+ previousRow[column - 1] === '/'
+ ) {
+ this.currentFlows[column] = this.takePreviousFlow(column - 1);
+ return column;
+ }
+
+ // if ' |_'
+ // '/? ' -> take the '/' flow (this may cause generation...)
+ // we can do this because we know that git graph
+ // doesn't create compact graphs like: ' |_'
+ // '//'
+ if (
+ column > 1 &&
+ column - 2 < previousRow.length &&
+ previousRow[column - 2] === '/'
+ ) {
+ this.currentFlows[column] = this.takePreviousFlow(column - 2);
+ return column;
+ }
+
+ // There really shouldn't be another way of doing this - generate and wait for bug-reports...
+
+ this.currentFlows[column] = this.generateNewFlow(column);
+ return column;
+ }
+
+ // setLeftFlow handles '----.' - returns the last column that was set
+ // generally we prefer to take the left most flow from the previous row that terminates this left recursion
+ setLeftFlow(column, currentRow, previousRow) {
+ // This is: '----------.' or the like
+ // ' \ \ /|\'
+
+ // Find the end of the '-' or nearest '/|\' in the previousRow :
+ let originalColumn = column;
+ let flow;
+ for (; column < currentRow.length && currentRow[column] === '-'; column++) {
+ if (column > 0 && column - 1 < previousRow.length && previousRow[column - 1] === '/') {
+ flow = this.takePreviousFlow(column - 1);
+ break;
+ } else if (column < previousRow.length && previousRow[column] === '|') {
+ flow = this.takePreviousFlow(column);
+ break;
+ } else if (
+ column + 1 < previousRow.length &&
+ previousRow[column + 1] === '\\'
+ ) {
+ flow = this.takePreviousFlow(column + 1);
+ break;
+ }
+ }
+
+ // if we have a flow then we found a '/|\' in the previousRow
+ if (flow) {
+ for (; originalColumn < column + 1; originalColumn++) {
+ this.currentFlows[originalColumn] = flow;
+ }
+ return column;
+ }
+
+ // If the symbol in the column is not a '.' then there's likely an error
+ if (currentRow[column] !== '.') {
+ // It really should end in a '.' but this one doesn't...
+ // 1. Step back - we don't want to eat this column
+ column--;
+ // 2. Generate a new flow and await bug-reports...
+ this.currentFlows[column] = this.generateNewFlow(column);
+
+ // 3. Assign all of the '-' to the same flow.
+ for (; originalColumn < column; originalColumn++) {
+ this.currentFlows[originalColumn] = this.currentFlows[column];
+ }
+ return column;
+ }
+
+ // We have a terminal '.' eg. the current row looks like '----.'
+ // the previous row should look like one of '/|\' eg. ' \'
+ if (column > 0 && column - 1 < previousRow.length && previousRow[column - 1] === '/') {
+ flow = this.takePreviousFlow(column - 1);
+ } else if (column < previousRow.length && previousRow[column] === '|') {
+ flow = this.takePreviousFlow(column);
+ } else if (
+ column + 1 < previousRow.length &&
+ previousRow[column + 1] === '\\'
+ ) {
+ flow = this.takePreviousFlow(column + 1);
+ } else {
+ // Again unexpected so let's generate and wait the bug-report
+ flow = this.generateNewFlow(column);
+ }
+
+ // Assign all of the rest of the ----. to this flow.
+ for (; originalColumn < column + 1; originalColumn++) {
+ this.currentFlows[originalColumn] = flow;
+ }
+
+ return column;
+ }
+}
+
+function generateRandomColorString() {
+ 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;
+}
+
export default async function initGitGraph() {
const graphCanvas = document.getElementById('graph-canvas');
- if (!graphCanvas) return;
-
- const {default: gitGraph} = await import(/* webpackChunkName: "gitgraph" */'../vendor/gitgraph.js');
+ if (!graphCanvas || !graphCanvas.getContext) return;
+ // Grab the raw graphList
const graphList = [];
$('#graph-raw-list li span.node-relation').each(function () {
graphList.push($(this).text());
});
- gitGraph(graphCanvas, graphList);
+ // Define some drawing parameters
+ const config = {
+ unitSize: 20,
+ lineWidth: 3,
+ nodeRadius: 4
+ };
+
+
+ const gitGraph = new GitGraph(graphCanvas, graphList, config);
+ gitGraph.draw();
+ graphCanvas.closest('#git-graph-container').classList.add('in');
}
diff --git a/web_src/js/vendor/gitgraph.js b/web_src/js/vendor/gitgraph.js
deleted file mode 100644
index 0cf5d0f75b..0000000000
--- a/web_src/js/vendor/gitgraph.js
+++ /dev/null
@@ -1,432 +0,0 @@
-/* This is a customized version of https://github.com/bluef/gitgraph.js/blob/master/gitgraph.js
- Changes include conversion to ES6 and linting fixes */
-
-/*
- * @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, '');
- midStr = midStr.replace(/(--)|(-\.)/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 // eslint-disable-line no-cond-assign
- && (findColomn('_', currentRow) === -1))) {
- flows.splice(nodePos - 1, 0, genNewFlow());
- }
-
- if (prevRowLength > currentRow.length
- && (nodePos = findColomn('*', prevRow)) !== -1) { // eslint-disable-line no-cond-assign
- 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] === '|') {
- /* eslint-disable-next-line */
- 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)) {
- /* eslint-disable-next-line */
- 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;
-
- 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 intersect
- 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;
- }
-
- if (colomn === '|' && currentRow[colomnIndex - 1] && currentRow[colomnIndex - 1] === '\\') {
- flows.splice(colomnIndex, 0, genNewFlow());
- }
-
- color = flows[colomnIndex].color;
-
- switch (colomn) {
- case '-':
- case '_':
- drawLineRight(x, y, color);
-
- x += config.unitSize;
- break;
-
- case '*':
- drawNode(x, y, color);
- break;
-
- case '|':
- if (prevColomn && prevColomn === '\\') {
- x += config.unitSize;
- }
- 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;
- }
-
- // do some clean up
- if (flows.length > condenseCurrentLength) {
- flows.splice(condenseCurrentLength, flows.length - condenseCurrentLength);
- }
- };
-
- init();
- draw(graphList);
-}
-// @end-license