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});