diff --git a/web_src/js/features/gitgraph.js b/web_src/js/features/gitgraph.js index c0006a259..3e6b27436 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 0cf5d0f75..000000000 --- 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 - * 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 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 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 diff --git a/web_src/less/_repository.less b/web_src/less/_repository.less index c10e8e59d..049d2b097 100644 --- a/web_src/less/_repository.less +++ b/web_src/less/_repository.less @@ -3078,3 +3078,11 @@ tbody.commit-list { } } } + +#git-graph-container { + display: none; +} + +#git-graph-container.in { + display: block; +}