1/** 2 * Explore plugin. 3 */ 4Draw.loadPlugin(function(ui) 5{ 6 // Adds resource for action 7 mxResources.parse('exploreFromHere=Explore from here...'); 8 9 // Max number of edges per page 10 var pageSize = 20; 11 12 var uiCreatePopupMenu = ui.menus.createPopupMenu; 13 ui.menus.createPopupMenu = function(menu, cell, evt) 14 { 15 uiCreatePopupMenu.apply(this, arguments); 16 17 var graph = ui.editor.graph; 18 19 if (graph.model.isVertex(graph.getSelectionCell())) 20 { 21 this.addMenuItems(menu, ['-', 'exploreFromHere'], null, evt); 22 } 23 }; 24 25 // 26 // Main function 27 // 28 function exploreFromHere(selectionCell) 29 { 30 var sourceGraph = ui.editor.graph; 31 32 var container = document.createElement('div'); 33 container.style.position = 'absolute'; 34 container.style.display = 'block'; 35 container.style.background = (Editor.isDarkMode()) ? 36 Editor.darkColor : '#ffffff'; 37 container.style.width = '100%'; 38 container.style.height = '100%'; 39 container.style.left = '0px'; 40 container.style.top = '0px'; 41 container.style.zIndex = 2; 42 43 var deleteImage = document.createElement('img'); 44 deleteImage.setAttribute('src', IMAGE_PATH + '/delete.png'); 45 deleteImage.style.position = 'absolute'; 46 deleteImage.style.cursor = 'pointer'; 47 deleteImage.style.right = '10px'; 48 deleteImage.style.top = '10px'; 49 container.appendChild(deleteImage); 50 51 var closeLabel = document.createElement('div'); 52 closeLabel.style.position = 'absolute'; 53 closeLabel.style.cursor = 'pointer'; 54 closeLabel.style.right = '38px'; 55 closeLabel.style.top = '14px'; 56 closeLabel.style.textAlign = 'right'; 57 closeLabel.style.verticalAlign = 'top'; 58 mxUtils.write(closeLabel, mxResources.get('close')); 59 container.appendChild(closeLabel); 60 document.body.appendChild(container); 61 62 var keyHandler = function(evt) 63 { 64 if (evt.keyCode == 27) 65 { 66 deleteImage.click(); 67 } 68 }; 69 70 mxEvent.addListener(document, 'keydown', keyHandler); 71 72 // Global variable to make sure each cell in a response has 73 // a unique ID throughout the complete life of the program, 74 // in a real-life setup each cell should have an external 75 // ID on the business object or else the cell ID should be 76 // globally unique for the lifetime of the graph model. 77 var requestId = 0; 78 79 function main(container) 80 { 81 // Checks if browser is supported 82 if (!mxClient.isBrowserSupported()) 83 { 84 // Displays an error message if the browser is 85 // not supported. 86 mxUtils.error('Browser is not supported!', 200, false); 87 } 88 else 89 { 90 // Creates the graph inside the given container 91 var graph = new Graph(container); 92 graph.keepEdgesInBackground = true; 93 graph.isCellResizable = function() 94 { 95 return false; 96 }; 97 // Workaround to hide custom handles 98 graph.isCellRotatable = function() 99 { 100 return false; 101 }; 102 103 // Shows hand cursor for all vertices 104 graph.getCursorForCell = function(cell) 105 { 106 if (this.model.isVertex(cell)) 107 { 108 return 'pointer'; 109 } 110 111 return null; 112 }; 113 114 graph.getFoldingImage = function() 115 { 116 return null; 117 }; 118 119 var closeHandler = function() 120 { 121 mxEvent.removeListener(document, 'keydown', keyHandler); 122 container.parentNode.removeChild(container); 123 124 // FIXME: Does not work 125 sourceGraph.scrollCellToVisible(selectionCell); 126 }; 127 128 mxEvent.addListener(deleteImage, 'click', closeHandler); 129 mxEvent.addListener(closeLabel, 'click', closeHandler); 130 131 // Disables all built-in interactions 132 graph.setEnabled(false); 133 134 // Handles clicks on cells 135 graph.click = function(me) 136 { 137 var evt = me.getEvent(); 138 var cell = me.getCell(); 139 140 if (cell != null && cell != graph.rootCell) 141 { 142 load(graph, cell); 143 } 144 }; 145 146 // Gets the default parent for inserting new cells. This 147 // is normally the first child of the root (ie. layer 0). 148 var parent = graph.getDefaultParent(); 149 150 var cx = graph.container.scrollWidth / 2; 151 var cy = graph.container.scrollHeight / 3; 152 153 graph.model.beginUpdate(); 154 var cell = graph.importCells([selectionCell])[0]; 155 cell.sourceCellId = selectionCell.id; 156 cell.geometry.x = cx - cell.geometry.width / 2; 157 cell.geometry.y = cy - cell.geometry.height / 2; 158 graph.model.endUpdate(); 159 160 // Animates the changes in the graph model 161 graph.getModel().addListener(mxEvent.CHANGE, function(sender, evt) 162 { 163 var changes = evt.getProperty('edit').changes; 164 var prev = mxText.prototype.enableBoundingBox; 165 mxText.prototype.enableBoundingBox = false; 166 graph.labelsVisible = false; 167 168 mxEffects.animateChanges(graph, changes, function() 169 { 170 mxText.prototype.prev = true; 171 graph.labelsVisible = true; 172 graph.refresh(); 173 graph.tooltipHandler.hide(); 174 }); 175 }); 176 177 load(graph, cell); 178 } 179 }; 180 181 // Loads the links for the given cell into the given graph 182 // by requesting the respective data in the server-side 183 // (implemented for this demo using the server-function) 184 function load(graph, cell) 185 { 186 if (graph.getModel().isVertex(cell)) 187 { 188 var cx = graph.container.scrollWidth / 2; 189 var cy = graph.container.scrollHeight / 3; 190 191 // Gets the default parent for inserting new cells. This 192 // is normally the first child of the root (ie. layer 0). 193 var parent = graph.getDefaultParent(); 194 graph.rootCell = cell.referenceCell || cell; 195 196 // Adds cells to the model in a single step 197 graph.getModel().beginUpdate(); 198 try 199 { 200 var cells = rootChanged(graph, cell); 201 202 // Removes all cells except the new root 203 for (var key in graph.getModel().cells) 204 { 205 var tmp = graph.getModel().getCell(key); 206 207 if (tmp != graph.rootCell && graph.getModel().isVertex(tmp)) 208 { 209 graph.removeCells([tmp]); 210 } 211 } 212 213 // Merges the response model with the client model 214 //graph.getModel().mergeChildren(model.getRoot().getChildAt(0), parent); 215 graph.addCells(cells); 216 217 // Moves the given cell to the center 218 var geo = graph.getModel().getGeometry(graph.rootCell); 219 220 if (geo != null) 221 { 222 geo = geo.clone(); 223 224 geo.x = cx - geo.width / 2; 225 geo.y = cy - geo.height / 3; 226 graph.getModel().setGeometry(graph.rootCell, geo); 227 } 228 229 // Creates a list of the new vertices, if there is more 230 // than the center vertex which might have existed 231 // previously, then this needs to be changed to analyze 232 // the target model before calling mergeChildren above 233 var vertices = []; 234 235 for (var key in graph.getModel().cells) 236 { 237 var tmp = graph.getModel().getCell(key); 238 239 if (tmp != graph.rootCell && graph.getModel().isVertex(tmp) && 240 graph.getModel().getParent(tmp) == graph.getDefaultParent()) 241 { 242 vertices.push(tmp); 243 244 // Changes the initial location "in-place" 245 // to get a nice animation effect from the 246 // center to the radius of the circle 247 var geo = graph.getModel().getGeometry(tmp); 248 249 if (geo != null) 250 { 251 geo.x = cx - geo.width / 2; 252 geo.y = cy - geo.height / 2; 253 } 254 } 255 } 256 257 // Arranges the response in a circle 258 var cellCount = vertices.length; 259 var phi = 2 * Math.PI / cellCount; 260 var r = Math.min(graph.container.scrollWidth / 3 - 80, 261 graph.container.scrollHeight / 3 - 80); 262 263 for (var i = 0; i < cellCount; i++) 264 { 265 var geo = graph.getModel().getGeometry(vertices[i]); 266 267 if (geo != null) 268 { 269 geo = geo.clone(); 270 geo.x += r * Math.sin(i * phi); 271 geo.y += r * Math.cos(i * phi); 272 273 graph.getModel().setGeometry(vertices[i], geo); 274 } 275 } 276 277 // Keeps parallel edges apart 278 var layout = new mxParallelEdgeLayout(graph); 279 layout.spacing = 60; 280 layout.execute(graph.getDefaultParent()); 281 } 282 finally 283 { 284 // Updates the display 285 graph.getModel().endUpdate(); 286 } 287 } 288 }; 289 290 // Gets the edges from the source cell and adds the targets 291 function rootChanged(graph, cell) 292 { 293 // TODO: Keep existing cells, probably best via XML to redirect IDs 294 var realCell = cell.referenceCell || cell; 295 var sourceCell = sourceGraph.model.getCell(realCell.sourceCellId); 296 var edges = sourceGraph.getEdges(sourceCell, null, true, true, false, true); 297 var cells = edges; 298 299 // Paging by selecting a window in the edges array 300 if (cell.startIndex != null || (pageSize > 0 && edges.length > pageSize)) 301 { 302 var start = cell.startIndex || 0; 303 304 cells = edges.slice(Math.max(0, start), Math.min(edges.length, start + pageSize)); 305 } 306 307 cells = cells.concat(sourceGraph.getOpposites(cells, sourceCell)); 308 var clones = graph.cloneCells(cells); 309 310 var edgeStyle = ';curved=1;noEdgeStyle=1;entryX=none;entryY=none;exitX=none;exitY=none;'; 311 var btnStyle = 'fillColor=green;fontColor=white;strokeColor=green;'; 312 313 for (var i = 0; i < cells.length; i++) 314 { 315 clones[i].sourceCellId = cells[i].id; 316 317 if (graph.model.isEdge(clones[i])) 318 { 319 // Removes waypoints, edge styles, constraints and centers the label 320 clones[i].geometry.x = 0; 321 clones[i].geometry.y = 0; 322 clones[i].geometry.points = null; 323 clones[i].setStyle(clones[i].getStyle() + edgeStyle); 324 clones[i].setTerminal(realCell, clones[i].getTerminal(true) == null); 325 } 326 } 327 328 if (cell.startIndex > 0) 329 { 330 var backCell = graph.createVertex(null, null, 'Back...', 0, 0, 80, 30, btnStyle); 331 backCell.referenceCell = realCell; 332 backCell.startIndex = Math.max(0, (cell.startIndex || 0) - pageSize); 333 clones.splice(0, 0, backCell); 334 } 335 336 if (edges.length > (cell.startIndex || 0) + pageSize) 337 { 338 var moreCell = graph.createVertex(null, null, 'More...', 0, 0, 80, 30, btnStyle); 339 moreCell.referenceCell = realCell; 340 moreCell.startIndex = (cell.startIndex || 0) + pageSize; 341 clones.splice(0, 0, moreCell); 342 } 343 344 return clones; 345 }; 346 347 main(container); 348 }; 349 350 // Adds action 351 ui.actions.addAction('exploreFromHere', function() 352 { 353 exploreFromHere(ui.editor.graph.getSelectionCell()); 354 }); 355 356 // Click handler for chromeless mode 357 if (ui.editor.isChromelessView()) 358 { 359 ui.editor.graph.click = function(me) 360 { 361 if (ui.editor.graph.model.isVertex(me.getCell()) && 362 ui.editor.graph.model.getEdgeCount(me.getCell()) > 0 && 363 this.getLinkForCell(me.getCell()) == null) 364 { 365 exploreFromHere(me.getCell()); 366 } 367 }; 368 } 369}); 370