1/**
2 * Update plugin. Use updateUrl and updateInterval (optional, default is 60000ms)
3 * in the meta data of the diagram to configure the plugin. (Alternatively, the
4 * update-url and update-interval URL parameters may be used instead.)
5 *
6 * It will send the XML of the current page to the given URL as a POST request
7 * (with a parameter called xml) and allows for the following type of XML response
8 * (with CORS headers):
9 *
10 * <updates>
11 * <update ...>
12 * <model>...</model>
13 * <view ...>
14 * <fit ...>
15 * </updates>
16 *
17 * The outermost updates node may contain an optional url and interval property
18 * to change the current updateUrl and updateInterval.
19 *
20 * Where update must contain an id attribute to reference the cell in the diagram.
21 *
22 * - An optional value attribute that contains XML markup is used as the value for
23 * the cell, with label and tooltip for the label and tooltip, respectively.
24 * Additionally, placeholders="1" can be used to enable placeholders in the label
25 * or tooltip of the cell.
26 *
27 * Example: <object label="Hello, %var1%!" var1="World" tooltip=
28 * 		"Click <a href=\"https://www.draw.io\">here</a>" placeholders="1">
29 *
30 * - An optional replace-value attribute that contains 1 can be specified to
31 * replace the value of the cell. Default is to add the attributes of the XML
32 * value specified above to the existing value of the cell. (Attributes with
33 * an empty string value are removed.)
34 *
35 * - An optional style attribute that contains the cell style is used to replace
36 * the existing cell style.
37 *
38 * Example: fillColor=red;gradientColor=white;"
39 *
40 * - An optional icon attribute that contains JSON is used to add an icon to the
41 * given cell. The object value that the icon attribute is parsed and may contain
42 * a tooltip (string), align ("left"|"center"|"right", default is "right"), valign
43 * (top|middle|bottom, default is bottom) and append (true|false, default is false)
44 * for adding or replacing existing icons. The image attribute is an object value
45 * with src, width and height for defining the icon to be displayed (default is
46 * mxGraph.warningImage). An empty string for the attribute removes all icons.
47 *
48 * Example: JSON.stringify({tooltip: 'Locked', append: true, image:
49 * 		{src: IMAGE_PATH + '/locked.png', width: 26, height:26}}
50 *
51 * - An optional geometry attribute that contains a JSON mxGeometry object can be used
52 * to replace the current geometry of the refenced cell. In addition to the existing
53 * field names in mxGeometry, dx and dy can be used to define a vector for moving the
54 * shape, and dh and dw can be used to resize the cell.
55 *
56 * Example: JSON.stringify({dx: (Math.random() * 100) - 50, dh: (Math.random() * 100) - 50}))
57 *
58 * - Additionally a model node may be specified to set the current graph model.
59 *
60 * Example: <model><mxGraphModel><root><mxCell id="0"/></mxCell>...</root></mxGraphModel></model>
61 *
62 * - A view node may be specified with a scale, dx and dy attribute to change the current
63 * scale and translate.
64 *
65 * Example: <view scale="0.5" dx="100" dy="100"/>
66 *
67 * - A fit node may be specified with a max-scale property to fit the diagram to the
68 * available viewport with the specified max-scale.
69 */
70Draw.loadPlugin(function(editorUi)
71{
72	if (editorUi.editor.isChromelessView())
73	{
74		var graph = editorUi.editor.graph;
75		var updateInterval = parseInt(urlParams['update-interval'] || 60000);
76		var updateUrlParam = urlParams['update-url'];
77		var updateUrl = null;
78
79		if (updateUrlParam != null)
80		{
81			updateUrl = decodeURIComponent(updateUrlParam);
82
83			// Creates empty file if update URL is in URL parameter
84			if (editorUi.createFile != null && editorUi.getCurrentFile() == null)
85			{
86				editorUi.createFile(editorUi.defaultFilename, null, null, null, null, null, null, true);
87			}
88		}
89
90		function createOverlay(desc)
91		{
92			var overlay = new mxCellOverlay(desc.image || graph.warningImage,
93				desc.tooltip, desc.align, desc.valign, desc.offset);
94
95			// Installs a handler for clicks on the overlay
96			overlay.addListener(mxEvent.CLICK, function(sender, evt)
97			{
98				editorUi.alert(desc.tooltip);
99			});
100
101			return overlay;
102		};
103
104		function parseResponse(xml)
105		{
106			var doc = editorUi.updateDiagram(xml);
107			var node = (doc != null) ? doc.documentElement : null;
108
109			if (node != null && node.nodeName == 'updates')
110			{
111				if (node.hasAttribute('url'))
112				{
113					updateUrl = node.getAttribute('url');
114				}
115
116				if (node.hasAttribute('interval'))
117				{
118					updateInterval = node.getAttribute('interval');
119				}
120			}
121		};
122
123		var currentThread = null;
124
125		function scheduleUpdates()
126		{
127			var page = editorUi.currentPage;
128			var root = editorUi.editor.graph.getModel().getRoot();
129			var result = false;
130
131			if (urlParams['update-url'] || (root.value != null && typeof(root.value) == 'object'))
132			{
133				if (root.value != null && typeof(root.value) == 'object')
134				{
135					updateInterval = parseInt(root.value.getAttribute('updateInterval') || updateInterval);
136					updateUrl = root.value.getAttribute('updateUrl') || updateUrl;
137				}
138
139				if (updateUrl != null)
140				{
141					var currentXml = mxUtils.getXml(editorUi.editor.getGraphXml());
142
143					function doUpdate()
144					{
145						if (updateUrl === 'demo')
146						{
147							parseResponse(mxUtils.getXml(createDemoResponse().documentElement));
148							schedule();
149						}
150						else
151						{
152							mxUtils.post(updateUrl, 'xml=' + encodeURIComponent(currentXml), function(req)
153							{
154								if (page === editorUi.currentPage)
155								{
156									if (req.getStatus() >= 200 && req.getStatus() <= 300)
157									{
158										parseResponse(mxUtils.getXml(req.getDocumentElement()));
159										schedule();
160									}
161									else
162									{
163										editorUi.handleError({message: mxResources.get('error') + ' ' +
164											req.getStatus()});
165									}
166								}
167							}, function(err)
168							{
169								editorUi.handleError(err);
170							});
171						}
172					};
173
174					function schedule()
175					{
176						currentThread = window.setTimeout(doUpdate, updateInterval);
177					};
178
179					doUpdate();
180					result = true;
181				}
182			}
183
184			return result;
185		};
186
187		function startUpdates()
188		{
189			var result = scheduleUpdates();
190
191			if (result)
192			{
193				editorUi.editor.addListener('pageSelected', function()
194				{
195					window.clearTimeout(currentThread);
196					scheduleUpdates();
197				});
198			}
199
200			return result;
201		};
202
203		function createDemoResponse()
204		{
205			var doc = mxUtils.createXmlDocument();
206			var status = doc.createElement('updates');
207
208			for (var id in graph.model.cells)
209			{
210				var cell = graph.model.cells[id];
211
212				if (graph.model.isEdge(cell))
213				{
214					// Ignores short edges
215					var state = graph.view.getState(cell);
216
217					if (Math.random() > 0.5 && state != null && state.length > 50)
218					{
219						var update = doc.createElement('update');
220						update.setAttribute('id', cell.id);
221						update.setAttribute('value', '<object label="%load% minutes" load="' +
222							Math.round(Math.random() * 100) + '" placeholders="1">');
223						update.setAttribute('style', cell.style + ';strokeColor=red;strokeWidth=' +
224							Math.round(Math.random() * 5) + ';');
225						status.appendChild(update);
226					}
227					else
228					{
229						var update = doc.createElement('update');
230						update.setAttribute('id', cell.id);
231						update.setAttribute('value', '<object label="" load="' +
232							Math.round(Math.random() * 100) + '" placeholders="1">');
233						update.setAttribute('style', cell.style + ';strokeColor=black;strokeWidth=;');
234						status.appendChild(update);
235					}
236				}
237				else if (graph.model.isVertex(cell))
238				{
239					// For the purpose of the demo we flag stuff to update with update="1".
240					// This is not needed for the general case.
241					if (cell.value != null && typeof(cell.value) == 'object' &&
242						cell.value.getAttribute('update') == '1')
243					{
244						// Restores original style in demo roundtrip
245						if (cell.prevStyle == null)
246						{
247							cell.prevStyle = cell.style;
248						}
249
250						if (Math.random() > 0.5)
251						{
252							var update = doc.createElement('update');
253							update.setAttribute('id', cell.id);
254							update.setAttribute('value', '<object tooltip="%load%% Done" load="' +
255								Math.round(Math.random() * 100) + '" placeholders="1">');
256							update.setAttribute('style', cell.prevStyle + ';fillColor=red;gradientColor=white;');
257							update.setAttribute('icon', JSON.stringify({tooltip: 'Alert', align: 'right',
258								valign: 'top', image: {src: 'https://www.draw.io/mxgraph/images/warning.gif', width: 26, height: 26}}));
259//							update.setAttribute('geometry', JSON.stringify({dx: (Math.random() * 100) - 50,
260//								y: cell.geometry.y + (Math.random() * 100) - 50, dh: (Math.random() * 100) - 50}));
261							status.appendChild(update);
262
263							// Adds another icon
264							if (Math.random() > 0.5)
265							{
266								var update = doc.createElement('update');
267								update.setAttribute('id', cell.id);
268								update.setAttribute('icon', JSON.stringify({tooltip: 'Busy', append: true,
269									image: {src: IMAGE_PATH + '/spin.gif', width: 26, height:26}}));
270								status.appendChild(update);
271							}
272						}
273						else
274						{
275							var update = doc.createElement('update');
276							update.setAttribute('id', cell.id);
277							update.setAttribute('style', cell.prevStyle + ';fillColor=#d4e1f5;gradientColor=white;');
278							update.setAttribute('value',
279								'<object tooltip="Click <a href=\"https://www.draw.io\">here</a>">');
280							update.setAttribute('icon', '');
281							status.appendChild(update);
282						}
283					}
284				}
285			}
286
287//			var modelNode = mxUtils.parseXml('<model><mxGraphModel>  <root>    <mxCell id="0"/>    <mxCell id="1" parent="0"/>    <mxCell id="12" value="Program" style="rounded=0;shadow=0;strokeWidth=1;fontSize=12;fillColor=#F0F0F0;" vertex="1" parent="1">      <mxGeometry x="274" y="227" width="100" height="40" as="geometry"/>    </mxCell>    <mxCell id="13" value="PDF&#xa;Outline" style="ellipse;rounded=0;shadow=0;strokeWidth=1;fillColor=none;fontSize=12;" vertex="1" parent="1">      <mxGeometry x="80" y="247" width="90" height="40" as="geometry"/>    </mxCell>    <mxCell id="14" style="rounded=0;html=0;shadow=0;startArrow=none;endArrow=none;endFill=0;endSize=10;strokeColor=#000000;strokeWidth=1;fontSize=12;startFill=0;" edge="1" source="13" target="12" parent="1">      <mxGeometry relative="1" as="geometry"/>    </mxCell>    <mxCell id="15" value="HTML&#xa;Outline" style="ellipse;rounded=0;shadow=0;strokeWidth=1;fillColor=none;fontSize=12;" vertex="1" parent="1">      <mxGeometry x="118" y="140" width="90" height="40" as="geometry"/>    </mxCell>    <mxCell id="16" style="rounded=0;html=0;shadow=0;startArrow=none;endArrow=none;endFill=0;endSize=10;strokeColor=#000000;strokeWidth=1;fontSize=12;startFill=0;" edge="1" source="15" target="12" parent="1">      <mxGeometry relative="1" as="geometry">        <mxPoint x="267" y="158.2814070351758" as="targetPoint"/>      </mxGeometry>    </mxCell>    <mxCell id="17" style="rounded=0;html=0;shadow=0;startArrow=none;endArrow=none;endFill=0;endSize=10;strokeColor=#000000;strokeWidth=1;fontSize=12;startFill=0;" edge="1" source="18" target="12" parent="1">      <mxGeometry relative="1" as="geometry">        <mxPoint x="413.7317073170732" y="171" as="targetPoint"/>      </mxGeometry>    </mxCell>    <mxCell id="18" value="Name" style="ellipse;rounded=0;shadow=0;strokeWidth=1;fillColor=none;fontSize=12;" vertex="1" parent="1">      <mxGeometry x="274" y="100" width="90" height="40" as="geometry"/>    </mxCell>    <mxCell id="19" style="rounded=0;html=0;shadow=0;startArrow=none;endArrow=none;endFill=0;endSize=10;strokeColor=#000000;strokeWidth=1;fontSize=12;startFill=0;" edge="1" source="20" target="12" parent="1">      <mxGeometry relative="1" as="geometry">        <mxPoint x="464.5244755244755" y="227" as="targetPoint"/>      </mxGeometry>    </mxCell>    <mxCell id="20" value="Description" style="ellipse;rounded=0;shadow=0;strokeWidth=1;fillColor=none;fontSize=12;" vertex="1" parent="1">      <mxGeometry x="437" y="124" width="90" height="40" as="geometry"/>    </mxCell>    <mxCell id="21" style="rounded=0;html=0;shadow=0;startArrow=none;endArrow=none;endFill=0;endSize=10;strokeColor=#000000;strokeWidth=1;fontSize=12;startFill=0;" edge="1" source="22" target="12" parent="1">      <mxGeometry relative="1" as="geometry">        <mxPoint x="436.80419580419584" y="319" as="targetPoint"/>      </mxGeometry>    </mxCell>    <mxCell id="22" value="Admission&#xa;Deadline" style="ellipse;rounded=0;shadow=0;strokeWidth=1;fillColor=none;fontSize=12;" vertex="1" parent="1">      <mxGeometry x="495" y="216" width="90" height="40" as="geometry"/>    </mxCell>    <mxCell id="23" value="courses" style="rhombus;whiteSpace=wrap;html=1;rounded=0;shadow=0;strokeWidth=1;fillColor=none;fontSize=12;" vertex="1" parent="1">      <mxGeometry x="284" y="349" width="80" height="50" as="geometry"/>    </mxCell>    <mxCell id="24" style="rounded=0;html=0;shadow=0;startArrow=none;endArrow=none;endFill=0;endSize=10;strokeColor=#000000;strokeWidth=1;fontSize=12;startFill=0;" edge="1" source="23" target="12" parent="1">      <mxGeometry relative="1" as="geometry">        <mxPoint x="495.26224188188667" y="238.15603653581252" as="sourcePoint"/>        <mxPoint x="374" y="244.4537037037037" as="targetPoint"/>      </mxGeometry>    </mxCell>    <mxCell id="25" value="Course" style="rounded=0;shadow=0;strokeWidth=1;fontSize=12;fillColor=#F0F0F0;" vertex="1" parent="1">      <mxGeometry x="274" y="458" width="100" height="40" as="geometry"/>    </mxCell>    <mxCell id="26" style="rounded=0;html=0;shadow=0;startArrow=none;endArrow=none;endFill=0;endSize=10;strokeColor=#000000;strokeWidth=1;fontSize=12;startFill=0;" edge="1" source="23" target="25" parent="1">      <mxGeometry relative="1" as="geometry">        <mxPoint x="324" y="349" as="sourcePoint"/>        <mxPoint x="324" y="267" as="targetPoint"/>      </mxGeometry>    </mxCell>    <mxCell id="27" value="Course&#xa;Number" style="ellipse;rounded=0;shadow=0;strokeWidth=1;fillColor=none;fontSize=12;" vertex="1" parent="1">      <mxGeometry x="80" y="418" width="90" height="40" as="geometry"/>    </mxCell>    <mxCell id="28" style="rounded=0;html=0;shadow=0;startArrow=none;endArrow=none;endFill=0;endSize=10;strokeColor=#000000;strokeWidth=1;fontSize=12;startFill=0;" edge="1" source="27" target="25" parent="1">      <mxGeometry relative="1" as="geometry">        <mxPoint x="271.91489361702133" y="652" as="targetPoint"/>      </mxGeometry>    </mxCell>    <mxCell id="29" value="Subject" style="ellipse;rounded=0;shadow=0;strokeWidth=1;fillColor=none;fontSize=12;" vertex="1" parent="1">      <mxGeometry x="80" y="514" width="90" height="40" as="geometry"/>    </mxCell>    <mxCell id="30" style="rounded=0;html=0;shadow=0;startArrow=none;endArrow=none;endFill=0;endSize=10;strokeColor=#000000;strokeWidth=1;fontSize=12;startFill=0;" edge="1" source="29" target="25" parent="1">      <mxGeometry relative="1" as="geometry">        <mxPoint x="274" y="563.9497487437186" as="targetPoint"/>      </mxGeometry>    </mxCell>    <mxCell id="31" value="PDF&#xa;Outline" style="ellipse;rounded=0;shadow=0;strokeWidth=1;fillColor=none;fontSize=12;" vertex="1" parent="1">      <mxGeometry x="163" y="587" width="90" height="40" as="geometry"/>    </mxCell>    <mxCell id="32" style="rounded=0;html=0;shadow=0;startArrow=none;endArrow=none;endFill=0;endSize=10;strokeColor=#000000;strokeWidth=1;fontSize=12;startFill=0;" edge="1" source="31" target="25" parent="1">      <mxGeometry relative="1" as="geometry">        <mxPoint x="364" y="584.070351758794" as="targetPoint"/>      </mxGeometry>    </mxCell>    <mxCell id="33" value="HTML&#xa;Outline" style="ellipse;rounded=0;shadow=0;strokeWidth=1;fillColor=none;fontSize=12;" vertex="1" parent="1">      <mxGeometry x="279" y="626" width="90" height="40" as="geometry"/>    </mxCell>    <mxCell id="34" style="rounded=0;html=0;shadow=0;startArrow=none;endArrow=none;endFill=0;endSize=10;strokeColor=#000000;strokeWidth=1;fontSize=12;startFill=0;" edge="1" source="33" target="25" parent="1">      <mxGeometry relative="1" as="geometry">        <mxPoint x="401.97468354430384" y="537" as="targetPoint"/>      </mxGeometry>    </mxCell>    <mxCell id="35" value="Description" style="ellipse;rounded=0;shadow=0;strokeWidth=1;fillColor=none;fontSize=12;" vertex="1" parent="1">      <mxGeometry x="420" y="567" width="90" height="40" as="geometry"/>    </mxCell>    <mxCell id="36" style="rounded=0;html=0;shadow=0;startArrow=none;endArrow=none;endFill=0;endSize=10;strokeColor=#000000;strokeWidth=1;fontSize=12;startFill=0;" edge="1" source="35" target="25" parent="1">      <mxGeometry relative="1" as="geometry">        <mxPoint x="465" y="456" as="targetPoint"/>      </mxGeometry>    </mxCell>    <mxCell id="37" value="Name" style="ellipse;rounded=0;shadow=0;strokeWidth=1;fillColor=none;fontSize=12;" vertex="1" parent="1">      <mxGeometry x="495" y="458" width="90" height="40" as="geometry"/>    </mxCell>    <mxCell id="38" style="rounded=0;html=0;shadow=0;startArrow=none;endArrow=none;endFill=0;endSize=10;strokeColor=#000000;strokeWidth=1;fontSize=12;startFill=0;" edge="1" source="37" target="25" parent="1">      <mxGeometry relative="1" as="geometry">        <mxPoint x="437.1935483870968" y="381" as="targetPoint"/>      </mxGeometry>    </mxCell>  </root></mxGraphModel></model>');
288//			status.appendChild(modelNode.documentElement);
289//
290//			var fitNode = mxUtils.parseXml('<view scale="0.5" dx="0"/>');
291//			status.appendChild(fitNode.documentElement);
292
293			doc.appendChild(status);
294
295			return doc;
296		};
297
298		// Wait for file to be loaded if no animation data is present
299		if (!startUpdates())
300		{
301			editorUi.editor.addListener('fileLoaded', startUpdates);
302		}
303	}
304});
305