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
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
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
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
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
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
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