1/** 2 * Plugin for Freemind import. 3 * See https://github.com/jiangxin/freemind-mmx/tree/master/freemind 4 */ 5Draw.loadPlugin(function(ui) 6{ 7 var graph = ui.editor.graph; 8 9 // Adds resource for action 10 mxResources.parse('importFreemind=Freemind'); 11 12 // Parses Freemind data 13 function importFreemindData(data) 14 { 15 // Gets the default parent for inserting new cells. This 16 // is normally the first child of the root (ie. layer 0). 17 var defaultParent = graph.getDefaultParent(); 18 var cells = []; 19 20 var defaultWidth = 80; 21 var defaultHeight = 30; 22 var mainConceptHeight = 40; 23 var defaultHorizontalSpaceBetweenVertex = 40; 24 var defaultVerticalSpaceBetweenVertex = 10; 25 26 var freeMindMainConceptVertexStyle = 'ellipse;whiteSpace=wrap;html=1;align=center;collapsible=0;container=1;recursiveResize=0;'; 27 var freeMindBranchVertexStyle = 'whiteSpace=wrap;html=1;shape=partialRectangle;top=0;left=0;bottom=1;right=0;points=[[0,1],[1,1]];strokeColor=#000000;fillColor=none;align=center;verticalAlign=bottom;routingCenterY=0.5;snapToPoint=1;collapsible=0;container=1;recursiveResize=0;autosize=1;'; 28 var freeMindConceptVertexStyle = 'whiteSpace=wrap;html=1;rounded=1;arcSize=50;align=center;verticalAlign=middle;collapsible=0;container=1;recursiveResize=0;strokeWidth=1;autosize=1;spacing=4;'; 29 var freeMindEdgeStyle = 'edgeStyle=entityRelationEdgeStyle;startArrow=none;endArrow=none;segment=10;curved=1;html=1;'; 30 31 // Tells whether or not a node has child ideas 32 var hasChilds = function(node) 33 { 34 for (var i = 0; i < node.childNodes.length; i++) 35 { 36 if (node.childNodes[i].nodeName == 'node') 37 { 38 return true; 39 } 40 } 41 42 return false; 43 }; 44 45 // Generates useful info on the nodes to be used later. 46 var generatePreprocessingNodeInfo = function(node) 47 { 48 var childCount = 0; 49 var maxChildsInHierarchy = 0; 50 51 for (var i = 0; i < node.childNodes.length; i++) 52 { 53 var childNode = node.childNodes[i]; 54 55 if (childNode.nodeName == 'node') 56 { 57 var maxChilds = generatePreprocessingNodeInfo(childNode); 58 maxChildsInHierarchy = Math.max(maxChildsInHierarchy, maxChilds); 59 childCount++; 60 } 61 } 62 63 node.childCount = childCount; 64 node.maxChilds = Math.max(childCount, maxChildsInHierarchy); 65 return childCount; 66 } 67 68 // Main node generation funcion (recursive) 69 var processFreeMindNode = function(node, nodeParent, x, y) 70 { 71 var mainConcept = false; 72 var vertexStyle = freeMindBranchVertexStyle; 73 74 if (nodeParent == defaultParent) 75 { 76 mainConcept = true; 77 vertexStyle = freeMindMainConceptVertexStyle; 78 } 79 else if (hasChilds(node)) 80 { 81 // Concept, style appropiately 82 vertexStyle = freeMindConceptVertexStyle; 83 } 84 85 var nodeName = node.getAttribute('TEXT') || ''; 86 var nodeVertex = graph.insertVertex(defaultParent, null, nodeName, x, y, defaultWidth, 87 defaultHeight, vertexStyle); 88 graph.cellLabelChanged(nodeVertex, nodeName, true); 89 90 if (mainConcept) 91 { 92 nodeVertex.geometry.height = mainConceptHeight; // TODO: Maybe set height according to it's width, so it's rounded? 93 } 94 95 if (nodeParent != defaultParent) 96 { 97 // Don't generate an edge for the first node 98 graph.insertEdge(defaultParent, null, '', nodeParent, nodeVertex, freeMindEdgeStyle); 99 } 100 101 cells.push(nodeVertex); 102 // Insert child nodes, on correct positions 103 var childNumber = 0; 104 105 for (var i = 0; i < node.childNodes.length; i++) 106 { 107 var childNode = node.childNodes[i]; 108 109 if (childNode.nodeName == 'node') 110 { 111 var childX = x + nodeVertex.geometry.width + defaultHorizontalSpaceBetweenVertex; 112 var childY = y + (defaultHeight + defaultVerticalSpaceBetweenVertex) * childNumber; 113 childNumber += childNode.maxChilds == 0 ? 1 : childNode.maxChilds; 114 processFreeMindNode(childNode, nodeVertex, childX, childY); 115 } 116 } 117 } 118 119 // Makes the import one undoable edit 120 graph.getModel().beginUpdate(); 121 try 122 { 123 // Gets point for free space in the graph for insert 124 var pt = graph.getFreeInsertPoint(); 125 var freeMindDOM = mxUtils.parseXml(data); 126 var freeMindDOMchilds = freeMindDOM.children[0]; 127 128 // Transverse the childs, and generate relevant input 129 for (var i = 0; i < freeMindDOMchilds.childNodes.length; i++) 130 { 131 if (freeMindDOMchilds.childNodes[i].nodeName == 'node') 132 { 133 generatePreprocessingNodeInfo(freeMindDOMchilds.childNodes[i]); 134 } 135 } 136 137 // Generate the nodes 138 for (var i = 0; i < freeMindDOMchilds.childNodes.length; i++) 139 { 140 if (freeMindDOMchilds.childNodes[i].nodeName == 'node') 141 { 142 processFreeMindNode(freeMindDOMchilds.childNodes[i], defaultParent, pt.x, pt.y); 143 } 144 } 145 146 // Applies current styles to new cells (might not be needed) 147 graph.fireEvent(new mxEventObject('cellsInserted', 'cells', cells)); 148 } 149 finally 150 { 151 graph.getModel().endUpdate(); 152 } 153 154 // Selects new cells and scrolls into view 155 graph.setSelectionCells(cells); 156 graph.scrollCellToVisible(graph.getSelectionCell()); 157 }; 158 159 // Adds action 160 ui.actions.addAction('importFreemind...', function() 161 { 162 // Only modern browsers for now. We'll move the import 163 // code above to the main codebase later 164 if (Graph.fileSupport) 165 { 166 if (ui.impFMFileInputElt == null) 167 { 168 var input = document.createElement('input'); 169 input.setAttribute('type', 'file'); 170 171 mxEvent.addListener(input, 'change', function() 172 { 173 if (input.files != null) 174 { 175 // Only one file for now... 176 var reader = new FileReader(); 177 178 reader.onload = function(e) 179 { 180 importFreemindData(e.target.result); 181 }; 182 183 reader.readAsText(input.files[0]); 184 185 // Resets input to force change event for same file (type reset required for IE) 186 input.type = ''; 187 input.type = 'file'; 188 input.value = ''; 189 } 190 }); 191 192 input.style.display = 'none'; 193 document.body.appendChild(input); 194 ui.impFMFileInputElt = input; 195 } 196 197 ui.impFMFileInputElt.click(); 198 } 199 }); 200 201 // Adds menu 202 ui.menubar.addMenu('Import', function(menu, parent) 203 { 204 ui.menus.addMenuItem(menu, 'importFreemind'); 205 }); 206 207 // Moves import menu to before help menu 208 ui.menubar.container.insertBefore(ui.menubar.container.lastChild, 209 ui.menubar.container.lastChild.previousSibling.previousSibling.previousSibling); 210}); 211