diff options
author | Wu Cheng-Han | 2015-05-04 15:53:29 +0800 |
---|---|---|
committer | Wu Cheng-Han | 2015-05-04 15:53:29 +0800 |
commit | 4b0ca55eb79e963523eb6c8197825e9e8ae904e2 (patch) | |
tree | 574f3923af77b37b41dbf1b00bcd7827ef724a28 /public/vendor/flowchart/flowchart-1.4.0.js | |
parent | 61eb11d23c65c9e5c493c67d055f785cbec139e2 (diff) |
First commit, version 0.2.7
Diffstat (limited to '')
-rw-r--r-- | public/vendor/flowchart/flowchart-1.4.0.js | 1423 |
1 files changed, 1423 insertions, 0 deletions
diff --git a/public/vendor/flowchart/flowchart-1.4.0.js b/public/vendor/flowchart/flowchart-1.4.0.js new file mode 100644 index 00000000..ffe2bacf --- /dev/null +++ b/public/vendor/flowchart/flowchart-1.4.0.js @@ -0,0 +1,1423 @@ +// flowchart, v1.4.0 +// Copyright (c)2015 Adriano Raiano (adrai). +// Distributed under MIT license +// http://adrai.github.io/flowchart.js +(function() { + + // add indexOf to non ECMA-262 standard compliant browsers + if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { + "use strict"; + if (this === null) { + throw new TypeError(); + } + var t = Object(this); + var len = t.length >>> 0; + if (len === 0) { + return -1; + } + var n = 0; + if (arguments.length > 0) { + n = Number(arguments[1]); + if (n != n) { // shortcut for verifying if it's NaN + n = 0; + } else if (n !== 0 && n != Infinity && n != -Infinity) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + } + if (n >= len) { + return -1; + } + var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); + for (; k < len; k++) { + if (k in t && t[k] === searchElement) { + return k; + } + } + return -1; + }; + } + + // add lastIndexOf to non ECMA-262 standard compliant browsers + if (!Array.prototype.lastIndexOf) { + Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/) { + "use strict"; + if (this === null) { + throw new TypeError(); + } + var t = Object(this); + var len = t.length >>> 0; + if (len === 0) { + return -1; + } + var n = len; + if (arguments.length > 1) { + n = Number(arguments[1]); + if (n != n) { + n = 0; + } else if (n !== 0 && n != (1 / 0) && n != -(1 / 0)) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + } + var k = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n); + for (; k >= 0; k--) { + if (k in t && t[k] === searchElement) { + return k; + } + } + return -1; + }; + } + + if (!String.prototype.trim) { + String.prototype.trim = function() { + return this.replace(/^\s+|\s+$/g, ''); + }; + } + + var root = this, + flowchart = {}; + + // Export the flowchart object for **CommonJS**. + // If we're not in CommonJS, add `flowchart` to the + // global object or to jquery. + if (typeof module !== 'undefined' && module.exports) { + module.exports = flowchart; + } else { + root.flowchart = root.flowchart || flowchart; + } + // defaults + var o = { + 'x': 0, + 'y': 0, + 'line-width': 3, + 'line-length': 50, + 'text-margin': 10, + 'font-size': 14, + 'font-color': 'black', + // 'font': 'normal', + // 'font-family': 'calibri', + // 'font-weight': 'normal', + 'line-color': 'black', + 'element-color': 'black', + 'fill': 'white', + 'yes-text': 'yes', + 'no-text': 'no', + 'arrow-end': 'block', + 'class': 'flowchart', + 'scale': 1, + 'symbols': { + 'start': {}, + 'end': {}, + 'condition': {}, + 'inputoutput': {}, + 'operation': {}, + 'subroutine': {} + }//, + // 'flowstate' : { + // 'past' : { 'fill': '#CCCCCC', 'font-size': 12}, + // 'current' : {'fill': 'yellow', 'font-color': 'red', 'font-weight': 'bold'}, + // 'future' : { 'fill': '#FFFF99'}, + // 'invalid': {'fill': '#444444'} + // } + }; + function _defaults(options, defaultOptions) { + if (!options || typeof options === 'function') { + return defaultOptions; + } + + var merged = {}; + for (var attrname in defaultOptions) { + merged[attrname] = defaultOptions[attrname]; + } + + for (attrname in options) { + if (options[attrname]) { + if (typeof merged[attrname] === 'object') { + merged[attrname] = _defaults(merged[attrname], options[attrname]); + } else { + merged[attrname] = options[attrname]; + } + } + } + return merged; + } + + function _inherits(ctor, superCtor) { + if (typeof(Object.create) === 'function') { + // implementation from standard node.js 'util' module + ctor.super_ = superCtor; + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + } else { + // old school shim for old browsers + ctor.super_ = superCtor; + var TempCtor = function () {}; + TempCtor.prototype = superCtor.prototype; + ctor.prototype = new TempCtor(); + ctor.prototype.constructor = ctor; + } + } + + // move dependent functions to a container so that + // they can be overriden easier in no jquery environment (node.js) + var f = { + defaults: _defaults, + inherits: _inherits + }; + function drawPath(chart, location, points) { + var i, len; + var path = 'M{0},{1}'; + for (i = 2, len = 2 * points.length + 2; i < len; i+=2) { + path += ' L{' + i + '},{' + (i + 1) + '}'; + } + var pathValues = [location.x, location.y]; + for (i = 0, len = points.length; i < len; i++) { + pathValues.push(points[i].x); + pathValues.push(points[i].y); + } + var symbol = chart.paper.path(path, pathValues); + symbol.attr('stroke', chart.options['element-color']); + symbol.attr('stroke-width', chart.options['line-width']); + + var font = chart.options['font']; + var fontF = chart.options['font-family']; + var fontW = chart.options['font-weight']; + + if (font) symbol.attr({ 'font': font }); + if (fontF) symbol.attr({ 'font-family': fontF }); + if (fontW) symbol.attr({ 'font-weight': fontW }); + + return symbol; + } + + function drawLine(chart, from, to, text) { + var i, len; + + if (Object.prototype.toString.call(to) !== '[object Array]') { + to = [to]; + } + + var path = 'M{0},{1}'; + for (i = 2, len = 2 * to.length + 2; i < len; i+=2) { + path += ' L{' + i + '},{' + (i + 1) + '}'; + } + var pathValues = [from.x, from.y]; + for (i = 0, len = to.length; i < len; i++) { + pathValues.push(to[i].x); + pathValues.push(to[i].y); + } + + var line = chart.paper.path(path, pathValues); + line.attr({ + stroke: chart.options['line-color'], + 'stroke-width': chart.options['line-width'], + 'arrow-end': chart.options['arrow-end'] + }); + + var font = chart.options['font']; + var fontF = chart.options['font-family']; + var fontW = chart.options['font-weight']; + + if (font) line.attr({ 'font': font }); + if (fontF) line.attr({ 'font-family': fontF }); + if (fontW) line.attr({ 'font-weight': fontW }); + + if (text) { + + var centerText = false; + + var textPath = chart.paper.text(0, 0, text); + + var isHorizontal = false; + var firstTo = to[0]; + + if (from.y === firstTo.y) { + isHorizontal = true; + } + + var x = 0, + y = 0; + + if (centerText) { + if (from.x > firstTo.x) { + x = from.x - (from.x - firstTo.x)/2; + } else { + x = firstTo.x - (firstTo.x - from.x)/2; + } + + if (from.y > firstTo.y) { + y = from.y - (from.y - firstTo.y)/2; + } else { + y = firstTo.y - (firstTo.y - from.y)/2; + } + + if (isHorizontal) { + x -= textPath.getBBox().width/2; + y -= chart.options['text-margin']; + } else { + x += chart.options['text-margin']; + y -= textPath.getBBox().height/2; + } + } else { + x = from.x; + y = from.y; + + if (isHorizontal) { + x += chart.options['text-margin']/2; + y -= chart.options['text-margin']; + } else { + x += chart.options['text-margin']/2; + y += chart.options['text-margin']; + } + } + + textPath.attr({ + 'text-anchor': 'start', + 'font-size': chart.options['font-size'], + 'fill': chart.options['font-color'], + x: x, + y: y + }); + + if (font) textPath.attr({ 'font': font }); + if (fontF) textPath.attr({ 'font-family': fontF }); + if (fontW) textPath.attr({ 'font-weight': fontW }); + } + + return line; + } + + function checkLineIntersection(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) { + // if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point + var denominator, a, b, numerator1, numerator2, result = { + x: null, + y: null, + onLine1: false, + onLine2: false + }; + denominator = ((line2EndY - line2StartY) * (line1EndX - line1StartX)) - ((line2EndX - line2StartX) * (line1EndY - line1StartY)); + if (denominator === 0) { + return result; + } + a = line1StartY - line2StartY; + b = line1StartX - line2StartX; + numerator1 = ((line2EndX - line2StartX) * a) - ((line2EndY - line2StartY) * b); + numerator2 = ((line1EndX - line1StartX) * a) - ((line1EndY - line1StartY) * b); + a = numerator1 / denominator; + b = numerator2 / denominator; + + // if we cast these lines infinitely in both directions, they intersect here: + result.x = line1StartX + (a * (line1EndX - line1StartX)); + result.y = line1StartY + (a * (line1EndY - line1StartY)); + /* + // it is worth noting that this should be the same as: + x = line2StartX + (b * (line2EndX - line2StartX)); + y = line2StartX + (b * (line2EndY - line2StartY)); + */ + // if line1 is a segment and line2 is infinite, they intersect if: + if (a > 0 && a < 1) { + result.onLine1 = true; + } + // if line2 is a segment and line1 is infinite, they intersect if: + if (b > 0 && b < 1) { + result.onLine2 = true; + } + // if line1 and line2 are segments, they intersect if both of the above are true + return result; + } + function FlowChart(container, options) { + options = options || {}; + + this.paper = new Raphael(container); + + this.options = f.defaults(options, o); + + this.symbols = []; + this.lines = []; + this.start = null; + } + + FlowChart.prototype.handle = function(symbol) { + if (this.symbols.indexOf(symbol) <= -1) { + this.symbols.push(symbol); + } + + var flowChart = this; + + if (symbol instanceof(Condition)) { + symbol.yes = function(nextSymbol) { + symbol.yes_symbol = nextSymbol; + if(symbol.no_symbol) { + symbol.pathOk = true; + } + return flowChart.handle(nextSymbol); + }; + symbol.no = function(nextSymbol) { + symbol.no_symbol = nextSymbol; + if(symbol.yes_symbol) { + symbol.pathOk = true; + } + return flowChart.handle(nextSymbol); + }; + } else { + symbol.then = function(nextSymbol) { + symbol.next = nextSymbol; + symbol.pathOk = true; + return flowChart.handle(nextSymbol); + }; + } + + return symbol; + }; + + FlowChart.prototype.startWith = function(symbol) { + this.start = symbol; + return this.handle(symbol); + }; + + FlowChart.prototype.render = function() { + var maxWidth = 0, + maxHeight = 0, + i = 0, + len = 0, + maxX = 0, + maxY = 0, + symbol; + + for (i = 0, len = this.symbols.length; i < len; i++) { + symbol = this.symbols[i]; + if (symbol.width > maxWidth) { + maxWidth = symbol.width; + } + if (symbol.height > maxHeight) { + maxHeight = symbol.height; + } + } + + for (i = 0, len = this.symbols.length; i < len; i++) { + symbol = this.symbols[i]; + symbol.shiftX(this.options.x + (maxWidth - symbol.width)/2 + this.options['line-width']); + symbol.shiftY(this.options.y + (maxHeight - symbol.height)/2 + this.options['line-width']); + } + + this.start.render(); + // for (i = 0, len = this.symbols.length; i < len; i++) { + // symbol = this.symbols[i]; + // symbol.render(); + // } + + for (i = 0, len = this.symbols.length; i < len; i++) { + symbol = this.symbols[i]; + symbol.renderLines(); + } + + maxX = this.maxXFromLine; + + for (i = 0, len = this.symbols.length; i < len; i++) { + symbol = this.symbols[i]; + var x = symbol.getX() + symbol.width; + var y = symbol.getY() + symbol.height; + if (x > maxX) { + maxX = x; + } + if (y > maxY) { + maxY = y; + } + } + + var scale = this.options['scale']; + var lineWidth = this.options['line-width']; + this.paper.setSize((maxX * scale) + (lineWidth * scale), (maxY * scale) + (lineWidth * scale)); + this.paper.setViewBox(0, 0, maxX + lineWidth, maxY + lineWidth, true); + }; + + FlowChart.prototype.clean = function() { + if (this.paper) { + var paperDom = this.paper.canvas; + paperDom.parentNode.removeChild(paperDom); + } + }; + function Symbol(chart, options, symbol) { + this.chart = chart; + this.group = this.chart.paper.set(); + this.symbol = symbol; + this.connectedTo = []; + this.symbolType = options.symbolType; + this.flowstate = (options.flowstate || 'future'); + + this.next_direction = options.next && options['direction_next'] ? options['direction_next'] : undefined; + + this.text = this.chart.paper.text(0, 0, options.text); + //Raphael does not support the svg group tag so setting the text node id to the symbol node id plus t + if (options.key) { this.text.node.id = options.key + 't'; } + this.text.node.setAttribute('class', this.getAttr('class') + 't'); + + this.text.attr({ + 'text-anchor': 'start', + 'x' : this.getAttr('text-margin'), + 'fill' : this.getAttr('font-color'), + 'font-size' : this.getAttr('font-size') + }); + + var font = this.getAttr('font'); + var fontF = this.getAttr('font-family'); + var fontW = this.getAttr('font-weight'); + + if (font) this.text.attr({ 'font': font }); + if (fontF) this.text.attr({ 'font-family': fontF }); + if (fontW) this.text.attr({ 'font-weight': fontW }); + + if (options.link) { this.text.attr('href', options.link); } + if (options.target) { this.text.attr('target', options.target); } + + var maxWidth = this.getAttr('maxWidth'); + if (maxWidth) { + // using this approach: http://stackoverflow.com/a/3153457/22466 + var words = options.text.split(' '); + var tempText = ""; + for (var i=0, ii=words.length; i<ii; i++) { + var word = words[i]; + this.text.attr("text", tempText + " " + word); + if (this.text.getBBox().width > maxWidth) { + tempText += "\n" + word; + } else { + tempText += " " + word; + } + } + this.text.attr("text", tempText.substring(1)); + } + + this.group.push(this.text); + + if (symbol) { + var tmpMargin = this.getAttr('text-margin'); + + symbol.attr({ + 'fill' : this.getAttr('fill'), + 'stroke' : this.getAttr('element-color'), + 'stroke-width' : this.getAttr('line-width'), + 'width' : this.text.getBBox().width + 2 * tmpMargin, + 'height' : this.text.getBBox().height + 2 * tmpMargin + }); + + symbol.node.setAttribute('class', this.getAttr('class')); + + if (options.link) { symbol.attr('href', options.link); } + if (options.target) { symbol.attr('target', options.target); } + if (options.key) { symbol.node.id = options.key; } + + this.group.push(symbol); + symbol.insertBefore(this.text); + + this.text.attr({ + 'y': symbol.getBBox().height/2 + }); + + this.initialize(); + } + + } + + /* Gets the attribute based on Flowstate, Symbol-Name and default, first found wins */ + Symbol.prototype.getAttr = function(attName) { + if (!this.chart) { + return undefined; + } + var opt3 = (this.chart.options) ? this.chart.options[attName] : undefined; + var opt2 = (this.chart.options.symbols) ? this.chart.options.symbols[this.symbolType][attName] : undefined; + var opt1; + if (this.chart.options.flowstate && this.chart.options.flowstate[this.flowstate]) { + opt1 = this.chart.options.flowstate[this.flowstate][attName]; + } + return (opt1 || opt2 || opt3); + }; + + Symbol.prototype.initialize = function() { + this.group.transform('t' + this.getAttr('line-width') + ',' + this.getAttr('line-width')); + + this.width = this.group.getBBox().width; + this.height = this.group.getBBox().height; + }; + + Symbol.prototype.getCenter = function() { + return {x: this.getX() + this.width/2, + y: this.getY() + this.height/2}; + }; + + Symbol.prototype.getX = function() { + return this.group.getBBox().x; + }; + + Symbol.prototype.getY = function() { + return this.group.getBBox().y; + }; + + Symbol.prototype.shiftX = function(x) { + this.group.transform('t' + (this.getX() + x) + ',' + this.getY()); + }; + + Symbol.prototype.setX = function(x) { + this.group.transform('t' + x + ',' + this.getY()); + }; + + Symbol.prototype.shiftY = function(y) { + this.group.transform('t' + this.getX() + ',' + (this.getY() + y)); + }; + + Symbol.prototype.setY = function(y) { + this.group.transform('t' + this.getX() + ',' + y); + }; + + Symbol.prototype.getTop = function() { + var y = this.getY(); + var x = this.getX() + this.width/2; + return {x: x, y: y}; + }; + + Symbol.prototype.getBottom = function() { + var y = this.getY() + this.height; + var x = this.getX() + this.width/2; + return {x: x, y: y}; + }; + + Symbol.prototype.getLeft = function() { + var y = this.getY() + this.group.getBBox().height/2; + var x = this.getX(); + return {x: x, y: y}; + }; + + Symbol.prototype.getRight = function() { + var y = this.getY() + this.group.getBBox().height/2; + var x = this.getX() + this.group.getBBox().width; + return {x: x, y: y}; + }; + + Symbol.prototype.render = function() { + if (this.next) { + + var lineLength = this.getAttr('line-length'); + + if (this.next_direction === 'right') { + + var rightPoint = this.getRight(); + var leftPoint = this.next.getLeft(); + + if (!this.next.isPositioned) { + + this.next.setY(rightPoint.y - this.next.height/2); + this.next.shiftX(this.group.getBBox().x + this.width + lineLength); + + var self = this; + (function shift() { + var hasSymbolUnder = false; + var symb; + for (var i = 0, len = self.chart.symbols.length; i < len; i++) { + symb = self.chart.symbols[i]; + + var diff = Math.abs(symb.getCenter().x - self.next.getCenter().x); + if (symb.getCenter().y > self.next.getCenter().y && diff <= self.next.width/2) { + hasSymbolUnder = true; + break; + } + } + + if (hasSymbolUnder) { + self.next.setX(symb.getX() + symb.width + lineLength); + shift(); + } + })(); + + this.next.isPositioned = true; + + this.next.render(); + } + } else { + var bottomPoint = this.getBottom(); + var topPoint = this.next.getTop(); + + if (!this.next.isPositioned) { + this.next.shiftY(this.getY() + this.height + lineLength); + this.next.setX(bottomPoint.x - this.next.width/2); + this.next.isPositioned = true; + + this.next.render(); + } + } + } + }; + + Symbol.prototype.renderLines = function() { + if (this.next) { + if (this.next_direction) { + this.drawLineTo(this.next, '', this.next_direction); + } else { + this.drawLineTo(this.next); + } + } + }; + + Symbol.prototype.drawLineTo = function(symbol, text, origin) { + if (this.connectedTo.indexOf(symbol) < 0) { + this.connectedTo.push(symbol); + } + + var x = this.getCenter().x, + y = this.getCenter().y, + top = this.getTop(), + right = this.getRight(), + bottom = this.getBottom(), + left = this.getLeft(); + + var symbolX = symbol.getCenter().x, + symbolY = symbol.getCenter().y, + symbolTop = symbol.getTop(), + symbolRight = symbol.getRight(), + symbolBottom = symbol.getBottom(), + symbolLeft = symbol.getLeft(); + + var isOnSameColumn = x === symbolX, + isOnSameLine = y === symbolY, + isUnder = y < symbolY, + isUpper = y > symbolY, + isLeft = x > symbolX, + isRight = x < symbolX; + + var maxX = 0, + line, + lineLength = this.getAttr('line-length'), + lineWith = this.getAttr('line-width'); + + if ((!origin || origin === 'bottom') && isOnSameColumn && isUnder) { + line = drawLine(this.chart, bottom, symbolTop, text); + this.bottomStart = true; + symbol.topEnd = true; + maxX = bottom.x; + } else if ((!origin || origin === 'right') && isOnSameLine && isRight) { + line = drawLine(this.chart, right, symbolLeft, text); + this.rightStart = true; + symbol.leftEnd = true; + maxX = symbolLeft.x; + } else if ((!origin || origin === 'left') && isOnSameLine && isLeft) { + line = drawLine(this.chart, left, symbolRight, text); + this.leftStart = true; + symbol.rightEnd = true; + maxX = symbolRight.x; + } else if ((!origin || origin === 'right') && isOnSameColumn && isUpper) { + line = drawLine(this.chart, right, [ + {x: right.x + lineLength/2, y: right.y}, + {x: right.x + lineLength/2, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y} + ], text); + this.rightStart = true; + symbol.topEnd = true; + maxX = right.x + lineLength/2; + } else if ((!origin || origin === 'right') && isOnSameColumn && isUnder) { + line = drawLine(this.chart, right, [ + {x: right.x + lineLength/2, y: right.y}, + {x: right.x + lineLength/2, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y} + ], text); + this.rightStart = true; + symbol.topEnd = true; + maxX = right.x + lineLength/2; + } else if ((!origin || origin === 'bottom') && isLeft) { + if (this.leftEnd && isUpper) { + line = drawLine(this.chart, bottom, [ + {x: bottom.x, y: bottom.y + lineLength/2}, + {x: bottom.x + (bottom.x - symbolTop.x)/2, y: bottom.y + lineLength/2}, + {x: bottom.x + (bottom.x - symbolTop.x)/2, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y} + ], text); + } else { + line = drawLine(this.chart, bottom, [ + {x: bottom.x, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y} + ], text); + } + this.bottomStart = true; + symbol.topEnd = true; + maxX = bottom.x + (bottom.x - symbolTop.x)/2; + } else if ((!origin || origin === 'bottom') && isRight) { + line = drawLine(this.chart, bottom, [ + {x: bottom.x, y: bottom.y + lineLength/2}, + {x: bottom.x + (bottom.x - symbolTop.x)/2, y: bottom.y + lineLength/2}, + {x: bottom.x + (bottom.x - symbolTop.x)/2, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y} + ], text); + this.bottomStart = true; + symbol.topEnd = true; + maxX = bottom.x + (bottom.x - symbolTop.x)/2; + } else if ((origin && origin === 'right') && isLeft) { + line = drawLine(this.chart, right, [ + {x: right.x + lineLength/2, y: right.y}, + {x: right.x + lineLength/2, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y} + ], text); + this.rightStart = true; + symbol.topEnd = true; + maxX = right.x + lineLength/2; + } else if ((origin && origin === 'right') && isRight) { + line = drawLine(this.chart, right, [ + {x: symbolTop.x, y: right.y}, + {x: symbolTop.x, y: symbolTop.y} + ], text); + this.rightStart = true; + symbol.topEnd = true; + maxX = right.x + lineLength/2; + } else if ((origin && origin === 'bottom') && isOnSameColumn && isUpper) { + line = drawLine(this.chart, bottom, [ + {x: bottom.x, y: bottom.y + lineLength/2}, + {x: right.x + lineLength/2, y: bottom.y + lineLength/2}, + {x: right.x + lineLength/2, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y} + ], text); + this.bottomStart = true; + symbol.topEnd = true; + maxX = bottom.x + lineLength/2; + } else if ((origin === 'left') && isOnSameColumn && isUpper) { + var diffX = left.x - lineLength/2; + if (symbolLeft.x < left.x) { + diffX = symbolLeft.x - lineLength/2; + } + line = drawLine(this.chart, left, [ + {x: diffX, y: left.y}, + {x: diffX, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y} + ], text); + this.leftStart = true; + symbol.topEnd = true; + maxX = left.x; + } else if ((origin === 'left')) { + line = drawLine(this.chart, left, [ + {x: symbolTop.x + (left.x - symbolTop.x)/ 2, y: left.y}, + {x: symbolTop.x + (left.x - symbolTop.x)/ 2, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y - lineLength/2}, + {x: symbolTop.x, y: symbolTop.y} + ], text); + this.leftStart = true; + symbol.topEnd = true; + maxX = left.x; + } + + if (line) { + var self = this; + for (var l = 0, llen = this.chart.lines.length; l < llen; l++) { + var otherLine = this.chart.lines[l]; + var i, + len, + intersections, + inter; + + var ePath = otherLine.attr('path'), + lPath = line.attr('path'); + + for (var iP = 0, lenP = ePath.length - 1; iP < lenP; iP++) { + var newPath = []; + newPath.push(['M', ePath[iP][1], ePath[iP][2]]); + newPath.push(['L', ePath[iP + 1][1], ePath[iP + 1][2]]); + + var line1_from_x = newPath[0][1]; + var line1_from_y = newPath[0][2]; + var line1_to_x = newPath[1][1]; + var line1_to_y = newPath[1][2]; + + for (var lP = 0, lenlP = lPath.length - 1; lP < lenlP; lP++) { + var newLinePath = []; + newLinePath.push(['M', lPath[lP][1], lPath[lP][2]]); + newLinePath.push(['L', lPath[lP + 1][1], lPath[lP + 1][2]]); + + var line2_from_x = newLinePath[0][1]; + var line2_from_y = newLinePath[0][2]; + var line2_to_x = newLinePath[1][1]; + var line2_to_y = newLinePath[1][2]; + + var res = checkLineIntersection(line1_from_x, line1_from_y, line1_to_x, line1_to_y, line2_from_x, line2_from_y, line2_to_x, line2_to_y); + if (res.onLine1 && res.onLine2) { + + var newSegment; + if (line2_from_y === line2_to_y) { + if (line2_from_x > line2_to_x) { + newSegment = ['L', res.x + lineWith * 2, line2_from_y]; + lPath.splice(lP + 1, 0, newSegment); + newSegment = ['C', res.x + lineWith * 2, line2_from_y, res.x, line2_from_y - lineWith * 4, res.x - lineWith * 2, line2_from_y]; + lPath.splice(lP + 2, 0, newSegment); + line.attr('path', lPath); + } else { + newSegment = ['L', res.x - lineWith * 2, line2_from_y]; + lPath.splice(lP + 1, 0, newSegment); + newSegment = ['C', res.x - lineWith * 2, line2_from_y, res.x, line2_from_y - lineWith * 4, res.x + lineWith * 2, line2_from_y]; + lPath.splice(lP + 2, 0, newSegment); + line.attr('path', lPath); + } + } else { + if (line2_from_y > line2_to_y) { + newSegment = ['L', line2_from_x, res.y + lineWith * 2]; + lPath.splice(lP + 1, 0, newSegment); + newSegment = ['C', line2_from_x, res.y + lineWith * 2, line2_from_x + lineWith * 4, res.y, line2_from_x, res.y - lineWith * 2]; + lPath.splice(lP + 2, 0, newSegment); + line.attr('path', lPath); + } else { + newSegment = ['L', line2_from_x, res.y - lineWith * 2]; + lPath.splice(lP + 1, 0, newSegment); + newSegment = ['C', line2_from_x, res.y - lineWith * 2, line2_from_x + lineWith * 4, res.y, line2_from_x, res.y + lineWith * 2]; + lPath.splice(lP + 2, 0, newSegment); + line.attr('path', lPath); + } + } + + lP += 2; + len += 2; + } + } + } + } + + this.chart.lines.push(line); + } + + if (!this.chart.maxXFromLine || (this.chart.maxXFromLine && maxX > this.chart.maxXFromLine)) { + this.chart.maxXFromLine = maxX; + } + }; + function Start(chart, options) { + var symbol = chart.paper.rect(0, 0, 0, 0, 20); + options = options || {}; + options.text = options.text || 'Start'; + Symbol.call(this, chart, options, symbol); + } + f.inherits(Start, Symbol); + + + // Start.prototype.render = function() { + // if (this.next) { + // var lineLength = this.chart.options.symbols[this.symbolType]['line-length'] || this.chart.options['line-length']; + + // var bottomPoint = this.getBottom(); + // var topPoint = this.next.getTop(); + + // if (!this.next.isPositioned) { + // this.next.shiftY(this.getY() + this.height + lineLength); + // this.next.setX(bottomPoint.x - this.next.width/2); + // this.next.isPositioned = true; + + // this.next.render(); + // } + // } + // }; + + // Start.prototype.renderLines = function() { + // if (this.next) { + // this.drawLineTo(this.next); + // } + // }; + function End(chart, options) { + var symbol = chart.paper.rect(0, 0, 0, 0, 20); + options = options || {}; + options.text = options.text || 'End'; + Symbol.call(this, chart, options, symbol); + } + f.inherits(End, Symbol); + function Operation(chart, options) { + var symbol = chart.paper.rect(0, 0, 0, 0); + options = options || {}; + Symbol.call(this, chart, options, symbol); + } + f.inherits(Operation, Symbol); + function Subroutine(chart, options) { + var symbol = chart.paper.rect(0, 0, 0, 0); + options = options || {}; + Symbol.call(this, chart, options, symbol); + + symbol.attr({ + width: this.text.getBBox().width + 4 * this.getAttr('text-margin') + }); + + this.text.attr({ + 'x': 2 * this.getAttr('text-margin') + }); + + var innerWrap = chart.paper.rect(0, 0, 0, 0); + innerWrap.attr({ + x: this.getAttr('text-margin'), + stroke: this.getAttr('element-color'), + 'stroke-width': this.getAttr('line-width'), + width: this.text.getBBox().width + 2 * this.getAttr('text-margin'), + height: this.text.getBBox().height + 2 * this.getAttr('text-margin'), + fill: this.getAttr('fill') + }); + if (options.key) { innerWrap.node.id = options.key + 'i'; } + + var font = this.getAttr('font'); + var fontF = this.getAttr('font-family'); + var fontW = this.getAttr('font-weight'); + + if (font) innerWrap.attr({ 'font': font }); + if (fontF) innerWrap.attr({ 'font-family': fontF }); + if (fontW) innerWrap.attr({ 'font-weight': fontW }); + + if (options.link) { innerWrap.attr('href', options.link); } + if (options.target) { innerWrap.attr('target', options.target); } + this.group.push(innerWrap); + innerWrap.insertBefore(this.text); + + this.initialize(); + } + f.inherits(Subroutine, Symbol); + function InputOutput(chart, options) { + options = options || {}; + Symbol.call(this, chart, options); + this.textMargin = this.getAttr('text-margin'); + + this.text.attr({ + x: this.textMargin * 3 + }); + + var width = this.text.getBBox().width + 4 * this.textMargin; + var height = this.text.getBBox().height + 2 * this.textMargin; + var startX = this.textMargin; + var startY = height/2; + + var start = {x: startX, y: startY}; + var points = [ + {x: startX - this.textMargin, y: height}, + {x: startX - this.textMargin + width, y: height}, + {x: startX - this.textMargin + width + 2 * this.textMargin, y: 0}, + {x: startX - this.textMargin + 2 * this.textMargin, y: 0}, + {x: startX, y: startY} + ]; + + var symbol = drawPath(chart, start, points); + + symbol.attr({ + stroke: this.getAttr('element-color'), + 'stroke-width': this.getAttr('line-width'), + fill: this.getAttr('fill') + }); + if (options.link) { symbol.attr('href', options.link); } + if (options.target) { symbol.attr('target', options.target); } + if (options.key) { symbol.node.id = options.key; } + symbol.node.setAttribute('class', this.getAttr('class')); + + this.text.attr({ + y: symbol.getBBox().height/2 + }); + + this.group.push(symbol); + symbol.insertBefore(this.text); + + this.initialize(); + } + f.inherits(InputOutput, Symbol); + + InputOutput.prototype.getLeft = function() { + var y = this.getY() + this.group.getBBox().height/2; + var x = this.getX() + this.textMargin; + return {x: x, y: y}; + }; + + InputOutput.prototype.getRight = function() { + var y = this.getY() + this.group.getBBox().height/2; + var x = this.getX() + this.group.getBBox().width - this.textMargin; + return {x: x, y: y}; + }; + function Condition(chart, options) { + options = options || {}; + Symbol.call(this, chart, options); + this.textMargin = this.getAttr('text-margin'); + this.yes_direction = 'bottom'; + this.no_direction = 'right'; + if (options.yes && options['direction_yes'] && options.no && !options['direction_no']) { + if (options['direction_yes'] === 'right') { + this.no_direction = 'bottom'; + this.yes_direction = 'right'; + } else { + this.no_direction = 'right'; + this.yes_direction = 'bottom'; + } + } else if (options.yes && !options['direction_yes'] && options.no && options['direction_no']) { + if (options['direction_no'] === 'right') { + this.yes_direction = 'bottom'; + this.no_direction = 'right'; + } else { + this.yes_direction = 'right'; + this.no_direction = 'bottom'; + } + } else { + this.yes_direction = 'bottom'; + this.no_direction = 'right'; + } + + this.yes_direction = this.yes_direction || 'bottom'; + this.no_direction = this.no_direction || 'right'; + + this.text.attr({ + x: this.textMargin * 2 + }); + + var width = this.text.getBBox().width + 3 * this.textMargin; + width += width/2; + var height = this.text.getBBox().height + 2 * this.textMargin; + height += height/2; + height = Math.max(width * 0.5, height); + var startX = width/4; + var startY = height/4; + + this.text.attr({ + x: startX + this.textMargin/2 + }); + + var start = {x: startX, y: startY}; + var points = [ + {x: startX - width/4, y: startY + height/4}, + {x: startX - width/4 + width/2, y: startY + height/4 + height/2}, + {x: startX - width/4 + width, y: startY + height/4}, + {x: startX - width/4 + width/2, y: startY + height/4 - height/2}, + {x: startX - width/4, y: startY + height/4} + ]; + + var symbol = drawPath(chart, start, points); + + symbol.attr({ + stroke: this.getAttr('element-color'), + 'stroke-width': this.getAttr('line-width'), + fill: this.getAttr('fill') + }); + if (options.link) { symbol.attr('href', options.link); } + if (options.target) { symbol.attr('target', options.target); } + if (options.key) { symbol.node.id = options.key; } + symbol.node.setAttribute('class', this.getAttr('class')); + + this.text.attr({ + y: symbol.getBBox().height/2 + }); + + this.group.push(symbol); + symbol.insertBefore(this.text); + + this.initialize(); + } + f.inherits(Condition, Symbol); + + Condition.prototype.render = function() { + + if (this.yes_direction) { + this[this.yes_direction + '_symbol'] = this.yes_symbol; + } + + if (this.no_direction) { + this[this.no_direction + '_symbol'] = this.no_symbol; + } + + var lineLength = this.getAttr('line-length'); + + if (this.bottom_symbol) { + var bottomPoint = this.getBottom(); + var topPoint = this.bottom_symbol.getTop(); + + if (!this.bottom_symbol.isPositioned) { + this.bottom_symbol.shiftY(this.getY() + this.height + lineLength); + this.bottom_symbol.setX(bottomPoint.x - this.bottom_symbol.width/2); + this.bottom_symbol.isPositioned = true; + + this.bottom_symbol.render(); + } + } + + if (this.right_symbol) { + var rightPoint = this.getRight(); + var leftPoint = this.right_symbol.getLeft(); + + if (!this.right_symbol.isPositioned) { + + this.right_symbol.setY(rightPoint.y - this.right_symbol.height/2); + this.right_symbol.shiftX(this.group.getBBox().x + this.width + lineLength); + + var self = this; + (function shift() { + var hasSymbolUnder = false; + var symb; + for (var i = 0, len = self.chart.symbols.length; i < len; i++) { + symb = self.chart.symbols[i]; + + var diff = Math.abs(symb.getCenter().x - self.right_symbol.getCenter().x); + if (symb.getCenter().y > self.right_symbol.getCenter().y && diff <= self.right_symbol.width/2) { + hasSymbolUnder = true; + break; + } + } + + if (hasSymbolUnder) { + self.right_symbol.setX(symb.getX() + symb.width + lineLength); + shift(); + } + })(); + + this.right_symbol.isPositioned = true; + + this.right_symbol.render(); + } + } + }; + + Condition.prototype.renderLines = function() { + if (this.yes_symbol) { + this.drawLineTo(this.yes_symbol, this.getAttr('yes-text'), this.yes_direction); + } + + if (this.no_symbol) { + this.drawLineTo(this.no_symbol, this.getAttr('no-text'), this.no_direction); + } + }; + function parse(input) { + input = input || ''; + input = input.trim(); + + var chart = { + symbols: {}, + start: null, + drawSVG: function(container, options) { + var self = this; + + if (this.diagram) { + this.diagram.clean(); + } + + var diagram = new FlowChart(container, options); + this.diagram = diagram; + var dispSymbols = {}; + + function getDisplaySymbol(s) { + if (dispSymbols[s.key]) { + return dispSymbols[s.key]; + } + + switch (s.symbolType) { + case 'start': + dispSymbols[s.key] = new Start(diagram, s); + break; + case 'end': + dispSymbols[s.key] = new End(diagram, s); + break; + case 'operation': + dispSymbols[s.key] = new Operation(diagram, s); + break; + case 'inputoutput': + dispSymbols[s.key] = new InputOutput(diagram, s); + break; + case 'subroutine': + dispSymbols[s.key] = new Subroutine(diagram, s); + break; + case 'condition': + dispSymbols[s.key] = new Condition(diagram, s); + break; + default: + return new Error('Wrong symbol type!'); + } + + return dispSymbols[s.key]; + } + + (function constructChart(s, prevDisp, prev) { + var dispSymb = getDisplaySymbol(s); + + if (self.start === s) { + diagram.startWith(dispSymb); + } else if (prevDisp && prev && !prevDisp.pathOk) { + if (prevDisp instanceof(Condition)) { + if (prev.yes === s) { + prevDisp.yes(dispSymb); + } + if (prev.no === s) { + prevDisp.no(dispSymb); + } + } else { + prevDisp.then(dispSymb); + } + } + + if (dispSymb.pathOk) { + return dispSymb; + } + + if (dispSymb instanceof(Condition)) { + if (s.yes) { + constructChart(s.yes, dispSymb, s); + } + if (s.no) { + constructChart(s.no, dispSymb, s); + } + } else if (s.next) { + constructChart(s.next, dispSymb, s); + } + + return dispSymb; + })(this.start); + + diagram.render(); + }, + clean: function() { + this.diagram.clean(); + } + }; + + var lines = []; + var prevBreak = 0; + for (var i0 = 1, i0len = input.length; i0 < i0len; i0++) { + if(input[i0] === '\n' && input[i0 - 1] !== '\\') { + var line0 = input.substring(prevBreak, i0); + prevBreak = i0 + 1; + lines.push(line0.replace(/\\\n/g, '\n')); + } + } + + if(prevBreak < input.length) { + lines.push(input.substr(prevBreak)); + } + + for (var l = 1, len = lines.length; l < len;) { + var currentLine = lines[l]; + + if (currentLine.indexOf(': ') < 0 && currentLine.indexOf('(') < 0 && currentLine.indexOf(')') < 0 && currentLine.indexOf('->') < 0 && currentLine.indexOf('=>') < 0) { + lines[l - 1] += '\n' + currentLine; + lines.splice(l, 1); + len--; + } else { + l++; + } + } + + function getSymbol(s) { + var startIndex = s.indexOf('(') + 1; + var endIndex = s.indexOf(')'); + if (startIndex >= 0 && endIndex >= 0) { + return chart.symbols[s.substring(0, startIndex - 1)]; + } + return chart.symbols[s]; + } + + function getNextPath(s) { + var next = 'next'; + var startIndex = s.indexOf('(') + 1; + var endIndex = s.indexOf(')'); + if (startIndex >= 0 && endIndex >= 0) { + next = flowSymb.substring(startIndex, endIndex); + if (next.indexOf(',') < 0) { + if (next !== 'yes' && next !== 'no') { + next = 'next, ' + next; + } + } + } + return next; + } + + while (lines.length > 0) { + var line = lines.splice(0, 1)[0]; + + if (line.indexOf('=>') >= 0) { + // definition + var parts = line.split('=>'); + var symbol = { + key: parts[0], + symbolType: parts[1], + text: null, + link: null, + target: null, + flowstate: null + }; + + var sub; + + if (symbol.symbolType.indexOf(': ') >= 0) { + sub = symbol.symbolType.split(': '); + symbol.symbolType = sub[0]; + symbol.text = sub[1]; + } + + if (symbol.text && symbol.text.indexOf(':>') >= 0) { + sub = symbol.text.split(':>'); + symbol.text = sub[0]; + symbol.link = sub[1]; + } else if (symbol.symbolType.indexOf(':>') >= 0) { + sub = symbol.symbolType.split(':>'); + symbol.symbolType = sub[0]; + symbol.link = sub[1]; + } + + if (symbol.symbolType.indexOf('\n') >= 0) { + symbol.symbolType = symbol.symbolType.split('\n')[0]; + } + + /* adding support for links */ + if (symbol.link) { + var startIndex = symbol.link.indexOf('[') + 1; + var endIndex = symbol.link.indexOf(']'); + if (startIndex >= 0 && endIndex >= 0) { + symbol.target = symbol.link.substring(startIndex, endIndex); + symbol.link = symbol.link.substring(0, startIndex - 1); + } + } + /* end of link support */ + + /* adding support for flowstates */ + if (symbol.text) { + if (symbol.text.indexOf('|') >= 0) { + var txtAndState = symbol.text.split('|'); + symbol.text = txtAndState[0]; + symbol.flowstate = txtAndState[1].trim(); + } + } + /* end of flowstate support */ + + chart.symbols[symbol.key] = symbol; + + } else if (line.indexOf('->') >= 0) { + // flow + var flowSymbols = line.split('->'); + for (var i = 0, lenS = flowSymbols.length; i < lenS; i++) { + var flowSymb = flowSymbols[i]; + + var realSymb = getSymbol(flowSymb); + var next = getNextPath(flowSymb); + + var direction = null; + if (next.indexOf(',') >= 0) { + var condOpt = next.split(','); + next = condOpt[0]; + direction = condOpt[1].trim(); + } + + if (!chart.start) { + chart.start = realSymb; + } + + if (i + 1 < lenS) { + var nextSymb = flowSymbols[i + 1]; + realSymb[next] = getSymbol(nextSymb); + realSymb['direction_' + next] = direction; + direction = null; + } + } + } + + } + return chart; + } + // public api interface + flowchart.parse = parse; + +})();
\ No newline at end of file |