You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1423 rivejä
44 KiB

  1. // flowchart, v1.4.1
  2. // Copyright (c)2015 Adriano Raiano (adrai).
  3. // Distributed under MIT license
  4. // http://adrai.github.io/flowchart.js
  5. (function() {
  6. // add indexOf to non ECMA-262 standard compliant browsers
  7. if (!Array.prototype.indexOf) {
  8. Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
  9. "use strict";
  10. if (this === null) {
  11. throw new TypeError();
  12. }
  13. var t = Object(this);
  14. var len = t.length >>> 0;
  15. if (len === 0) {
  16. return -1;
  17. }
  18. var n = 0;
  19. if (arguments.length > 0) {
  20. n = Number(arguments[1]);
  21. if (n != n) { // shortcut for verifying if it's NaN
  22. n = 0;
  23. } else if (n !== 0 && n != Infinity && n != -Infinity) {
  24. n = (n > 0 || -1) * Math.floor(Math.abs(n));
  25. }
  26. }
  27. if (n >= len) {
  28. return -1;
  29. }
  30. var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
  31. for (; k < len; k++) {
  32. if (k in t && t[k] === searchElement) {
  33. return k;
  34. }
  35. }
  36. return -1;
  37. };
  38. }
  39. // add lastIndexOf to non ECMA-262 standard compliant browsers
  40. if (!Array.prototype.lastIndexOf) {
  41. Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/) {
  42. "use strict";
  43. if (this === null) {
  44. throw new TypeError();
  45. }
  46. var t = Object(this);
  47. var len = t.length >>> 0;
  48. if (len === 0) {
  49. return -1;
  50. }
  51. var n = len;
  52. if (arguments.length > 1) {
  53. n = Number(arguments[1]);
  54. if (n != n) {
  55. n = 0;
  56. } else if (n !== 0 && n != (1 / 0) && n != -(1 / 0)) {
  57. n = (n > 0 || -1) * Math.floor(Math.abs(n));
  58. }
  59. }
  60. var k = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n);
  61. for (; k >= 0; k--) {
  62. if (k in t && t[k] === searchElement) {
  63. return k;
  64. }
  65. }
  66. return -1;
  67. };
  68. }
  69. if (!String.prototype.trim) {
  70. String.prototype.trim = function() {
  71. return this.replace(/^\s+|\s+$/g, '');
  72. };
  73. }
  74. var root = this,
  75. flowchart = {};
  76. // Export the flowchart object for **CommonJS**.
  77. // If we're not in CommonJS, add `flowchart` to the
  78. // global object or to jquery.
  79. if (typeof module !== 'undefined' && module.exports) {
  80. module.exports = flowchart;
  81. } else {
  82. root.flowchart = root.flowchart || flowchart;
  83. }
  84. // defaults
  85. var o = {
  86. 'x': 0,
  87. 'y': 0,
  88. 'line-width': 3,
  89. 'line-length': 50,
  90. 'text-margin': 10,
  91. 'font-size': 14,
  92. 'font-color': 'black',
  93. // 'font': 'normal',
  94. // 'font-family': 'calibri',
  95. // 'font-weight': 'normal',
  96. 'line-color': 'black',
  97. 'element-color': 'black',
  98. 'fill': 'white',
  99. 'yes-text': 'yes',
  100. 'no-text': 'no',
  101. 'arrow-end': 'block',
  102. 'class': 'flowchart',
  103. 'scale': 1,
  104. 'symbols': {
  105. 'start': {},
  106. 'end': {},
  107. 'condition': {},
  108. 'inputoutput': {},
  109. 'operation': {},
  110. 'subroutine': {}
  111. }//,
  112. // 'flowstate' : {
  113. // 'past' : { 'fill': '#CCCCCC', 'font-size': 12},
  114. // 'current' : {'fill': 'yellow', 'font-color': 'red', 'font-weight': 'bold'},
  115. // 'future' : { 'fill': '#FFFF99'},
  116. // 'invalid': {'fill': '#444444'}
  117. // }
  118. };
  119. function _defaults(options, defaultOptions) {
  120. if (!options || typeof options === 'function') {
  121. return defaultOptions;
  122. }
  123. var merged = {};
  124. for (var attrname in defaultOptions) {
  125. merged[attrname] = defaultOptions[attrname];
  126. }
  127. for (attrname in options) {
  128. if (options[attrname]) {
  129. if (typeof merged[attrname] === 'object') {
  130. merged[attrname] = _defaults(merged[attrname], options[attrname]);
  131. } else {
  132. merged[attrname] = options[attrname];
  133. }
  134. }
  135. }
  136. return merged;
  137. }
  138. function _inherits(ctor, superCtor) {
  139. if (typeof(Object.create) === 'function') {
  140. // implementation from standard node.js 'util' module
  141. ctor.super_ = superCtor;
  142. ctor.prototype = Object.create(superCtor.prototype, {
  143. constructor: {
  144. value: ctor,
  145. enumerable: false,
  146. writable: true,
  147. configurable: true
  148. }
  149. });
  150. } else {
  151. // old school shim for old browsers
  152. ctor.super_ = superCtor;
  153. var TempCtor = function () {};
  154. TempCtor.prototype = superCtor.prototype;
  155. ctor.prototype = new TempCtor();
  156. ctor.prototype.constructor = ctor;
  157. }
  158. }
  159. // move dependent functions to a container so that
  160. // they can be overriden easier in no jquery environment (node.js)
  161. var f = {
  162. defaults: _defaults,
  163. inherits: _inherits
  164. };
  165. function drawPath(chart, location, points) {
  166. var i, len;
  167. var path = 'M{0},{1}';
  168. for (i = 2, len = 2 * points.length + 2; i < len; i+=2) {
  169. path += ' L{' + i + '},{' + (i + 1) + '}';
  170. }
  171. var pathValues = [location.x, location.y];
  172. for (i = 0, len = points.length; i < len; i++) {
  173. pathValues.push(points[i].x);
  174. pathValues.push(points[i].y);
  175. }
  176. var symbol = chart.paper.path(path, pathValues);
  177. symbol.attr('stroke', chart.options['element-color']);
  178. symbol.attr('stroke-width', chart.options['line-width']);
  179. var font = chart.options['font'];
  180. var fontF = chart.options['font-family'];
  181. var fontW = chart.options['font-weight'];
  182. if (font) symbol.attr({ 'font': font });
  183. if (fontF) symbol.attr({ 'font-family': fontF });
  184. if (fontW) symbol.attr({ 'font-weight': fontW });
  185. return symbol;
  186. }
  187. function drawLine(chart, from, to, text) {
  188. var i, len;
  189. if (Object.prototype.toString.call(to) !== '[object Array]') {
  190. to = [to];
  191. }
  192. var path = 'M{0},{1}';
  193. for (i = 2, len = 2 * to.length + 2; i < len; i+=2) {
  194. path += ' L{' + i + '},{' + (i + 1) + '}';
  195. }
  196. var pathValues = [from.x, from.y];
  197. for (i = 0, len = to.length; i < len; i++) {
  198. pathValues.push(to[i].x);
  199. pathValues.push(to[i].y);
  200. }
  201. var line = chart.paper.path(path, pathValues);
  202. line.attr({
  203. stroke: chart.options['line-color'],
  204. 'stroke-width': chart.options['line-width'],
  205. 'arrow-end': chart.options['arrow-end']
  206. });
  207. var font = chart.options['font'];
  208. var fontF = chart.options['font-family'];
  209. var fontW = chart.options['font-weight'];
  210. if (font) line.attr({ 'font': font });
  211. if (fontF) line.attr({ 'font-family': fontF });
  212. if (fontW) line.attr({ 'font-weight': fontW });
  213. if (text) {
  214. var centerText = false;
  215. var textPath = chart.paper.text(0, 0, text);
  216. var isHorizontal = false;
  217. var firstTo = to[0];
  218. if (from.y === firstTo.y) {
  219. isHorizontal = true;
  220. }
  221. var x = 0,
  222. y = 0;
  223. if (centerText) {
  224. if (from.x > firstTo.x) {
  225. x = from.x - (from.x - firstTo.x)/2;
  226. } else {
  227. x = firstTo.x - (firstTo.x - from.x)/2;
  228. }
  229. if (from.y > firstTo.y) {
  230. y = from.y - (from.y - firstTo.y)/2;
  231. } else {
  232. y = firstTo.y - (firstTo.y - from.y)/2;
  233. }
  234. if (isHorizontal) {
  235. x -= textPath.getBBox().width/2;
  236. y -= chart.options['text-margin'];
  237. } else {
  238. x += chart.options['text-margin'];
  239. y -= textPath.getBBox().height/2;
  240. }
  241. } else {
  242. x = from.x;
  243. y = from.y;
  244. if (isHorizontal) {
  245. x += chart.options['text-margin']/2;
  246. y -= chart.options['text-margin'];
  247. } else {
  248. x += chart.options['text-margin']/2;
  249. y += chart.options['text-margin'];
  250. }
  251. }
  252. textPath.attr({
  253. 'text-anchor': 'start',
  254. 'font-size': chart.options['font-size'],
  255. 'fill': chart.options['font-color'],
  256. x: x,
  257. y: y
  258. });
  259. if (font) textPath.attr({ 'font': font });
  260. if (fontF) textPath.attr({ 'font-family': fontF });
  261. if (fontW) textPath.attr({ 'font-weight': fontW });
  262. }
  263. return line;
  264. }
  265. function checkLineIntersection(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) {
  266. // 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
  267. var denominator, a, b, numerator1, numerator2, result = {
  268. x: null,
  269. y: null,
  270. onLine1: false,
  271. onLine2: false
  272. };
  273. denominator = ((line2EndY - line2StartY) * (line1EndX - line1StartX)) - ((line2EndX - line2StartX) * (line1EndY - line1StartY));
  274. if (denominator === 0) {
  275. return result;
  276. }
  277. a = line1StartY - line2StartY;
  278. b = line1StartX - line2StartX;
  279. numerator1 = ((line2EndX - line2StartX) * a) - ((line2EndY - line2StartY) * b);
  280. numerator2 = ((line1EndX - line1StartX) * a) - ((line1EndY - line1StartY) * b);
  281. a = numerator1 / denominator;
  282. b = numerator2 / denominator;
  283. // if we cast these lines infinitely in both directions, they intersect here:
  284. result.x = line1StartX + (a * (line1EndX - line1StartX));
  285. result.y = line1StartY + (a * (line1EndY - line1StartY));
  286. /*
  287. // it is worth noting that this should be the same as:
  288. x = line2StartX + (b * (line2EndX - line2StartX));
  289. y = line2StartX + (b * (line2EndY - line2StartY));
  290. */
  291. // if line1 is a segment and line2 is infinite, they intersect if:
  292. if (a > 0 && a < 1) {
  293. result.onLine1 = true;
  294. }
  295. // if line2 is a segment and line1 is infinite, they intersect if:
  296. if (b > 0 && b < 1) {
  297. result.onLine2 = true;
  298. }
  299. // if line1 and line2 are segments, they intersect if both of the above are true
  300. return result;
  301. }
  302. function FlowChart(container, options) {
  303. options = options || {};
  304. this.paper = new Raphael(container);
  305. this.options = f.defaults(options, o);
  306. this.symbols = [];
  307. this.lines = [];
  308. this.start = null;
  309. }
  310. FlowChart.prototype.handle = function(symbol) {
  311. if (this.symbols.indexOf(symbol) <= -1) {
  312. this.symbols.push(symbol);
  313. }
  314. var flowChart = this;
  315. if (symbol instanceof(Condition)) {
  316. symbol.yes = function(nextSymbol) {
  317. symbol.yes_symbol = nextSymbol;
  318. if(symbol.no_symbol) {
  319. symbol.pathOk = true;
  320. }
  321. return flowChart.handle(nextSymbol);
  322. };
  323. symbol.no = function(nextSymbol) {
  324. symbol.no_symbol = nextSymbol;
  325. if(symbol.yes_symbol) {
  326. symbol.pathOk = true;
  327. }
  328. return flowChart.handle(nextSymbol);
  329. };
  330. } else {
  331. symbol.then = function(nextSymbol) {
  332. symbol.next = nextSymbol;
  333. symbol.pathOk = true;
  334. return flowChart.handle(nextSymbol);
  335. };
  336. }
  337. return symbol;
  338. };
  339. FlowChart.prototype.startWith = function(symbol) {
  340. this.start = symbol;
  341. return this.handle(symbol);
  342. };
  343. FlowChart.prototype.render = function() {
  344. var maxWidth = 0,
  345. maxHeight = 0,
  346. i = 0,
  347. len = 0,
  348. maxX = 0,
  349. maxY = 0,
  350. symbol;
  351. for (i = 0, len = this.symbols.length; i < len; i++) {
  352. symbol = this.symbols[i];
  353. if (symbol.width > maxWidth) {
  354. maxWidth = symbol.width;
  355. }
  356. if (symbol.height > maxHeight) {
  357. maxHeight = symbol.height;
  358. }
  359. }
  360. for (i = 0, len = this.symbols.length; i < len; i++) {
  361. symbol = this.symbols[i];
  362. symbol.shiftX(this.options.x + (maxWidth - symbol.width)/2 + this.options['line-width']);
  363. symbol.shiftY(this.options.y + (maxHeight - symbol.height)/2 + this.options['line-width']);
  364. }
  365. this.start.render();
  366. // for (i = 0, len = this.symbols.length; i < len; i++) {
  367. // symbol = this.symbols[i];
  368. // symbol.render();
  369. // }
  370. for (i = 0, len = this.symbols.length; i < len; i++) {
  371. symbol = this.symbols[i];
  372. symbol.renderLines();
  373. }
  374. maxX = this.maxXFromLine;
  375. for (i = 0, len = this.symbols.length; i < len; i++) {
  376. symbol = this.symbols[i];
  377. var x = symbol.getX() + symbol.width;
  378. var y = symbol.getY() + symbol.height;
  379. if (x > maxX) {
  380. maxX = x;
  381. }
  382. if (y > maxY) {
  383. maxY = y;
  384. }
  385. }
  386. var scale = this.options['scale'];
  387. var lineWidth = this.options['line-width'];
  388. this.paper.setSize((maxX * scale) + (lineWidth * scale), (maxY * scale) + (lineWidth * scale));
  389. this.paper.setViewBox(0, 0, maxX + lineWidth, maxY + lineWidth, true);
  390. };
  391. FlowChart.prototype.clean = function() {
  392. if (this.paper) {
  393. var paperDom = this.paper.canvas;
  394. paperDom.parentNode.removeChild(paperDom);
  395. }
  396. };
  397. function Symbol(chart, options, symbol) {
  398. this.chart = chart;
  399. this.group = this.chart.paper.set();
  400. this.symbol = symbol;
  401. this.connectedTo = [];
  402. this.symbolType = options.symbolType;
  403. this.flowstate = (options.flowstate || 'future');
  404. this.next_direction = options.next && options['direction_next'] ? options['direction_next'] : undefined;
  405. this.text = this.chart.paper.text(0, 0, options.text);
  406. //Raphael does not support the svg group tag so setting the text node id to the symbol node id plus t
  407. if (options.key) { this.text.node.id = options.key + 't'; }
  408. this.text.node.setAttribute('class', this.getAttr('class') + 't');
  409. this.text.attr({
  410. 'text-anchor': 'start',
  411. 'x' : this.getAttr('text-margin'),
  412. 'fill' : this.getAttr('font-color'),
  413. 'font-size' : this.getAttr('font-size')
  414. });
  415. var font = this.getAttr('font');
  416. var fontF = this.getAttr('font-family');
  417. var fontW = this.getAttr('font-weight');
  418. if (font) this.text.attr({ 'font': font });
  419. if (fontF) this.text.attr({ 'font-family': fontF });
  420. if (fontW) this.text.attr({ 'font-weight': fontW });
  421. if (options.link) { this.text.attr('href', options.link); }
  422. if (options.target) { this.text.attr('target', options.target); }
  423. var maxWidth = this.getAttr('maxWidth');
  424. if (maxWidth) {
  425. // using this approach: http://stackoverflow.com/a/3153457/22466
  426. var words = options.text.split(' ');
  427. var tempText = "";
  428. for (var i=0, ii=words.length; i<ii; i++) {
  429. var word = words[i];
  430. this.text.attr("text", tempText + " " + word);
  431. if (this.text.getBBox().width > maxWidth) {
  432. tempText += "\n" + word;
  433. } else {
  434. tempText += " " + word;
  435. }
  436. }
  437. this.text.attr("text", tempText.substring(1));
  438. }
  439. this.group.push(this.text);
  440. if (symbol) {
  441. var tmpMargin = this.getAttr('text-margin');
  442. symbol.attr({
  443. 'fill' : this.getAttr('fill'),
  444. 'stroke' : this.getAttr('element-color'),
  445. 'stroke-width' : this.getAttr('line-width'),
  446. 'width' : this.text.getBBox().width + 2 * tmpMargin,
  447. 'height' : this.text.getBBox().height + 2 * tmpMargin
  448. });
  449. symbol.node.setAttribute('class', this.getAttr('class'));
  450. if (options.link) { symbol.attr('href', options.link); }
  451. if (options.target) { symbol.attr('target', options.target); }
  452. if (options.key) { symbol.node.id = options.key; }
  453. this.group.push(symbol);
  454. symbol.insertBefore(this.text);
  455. this.text.attr({
  456. 'y': symbol.getBBox().height/2
  457. });
  458. this.initialize();
  459. }
  460. }
  461. /* Gets the attribute based on Flowstate, Symbol-Name and default, first found wins */
  462. Symbol.prototype.getAttr = function(attName) {
  463. if (!this.chart) {
  464. return undefined;
  465. }
  466. var opt3 = (this.chart.options) ? this.chart.options[attName] : undefined;
  467. var opt2 = (this.chart.options.symbols) ? this.chart.options.symbols[this.symbolType][attName] : undefined;
  468. var opt1;
  469. if (this.chart.options.flowstate && this.chart.options.flowstate[this.flowstate]) {
  470. opt1 = this.chart.options.flowstate[this.flowstate][attName];
  471. }
  472. return (opt1 || opt2 || opt3);
  473. };
  474. Symbol.prototype.initialize = function() {
  475. this.group.transform('t' + this.getAttr('line-width') + ',' + this.getAttr('line-width'));
  476. this.width = this.group.getBBox().width;
  477. this.height = this.group.getBBox().height;
  478. };
  479. Symbol.prototype.getCenter = function() {
  480. return {x: this.getX() + this.width/2,
  481. y: this.getY() + this.height/2};
  482. };
  483. Symbol.prototype.getX = function() {
  484. return this.group.getBBox().x;
  485. };
  486. Symbol.prototype.getY = function() {
  487. return this.group.getBBox().y;
  488. };
  489. Symbol.prototype.shiftX = function(x) {
  490. this.group.transform('t' + (this.getX() + x) + ',' + this.getY());
  491. };
  492. Symbol.prototype.setX = function(x) {
  493. this.group.transform('t' + x + ',' + this.getY());
  494. };
  495. Symbol.prototype.shiftY = function(y) {
  496. this.group.transform('t' + this.getX() + ',' + (this.getY() + y));
  497. };
  498. Symbol.prototype.setY = function(y) {
  499. this.group.transform('t' + this.getX() + ',' + y);
  500. };
  501. Symbol.prototype.getTop = function() {
  502. var y = this.getY();
  503. var x = this.getX() + this.width/2;
  504. return {x: x, y: y};
  505. };
  506. Symbol.prototype.getBottom = function() {
  507. var y = this.getY() + this.height;
  508. var x = this.getX() + this.width/2;
  509. return {x: x, y: y};
  510. };
  511. Symbol.prototype.getLeft = function() {
  512. var y = this.getY() + this.group.getBBox().height/2;
  513. var x = this.getX();
  514. return {x: x, y: y};
  515. };
  516. Symbol.prototype.getRight = function() {
  517. var y = this.getY() + this.group.getBBox().height/2;
  518. var x = this.getX() + this.group.getBBox().width;
  519. return {x: x, y: y};
  520. };
  521. Symbol.prototype.render = function() {
  522. if (this.next) {
  523. var lineLength = this.getAttr('line-length');
  524. if (this.next_direction === 'right') {
  525. var rightPoint = this.getRight();
  526. var leftPoint = this.next.getLeft();
  527. if (!this.next.isPositioned) {
  528. this.next.setY(rightPoint.y - this.next.height/2);
  529. this.next.shiftX(this.group.getBBox().x + this.width + lineLength);
  530. var self = this;
  531. (function shift() {
  532. var hasSymbolUnder = false;
  533. var symb;
  534. for (var i = 0, len = self.chart.symbols.length; i < len; i++) {
  535. symb = self.chart.symbols[i];
  536. var diff = Math.abs(symb.getCenter().x - self.next.getCenter().x);
  537. if (symb.getCenter().y > self.next.getCenter().y && diff <= self.next.width/2) {
  538. hasSymbolUnder = true;
  539. break;
  540. }
  541. }
  542. if (hasSymbolUnder) {
  543. self.next.setX(symb.getX() + symb.width + lineLength);
  544. shift();
  545. }
  546. })();
  547. this.next.isPositioned = true;
  548. this.next.render();
  549. }
  550. } else {
  551. var bottomPoint = this.getBottom();
  552. var topPoint = this.next.getTop();
  553. if (!this.next.isPositioned) {
  554. this.next.shiftY(this.getY() + this.height + lineLength);
  555. this.next.setX(bottomPoint.x - this.next.width/2);
  556. this.next.isPositioned = true;
  557. this.next.render();
  558. }
  559. }
  560. }
  561. };
  562. Symbol.prototype.renderLines = function() {
  563. if (this.next) {
  564. if (this.next_direction) {
  565. this.drawLineTo(this.next, '', this.next_direction);
  566. } else {
  567. this.drawLineTo(this.next);
  568. }
  569. }
  570. };
  571. Symbol.prototype.drawLineTo = function(symbol, text, origin) {
  572. if (this.connectedTo.indexOf(symbol) < 0) {
  573. this.connectedTo.push(symbol);
  574. }
  575. var x = this.getCenter().x,
  576. y = this.getCenter().y,
  577. top = this.getTop(),
  578. right = this.getRight(),
  579. bottom = this.getBottom(),
  580. left = this.getLeft();
  581. var symbolX = symbol.getCenter().x,
  582. symbolY = symbol.getCenter().y,
  583. symbolTop = symbol.getTop(),
  584. symbolRight = symbol.getRight(),
  585. symbolBottom = symbol.getBottom(),
  586. symbolLeft = symbol.getLeft();
  587. var isOnSameColumn = x === symbolX,
  588. isOnSameLine = y === symbolY,
  589. isUnder = y < symbolY,
  590. isUpper = y > symbolY,
  591. isLeft = x > symbolX,
  592. isRight = x < symbolX;
  593. var maxX = 0,
  594. line,
  595. lineLength = this.getAttr('line-length'),
  596. lineWith = this.getAttr('line-width');
  597. if ((!origin || origin === 'bottom') && isOnSameColumn && isUnder) {
  598. line = drawLine(this.chart, bottom, symbolTop, text);
  599. this.bottomStart = true;
  600. symbol.topEnd = true;
  601. maxX = bottom.x;
  602. } else if ((!origin || origin === 'right') && isOnSameLine && isRight) {
  603. line = drawLine(this.chart, right, symbolLeft, text);
  604. this.rightStart = true;
  605. symbol.leftEnd = true;
  606. maxX = symbolLeft.x;
  607. } else if ((!origin || origin === 'left') && isOnSameLine && isLeft) {
  608. line = drawLine(this.chart, left, symbolRight, text);
  609. this.leftStart = true;
  610. symbol.rightEnd = true;
  611. maxX = symbolRight.x;
  612. } else if ((!origin || origin === 'right') && isOnSameColumn && isUpper) {
  613. line = drawLine(this.chart, right, [
  614. {x: right.x + lineLength/2, y: right.y},
  615. {x: right.x + lineLength/2, y: symbolTop.y - lineLength/2},
  616. {x: symbolTop.x, y: symbolTop.y - lineLength/2},
  617. {x: symbolTop.x, y: symbolTop.y}
  618. ], text);
  619. this.rightStart = true;
  620. symbol.topEnd = true;
  621. maxX = right.x + lineLength/2;
  622. } else if ((!origin || origin === 'right') && isOnSameColumn && isUnder) {
  623. line = drawLine(this.chart, right, [
  624. {x: right.x + lineLength/2, y: right.y},
  625. {x: right.x + lineLength/2, y: symbolTop.y - lineLength/2},
  626. {x: symbolTop.x, y: symbolTop.y - lineLength/2},
  627. {x: symbolTop.x, y: symbolTop.y}
  628. ], text);
  629. this.rightStart = true;
  630. symbol.topEnd = true;
  631. maxX = right.x + lineLength/2;
  632. } else if ((!origin || origin === 'bottom') && isLeft) {
  633. if (this.leftEnd && isUpper) {
  634. line = drawLine(this.chart, bottom, [
  635. {x: bottom.x, y: bottom.y + lineLength/2},
  636. {x: bottom.x + (bottom.x - symbolTop.x)/2, y: bottom.y + lineLength/2},
  637. {x: bottom.x + (bottom.x - symbolTop.x)/2, y: symbolTop.y - lineLength/2},
  638. {x: symbolTop.x, y: symbolTop.y - lineLength/2},
  639. {x: symbolTop.x, y: symbolTop.y}
  640. ], text);
  641. } else {
  642. line = drawLine(this.chart, bottom, [
  643. {x: bottom.x, y: symbolTop.y - lineLength/2},
  644. {x: symbolTop.x, y: symbolTop.y - lineLength/2},
  645. {x: symbolTop.x, y: symbolTop.y}
  646. ], text);
  647. }
  648. this.bottomStart = true;
  649. symbol.topEnd = true;
  650. maxX = bottom.x + (bottom.x - symbolTop.x)/2;
  651. } else if ((!origin || origin === 'bottom') && isRight) {
  652. line = drawLine(this.chart, bottom, [
  653. {x: bottom.x, y: bottom.y + lineLength/2},
  654. {x: bottom.x + (bottom.x - symbolTop.x)/2, y: bottom.y + lineLength/2},
  655. {x: bottom.x + (bottom.x - symbolTop.x)/2, y: symbolTop.y - lineLength/2},
  656. {x: symbolTop.x, y: symbolTop.y - lineLength/2},
  657. {x: symbolTop.x, y: symbolTop.y}
  658. ], text);
  659. this.bottomStart = true;
  660. symbol.topEnd = true;
  661. maxX = bottom.x + (bottom.x - symbolTop.x)/2;
  662. } else if ((origin && origin === 'right') && isLeft) {
  663. line = drawLine(this.chart, right, [
  664. {x: right.x + lineLength/2, y: right.y},
  665. {x: right.x + lineLength/2, y: symbolTop.y - lineLength/2},
  666. {x: symbolTop.x, y: symbolTop.y - lineLength/2},
  667. {x: symbolTop.x, y: symbolTop.y}
  668. ], text);
  669. this.rightStart = true;
  670. symbol.topEnd = true;
  671. maxX = right.x + lineLength/2;
  672. } else if ((origin && origin === 'right') && isRight) {
  673. line = drawLine(this.chart, right, [
  674. {x: symbolTop.x, y: right.y},
  675. {x: symbolTop.x, y: symbolTop.y}
  676. ], text);
  677. this.rightStart = true;
  678. symbol.topEnd = true;
  679. maxX = right.x + lineLength/2;
  680. } else if ((origin && origin === 'bottom') && isOnSameColumn && isUpper) {
  681. line = drawLine(this.chart, bottom, [
  682. {x: bottom.x, y: bottom.y + lineLength/2},
  683. {x: right.x + lineLength/2, y: bottom.y + lineLength/2},
  684. {x: right.x + lineLength/2, y: symbolTop.y - lineLength/2},
  685. {x: symbolTop.x, y: symbolTop.y - lineLength/2},
  686. {x: symbolTop.x, y: symbolTop.y}
  687. ], text);
  688. this.bottomStart = true;
  689. symbol.topEnd = true;
  690. maxX = bottom.x + lineLength/2;
  691. } else if ((origin === 'left') && isOnSameColumn && isUpper) {
  692. var diffX = left.x - lineLength/2;
  693. if (symbolLeft.x < left.x) {
  694. diffX = symbolLeft.x - lineLength/2;
  695. }
  696. line = drawLine(this.chart, left, [
  697. {x: diffX, y: left.y},
  698. {x: diffX, y: symbolTop.y - lineLength/2},
  699. {x: symbolTop.x, y: symbolTop.y - lineLength/2},
  700. {x: symbolTop.x, y: symbolTop.y}
  701. ], text);
  702. this.leftStart = true;
  703. symbol.topEnd = true;
  704. maxX = left.x;
  705. } else if ((origin === 'left')) {
  706. line = drawLine(this.chart, left, [
  707. {x: symbolTop.x + (left.x - symbolTop.x)/ 2, y: left.y},
  708. {x: symbolTop.x + (left.x - symbolTop.x)/ 2, y: symbolTop.y - lineLength/2},
  709. {x: symbolTop.x, y: symbolTop.y - lineLength/2},
  710. {x: symbolTop.x, y: symbolTop.y}
  711. ], text);
  712. this.leftStart = true;
  713. symbol.topEnd = true;
  714. maxX = left.x;
  715. }
  716. if (line) {
  717. var self = this;
  718. for (var l = 0, llen = this.chart.lines.length; l < llen; l++) {
  719. var otherLine = this.chart.lines[l];
  720. var i,
  721. len,
  722. intersections,
  723. inter;
  724. var ePath = otherLine.attr('path'),
  725. lPath = line.attr('path');
  726. for (var iP = 0, lenP = ePath.length - 1; iP < lenP; iP++) {
  727. var newPath = [];
  728. newPath.push(['M', ePath[iP][1], ePath[iP][2]]);
  729. newPath.push(['L', ePath[iP + 1][1], ePath[iP + 1][2]]);
  730. var line1_from_x = newPath[0][1];
  731. var line1_from_y = newPath[0][2];
  732. var line1_to_x = newPath[1][1];
  733. var line1_to_y = newPath[1][2];
  734. for (var lP = 0, lenlP = lPath.length - 1; lP < lenlP; lP++) {
  735. var newLinePath = [];
  736. newLinePath.push(['M', lPath[lP][1], lPath[lP][2]]);
  737. newLinePath.push(['L', lPath[lP + 1][1], lPath[lP + 1][2]]);
  738. var line2_from_x = newLinePath[0][1];
  739. var line2_from_y = newLinePath[0][2];
  740. var line2_to_x = newLinePath[1][1];
  741. var line2_to_y = newLinePath[1][2];
  742. 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);
  743. if (res.onLine1 && res.onLine2) {
  744. var newSegment;
  745. if (line2_from_y === line2_to_y) {
  746. if (line2_from_x > line2_to_x) {
  747. newSegment = ['L', res.x + lineWith * 2, line2_from_y];
  748. lPath.splice(lP + 1, 0, newSegment);
  749. newSegment = ['C', res.x + lineWith * 2, line2_from_y, res.x, line2_from_y - lineWith * 4, res.x - lineWith * 2, line2_from_y];
  750. lPath.splice(lP + 2, 0, newSegment);
  751. line.attr('path', lPath);
  752. } else {
  753. newSegment = ['L', res.x - lineWith * 2, line2_from_y];
  754. lPath.splice(lP + 1, 0, newSegment);
  755. newSegment = ['C', res.x - lineWith * 2, line2_from_y, res.x, line2_from_y - lineWith * 4, res.x + lineWith * 2, line2_from_y];
  756. lPath.splice(lP + 2, 0, newSegment);
  757. line.attr('path', lPath);
  758. }
  759. } else {
  760. if (line2_from_y > line2_to_y) {
  761. newSegment = ['L', line2_from_x, res.y + lineWith * 2];
  762. lPath.splice(lP + 1, 0, newSegment);
  763. newSegment = ['C', line2_from_x, res.y + lineWith * 2, line2_from_x + lineWith * 4, res.y, line2_from_x, res.y - lineWith * 2];
  764. lPath.splice(lP + 2, 0, newSegment);
  765. line.attr('path', lPath);
  766. } else {
  767. newSegment = ['L', line2_from_x, res.y - lineWith * 2];
  768. lPath.splice(lP + 1, 0, newSegment);
  769. newSegment = ['C', line2_from_x, res.y - lineWith * 2, line2_from_x + lineWith * 4, res.y, line2_from_x, res.y + lineWith * 2];
  770. lPath.splice(lP + 2, 0, newSegment);
  771. line.attr('path', lPath);
  772. }
  773. }
  774. lP += 2;
  775. len += 2;
  776. }
  777. }
  778. }
  779. }
  780. this.chart.lines.push(line);
  781. }
  782. if (!this.chart.maxXFromLine || (this.chart.maxXFromLine && maxX > this.chart.maxXFromLine)) {
  783. this.chart.maxXFromLine = maxX;
  784. }
  785. };
  786. function Start(chart, options) {
  787. var symbol = chart.paper.rect(0, 0, 0, 0, 20);
  788. options = options || {};
  789. options.text = options.text || 'Start';
  790. Symbol.call(this, chart, options, symbol);
  791. }
  792. f.inherits(Start, Symbol);
  793. // Start.prototype.render = function() {
  794. // if (this.next) {
  795. // var lineLength = this.chart.options.symbols[this.symbolType]['line-length'] || this.chart.options['line-length'];
  796. // var bottomPoint = this.getBottom();
  797. // var topPoint = this.next.getTop();
  798. // if (!this.next.isPositioned) {
  799. // this.next.shiftY(this.getY() + this.height + lineLength);
  800. // this.next.setX(bottomPoint.x - this.next.width/2);
  801. // this.next.isPositioned = true;
  802. // this.next.render();
  803. // }
  804. // }
  805. // };
  806. // Start.prototype.renderLines = function() {
  807. // if (this.next) {
  808. // this.drawLineTo(this.next);
  809. // }
  810. // };
  811. function End(chart, options) {
  812. var symbol = chart.paper.rect(0, 0, 0, 0, 20);
  813. options = options || {};
  814. options.text = options.text || 'End';
  815. Symbol.call(this, chart, options, symbol);
  816. }
  817. f.inherits(End, Symbol);
  818. function Operation(chart, options) {
  819. var symbol = chart.paper.rect(0, 0, 0, 0);
  820. options = options || {};
  821. Symbol.call(this, chart, options, symbol);
  822. }
  823. f.inherits(Operation, Symbol);
  824. function Subroutine(chart, options) {
  825. var symbol = chart.paper.rect(0, 0, 0, 0);
  826. options = options || {};
  827. Symbol.call(this, chart, options, symbol);
  828. symbol.attr({
  829. width: this.text.getBBox().width + 4 * this.getAttr('text-margin')
  830. });
  831. this.text.attr({
  832. 'x': 2 * this.getAttr('text-margin')
  833. });
  834. var innerWrap = chart.paper.rect(0, 0, 0, 0);
  835. innerWrap.attr({
  836. x: this.getAttr('text-margin'),
  837. stroke: this.getAttr('element-color'),
  838. 'stroke-width': this.getAttr('line-width'),
  839. width: this.text.getBBox().width + 2 * this.getAttr('text-margin'),
  840. height: this.text.getBBox().height + 2 * this.getAttr('text-margin'),
  841. fill: this.getAttr('fill')
  842. });
  843. if (options.key) { innerWrap.node.id = options.key + 'i'; }
  844. var font = this.getAttr('font');
  845. var fontF = this.getAttr('font-family');
  846. var fontW = this.getAttr('font-weight');
  847. if (font) innerWrap.attr({ 'font': font });
  848. if (fontF) innerWrap.attr({ 'font-family': fontF });
  849. if (fontW) innerWrap.attr({ 'font-weight': fontW });
  850. if (options.link) { innerWrap.attr('href', options.link); }
  851. if (options.target) { innerWrap.attr('target', options.target); }
  852. this.group.push(innerWrap);
  853. innerWrap.insertBefore(this.text);
  854. this.initialize();
  855. }
  856. f.inherits(Subroutine, Symbol);
  857. function InputOutput(chart, options) {
  858. options = options || {};
  859. Symbol.call(this, chart, options);
  860. this.textMargin = this.getAttr('text-margin');
  861. this.text.attr({
  862. x: this.textMargin * 3
  863. });
  864. var width = this.text.getBBox().width + 4 * this.textMargin;
  865. var height = this.text.getBBox().height + 2 * this.textMargin;
  866. var startX = this.textMargin;
  867. var startY = height/2;
  868. var start = {x: startX, y: startY};
  869. var points = [
  870. {x: startX - this.textMargin, y: height},
  871. {x: startX - this.textMargin + width, y: height},
  872. {x: startX - this.textMargin + width + 2 * this.textMargin, y: 0},
  873. {x: startX - this.textMargin + 2 * this.textMargin, y: 0},
  874. {x: startX, y: startY}
  875. ];
  876. var symbol = drawPath(chart, start, points);
  877. symbol.attr({
  878. stroke: this.getAttr('element-color'),
  879. 'stroke-width': this.getAttr('line-width'),
  880. fill: this.getAttr('fill')
  881. });
  882. if (options.link) { symbol.attr('href', options.link); }
  883. if (options.target) { symbol.attr('target', options.target); }
  884. if (options.key) { symbol.node.id = options.key; }
  885. symbol.node.setAttribute('class', this.getAttr('class'));
  886. this.text.attr({
  887. y: symbol.getBBox().height/2
  888. });
  889. this.group.push(symbol);
  890. symbol.insertBefore(this.text);
  891. this.initialize();
  892. }
  893. f.inherits(InputOutput, Symbol);
  894. InputOutput.prototype.getLeft = function() {
  895. var y = this.getY() + this.group.getBBox().height/2;
  896. var x = this.getX() + this.textMargin;
  897. return {x: x, y: y};
  898. };
  899. InputOutput.prototype.getRight = function() {
  900. var y = this.getY() + this.group.getBBox().height/2;
  901. var x = this.getX() + this.group.getBBox().width - this.textMargin;
  902. return {x: x, y: y};
  903. };
  904. function Condition(chart, options) {
  905. options = options || {};
  906. Symbol.call(this, chart, options);
  907. this.textMargin = this.getAttr('text-margin');
  908. this.yes_direction = 'bottom';
  909. this.no_direction = 'right';
  910. if (options.yes && options['direction_yes'] && options.no && !options['direction_no']) {
  911. if (options['direction_yes'] === 'right') {
  912. this.no_direction = 'bottom';
  913. this.yes_direction = 'right';
  914. } else {
  915. this.no_direction = 'right';
  916. this.yes_direction = 'bottom';
  917. }
  918. } else if (options.yes && !options['direction_yes'] && options.no && options['direction_no']) {
  919. if (options['direction_no'] === 'right') {
  920. this.yes_direction = 'bottom';
  921. this.no_direction = 'right';
  922. } else {
  923. this.yes_direction = 'right';
  924. this.no_direction = 'bottom';
  925. }
  926. } else {
  927. this.yes_direction = 'bottom';
  928. this.no_direction = 'right';
  929. }
  930. this.yes_direction = this.yes_direction || 'bottom';
  931. this.no_direction = this.no_direction || 'right';
  932. this.text.attr({
  933. x: this.textMargin * 2
  934. });
  935. var width = this.text.getBBox().width + 3 * this.textMargin;
  936. width += width/2;
  937. var height = this.text.getBBox().height + 2 * this.textMargin;
  938. height += height/2;
  939. height = Math.max(width * 0.5, height);
  940. var startX = width/4;
  941. var startY = height/4;
  942. this.text.attr({
  943. x: startX + this.textMargin/2
  944. });
  945. var start = {x: startX, y: startY};
  946. var points = [
  947. {x: startX - width/4, y: startY + height/4},
  948. {x: startX - width/4 + width/2, y: startY + height/4 + height/2},
  949. {x: startX - width/4 + width, y: startY + height/4},
  950. {x: startX - width/4 + width/2, y: startY + height/4 - height/2},
  951. {x: startX - width/4, y: startY + height/4}
  952. ];
  953. var symbol = drawPath(chart, start, points);
  954. symbol.attr({
  955. stroke: this.getAttr('element-color'),
  956. 'stroke-width': this.getAttr('line-width'),
  957. fill: this.getAttr('fill')
  958. });
  959. if (options.link) { symbol.attr('href', options.link); }
  960. if (options.target) { symbol.attr('target', options.target); }
  961. if (options.key) { symbol.node.id = options.key; }
  962. symbol.node.setAttribute('class', this.getAttr('class'));
  963. this.text.attr({
  964. y: symbol.getBBox().height/2
  965. });
  966. this.group.push(symbol);
  967. symbol.insertBefore(this.text);
  968. this.initialize();
  969. }
  970. f.inherits(Condition, Symbol);
  971. Condition.prototype.render = function() {
  972. if (this.yes_direction) {
  973. this[this.yes_direction + '_symbol'] = this.yes_symbol;
  974. }
  975. if (this.no_direction) {
  976. this[this.no_direction + '_symbol'] = this.no_symbol;
  977. }
  978. var lineLength = this.getAttr('line-length');
  979. if (this.bottom_symbol) {
  980. var bottomPoint = this.getBottom();
  981. var topPoint = this.bottom_symbol.getTop();
  982. if (!this.bottom_symbol.isPositioned) {
  983. this.bottom_symbol.shiftY(this.getY() + this.height + lineLength);
  984. this.bottom_symbol.setX(bottomPoint.x - this.bottom_symbol.width/2);
  985. this.bottom_symbol.isPositioned = true;
  986. this.bottom_symbol.render();
  987. }
  988. }
  989. if (this.right_symbol) {
  990. var rightPoint = this.getRight();
  991. var leftPoint = this.right_symbol.getLeft();
  992. if (!this.right_symbol.isPositioned) {
  993. this.right_symbol.setY(rightPoint.y - this.right_symbol.height/2);
  994. this.right_symbol.shiftX(this.group.getBBox().x + this.width + lineLength);
  995. var self = this;
  996. (function shift() {
  997. var hasSymbolUnder = false;
  998. var symb;
  999. for (var i = 0, len = self.chart.symbols.length; i < len; i++) {
  1000. symb = self.chart.symbols[i];
  1001. var diff = Math.abs(symb.getCenter().x - self.right_symbol.getCenter().x);
  1002. if (symb.getCenter().y > self.right_symbol.getCenter().y && diff <= self.right_symbol.width/2) {
  1003. hasSymbolUnder = true;
  1004. break;
  1005. }
  1006. }
  1007. if (hasSymbolUnder) {
  1008. self.right_symbol.setX(symb.getX() + symb.width + lineLength);
  1009. shift();
  1010. }
  1011. })();
  1012. this.right_symbol.isPositioned = true;
  1013. this.right_symbol.render();
  1014. }
  1015. }
  1016. };
  1017. Condition.prototype.renderLines = function() {
  1018. if (this.yes_symbol) {
  1019. this.drawLineTo(this.yes_symbol, this.getAttr('yes-text'), this.yes_direction);
  1020. }
  1021. if (this.no_symbol) {
  1022. this.drawLineTo(this.no_symbol, this.getAttr('no-text'), this.no_direction);
  1023. }
  1024. };
  1025. function parse(input) {
  1026. input = input || '';
  1027. input = input.trim();
  1028. var chart = {
  1029. symbols: {},
  1030. start: null,
  1031. drawSVG: function(container, options) {
  1032. var self = this;
  1033. if (this.diagram) {
  1034. this.diagram.clean();
  1035. }
  1036. var diagram = new FlowChart(container, options);
  1037. this.diagram = diagram;
  1038. var dispSymbols = {};
  1039. function getDisplaySymbol(s) {
  1040. if (dispSymbols[s.key]) {
  1041. return dispSymbols[s.key];
  1042. }
  1043. switch (s.symbolType) {
  1044. case 'start':
  1045. dispSymbols[s.key] = new Start(diagram, s);
  1046. break;
  1047. case 'end':
  1048. dispSymbols[s.key] = new End(diagram, s);
  1049. break;
  1050. case 'operation':
  1051. dispSymbols[s.key] = new Operation(diagram, s);
  1052. break;
  1053. case 'inputoutput':
  1054. dispSymbols[s.key] = new InputOutput(diagram, s);
  1055. break;
  1056. case 'subroutine':
  1057. dispSymbols[s.key] = new Subroutine(diagram, s);
  1058. break;
  1059. case 'condition':
  1060. dispSymbols[s.key] = new Condition(diagram, s);
  1061. break;
  1062. default:
  1063. return new Error('Wrong symbol type!');
  1064. }
  1065. return dispSymbols[s.key];
  1066. }
  1067. (function constructChart(s, prevDisp, prev) {
  1068. var dispSymb = getDisplaySymbol(s);
  1069. if (self.start === s) {
  1070. diagram.startWith(dispSymb);
  1071. } else if (prevDisp && prev && !prevDisp.pathOk) {
  1072. if (prevDisp instanceof(Condition)) {
  1073. if (prev.yes === s) {
  1074. prevDisp.yes(dispSymb);
  1075. }
  1076. if (prev.no === s) {
  1077. prevDisp.no(dispSymb);
  1078. }
  1079. } else {
  1080. prevDisp.then(dispSymb);
  1081. }
  1082. }
  1083. if (dispSymb.pathOk) {
  1084. return dispSymb;
  1085. }
  1086. if (dispSymb instanceof(Condition)) {
  1087. if (s.yes) {
  1088. constructChart(s.yes, dispSymb, s);
  1089. }
  1090. if (s.no) {
  1091. constructChart(s.no, dispSymb, s);
  1092. }
  1093. } else if (s.next) {
  1094. constructChart(s.next, dispSymb, s);
  1095. }
  1096. return dispSymb;
  1097. })(this.start);
  1098. diagram.render();
  1099. },
  1100. clean: function() {
  1101. this.diagram.clean();
  1102. }
  1103. };
  1104. var lines = [];
  1105. var prevBreak = 0;
  1106. for (var i0 = 1, i0len = input.length; i0 < i0len; i0++) {
  1107. if(input[i0] === '\n' && input[i0 - 1] !== '\\') {
  1108. var line0 = input.substring(prevBreak, i0);
  1109. prevBreak = i0 + 1;
  1110. lines.push(line0.replace(/\\\n/g, '\n'));
  1111. }
  1112. }
  1113. if(prevBreak < input.length) {
  1114. lines.push(input.substr(prevBreak));
  1115. }
  1116. for (var l = 1, len = lines.length; l < len;) {
  1117. var currentLine = lines[l];
  1118. if (currentLine.indexOf(': ') < 0 && currentLine.indexOf('(') < 0 && currentLine.indexOf(')') < 0 && currentLine.indexOf('->') < 0 && currentLine.indexOf('=>') < 0) {
  1119. lines[l - 1] += '\n' + currentLine;
  1120. lines.splice(l, 1);
  1121. len--;
  1122. } else {
  1123. l++;
  1124. }
  1125. }
  1126. function getSymbol(s) {
  1127. var startIndex = s.indexOf('(') + 1;
  1128. var endIndex = s.indexOf(')');
  1129. if (startIndex >= 0 && endIndex >= 0) {
  1130. return chart.symbols[s.substring(0, startIndex - 1)];
  1131. }
  1132. return chart.symbols[s];
  1133. }
  1134. function getNextPath(s) {
  1135. var next = 'next';
  1136. var startIndex = s.indexOf('(') + 1;
  1137. var endIndex = s.indexOf(')');
  1138. if (startIndex >= 0 && endIndex >= 0) {
  1139. next = flowSymb.substring(startIndex, endIndex);
  1140. if (next.indexOf(',') < 0) {
  1141. if (next !== 'yes' && next !== 'no') {
  1142. next = 'next, ' + next;
  1143. }
  1144. }
  1145. }
  1146. return next;
  1147. }
  1148. while (lines.length > 0) {
  1149. var line = lines.splice(0, 1)[0];
  1150. if (line.indexOf('=>') >= 0) {
  1151. // definition
  1152. var parts = line.split('=>');
  1153. var symbol = {
  1154. key: parts[0],
  1155. symbolType: parts[1],
  1156. text: null,
  1157. link: null,
  1158. target: null,
  1159. flowstate: null
  1160. };
  1161. var sub;
  1162. if (symbol.symbolType.indexOf(': ') >= 0) {
  1163. sub = symbol.symbolType.split(': ');
  1164. symbol.symbolType = sub[0];
  1165. symbol.text = sub[1];
  1166. }
  1167. if (symbol.text && symbol.text.indexOf(':>') >= 0) {
  1168. sub = symbol.text.split(':>');
  1169. symbol.text = sub[0];
  1170. symbol.link = sub[1];
  1171. } else if (symbol.symbolType.indexOf(':>') >= 0) {
  1172. sub = symbol.symbolType.split(':>');
  1173. symbol.symbolType = sub[0];
  1174. symbol.link = sub[1];
  1175. }
  1176. if (symbol.symbolType.indexOf('\n') >= 0) {
  1177. symbol.symbolType = symbol.symbolType.split('\n')[0];
  1178. }
  1179. /* adding support for links */
  1180. if (symbol.link) {
  1181. var startIndex = symbol.link.indexOf('[') + 1;
  1182. var endIndex = symbol.link.indexOf(']');
  1183. if (startIndex >= 0 && endIndex >= 0) {
  1184. symbol.target = symbol.link.substring(startIndex, endIndex);
  1185. symbol.link = symbol.link.substring(0, startIndex - 1);
  1186. }
  1187. }
  1188. /* end of link support */
  1189. /* adding support for flowstates */
  1190. if (symbol.text) {
  1191. if (symbol.text.indexOf('|') >= 0) {
  1192. var txtAndState = symbol.text.split('|');
  1193. symbol.text = txtAndState[0];
  1194. symbol.flowstate = txtAndState[1].trim();
  1195. }
  1196. }
  1197. /* end of flowstate support */
  1198. chart.symbols[symbol.key] = symbol;
  1199. } else if (line.indexOf('->') >= 0) {
  1200. // flow
  1201. var flowSymbols = line.split('->');
  1202. for (var i = 0, lenS = flowSymbols.length; i < lenS; i++) {
  1203. var flowSymb = flowSymbols[i];
  1204. var realSymb = getSymbol(flowSymb);
  1205. var next = getNextPath(flowSymb);
  1206. var direction = null;
  1207. if (next.indexOf(',') >= 0) {
  1208. var condOpt = next.split(',');
  1209. next = condOpt[0];
  1210. direction = condOpt[1].trim();
  1211. }
  1212. if (!chart.start) {
  1213. chart.start = realSymb;
  1214. }
  1215. if (i + 1 < lenS) {
  1216. var nextSymb = flowSymbols[i + 1];
  1217. realSymb[next] = getSymbol(nextSymb);
  1218. realSymb['direction_' + next] = direction;
  1219. direction = null;
  1220. }
  1221. }
  1222. }
  1223. }
  1224. return chart;
  1225. }
  1226. // public api interface
  1227. flowchart.parse = parse;
  1228. })();