1/** 2 * Explore plugin. 3 */ 4Draw.loadPlugin(function(editorUi) 5{ 6 // Adds resource for action 7 mxResources.parse('animation=Animation...'); 8 9 // Adds action 10 editorUi.actions.addAction('animation', function() 11 { 12 if (this.animationWindow == null) 13 { 14 // LATER: Check outline window for initial placement 15 this.animationWindow = new AnimationWindow(editorUi, (document.body.offsetWidth - 480) / 2, 16 120, 640, 480); 17 this.animationWindow.window.setVisible(true); 18 } 19 else 20 { 21 this.animationWindow.window.setVisible(!this.animationWindow.window.isVisible()); 22 } 23 }); 24 25 var menu = editorUi.menus.get('extras'); 26 var oldFunct = menu.funct; 27 28 menu.funct = function(menu, parent) 29 { 30 oldFunct.apply(this, arguments); 31 32 editorUi.menus.addMenuItems(menu, ['-', 'animation'], parent); 33 }; 34 35 function animateCells(graph, cells, steps, delay) 36 { 37 graph.executeAnimations(graph.createWipeAnimations(cells, true), null, steps, delay); 38 }; 39 40 function mapCell(cell, clone, mapping) 41 { 42 mapping = (mapping != null) ? mapping : new Object(); 43 mapping[cell.id] = clone; 44 45 var childCount = cell.getChildCount(); 46 47 for (var i = 0; i < childCount; i++) 48 { 49 mapCell(cell.getChildAt(i), clone.getChildAt(i), mapping); 50 } 51 52 return mapping; 53 }; 54 55 var allowedToRun = false; 56 var running = false; 57 58 function stop() 59 { 60 allowedToRun = false; 61 }; 62 63 function run(graph, steps, loop) 64 { 65 if (!running) 66 { 67 allowedToRun = true; 68 running = true; 69 70 graph.getModel().beginUpdate(); 71 try 72 { 73 for (var id in graph.getModel().cells) 74 { 75 var cell = graph.getModel().cells[id]; 76 77 if (graph.getModel().isVertex(cell) || graph.getModel().isEdge(cell)) 78 { 79 graph.setCellStyles('opacity', '0', [cell]); 80 graph.setCellStyles('noLabel', '1', [cell]); 81 } 82 } 83 } 84 finally 85 { 86 graph.getModel().endUpdate(); 87 } 88 89 var mapping = mapCell(editorUi.editor.graph.getModel().getRoot(), graph.getModel().getRoot()); 90 var step = 0; 91 92 function next() 93 { 94 if (allowedToRun && step < steps.length) 95 { 96 var tokens = steps[step].split(' '); 97 98 if (tokens.length > 0) 99 { 100 if (tokens[0] == 'wait' && tokens.length > 1) 101 { 102 window.setTimeout(function() 103 { 104 step++; 105 next(); 106 }, parseFloat(tokens[1])); 107 } 108 else 109 { 110 if (tokens.length > 1) 111 { 112 var cell = mapping[tokens[1]]; 113 114 if (cell != null) 115 { 116 if (tokens[0] == 'show') 117 { 118 graph.setCellStyles('opacity', '100', [cell]); 119 graph.setCellStyles('noLabel', null, [cell]); 120 121 if (tokens.length > 2 && tokens[2] == 'fade') 122 { 123 Graph.fadeNodes(graph.getNodesForCells([cell]), 0, 1); 124 } 125 else 126 { 127 animateCells(graph, [cell]); 128 } 129 } 130 else if (tokens[0] == 'flow') 131 { 132 if (graph.model.isEdge(cell)) 133 { 134 toggleFlowAnim(graph, [cell], tokens[2]); 135 } 136 } 137 else if (tokens[0] == 'hide') 138 { 139 graph.fadeNodes(graph.getNodesForCells([cell]), false); 140 } 141 } 142 else 143 { 144 console.log('cell not found', id, steps[step]); 145 } 146 } 147 148 step++; 149 next(); 150 } 151 } 152 } 153 else 154 { 155 running = false; 156 157 if (loop) 158 { 159 // Workaround for edge animation 160 graph.refresh(); 161 run(graph, steps, loop); 162 } 163 } 164 }; 165 166 next(); 167 } 168 }; 169 170 /** 171 * 172 */ 173 var AnimationWindow = function(editorUi, x, y, w, h) 174 { 175 var table = document.createElement('table'); 176 table.style.width = '100%'; 177 table.style.height = '100%'; 178 var tbody = document.createElement('tbody'); 179 var tr1 = document.createElement('tr'); 180 var td11 = document.createElement('td'); 181 td11.style.width = '140px'; 182 var td12 = document.createElement('td'); 183 var tr2 = document.createElement('tr'); 184 tr2.style.height = '40px'; 185 var td21 = document.createElement('td'); 186 td21.setAttribute('colspan', '2'); 187 188 var list = document.createElement('textarea'); 189 list.style.overflow = 'auto'; 190 list.style.width = '100%'; 191 list.style.height = '100%'; 192 td11.appendChild(list); 193 194 var root = editorUi.editor.graph.getModel().getRoot(); 195 196 if (root.value != null && typeof(root.value) == 'object') 197 { 198 list.value = root.value.getAttribute('animation'); 199 } 200 201 var container = document.createElement('div'); 202 container.style.border = '1px solid lightGray'; 203 container.style.background = '#ffffff'; 204 container.style.width = '100%'; 205 container.style.height = '100%'; 206 container.style.overflow = 'auto'; 207 208 mxEvent.disableContextMenu(container); 209 td12.appendChild(container); 210 211 var graph = new Graph(container); 212 graph.setEnabled(false); 213 graph.setPanning(true); 214 graph.foldingEnabled = false; 215 graph.panningHandler.ignoreCell = true; 216 graph.panningHandler.useLeftButtonForPanning = true; 217 graph.minFitScale = null; 218 graph.maxFitScale = null; 219 graph.centerZoom = true; 220 221 var buttons = { 222 'Fade In': 'show CELL fade', 223 'Wipe In': 'show CELL', 224 'Fade Out': 'hide CELL', 225 'Flow On': 'flow CELL start', 226 'Flow Off': 'flow CELL stop', 227 'Flow Toggle': 'flow CELL', 228 'Wait': '', // added by default 229 } 230 231 var bkeys = Object.keys(buttons); 232 233 for (var i = 0; i < bkeys.length; i++) 234 { 235 var wait = 'wait 1000\n'; 236 237 (function(key) 238 { 239 var btn = mxUtils.button(key, function() 240 { 241 // we have a cell object 242 var val = buttons[key] 243 244 if (val.indexOf('CELL') > -1) 245 { 246 var cells = editorUi.editor.graph.getSelectionCells(); 247 248 if (cells.length > 0) 249 { 250 for (var i = 0; i < cells.length; i++) 251 { 252 var tmp = val.replace('CELL', cells[i].id) 253 list.value += tmp + '\n' 254 } 255 256 list.value += wait 257 } 258 } 259 else 260 { 261 if (val) 262 { 263 list.value += val + '\n' 264 } 265 266 list.value += wait 267 } 268 269 }); 270 td21.appendChild(btn); 271 })(bkeys[i]); 272 } 273 274 var runBtn = mxUtils.button('Preview', function() 275 { 276 graph.getModel().clear(); 277 graph.getModel().setRoot(graph.cloneCells([editorUi.editor.graph.getModel().getRoot()])[0]); 278 graph.maxFitScale = 1; 279 graph.fit(8); 280 graph.center(); 281 282 run(graph, list.value.split('\n')); 283 }); 284 td21.appendChild(runBtn); 285 286 var stopBtn = mxUtils.button('Stop', function() 287 { 288 graph.getModel().clear(); 289 stop(); 290 }); 291 td21.appendChild(stopBtn); 292 293 var applyBtn = mxUtils.button('Apply', function() 294 { 295 editorUi.editor.graph.setAttributeForCell(root, 'animation', list.value); 296 }); 297 td21.appendChild(applyBtn); 298 299 tr1.appendChild(td11); 300 tr1.appendChild(td12); 301 tbody.appendChild(tr1); 302 tr2.appendChild(td21); 303 tbody.appendChild(tr2); 304 table.appendChild(tbody); 305 306 this.window = new mxWindow('Animation', table, x, y, w, h, true, true); 307 this.window.destroyOnClose = false; 308 this.window.setMaximizable(false); 309 this.window.setResizable(true); 310 this.window.setClosable(true); 311 this.window.setVisible(true); 312 }; 313 314 // Autostart in chromeless mode 315 if (editorUi.editor.isChromelessView()) 316 { 317 function startAnimation() 318 { 319 var root = editorUi.editor.graph.getModel().getRoot(); 320 var result = false; 321 322 if (root.value != null && typeof(root.value) == 'object') 323 { 324 var desc = root.value.getAttribute('animation'); 325 326 if (desc != null) 327 { 328 run(editorUi.editor.graph, desc.split('\n'), true); 329 result = true; 330 } 331 } 332 333 return result; 334 }; 335 336 // Wait for file to be loaded if no animation data is present 337 if (!startAnimation()) 338 { 339 editorUi.editor.addListener('fileLoaded', startAnimation); 340 } 341 } 342 343 // Add flow capability 344 function toggleFlowAnim(graph, cells, status) 345 { 346 if (!status) 347 { 348 status = 'toggle' 349 } 350 351 for (var i = 0; i < cells.length; i++) 352 { 353 if (editorUi.editor.graph.model.isEdge(cells[i])) 354 { 355 var state = graph.view.getState(cells[i]); 356 357 if (state && state.shape != null) 358 { 359 var paths = state.shape.node.getElementsByTagName('path'); 360 361 if (paths.length > 1) 362 { 363 if ((status == 'toggle' && paths[1].getAttribute('class') == 'mxEdgeFlow') || status == 'stop') 364 { 365 paths[1].removeAttribute('class'); 366 367 if (mxUtils.getValue(state.style, mxConstants.STYLE_DASHED, '0') != '1') 368 { 369 paths[1].removeAttribute('stroke-dasharray'); 370 } 371 } 372 else if ((status == 'toggle' && paths[1].getAttribute('class') != 'mxEdgeFlow') || status == 'start') 373 { 374 paths[1].setAttribute('class', 'mxEdgeFlow'); 375 376 if (mxUtils.getValue(state.style, mxConstants.STYLE_DASHED, '0') != '1') 377 { 378 paths[1].setAttribute('stroke-dasharray', '8'); 379 } 380 } 381 } 382 } 383 } 384 } 385 }; 386 387 function showCell(graph, cell) 388 { 389 graph.setCellStyles('opacity', '100', cell); 390 graph.setCellStyles('noLabel', null, [cell]); 391 nodes = graph.getNodesForCells([cell]); 392 if (nodes != null) 393 { 394 for (var i = 0; i < nodes.length; i++) 395 { 396 mxUtils.setPrefixedStyle(nodes[i].style, 'transition', null); 397 nodes[i].style.opacity = '0'; 398 } 399 } 400 } 401 402 try 403 { 404 var style = document.createElement('style') 405 style.type = 'text/css'; 406 style.innerHTML = ['.mxEdgeFlow {', 407 'animation: mxEdgeFlow 0.5s linear;', 408 'animation-iteration-count: infinite;', 409 '}', 410 '@keyframes mxEdgeFlow {', 411 'to {', 412 'stroke-dashoffset: -16;', 413 '}', 414 '}'].join('\n'); 415 document.getElementsByTagName('head')[0].appendChild(style); 416 } 417 catch (e) 418 { 419 // ignore 420 } 421});