MiroImporter.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. function MiroImporter()
  2. {
  3. var stencilsMap = {
  4. 'amazon-aws-cost-management-reserved-instance-reporting': 'shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.reserved_instance_reporting;gradientColor=#60A337;gradientDirection=north;fillColor=#277116;strokeColor=#ffffff;dashed=0;aspect=fixed;align=center;verticalAlign=top;labelPosition=center;verticalLabelPosition=bottom;',
  5. 'flowchart-process': 'shape=mxgraph.flowchart.process;',
  6. 'flowchart-decision': 'shape=mxgraph.flowchart.decision;',
  7. 'flowchart-data': 'shape=mxgraph.flowchart.data;',
  8. 'flowchart-predefined-process': 'shape=mxgraph.flowchart.predefined_process;',
  9. 'flowchart-internal-storage': 'shape=mxgraph.flowchart.internal_storage;',
  10. 'flowchart-document': 'shape=mxgraph.flowchart.document;',
  11. 'flowchart-preparation': 'shape=mxgraph.flowchart.preparation;',
  12. 'flowchart-manual-operation': 'shape=mxgraph.flowchart.manual_operation;',
  13. 'flowchart-multiple-documents': 'shape=mxgraph.flowchart.multi-document;',
  14. 'flowchart-terminator': 'shape=mxgraph.flowchart.terminator;',
  15. 'flowchart-manual-input': 'shape=mxgraph.flowchart.manual_input;',
  16. 'flowchart-database': 'shape=mxgraph.flowchart.database;',
  17. 'flowchart-hard-disk': 'shape=mxgraph.flowchart.direct_data;',
  18. 'flowchart-delay': 'shape=mxgraph.flowchart.delay;',
  19. 'flowchart-stored-data': 'shape=mxgraph.flowchart.stored_data;',
  20. 'flowchart-merge': 'shape=mxgraph.flowchart.merge_or_storage;',
  21. 'flowchart-connector': 'ellipse;',
  22. 'flowchart-or': 'shape=mxgraph.flowchart.or;',
  23. 'flowchart-summing-junction': 'shape=mxgraph.flowchart.summing_function;',
  24. 'flowchart-display': 'shape=mxgraph.flowchart.display;',
  25. 'flowchart-off-page-link': 'shape=offPageConnector;',
  26. //'flowchart-note-curly-right': 'shape=mxgraph.flowchart.note_curly_right;',
  27. //'flowchart-note-curly-left': 'shape=mxgraph.flowchart.note_curly_left;',
  28. 'flowchart-note-square': 'shape=mxgraph.flowchart.annotation_1;',
  29. };
  30. var typeStylesMap = {
  31. 'card': function (vertex, obj, tags, graph)
  32. {
  33. vertex.value = obj.title.title + obj.description.description;
  34. var styleMap = parseStyles(obj);
  35. styleMap['strokeColor'] = styleMap['fillColor'];
  36. styleMap['fillColor'] = '#ffffff';
  37. styleMap['strokeWidth'] = 10;
  38. vertex.style = buildStyleString('shape=partialRectangle;top=0;bottom=0;right=0;align=left;verticalAlign=top;spacing=20;fontSize=28;spacingLeft=40;', styleMap);
  39. },
  40. 'sticker': 'rect;shadow=1;strokeColor=none;',
  41. 'text': 'rect;rounded=1;arcSize=50;',
  42. 'stencil': function(vertex, obj, tags, graph)
  43. {
  44. var style = '';
  45. try
  46. {
  47. var texts = obj.schema.data.texts;
  48. if (texts)
  49. {
  50. vertex.value = texts.caption? texts.caption.text :
  51. (texts.name? texts.name.text : '');
  52. }
  53. style = stencilsMap[obj.schema.id];
  54. }
  55. catch (e)
  56. {
  57. console.log(e);
  58. }
  59. vertex.style = buildStyleString(style, parseStyles(obj));
  60. }
  61. };
  62. function colorNum2Hex(color)
  63. {
  64. return color == -1 ? 'none' :
  65. '#' + ('0' + (color >> 16).toString(16)).slice(-2) +
  66. ('0' + ((color >> 8) & 0xFF).toString(16)).slice(-2) +
  67. ('0' + (color & 0xFF).toString(16)).slice(-2);
  68. };
  69. fontNameMap = {
  70. 0: 'Arial',
  71. 2: 'Abril Fatface',
  72. 3: 'Bangers',
  73. 4: 'EB Garamond',
  74. 5: 'Georgia',
  75. 6: 'Graduate',
  76. 7: 'Gravitas One',
  77. 8: 'Fredoka One',
  78. 9: 'Nixie One',
  79. 10: 'OpenSans',
  80. 11: 'Permanent Marker',
  81. 12: 'PT Sans',
  82. 13: 'PT Sans Narrow',
  83. 14: 'PT Serif',
  84. 15: 'Rammetto One',
  85. 16: 'Roboto',
  86. 17: 'Roboto Condensed',
  87. 18: 'Roboto Slab',
  88. 19: 'Caveat',
  89. 20: 'Times New Roman',
  90. 21: 'Titan One',
  91. 22: 'Lemon Tuesday',
  92. 23: 'Roboto Mono',
  93. 24: 'Noto Sans',
  94. 25: 'IBM Plex Sans',
  95. 26: 'IBM Plex Serif',
  96. 27: 'IBM Plex Mono',
  97. 28: 'Spoof',
  98. 29: 'Tiempos Text',
  99. };
  100. var shapesMap = {
  101. 3: '',
  102. 4: 'ellipse;',
  103. 5: 'shape=mxgraph.basic.acute_triangle;dx=0.5;',
  104. 6: 'shape=callout;perimeter=calloutPerimeter;size=30;position=0.13;position2=0.13;rounded=1;',
  105. 7: ';rounded=1;',
  106. 8: 'shape=isoRectangle;',
  107. 10: 'shape=parallelogram;perimeter=parallelogramPerimeter;',
  108. 11: 'shape=mxgraph.basic.star;',
  109. 12: 'shape=singleArrow;arrowWidth=0.622;arrowSize=0.277;',
  110. 13: 'shape=singleArrow;arrowWidth=0.622;arrowSize=0.277;direction=west;',
  111. 16: 'shape=mxgraph.basic.pentagon;',
  112. 17: 'shape=mxgraph.basic.polygon;polyCoords=[[0.25,0],[0.75,0],[1,0.5],[0.75,1],[0.25,1],[0,0.5]];',
  113. 18: 'shape=mxgraph.basic.polygon;polyCoords=[[0.25,0],[0.75,0],[1,0.25],[1,0.75],[0.75,1],[0.25,1],[0,0.75],[0,0.25]];',
  114. 19: 'shape=trapezoid;perimeter=trapezoidPerimeter;fixedSize=1;',
  115. 20: '',
  116. 21: 'shape=doubleArrow;arrowWidth=0.622;arrowSize=0.277;',
  117. 22: 'ellipse;shape=cloud;',
  118. 23: function(vertex, obj, tags, graph)
  119. {
  120. var geo = vertex.geometry;
  121. var w = geo.width;
  122. geo.width = 20;
  123. geo.x += w - 20;
  124. vertex.style = buildStyleString('shape=curlyBracket;rounded=1;labelPosition=left;verticalLabelPosition=middle;align=right;verticalAlign=middle;', parseStyles(obj));
  125. vertex.style = vertex.style.replace('strokeColor=none;', '');
  126. },
  127. 24: function(vertex, obj, tags, graph)
  128. {
  129. vertex.geometry.width = 20;
  130. vertex.style = buildStyleString('shape=curlyBracket;rounded=1;flipH=1;labelPosition=right;verticalLabelPosition=middle;align=left;verticalAlign=middle;', parseStyles(obj));
  131. vertex.style = vertex.style.replace('strokeColor=none;', '');
  132. },
  133. 25: 'shape=cross;size=0.425;',
  134. 26: 'shape=cylinder3;boundedLbl=1;size=7;',
  135. };
  136. function getTypeStyle(type, obj)
  137. {
  138. var style = '';
  139. if (type == 'paint')
  140. {
  141. var drawPoints = obj.points, w = obj.size.width, h = obj.size.height;
  142. var drawShape = '<shape strokewidth="inherit"><foreground>';
  143. for (var i = 0; i < drawPoints.length; i++)
  144. {
  145. var p = drawPoints[i];
  146. drawShape += (i == 0? '<path><move' : '<line') + ' x="' + (p.x / w * 100).toFixed(2) +
  147. '" y="' + (p.y / h * 100).toFixed(2) + '"/>';
  148. }
  149. drawShape += '</path><stroke/></foreground></shape>';
  150. style = 'shape=stencil(' + Graph.compress(drawShape) + ');';
  151. }
  152. else if (type == 'shape')
  153. {
  154. style = shapesMap[obj.shape];
  155. }
  156. else
  157. {
  158. style = typeStylesMap[type];
  159. }
  160. if (typeof style == 'function')
  161. {
  162. return style;
  163. }
  164. return style;
  165. };
  166. var arrowsMap = {
  167. 0: ['none', 0],
  168. 1: ['classic'],
  169. 2: ['diamond', 0],
  170. 3: ['diamond', 1],
  171. 4: ['oval', 0],
  172. 5: ['oval', 1],
  173. 6: ['block', 0],
  174. 7: ['open'],
  175. 8: ['block', 1]
  176. };
  177. function addArrowStyle (style, val, end)
  178. {
  179. var es = arrowsMap[val];
  180. if (es)
  181. {
  182. style[end + 'Arrow'] = es[0];
  183. if (es[1] != null)
  184. {
  185. style[end + 'Fill'] = es[1];
  186. }
  187. }
  188. };
  189. function parseStyles(obj)
  190. {
  191. var style = {
  192. html: 1,
  193. whiteSpace: 'wrap'
  194. }, fontStyle = 0;
  195. try
  196. {
  197. var scale = obj.scale? (obj.scale.scale || 1) : 1;
  198. var styleMap = JSON.parse(obj.style);
  199. for (var key in styleMap)
  200. {
  201. var val = styleMap[key];
  202. if (val === null) continue;
  203. switch (key)
  204. {
  205. case 'sbc': //Fill Color
  206. case 'bc':
  207. style['fillColor'] = colorNum2Hex(val);
  208. break;
  209. case 'fs': //Font Size
  210. style['fontSize'] = (val || 48) * scale; //TODO support auto font size (0)
  211. break;
  212. case 'fsc': //Font Color?
  213. break;
  214. case 'fsa': //Font Style?
  215. break;
  216. case 'ffn': //Font Family
  217. val = fontNameMap[val];
  218. if (val)
  219. {
  220. style['fontFamily'] = val;
  221. }
  222. break;
  223. case 'ta': //Text Align
  224. style['align'] = (val == 't' ? 'left' :
  225. (val == 'r'? 'right' : 'center'));
  226. break;
  227. case 'tav': //Text Vertical Align
  228. style['verticalAlign'] = (val == 't' ? 'top' :
  229. (val == 'b'? 'bottom' : 'middle'));
  230. break;
  231. case 'taw': //Text Wrap?
  232. break;
  233. case 'tah': //Text Height?
  234. break;
  235. case 'lh': //Line Height?
  236. break;
  237. case 'bo': //Fill Opacity
  238. style['opacity'] = val * 100;
  239. break;
  240. case 'ss': //Stroke Size?
  241. break;
  242. case 'st': //Stroke Type?
  243. break;
  244. case 'brw': //Stroke width
  245. case 't': //Edge Thickness
  246. style['strokeWidth'] = val * scale;
  247. break;
  248. case 'brc': //Stroke Color
  249. case 'lc': //Edge Line Color
  250. style['strokeColor'] = colorNum2Hex(val);
  251. break;
  252. case 'bro': //Stroke Opacity
  253. style['strokeOpacity'] = val * 100;
  254. break;
  255. case 'brs': //Stroke Style
  256. case 'ls': //Edge Line Style
  257. //1 dashed, 0 dotted, 2 solid
  258. if (val != 2)
  259. {
  260. style['dashed'] = 1;
  261. if (val == 0)
  262. {
  263. style['dashPattern'] = '1 4';
  264. }
  265. }
  266. break;
  267. case 'b': //Bold
  268. if (val) fontStyle |= 1;
  269. break;
  270. case 'i': //Italic
  271. if (val) fontStyle |= 2;
  272. break;
  273. case 'u': //Underline
  274. if (val) fontStyle |= 4;
  275. break;
  276. case 's': //Stroke throw
  277. if (val) fontStyle |= 8;
  278. break;
  279. case 'hl': //Highlight color
  280. style['labelBackgroundColor'] = val;
  281. break;
  282. case 'tc': //Text Color
  283. style['fontColor'] = colorNum2Hex(val);
  284. break;
  285. case 'sc': //Shadow Color?
  286. break;
  287. //Edge styles
  288. case 'lt': //Line Type
  289. //(val == 0) simple
  290. if (val == 1)
  291. {
  292. style['edgeStyle'] = 'orthogonalEdgeStyle';
  293. style['elbow'] = 'vertical';
  294. }
  295. else if (val >= 2)
  296. {
  297. style['edgeStyle'] = 'orthogonalEdgeStyle';
  298. style['curved'] = 1;
  299. }
  300. break;
  301. case 'a_start': //Start Arrow
  302. addArrowStyle(style, val, 'start');
  303. break;
  304. case 'a_end': //End Arrow
  305. addArrowStyle(style, val, 'end');
  306. break;
  307. }
  308. }
  309. }
  310. catch (e)
  311. {
  312. console.error(e);
  313. }
  314. if (fontStyle)
  315. {
  316. style['fontStyle'] = fontStyle;
  317. }
  318. if (obj.rotation && obj.rotation.rotation)
  319. {
  320. style['rotation'] = obj.rotation.rotation;
  321. }
  322. return style;
  323. }
  324. function buildStyleString(curStyle, styleMap)
  325. {
  326. var style = curStyle || '';
  327. for (var key in styleMap)
  328. {
  329. if (style.indexOf(';' + key + '=') == -1)
  330. {
  331. style += key + '=' + styleMap[key] + ';';
  332. }
  333. }
  334. return style;
  335. };
  336. function importVertex(obj, type, tags, graph)
  337. {
  338. try
  339. {
  340. var style = getTypeStyle(type, obj) || '';
  341. var scale = obj.scale.scale, //TODO Check this is the correct use of the scale
  342. w = obj.size.width, h = obj.size.height,
  343. x = obj.position.x, y = obj.position.y;
  344. var vertex = new mxCell(obj.text, new mxGeometry((x - w / 2 * scale), (y - h / 2 * scale),
  345. w * scale, h * scale), '');
  346. if (typeof style == 'function')
  347. {
  348. style(vertex, obj, tags, graph);
  349. }
  350. else
  351. {
  352. vertex.style = buildStyleString(style, parseStyles(obj));
  353. }
  354. vertex.setVertex(true);
  355. graph.addCell(vertex);
  356. return vertex;
  357. }
  358. catch (e)
  359. {
  360. console.error(e);
  361. }
  362. };
  363. function importEdge(obj, vertexes, tags, graph)
  364. {
  365. try
  366. {
  367. var e = new mxCell('', new mxGeometry(0, 0, 100, 100), '');
  368. e.geometry.relative = true;
  369. e.edge = true;
  370. var edgeStyle = 'orthogonalLoop=1;jettySize=auto;';
  371. var source = vertexes[obj.primary.widgetIndex]; //-1 would be null
  372. var target = vertexes[obj.secondary.widgetIndex]; //-1 would be null
  373. var pp = obj.primary.point, sp = obj.secondary.point;
  374. var sourcePoint = pp? new mxPoint(pp.x, pp.y) : null,
  375. targetPoint = sp? new mxPoint(sp.x, sp.y) : null;
  376. if (sourcePoint)
  377. {
  378. if (source)
  379. {
  380. edgeStyle += 'exitX=' + sourcePoint.x + ';exitY=' + sourcePoint.y + ';';
  381. }
  382. else
  383. {
  384. e.geometry.sourcePoint = sourcePoint;
  385. }
  386. }
  387. if (targetPoint)
  388. {
  389. if (target)
  390. {
  391. edgeStyle += 'entryX=' + targetPoint.x + ';entryY=' + targetPoint.y + ';';
  392. }
  393. else
  394. {
  395. e.geometry.targetPoint = targetPoint;
  396. }
  397. }
  398. //Labels
  399. var lbls = obj.line.captions;
  400. if (lbls.length > 0)
  401. {
  402. //Add basic support for multiple labels by joining them together
  403. var lbl = '';
  404. for (var i = 0; i < lbls.length; i++)
  405. {
  406. lbl += lbls[i].text;
  407. }
  408. e.value = lbl;
  409. e.geometry.offset = new mxPoint(lbls[0].position.x, lbls[0].position.y);
  410. edgeStyle += 'labelBackgroundColor=none;fontSize=' + lbls[0].fontSize
  411. + ';fontColor=' + colorNum2Hex(lbls[0].color) + ';';
  412. }
  413. e.style = buildStyleString(edgeStyle, parseStyles(obj));
  414. graph.addCell(e, null, null, source, target);
  415. }
  416. catch (e)
  417. {
  418. console.error(e);
  419. }
  420. }
  421. function createGraph()
  422. {
  423. var graph = new Graph();
  424. graph.setExtendParents(false);
  425. graph.setExtendParentsOnAdd(false);
  426. graph.setConstrainChildren(false);
  427. graph.setHtmlLabels(true);
  428. graph.getModel().maintainEdgeParent = false;
  429. return graph;
  430. };
  431. this.importMiroJson = function(data)
  432. {
  433. try
  434. {
  435. var graph = createGraph();
  436. //Currently this handles version 2
  437. if (data.version != 2)
  438. {
  439. throw new Error('Unsupported Version');
  440. }
  441. var objects = data.data.objects;
  442. var edges = [], vertexes = {}, tags = [];
  443. for (var i = 0; i < objects.length; i++)
  444. {
  445. var o = objects[i];
  446. if (o.type == 13) //Tags (assuming they are always at the top)
  447. {
  448. tags.push({color: colorNum2Hex(o.color), text: o.text});
  449. }
  450. else if (o.type == 14)
  451. {
  452. var obj = o.widgetData;
  453. var type = obj.type;
  454. obj = obj.json;
  455. if (type == 'line')
  456. {
  457. edges.push(obj);
  458. }
  459. else
  460. {
  461. vertexes[i] = importVertex(obj, type, tags, graph);
  462. }
  463. }
  464. }
  465. for (var i = 0; i < edges.length; i++)
  466. {
  467. importEdge(edges[i], vertexes, tags, graph);
  468. }
  469. var node = new mxCodec().encode(graph.getModel());
  470. return mxUtils.getXml(node);
  471. }
  472. catch (e)
  473. {
  474. console.error(e);
  475. }
  476. }
  477. }