trees.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437
  1. /**
  2. * Mindmaps plugin.
  3. *
  4. * Todo:
  5. * - Make cursor key selection more generic
  6. * - Handle single-cell movement on touch
  7. * - Move multiple cells without subtrees
  8. * - Make offset subtrees more generic
  9. */
  10. Draw.loadPlugin(function(ui)
  11. {
  12. var spacing = 10;
  13. var level = 40;
  14. var graph = ui.editor.graph;
  15. var model = graph.getModel();
  16. // Adds resources for actions
  17. mxResources.parse('selectChildren=Select Children');
  18. mxResources.parse('selectSiblings=Select Siblings');
  19. mxResources.parse('selectSubtree=Select Subtree');
  20. mxResources.parse('selectParent=Select Parent');
  21. function isTreeCell(cell)
  22. {
  23. var result = false;
  24. if (cell != null)
  25. {
  26. graph.traverse(cell, true, function(vertex)
  27. {
  28. result = vertex.getAttribute('treeRoot') == '1';
  29. return !result;
  30. }, null, null, true);
  31. }
  32. return result;
  33. };
  34. var uiCreatePopupMenu = ui.menus.createPopupMenu;
  35. ui.menus.createPopupMenu = function(menu, cell, evt)
  36. {
  37. uiCreatePopupMenu.apply(this, arguments);
  38. if (isTreeCell(graph.getSelectionCell()) && graph.getSelectionCount() == 1)
  39. {
  40. var cell = graph.getSelectionCell();
  41. var sib = graph.getOutgoingEdges(cell);
  42. menu.addSeparator();
  43. if (sib != null && sib.length > 0)
  44. {
  45. this.addMenuItems(menu, ['selectChildren', 'selectSubtree'], null, evt);
  46. }
  47. menu.addSeparator();
  48. if (cell.getAttribute('treeRoot') != '1')
  49. {
  50. this.addMenuItems(menu, ['selectSiblings', 'selectParent'], null, evt);
  51. }
  52. }
  53. };
  54. // Adds actions
  55. ui.actions.addAction('selectChildren', function()
  56. {
  57. if (graph.isEnabled() && graph.getSelectionCount() == 1)
  58. {
  59. var cell = graph.getSelectionCell();
  60. var sib = graph.getOutgoingEdges(cell);
  61. if (sib != null)
  62. {
  63. var tmp = [];
  64. for (var i = 0; i < sib.length; i++)
  65. {
  66. tmp.push(graph.model.getTerminal(sib[i], false));
  67. }
  68. graph.setSelectionCells(tmp);
  69. }
  70. }
  71. }, null, null, 'Alt+Shift+X');
  72. // Adds actions
  73. ui.actions.addAction('selectSiblings', function()
  74. {
  75. if (graph.isEnabled() && graph.getSelectionCount() == 1)
  76. {
  77. var cell = graph.getSelectionCell();
  78. var edges = graph.getIncomingEdges(cell);
  79. if (edges != null && edges.length > 0)
  80. {
  81. var sib = graph.getOutgoingEdges(graph.model.getTerminal(edges[0], true));
  82. if (sib != null)
  83. {
  84. var tmp = [];
  85. for (var i = 0; i < sib.length; i++)
  86. {
  87. tmp.push(graph.model.getTerminal(sib[i], false));
  88. }
  89. graph.setSelectionCells(tmp);
  90. }
  91. }
  92. }
  93. }, null, null, 'Alt+Shift+S');
  94. // Adds actions
  95. ui.actions.addAction('selectParent', function()
  96. {
  97. if (graph.isEnabled() && graph.getSelectionCount() == 1)
  98. {
  99. var cell = graph.getSelectionCell();
  100. var edges = graph.getIncomingEdges(cell);
  101. if (edges != null && edges.length > 0)
  102. {
  103. graph.setSelectionCell(graph.model.getTerminal(edges[0], true));
  104. }
  105. }
  106. }, null, null, 'Alt+Shift+P');
  107. ui.actions.addAction('selectSubtree', function()
  108. {
  109. if (graph.isEnabled() && graph.getSelectionCount() == 1)
  110. {
  111. var cell = graph.getSelectionCell();
  112. // Makes space for new parent
  113. var subtree = [];
  114. graph.traverse(cell, true, function(vertex, edge)
  115. {
  116. if (edge != null)
  117. {
  118. subtree.push(edge);
  119. }
  120. subtree.push(vertex);
  121. return true;
  122. });
  123. graph.setSelectionCells(subtree);
  124. }
  125. }, null, null, 'Alt+Shift+T');
  126. /**
  127. * Overriddes
  128. */
  129. var graphFoldCells = graph.foldCells;
  130. graph.foldCells = function(collapse, recurse, cells, checkFoldable, evt)
  131. {
  132. //console.log('cells', cells, collapse);
  133. this.stopEditing();
  134. this.model.beginUpdate();
  135. try
  136. {
  137. var newCells = cells.splice();
  138. var tmp = [];
  139. for (var i = 0; i < cells.length; i++)
  140. {
  141. if (isTreeCell(cells[i]))
  142. {
  143. graph.traverse(cells[i], true, function(vertex, edge)
  144. {
  145. if (edge != null)
  146. {
  147. tmp.push(edge);
  148. }
  149. if (vertex != cells[i])
  150. {
  151. tmp.push(vertex);
  152. }
  153. // Stop traversal on collapsed vertices
  154. return vertex == cells[i] || !graph.model.isCollapsed(vertex);
  155. });
  156. graph.model.setCollapsed(cells[i], collapse);
  157. }
  158. }
  159. for (var i = 0; i < tmp.length; i++)
  160. {
  161. graph.model.setVisible(tmp[i], !collapse);
  162. }
  163. cells = newCells;
  164. graphFoldCells.apply(this, arguments);
  165. }
  166. finally
  167. {
  168. this.model.endUpdate();
  169. }
  170. };
  171. var graphRemoveCells = graph.removeCells;
  172. graph.removeCells = function(cells, includeEdges)
  173. {
  174. var tmp = [];
  175. for (var i = 0; i < cells.length; i++)
  176. {
  177. if (isTreeCell(cells[i]))
  178. {
  179. graph.traverse(cells[i], true, function(vertex, edge)
  180. {
  181. if (edge != null)
  182. {
  183. tmp.push(edge);
  184. }
  185. tmp.push(vertex);
  186. return true;
  187. });
  188. var edges = graph.getIncomingEdges(cells[i]);
  189. cells = cells.concat(edges);
  190. }
  191. else
  192. {
  193. tmp.push(cells[i]);
  194. }
  195. }
  196. cells = tmp;
  197. graphRemoveCells.apply(this, arguments);
  198. };
  199. ui.hoverIcons.getStateAt = function(state, x, y)
  200. {
  201. return (isTreeCell(state.cell)) ? null : this.graph.view.getState(this.graph.getCellAt(x, y));
  202. };
  203. var graphDuplicateCells = graph.duplicateCells;
  204. graph.duplicateCells = function(cells, append)
  205. {
  206. cells = (cells != null) ? cells : this.getSelectionCells();
  207. var temp = cells.slice(0);
  208. for (var i = 0; i < temp.length; i++)
  209. {
  210. var cell = temp[i];
  211. var state = graph.view.getState(cell);
  212. if (state != null && isTreeCell(state.cell))
  213. {
  214. // Avoids disconnecting subtree by removing all incoming edges
  215. var edges = graph.getIncomingEdges(state.cell);
  216. for (var j = 0; j < edges.length; j++)
  217. {
  218. mxUtils.remove(edges[j], cells);
  219. }
  220. }
  221. }
  222. this.model.beginUpdate();
  223. try
  224. {
  225. var result = graphDuplicateCells.call(this, cells, append);
  226. if (result.length == cells.length)
  227. {
  228. for (var i = 0; i < cells.length; i++)
  229. {
  230. if (isTreeCell(cells[i]))
  231. {
  232. var newEdges = graph.getIncomingEdges(result[i]);
  233. var edges = graph.getIncomingEdges(cells[i]);
  234. if (newEdges.length == 0 && edges.length > 0)
  235. {
  236. var clone = this.cloneCells([edges[0]])[0];
  237. this.addEdge(clone, graph.getDefaultParent(),
  238. this.model.getTerminal(edges[0], true), result[i]);
  239. }
  240. }
  241. }
  242. }
  243. }
  244. finally
  245. {
  246. this.model.endUpdate();
  247. }
  248. return result;
  249. };
  250. var graphMoveCells = graph.moveCells;
  251. graph.moveCells = function(cells, dx, dy, clone, target, evt, mapping)
  252. {
  253. var result = null;
  254. this.model.beginUpdate();
  255. try
  256. {
  257. var newSource = target;
  258. if (isTreeCell(target))
  259. {
  260. // Handles only drag from tree or from sidebar with dangling edges
  261. for (var i = 0; i < cells.length; i++)
  262. {
  263. if (isTreeCell(cells[i]) || (graph.model.isEdge(cells[i]) &&
  264. graph.model.getTerminal(cells[i], true) == null))
  265. {
  266. target = null;
  267. break;
  268. }
  269. }
  270. // Applies distance between previous and current parent for non-sidebar drags
  271. if (newSource != null && target == null && this.view.getState(cells[0]) != null)
  272. {
  273. var edges = graph.getIncomingEdges(cells[0]);
  274. if (edges.length > 0)
  275. {
  276. var state1 = graph.view.getState(graph.model.getTerminal(edges[0], true));
  277. if (state1 != null)
  278. {
  279. var state2 = graph.view.getState(newSource);
  280. if (state2 != null)
  281. {
  282. dx = state2.getCenterX() - state1.getCenterX();
  283. dy = state2.getCenterY() - state1.getCenterY();
  284. }
  285. }
  286. }
  287. }
  288. }
  289. result = graphMoveCells.apply(this, arguments);
  290. if (result.length == cells.length)
  291. {
  292. for (var i = 0; i < result.length; i++)
  293. {
  294. // Connects all dangling edges from the sidebar when dropped into drop target (not hover icon)
  295. if (this.model.isEdge(result[i]))
  296. {
  297. if (isTreeCell(newSource) && mxUtils.indexOf(result, this.model.getTerminal(result[i], true)) < 0)
  298. {
  299. this.model.setTerminal(result[i], newSource, true);
  300. }
  301. }
  302. else if (isTreeCell(cells[i]))
  303. {
  304. var edges = graph.getIncomingEdges(cells[i]);
  305. if (edges.length > 0)
  306. {
  307. if (!clone)
  308. {
  309. if (isTreeCell(newSource) && mxUtils.indexOf(cells, this.model.getTerminal(edges[0], true)) < 0)
  310. {
  311. this.model.setTerminal(edges[0], newSource, true);
  312. }
  313. }
  314. else
  315. {
  316. var newEdges = graph.getIncomingEdges(result[i]);
  317. if (newEdges.length == 0)
  318. {
  319. var temp = newSource;
  320. if (temp == null)
  321. {
  322. temp = graph.model.getTerminal(edges[0], true);
  323. }
  324. var clone = this.cloneCells([edges[0]])[0];
  325. this.addEdge(clone, graph.getDefaultParent(), temp, result[i]);
  326. }
  327. }
  328. }
  329. }
  330. }
  331. }
  332. }
  333. finally
  334. {
  335. this.model.endUpdate();
  336. }
  337. return result;
  338. };
  339. // Connects all dangling edges from the sidebar (by
  340. // default only first dangling edge gets connected)
  341. var sidebarDropAndConnect = ui.sidebar.dropAndConnect;
  342. ui.sidebar.dropAndConnect = function(source, targets, direction, dropCellIndex)
  343. {
  344. var model = graph.model;
  345. var result = null;
  346. model.beginUpdate();
  347. try
  348. {
  349. result = sidebarDropAndConnect.apply(this, arguments);
  350. if (isTreeCell(source))
  351. {
  352. for (var i = 0; i < result.length; i++)
  353. {
  354. if (model.isEdge(result[i]) && model.getTerminal(result[i], true) == null)
  355. {
  356. model.setTerminal(result[i], source, true);
  357. var geo = graph.getCellGeometry(result[i]);
  358. geo.points = null;
  359. if (geo.getTerminalPoint(true) != null)
  360. {
  361. geo.setTerminalPoint(null, true);
  362. }
  363. }
  364. }
  365. }
  366. }
  367. finally
  368. {
  369. model.endUpdate();
  370. }
  371. return result;
  372. };
  373. /**
  374. * Checks source point of incoming edge relative to target terminal.
  375. */
  376. function getTreeDirection(cell)
  377. {
  378. var state = graph.view.getState(cell);
  379. if (state != null)
  380. {
  381. var edges = graph.getIncomingEdges(state.cell);
  382. if (edges.length > 0)
  383. {
  384. var edgeState = graph.view.getState(edges[0]);
  385. if (edgeState != null)
  386. {
  387. var abs = edgeState.absolutePoints;
  388. if (abs != null && abs.length > 0)
  389. {
  390. var pt = abs[abs.length - 1];
  391. if (pt != null)
  392. {
  393. if (pt.y == state.y && Math.abs(pt.x - state.getCenterX()) < state.width / 2)
  394. {
  395. return mxConstants.DIRECTION_SOUTH;
  396. }
  397. else if (pt.y == state.y + state.height && Math.abs(pt.x - state.getCenterX()) < state.width / 2)
  398. {
  399. return mxConstants.DIRECTION_NORTH;
  400. }
  401. else if (pt.x > state.getCenterX())
  402. {
  403. return mxConstants.DIRECTION_WEST;
  404. }
  405. }
  406. }
  407. }
  408. }
  409. }
  410. return mxConstants.DIRECTION_EAST;
  411. };
  412. function addSibling(cell, after)
  413. {
  414. after = (after != null) ? after : true;
  415. graph.model.beginUpdate();
  416. try
  417. {
  418. var edges = graph.getIncomingEdges(cell);
  419. var clones = graph.cloneCells([edges[0], cell]);
  420. graph.model.setTerminal(clones[0], graph.model.getTerminal(edges[0], true), true);
  421. var dir = getTreeDirection(cell);
  422. if (dir == mxConstants.DIRECTION_SOUTH || dir == mxConstants.DIRECTION_NORTH)
  423. {
  424. clones[1].geometry.x += (after) ? cell.geometry.width + spacing :
  425. -clones[1].geometry.width - spacing;
  426. }
  427. else
  428. {
  429. clones[1].geometry.y += (after) ? cell.geometry.height + spacing :
  430. -clones[1].geometry.height - spacing;
  431. }
  432. if (dir == mxConstants.DIRECTION_WEST)
  433. {
  434. clones[1].geometry.x = cell.geometry.x + cell.geometry.width - clones[1].geometry.width;
  435. }
  436. // Moves existing siblings
  437. var state = graph.view.getState(cell);
  438. var s = graph.view.scale;
  439. if (state != null)
  440. {
  441. var bbox = mxRectangle.fromRectangle(state);
  442. if (dir == mxConstants.DIRECTION_SOUTH ||
  443. dir == mxConstants.DIRECTION_NORTH)
  444. {
  445. bbox.x += ((after) ? cell.geometry.width + spacing :
  446. -clones[1].geometry.width - spacing) * s;
  447. }
  448. else
  449. {
  450. bbox.y += ((after) ? cell.geometry.height + spacing :
  451. -clones[1].geometry.height - spacing) * s;
  452. }
  453. var sib = graph.getOutgoingEdges(graph.model.getTerminal(edges[0], true));
  454. if (sib != null)
  455. {
  456. var hor = (dir == mxConstants.DIRECTION_SOUTH || dir == mxConstants.DIRECTION_NORTH);
  457. var dx = 0;
  458. var dy = 0;
  459. for (var i = 0; i < sib.length; i++)
  460. {
  461. var temp = graph.model.getTerminal(sib[i], false);
  462. if (dir == getTreeDirection(temp))
  463. {
  464. var sibling = graph.view.getState(temp);
  465. if (temp != cell && sibling != null)
  466. {
  467. if ((hor && after != sibling.getCenterX() < state.getCenterX()) ||
  468. (!hor && after != sibling.getCenterY() < state.getCenterY()))
  469. {
  470. if (mxUtils.intersects(bbox, sibling))
  471. {
  472. dx = spacing + Math.max(dx, (Math.min(bbox.x + bbox.width,
  473. sibling.x + sibling.width) - Math.max(bbox.x, sibling.x)) / s);
  474. dy = spacing + Math.max(dy, (Math.min(bbox.y + bbox.height,
  475. sibling.y + sibling.height) - Math.max(bbox.y, sibling.y)) / s);
  476. }
  477. }
  478. }
  479. }
  480. }
  481. if (hor)
  482. {
  483. dy = 0;
  484. }
  485. else
  486. {
  487. dx = 0;
  488. }
  489. for (var i = 0; i < sib.length; i++)
  490. {
  491. var temp = graph.model.getTerminal(sib[i], false);
  492. if (dir == getTreeDirection(temp))
  493. {
  494. var sibling = graph.view.getState(temp);
  495. if (temp != cell && sibling != null)
  496. {
  497. if ((hor && after != sibling.getCenterX() < state.getCenterX()) ||
  498. (!hor && after != sibling.getCenterY() < state.getCenterY()))
  499. {
  500. var subtree = [];
  501. graph.traverse(sibling.cell, true, function(vertex, edge)
  502. {
  503. if (edge != null)
  504. {
  505. subtree.push(edge);
  506. }
  507. subtree.push(vertex);
  508. return true;
  509. });
  510. graph.moveCells(subtree, ((after) ? 1 : -1) * dx, ((after) ? 1 : -1) * dy);
  511. }
  512. }
  513. }
  514. }
  515. }
  516. }
  517. return graph.addCells(clones);
  518. }
  519. finally
  520. {
  521. graph.model.endUpdate();
  522. }
  523. };
  524. function addParent(cell)
  525. {
  526. graph.model.beginUpdate();
  527. try
  528. {
  529. var dir = getTreeDirection(cell);
  530. var edges = graph.getIncomingEdges(cell);
  531. var clones = graph.cloneCells([edges[0], cell]);
  532. graph.model.setTerminal(edges[0], clones[1], false);
  533. graph.model.setTerminal(clones[0], clones[1], true);
  534. graph.model.setTerminal(clones[0], cell, false);
  535. // Makes space for new parent
  536. var subtree = [];
  537. graph.traverse(cell, true, function(vertex, edge)
  538. {
  539. if (edge != null)
  540. {
  541. subtree.push(edge);
  542. }
  543. subtree.push(vertex);
  544. return true;
  545. });
  546. var dx = cell.geometry.width + level;
  547. var dy = cell.geometry.height + level;
  548. if (dir == mxConstants.DIRECTION_SOUTH)
  549. {
  550. dx = 0;
  551. }
  552. else if (dir == mxConstants.DIRECTION_NORTH)
  553. {
  554. dx = 0;
  555. dy = -level;
  556. }
  557. else if (dir == mxConstants.DIRECTION_WEST)
  558. {
  559. dx = -level;
  560. dy = 0;
  561. }
  562. else if (dir == mxConstants.DIRECTION_EAST)
  563. {
  564. dy = 0;
  565. }
  566. graph.moveCells(subtree, dx, dy);
  567. return graph.addCells(clones);
  568. }
  569. finally
  570. {
  571. graph.model.endUpdate();
  572. }
  573. };
  574. function addChild(cell)
  575. {
  576. graph.model.beginUpdate();
  577. try
  578. {
  579. var edges = graph.getIncomingEdges(cell);
  580. var clones = graph.cloneCells([edges[0], cell]);
  581. graph.model.setTerminal(clones[0], cell, true);
  582. // Finds free space
  583. var edges = graph.getOutgoingEdges(cell);
  584. var targets = [];
  585. for (var i = 0; i < edges.length; i++)
  586. {
  587. var target = graph.model.getTerminal(edges[i], false);
  588. if (target != null)
  589. {
  590. targets.push(target);
  591. }
  592. }
  593. var bbox = graph.view.getBounds(targets);
  594. var dir = getTreeDirection(cell);
  595. var tr = graph.view.translate;
  596. var s = graph.view.scale;
  597. if (dir == mxConstants.DIRECTION_SOUTH)
  598. {
  599. clones[1].geometry.x = (bbox == null) ? cell.geometry.x + (cell.geometry.width -
  600. clones[1].geometry.width) / 2 : (bbox.x + bbox.width) / s - tr.x + spacing;
  601. clones[1].geometry.y += cell.geometry.height + level;
  602. }
  603. else if (dir == mxConstants.DIRECTION_NORTH)
  604. {
  605. clones[1].geometry.x = (bbox == null) ? cell.geometry.x + (cell.geometry.width -
  606. clones[1].geometry.width) / 2 : (bbox.x + bbox.width) / s - tr.x + spacing;
  607. clones[1].geometry.y -= clones[1].geometry.height + level;
  608. }
  609. else if (dir == mxConstants.DIRECTION_WEST)
  610. {
  611. clones[1].geometry.x -= clones[1].geometry.width + level;
  612. clones[1].geometry.y = (bbox == null) ? cell.geometry.y + (cell.geometry.height -
  613. clones[1].geometry.height) / 2 : (bbox.y + bbox.height) / s - tr.y + spacing;
  614. }
  615. else
  616. {
  617. clones[1].geometry.x += cell.geometry.width + level;
  618. clones[1].geometry.y = (bbox == null) ? cell.geometry.y + (cell.geometry.height -
  619. clones[1].geometry.height) / 2 : (bbox.y + bbox.height) / s - tr.y + spacing;
  620. }
  621. return graph.addCells(clones);
  622. }
  623. finally
  624. {
  625. graph.model.endUpdate();
  626. }
  627. };
  628. function getOrderedTargets(cell, horizontal, ref)
  629. {
  630. var sib = graph.getOutgoingEdges(cell);
  631. var state = graph.view.getState(ref);
  632. var targets = [];
  633. if (state != null && sib != null)
  634. {
  635. for (var i = 0; i < sib.length; i++)
  636. {
  637. var temp = graph.view.getState(graph.model.getTerminal(sib[i], false));
  638. if (temp != null && ((!horizontal && (Math.min(temp.x + temp.width,
  639. state.x + state.width) >= Math.max(temp.x, state.x))) ||
  640. (horizontal && (Math.min(temp.y + temp.height, state.y + state.height) >=
  641. Math.max(temp.y, state.y)))))
  642. {
  643. targets.push(temp);
  644. }
  645. }
  646. targets.sort(function(a, b)
  647. {
  648. return (horizontal) ? a.x + a.width - b.x - b.width : a.y + a.height - b.y - b.height;
  649. });
  650. }
  651. return targets;
  652. };
  653. function selectCell(cell, direction)
  654. {
  655. var dir = getTreeDirection(cell);
  656. var h1 = dir == mxConstants.DIRECTION_EAST || dir == mxConstants.DIRECTION_WEST;
  657. var h2 = direction == mxConstants.DIRECTION_EAST || direction == mxConstants.DIRECTION_WEST;
  658. if (h1 == h2 && dir != direction)
  659. {
  660. ui.actions.get('selectParent').funct();
  661. }
  662. else if (dir == direction)
  663. {
  664. var sib = graph.getOutgoingEdges(cell);
  665. if (sib != null && sib.length > 0)
  666. {
  667. graph.setSelectionCell(graph.model.getTerminal(sib[0], false));
  668. }
  669. }
  670. else
  671. {
  672. var edges = graph.getIncomingEdges(cell);
  673. if (edges != null && edges.length > 0)
  674. {
  675. var targets = getOrderedTargets(graph.model.getTerminal(edges[0], true), h2, cell);
  676. var state = graph.view.getState(cell);
  677. if (state != null)
  678. {
  679. var idx = mxUtils.indexOf(targets, state);
  680. if (idx >= 0)
  681. {
  682. idx += (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_WEST) ? -1 : 1;
  683. if (idx >= 0 && idx <= targets.length - 1)
  684. {
  685. graph.setSelectionCell(targets[idx].cell);
  686. }
  687. }
  688. }
  689. }
  690. }
  691. };
  692. // Overrides keyboard shortcuts
  693. var altShiftActions = {88: ui.actions.get('selectChildren'), // Alt+Shift+X
  694. 84: ui.actions.get('selectSubtree'), // Alt+Shift+T
  695. 80: ui.actions.get('selectParent'), // Alt+Shift+P
  696. 83: ui.actions.get('selectSiblings')} // Alt+Shift+S
  697. var editorUiOnKeyDown = ui.onKeyDown;
  698. ui.onKeyDown = function(evt)
  699. {
  700. try
  701. {
  702. if (graph.isEnabled() && !graph.isEditing() && graph.getSelectionCount() == 1 &&
  703. isTreeCell(graph.getSelectionCell()))
  704. {
  705. var cells = null;
  706. if (graph.getSelectionCell().getAttribute('treeRoot') != '1')
  707. {
  708. if (evt.which == 9) // Tab adds child
  709. {
  710. cells = (mxEvent.isShiftDown(evt)) ?
  711. addParent(graph.getSelectionCell()) :
  712. addChild(graph.getSelectionCell());
  713. }
  714. else if (evt.which == 13) // Enter adds sibling
  715. {
  716. cells = addSibling(graph.getSelectionCell(), !mxEvent.isShiftDown(evt));
  717. }
  718. }
  719. if (cells != null && cells.length > 0)
  720. {
  721. if (cells.length == 1 && graph.model.isEdge(cells[0]))
  722. {
  723. graph.setSelectionCell(graph.model.getTerminal(cells[0], false));
  724. }
  725. else
  726. {
  727. graph.setSelectionCell(cells[cells.length - 1]);
  728. }
  729. if (ui.hoverIcons != null)
  730. {
  731. ui.hoverIcons.update(graph.view.getState(graph.getSelectionCell()));
  732. }
  733. graph.startEditingAtCell(graph.getSelectionCell());
  734. mxEvent.consume(evt);
  735. }
  736. else
  737. {
  738. if (mxEvent.isAltDown(evt) && mxEvent.isShiftDown(evt))
  739. {
  740. var action = altShiftActions[evt.keyCode];
  741. if (action != null)
  742. {
  743. action.funct(evt);
  744. mxEvent.consume(evt);
  745. }
  746. }
  747. else
  748. {
  749. if (evt.keyCode == 37) // left
  750. {
  751. selectCell(graph.getSelectionCell(), mxConstants.DIRECTION_WEST);
  752. mxEvent.consume(evt);
  753. }
  754. else if (evt.keyCode == 38) // up
  755. {
  756. selectCell(graph.getSelectionCell(), mxConstants.DIRECTION_NORTH);
  757. mxEvent.consume(evt);
  758. }
  759. else if (evt.keyCode == 39) // right
  760. {
  761. selectCell(graph.getSelectionCell(), mxConstants.DIRECTION_EAST);
  762. mxEvent.consume(evt);
  763. }
  764. else if (evt.keyCode == 40) // down
  765. {
  766. selectCell(graph.getSelectionCell(), mxConstants.DIRECTION_SOUTH);
  767. mxEvent.consume(evt);
  768. }
  769. }
  770. }
  771. }
  772. }
  773. catch (e)
  774. {
  775. console.log('error', e);
  776. }
  777. if (!mxEvent.isConsumed(evt))
  778. {
  779. editorUiOnKeyDown.apply(this, arguments);
  780. }
  781. };
  782. var graphConnectVertex = graph.connectVertex;
  783. graph.connectVertex = function(source, direction, length, evt, forceClone, ignoreCellAt)
  784. {
  785. if (isTreeCell(source) && source.getAttribute('treeRoot') != '1')
  786. {
  787. var dir = getTreeDirection(source);
  788. var h1 = dir == mxConstants.DIRECTION_EAST || dir == mxConstants.DIRECTION_WEST;
  789. var h2 = direction == mxConstants.DIRECTION_EAST || direction == mxConstants.DIRECTION_WEST;
  790. if (dir == direction)
  791. {
  792. return addChild(source);
  793. }
  794. else if (h1 == h2)
  795. {
  796. return addParent(source);
  797. }
  798. else
  799. {
  800. return addSibling(source, direction != mxConstants.DIRECTION_NORTH &&
  801. direction != mxConstants.DIRECTION_WEST);
  802. }
  803. return [];
  804. }
  805. else
  806. {
  807. this.model.beginUpdate();
  808. try
  809. {
  810. var cells = graphConnectVertex.call(this, source, direction, length, evt, forceClone,
  811. ignoreCellAt || source.getAttribute('treeRoot') == '1');
  812. // Removes treeRoot flag in clones
  813. if (source.getAttribute('treeRoot') == '1')
  814. {
  815. for (var i = 0; i < cells.length; i++)
  816. {
  817. if (cells[i].getAttribute('treeRoot') == '1')
  818. {
  819. graph.setAttributeForCell(cells[i], 'treeRoot', null);
  820. }
  821. }
  822. }
  823. }
  824. finally
  825. {
  826. this.model.endUpdate();
  827. }
  828. return cells;
  829. }
  830. };
  831. var graphHandlerGetCells = graph.graphHandler.getCells;
  832. graph.graphHandler.getCells = function(initialCell)
  833. {
  834. var cells = graphHandlerGetCells.apply(this, arguments);
  835. var temp = cells.slice(0);
  836. // Removes all edges first
  837. for (var i = 0; i < temp.length; i++)
  838. {
  839. if (isTreeCell(temp[i]))
  840. {
  841. // Avoids disconnecting subtree by removing all incoming edges
  842. var edges = graph.getIncomingEdges(temp[i]);
  843. for (var j = 0; j < edges.length; j++)
  844. {
  845. mxUtils.remove(edges[j], cells);
  846. }
  847. }
  848. }
  849. for (var i = 0; i < temp.length; i++)
  850. {
  851. if (isTreeCell(temp[i]))
  852. {
  853. // Gets the subtree from cell downwards
  854. graph.traverse(temp[i], true, function(vertex, edge)
  855. {
  856. // TODO: Use dictionary to avoid duplicates
  857. if (edge != null && mxUtils.indexOf(cells, edge) < 0)
  858. {
  859. cells.push(edge);
  860. }
  861. if (mxUtils.indexOf(cells, vertex) < 0)
  862. {
  863. cells.push(vertex);
  864. }
  865. return true;
  866. });
  867. }
  868. }
  869. return cells;
  870. };
  871. // var ignoreMove = false;
  872. //
  873. // graph.addListener(mxEvent.MOVE_CELLS, function(sender, evt)
  874. // {
  875. // if (!ignoreMove)
  876. // {
  877. // var cells = evt.getProperty('cells');
  878. // var dx = evt.getProperty('dx');
  879. // var dy = evt.getProperty('dy');
  880. // ignoreMove = true;
  881. //
  882. // for (var i = 0; i < cells.length; i++)
  883. // {
  884. // var state = graph.view.getState(cells[i]);
  885. //
  886. // if (state != null && state.style['mindmapRoot'] == '1')
  887. // {
  888. // // TODO: Move subtree by same dx/dy
  889. // //layout.execute(model.getParent(state.cell), state.cell);
  890. //
  891. // // Gets the subtree from cell downwards
  892. // var tmp = [];
  893. // graph.traverse(cells[i], true, function(vertex)
  894. // {
  895. // tmp.push(vertex);
  896. //
  897. // return true;
  898. // });
  899. //
  900. // mxUtils.remove(cells[i], tmp);
  901. // graph.moveCells(tmp, dx, dy);
  902. // }
  903. // }
  904. //
  905. // ignoreMove = false;
  906. // }
  907. // });
  908. // Defines a new class for all icons
  909. function mxIconSet(state)
  910. {
  911. this.images = [];
  912. var graph = state.view.graph;
  913. // Icon1
  914. // var img = mxUtils.createImage('images/handle-connect.png');
  915. // img.setAttribute('title', 'Duplicate');
  916. // img.style.position = 'absolute';
  917. // img.style.cursor = 'pointer';
  918. // img.style.width = '26px';
  919. // img.style.height = '26px';
  920. // img.style.left = (state.x - 13) + 'px';
  921. // img.style.top = (state.getCenterY() - 13) + 'px';
  922. //
  923. // mxEvent.addGestureListeners(img,
  924. // mxUtils.bind(this, function(evt)
  925. // {
  926. // var s = graph.gridSize;
  927. // graph.setSelectionCells(graph.moveCells([state.cell], s, s, true));
  928. // mxEvent.consume(evt);
  929. // this.destroy();
  930. // })
  931. // );
  932. //
  933. // state.view.graph.container.appendChild(img);
  934. // this.images.push(img);
  935. // Delete
  936. var img = mxUtils.createImage('plugins/trees/handle-move.gif');
  937. img.setAttribute('title', 'Move Cell without Subtree');
  938. img.style.position = 'absolute';
  939. img.style.cursor = 'pointer';
  940. img.style.width = '26px';
  941. img.style.height = '26px';
  942. img.style.left = (state.getCenterX() - 13) + 'px';
  943. img.style.top = (state.getCenterY() - 13) + 'px';
  944. mxEvent.addGestureListeners(img, mxUtils.bind(this, function(evt)
  945. {
  946. graph.stopEditing(false);
  947. ui.hoverIcons.reset();
  948. if (!graph.isCellSelected(state.cell))
  949. {
  950. graph.setSelectionCell(state.cell);
  951. }
  952. graph.graphHandler.start(state.cell, mxEvent.getClientX(evt), mxEvent.getClientY(evt));
  953. graph.graphHandler.cells = [state.cell];
  954. graph.graphHandler.bounds = graph.graphHandler.graph.getView().getBounds(graph.graphHandler.cells);
  955. graph.graphHandler.pBounds = graph.graphHandler.getPreviewBounds(graph.graphHandler.cells);
  956. graph.graphHandler.cellWasClicked = true;
  957. graph.isMouseDown = true;
  958. graph.isMouseTrigger = mxEvent.isMouseEvent(evt);
  959. mxEvent.consume(evt);
  960. // Disables dragging the image
  961. mxEvent.consume(evt);
  962. this.destroy();
  963. }));
  964. // mxEvent.addListener(img, 'click',
  965. // mxUtils.bind(this, function(evt)
  966. // {
  967. // console.log('here', graph.graphHandler.dx);
  968. //
  969. // if (Math.abs(graph.graphHandler.currentDx) < graph.tolerance)
  970. // {
  971. // graph.setSelectionCell(state.cell);
  972. // }
  973. // })
  974. // );
  975. state.view.graph.container.appendChild(img);
  976. this.images.push(img);
  977. };
  978. mxIconSet.prototype.destroy = function()
  979. {
  980. if (this.images != null)
  981. {
  982. for (var i = 0; i < this.images.length; i++)
  983. {
  984. var img = this.images[i];
  985. img.parentNode.removeChild(img);
  986. }
  987. }
  988. this.images = null;
  989. };
  990. // Defines the tolerance before removing the icons
  991. var iconTolerance = 20;
  992. // Shows icons if the mouse is over a cell
  993. graph.addMouseListener(
  994. {
  995. currentState: null,
  996. currentIconSet: null,
  997. mouseDown: function(sender, me)
  998. {
  999. // Hides icons on mouse down
  1000. if (this.currentState != null)
  1001. {
  1002. this.dragLeave(me.getEvent(), this.currentState);
  1003. this.currentState = null;
  1004. }
  1005. // TODO: Fix single cell movement on touch devices
  1006. // if (mxEvent.isTouchEvent(me.getEvent()))
  1007. // {
  1008. // this.mouseMove(sender, me);
  1009. // }
  1010. },
  1011. mouseMove: function(sender, me)
  1012. {
  1013. if (this.currentState != null && (me.getState() == this.currentState ||
  1014. me.getState() == null))
  1015. {
  1016. var tol = iconTolerance;
  1017. var tmp = new mxRectangle(me.getGraphX() - tol,
  1018. me.getGraphY() - tol, 2 * tol, 2 * tol);
  1019. if (mxUtils.intersects(tmp, this.currentState))
  1020. {
  1021. return;
  1022. }
  1023. }
  1024. var tmp = me.getState();
  1025. // Ignores everything but vertices
  1026. if ((graph.isMouseDown && !mxEvent.isTouchEvent(me.getEvent())) ||
  1027. graph.isEditing() || (tmp != null &&
  1028. (!graph.getModel().isVertex(tmp.cell) || !isTreeCell(me.getCell()))))
  1029. {
  1030. tmp = null;
  1031. }
  1032. if (tmp != this.currentState)
  1033. {
  1034. if (this.currentState != null)
  1035. {
  1036. this.dragLeave(me.getEvent(), this.currentState);
  1037. }
  1038. this.currentState = tmp;
  1039. if (this.currentState != null)
  1040. {
  1041. this.dragEnter(me.getEvent(), this.currentState);
  1042. }
  1043. }
  1044. },
  1045. mouseUp: function(sender, me) { },
  1046. dragEnter: function(evt, state)
  1047. {
  1048. if (this.currentIconSet == null)
  1049. {
  1050. this.currentIconSet = new mxIconSet(state);
  1051. }
  1052. },
  1053. dragLeave: function(evt, state)
  1054. {
  1055. if (this.currentIconSet != null)
  1056. {
  1057. this.currentIconSet.destroy();
  1058. this.currentIconSet = null;
  1059. }
  1060. }
  1061. });
  1062. // Adds sidebar entries
  1063. var sb = ui.sidebar;
  1064. sb.addPalette('trees', 'Trees', true, function(content)
  1065. {
  1066. (function()
  1067. {
  1068. var cell = new mxCell('Central Idea', new mxGeometry(0, 20, 100, 40),
  1069. 'ellipse;whiteSpace=wrap;html=1;align=center;' +
  1070. 'collapsible=0;container=1;recursiveResize=0;');
  1071. graph.setAttributeForCell(cell, 'treeRoot', '1');
  1072. cell.vertex = true;
  1073. var cell2 = new mxCell('Branch', new mxGeometry(160, 0, 80, 20),
  1074. 'whiteSpace=wrap;html=1;shape=partialRectangle;top=0;left=0;bottom=1;right=0;points=[[0,1],[1,1]];' +
  1075. 'strokeColor=#000000;fillColor=none;align=center;verticalAlign=bottom;routingCenterY=0.5;' +
  1076. 'snapToPoint=1;collapsible=0;container=1;recursiveResize=0;autosize=1;');
  1077. cell2.vertex = true;
  1078. var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=entityRelationEdgeStyle;' +
  1079. 'startArrow=none;endArrow=none;segment=10;curved=1;');
  1080. edge.geometry.relative = true;
  1081. edge.edge = true;
  1082. cell.insertEdge(edge, true);
  1083. cell2.insertEdge(edge, false);
  1084. var cell3 = new mxCell('Sub Topic', new mxGeometry(160, 40, 72, 26),
  1085. 'whiteSpace=wrap;html=1;rounded=1;arcSize=50;align=center;verticalAlign=middle;' +
  1086. 'collapsible=0;container=1;recursiveResize=0;strokeWidth=1;autosize=1;spacing=4;');
  1087. cell3.vertex = true;
  1088. var edge2 = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=entityRelationEdgeStyle;' +
  1089. 'startArrow=none;endArrow=none;segment=10;curved=1;');
  1090. edge2.geometry.setTerminalPoint(new mxPoint(-40, 40), true);
  1091. edge2.geometry.relative = true;
  1092. edge2.edge = true;
  1093. cell.insertEdge(edge2, true);
  1094. cell3.insertEdge(edge2, false);
  1095. content.appendChild(sb.createVertexTemplateFromCells([edge, edge2, cell, cell2, cell3], 240, 66, 'Mindmap'));
  1096. })();
  1097. (function()
  1098. {
  1099. var cell = new mxCell('Central Idea', new mxGeometry(0, 0, 100, 40),
  1100. 'ellipse;whiteSpace=wrap;html=1;align=center;' +
  1101. 'collapsible=0;container=1;recursiveResize=0;');
  1102. graph.setAttributeForCell(cell, 'treeRoot', '1');
  1103. cell.vertex = true;
  1104. content.appendChild(sb.createVertexTemplateFromCells([cell], 100, 40, 'Central Idea'));
  1105. })();
  1106. (function()
  1107. {
  1108. var cell = new mxCell('Branch', new mxGeometry(0, 0, 80, 20),
  1109. 'whiteSpace=wrap;html=1;shape=partialRectangle;top=0;left=0;bottom=1;right=0;points=[[0,1],[1,1]];' +
  1110. 'strokeColor=#000000;fillColor=none;align=center;verticalAlign=bottom;routingCenterY=0.5;' +
  1111. 'snapToPoint=1;collapsible=0;container=1;recursiveResize=0;autosize=1;');
  1112. cell.vertex = true;
  1113. var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=entityRelationEdgeStyle;' +
  1114. 'startArrow=none;endArrow=none;segment=10;curved=1;');
  1115. edge.geometry.setTerminalPoint(new mxPoint(-40, 40), true);
  1116. edge.geometry.relative = true;
  1117. edge.edge = true;
  1118. cell.insertEdge(edge, false);
  1119. content.appendChild(sb.createVertexTemplateFromCells([edge, cell], 80, 20, 'Branch'));
  1120. })();
  1121. (function()
  1122. {
  1123. var cell = new mxCell('Sub Topic', new mxGeometry(0, 0, 72, 26),
  1124. 'whiteSpace=wrap;html=1;rounded=1;arcSize=50;align=center;verticalAlign=middle;' +
  1125. 'collapsible=0;container=1;recursiveResize=0;strokeWidth=1;autosize=1;spacing=4;');
  1126. cell.vertex = true;
  1127. var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=entityRelationEdgeStyle;' +
  1128. 'startArrow=none;endArrow=none;segment=10;curved=1;');
  1129. edge.geometry.setTerminalPoint(new mxPoint(-40, 40), true);
  1130. edge.geometry.relative = true;
  1131. edge.edge = true;
  1132. cell.insertEdge(edge, false);
  1133. content.appendChild(sb.createVertexTemplateFromCells([edge, cell], 72, 26, 'Sub Topic'));
  1134. })();
  1135. (function()
  1136. {
  1137. var cell = new mxCell('Organization', new mxGeometry(60, 0, 120, 60),
  1138. 'whiteSpace=wrap;html=1;align=center;' +
  1139. 'collapsible=0;container=1;recursiveResize=0;');
  1140. graph.setAttributeForCell(cell, 'treeRoot', '1');
  1141. cell.vertex = true;
  1142. var cell2 = new mxCell('Division', new mxGeometry(0, 100, 100, 60),
  1143. 'whiteSpace=wrap;html=1;align=center;verticalAlign=middle;' +
  1144. 'collapsible=0;container=1;recursiveResize=0;');
  1145. cell2.vertex = true;
  1146. var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=elbowEdgeStyle;elbow=vertical;' +
  1147. 'startArrow=none;endArrow=none;rounded=0;');
  1148. edge.geometry.relative = true;
  1149. edge.edge = true;
  1150. cell.insertEdge(edge, true);
  1151. cell2.insertEdge(edge, false);
  1152. var cell3 = new mxCell('Division', new mxGeometry(140, 100, 100, 60),
  1153. 'whiteSpace=wrap;html=1;align=center;verticalAlign=middle;' +
  1154. 'collapsible=0;container=1;recursiveResize=0;');
  1155. cell3.vertex = true;
  1156. var edge2 = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=elbowEdgeStyle;elbow=vertical;' +
  1157. 'startArrow=none;endArrow=none;rounded=0;');
  1158. edge2.geometry.relative = true;
  1159. edge2.edge = true;
  1160. cell.insertEdge(edge2, true);
  1161. cell3.insertEdge(edge2, false);
  1162. content.appendChild(sb.createVertexTemplateFromCells([edge, edge2, cell, cell2, cell3], 240, 160, 'Orgchart'));
  1163. })();
  1164. (function()
  1165. {
  1166. var cell = new mxCell('Tree Root', new mxGeometry(0, 0, 120, 60),
  1167. 'whiteSpace=wrap;html=1;align=center;' +
  1168. 'collapsible=0;container=1;recursiveResize=0;');
  1169. graph.setAttributeForCell(cell, 'treeRoot', '1');
  1170. cell.vertex = true;
  1171. content.appendChild(sb.createVertexTemplateFromCells([cell], 120, 60, 'Tree Root'));
  1172. })();
  1173. (function()
  1174. {
  1175. var cell = new mxCell('Sub Tree', new mxGeometry(0, 0, 100, 60),
  1176. 'whiteSpace=wrap;html=1;align=center;verticalAlign=middle;' +
  1177. 'collapsible=0;container=1;recursiveResize=0;');
  1178. cell.vertex = true;
  1179. var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=elbowEdgeStyle;elbow=vertical;' +
  1180. 'startArrow=none;endArrow=none;rounded=0;');
  1181. edge.geometry.setTerminalPoint(new mxPoint(0, -40), true);
  1182. edge.geometry.relative = true;
  1183. edge.edge = true;
  1184. cell.insertEdge(edge, false);
  1185. content.appendChild(sb.createVertexTemplateFromCells([edge, cell], 100, 60, 'Sub Tree'));
  1186. })();
  1187. (function()
  1188. {
  1189. var cell = new mxCell('Sub Section', new mxGeometry(0, 0, 100, 60),
  1190. 'whiteSpace=wrap;html=1;align=center;verticalAlign=middle;' +
  1191. 'collapsible=0;container=1;recursiveResize=0;');
  1192. cell.vertex = true;
  1193. var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;' +
  1194. 'startArrow=none;endArrow=none;rounded=0;targetPortConstraint=eastwest;sourcePortConstraint=northsouth;');
  1195. edge.geometry.setTerminalPoint(new mxPoint(110, -40), true);
  1196. edge.geometry.relative = true;
  1197. edge.edge = true;
  1198. cell.insertEdge(edge, false);
  1199. var cell2 = new mxCell('Sub Section', new mxGeometry(120, 0, 100, 60),
  1200. 'whiteSpace=wrap;html=1;align=center;verticalAlign=middle;' +
  1201. 'collapsible=0;container=1;recursiveResize=0;');
  1202. cell2.vertex = true;
  1203. var edge2 = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;' +
  1204. 'startArrow=none;endArrow=none;rounded=0;targetPortConstraint=eastwest;sourcePortConstraint=northsouth;');
  1205. edge2.geometry.setTerminalPoint(new mxPoint(110, -40), true);
  1206. edge2.geometry.relative = true;
  1207. edge2.edge = true;
  1208. cell2.insertEdge(edge2, false);
  1209. content.appendChild(sb.createVertexTemplateFromCells([edge, edge2, cell, cell2], 220, 60, 'Sub Sections'));
  1210. })();
  1211. });
  1212. // Collapses default sidebar entry and inserts this before
  1213. var c = ui.sidebar.container;
  1214. var general = c.getElementsByTagName('a')[0];
  1215. general.click();
  1216. c.insertBefore(c.lastChild.previousSibling, general);
  1217. c.insertBefore(c.lastChild, general);
  1218. });