Built motion from commit a8ab0ae.|0.0.30
[motion.git] / public / assets / plugins / square / js / EditorUi.js
1 /**
2  * $Id: EditorUi.js,v 1.21 2013/03/14 20:46:36 david Exp $
3  * Copyright (c) 2006-2012, JGraph Ltd
4  */
5 /**
6  * Constructs a new graph editor
7  */
8 EditorUi = function(editor, container) {
9         this.editor = editor || new Editor();
10         this.container = container || document.getElementById('geEditor');
11         var graph = editor.graph;
12         var self = this;
13
14         // Disables scrollbars
15         this.container.style.overflow = 'hidden';
16
17         // Pre-fetches submenu image
18         new Image().src = mxPopupMenu.prototype.submenuImage;
19
20         // Pre-fetches connect image
21         if (mxConnectionHandler.prototype.connectImage != null) {
22                 new Image().src = mxConnectionHandler.prototype.connectImage.src;
23         }
24
25         // Creates the user interface
26         this.actions = new Actions(this);
27         this.menus = new Menus(this);
28         this.createDivs();
29         this.refresh();
30         this.createUi();
31
32         // Disables HTML and text selection
33         var textEditing = mxUtils.bind(this, function(evt) {
34                 if (evt == null) {
35                         evt = window.event;
36                 }
37
38                 if (this.isSelectionAllowed(evt)) {
39                         return true;
40                 }
41
42                 return graph.isEditing();
43         });
44
45         // Disables text selection while not editing and no dialog visible
46         if (this.container == document.body) {
47                 this.menubarContainer.onselectstart = textEditing;
48                 this.menubarContainer.onmousedown = textEditing;
49                 this.toolbarContainer.onselectstart = textEditing;
50                 this.toolbarContainer.onmousedown = textEditing;
51                 this.diagramContainer.onselectstart = textEditing;
52                 this.diagramContainer.onmousedown = textEditing;
53                 this.sidebarContainer.onselectstart = textEditing;
54                 this.sidebarContainer.onmousedown = textEditing;
55                 this.footerContainer.onselectstart = textEditing;
56                 this.footerContainer.onmousedown = textEditing;
57         }
58
59         // And uses built-in context menu while editing
60         if (mxClient.IS_IE && (typeof(document.documentMode) === 'undefined' ||
61                         document.documentMode < 9)) {
62                 mxEvent.addListener(this.diagramContainer, 'contextmenu', textEditing);
63                 mxEvent.addListener(this.sidebarContainer, 'contextmenu', textEditing);
64         } else {
65                 // Allows browser context menu outside of diagram and sidebar
66                 this.diagramContainer.oncontextmenu = textEditing;
67                 this.sidebarContainer.oncontextmenu = textEditing;
68         }
69
70         // Contains the main graph instance inside the given panel
71         graph.init(this.diagramContainer);
72         graph.refresh();
73
74         // Enables scrollbars and sets cursor style for the container
75         graph.container.setAttribute('tabindex', '0');
76         // graph.container.style.overflow = (touchStyle) ? 'hidden' : 'auto';
77         graph.container.style.cursor = 'default';
78         graph.container.style.backgroundImage = 'url(' + editor.gridImage + ')';
79         graph.container.style.backgroundPosition = '-1px -1px';
80         graph.container.focus();
81
82         // Keeps graph container focused on mouse down
83         var graphFireMouseEvent = graph.fireMouseEvent;
84         graph.fireMouseEvent = function(evtName, me, sender) {
85                 if (evtName == mxEvent.MOUSE_DOWN) {
86                         this.container.focus();
87                 }
88
89                 graphFireMouseEvent.apply(this, arguments);
90         };
91
92         // Giuseppe Careri
93         // Defines invalid connections along with the error messages that they produce.
94         this.createMultiplicities(graph.multiplicities, ['start', 'finally', 'end'], [
95                 [1, 1],
96                 [1, 1],
97                 [0, 0]
98         ], [
99                 [0, 0],
100                 [0, 0],
101                 [1, 'n']
102         ]);
103         this.createMultiplicities(graph.multiplicities, ['answer', 'hangup', 'dial',
104                 'ext_dial', 'queue', 'voicemail', 'callback'
105         ], [
106                 [1, 1],
107                 [0, 0],
108                 [0, 1],
109                 [0, 1],
110                 [0, 1],
111                 [1, 1],
112                 [0, 1]
113         ], [
114                 [1, 'n'],
115                 [1, 'n'],
116                 [1, 'n'],
117                 [1, 'n'],
118                 [1, 'n'],
119                 [1, 'n'],
120                 [1, 'n']
121         ]);
122         this.createMultiplicities(graph.multiplicities, ['set', 'math'], [
123                 [1, 1],
124                 [1, 1]
125         ], [
126                 [1, 'n'],
127                 [1, 'n']
128         ]);
129         this.createMultiplicities(graph.multiplicities, ['background', 'playback',
130                 'menu', 'saydigits', 'saynumber', 'sayphonetic', 'tts', 'ispeechtts', 'getdigits'
131         ], [
132                 [1, 'n'],
133                 [1, 1],
134                 [1, 'n'],
135                 [1, 1],
136                 [1, 1],
137                 [1, 1],
138                 [1, 1],
139                 [1, 1],
140                 [1, 'n']
141         ], [
142                 [1, 'n'],
143                 [1, 'n'],
144                 [1, 'n'],
145                 [1, 'n'],
146                 [1, 'n'],
147                 [1, 'n'],
148                 [1, 'n'],
149                 [1, 'n'],
150                 [1, 'n']
151         ]);
152         this.createMultiplicities(graph.multiplicities, ['record'], [
153                 [1, 1]
154         ], [
155                 [1, 'n']
156         ]);
157         this.createMultiplicities(graph.multiplicities, ['database', 'ispeechasr'], [
158                 [1, 1],
159                 [1, 1]
160         ], [
161                 [1, 'n'],
162                 [1, 'n']
163         ]);
164         this.createMultiplicities(graph.multiplicities, ['gotoc', 'gotoif',
165                 'gotoiftime', 'gotoifmultitime', 'vswitch'
166         ], [
167                 [0, 0],
168                 [1, 2],
169                 [1, 2],
170                 [1, 2],
171                 [1, 'n']
172         ], [
173                 [1, 'n'],
174                 [1, 'n'],
175                 [1, 'n'],
176                 [1, 'n'],
177                 [1, 'n']
178         ]);
179         this.createMultiplicities(graph.multiplicities, ['queuelog', 'goal'], [
180                 [1, 1],
181                 [1, 1]
182         ], [
183                 [1, 'n'],
184                 [1, 'n']
185         ]);
186         this.createMultiplicities(graph.multiplicities, ['noop', 'system', 'agi',
187                 'subproject', 'custom_app'
188         ], [
189                 [1, 1],
190                 [1, 1],
191                 [0, 1],
192                 [0, 1],
193                 [0, 1]
194         ], [
195                 [1, 'n'],
196                 [1, 'n'],
197                 [1, 'n'],
198                 [1, 'n'],
199                 [1, 'n']
200         ]);
201
202         // Giuseppe Careri
203         // Processes a doubleclick on an optional cell and fires a <dblclick> event.
204         // The event is fired initially.
205         // If the graph is enabled and the event has not been consumed, then <edit> is called with the given cell.
206         // The event is ignored if no cell was specified.
207         graph.dblClick = function(evt, cell) {
208                 var edges = ['background', 'gotoif', 'gotoiftime', 'menu', 'getdigits', 'vswitch'];
209                 var mxe = new mxEventObject(mxEvent.DOUBLE_CLICK, 'event', evt, 'cell',
210                         cell);
211                 this.fireEvent(mxe);
212
213                 if (this.isEnabled() && !mxEvent.isConsumed(evt) && !mxe.isConsumed()) {
214                         // Override dblClick Vertices and Edges
215                         if (this.getModel().isEdge(cell)) {
216                                 if (edges.indexOf(cell.source.value.tagName) >= 0)
217                                         this.startEditingAtCell(cell);
218                         } else if (this.getModel().isVertex(cell)) {
219                                 console.log(self);
220                                 console.log(cell);
221                                 self.showDialog(new GeneralDialog(self, cell).container, 320, 280, true,
222                                         true);
223                         }
224                         mxe.consume();
225                 }
226         }
227
228         // Configures automatic expand on mouseover
229         graph.panningHandler.autoExpand = true;
230
231         // Installs context menu
232         graph.panningHandler.factoryMethod = mxUtils.bind(this, function(menu, cell,
233                 evt) {
234                 this.menus.createPopupMenu(menu, cell, evt);
235         });
236
237         // Initializes the outline
238         editor.outline.init(this.outlineContainer);
239
240         // Hides context menu
241         mxEvent.addGestureListeners(document, mxUtils.bind(this, function(evt) {
242                 graph.panningHandler.hideMenu();
243         }));
244
245         // Adds gesture handling (pinch to zoom)
246         if (mxClient.IS_TOUCH) {
247                 mxEvent.addListener(graph.container, 'gesturechange',
248                         mxUtils.bind(this, function(evt) {
249                                 graph.view.getDrawPane().setAttribute('transform', 'scale(' + evt.scale +
250                                         ')');
251                                 graph.view.getOverlayPane().style.visibility = 'hidden';
252                         })
253                 );
254
255                 mxEvent.addListener(graph.container, 'gestureend',
256                         mxUtils.bind(this, function(evt) {
257                                 graph.view.getDrawPane().removeAttribute('transform');
258                                 graph.zoomToCenter = true;
259                                 graph.zoom(evt.scale);
260                                 graph.view.getOverlayPane().style.visibility = 'visible';
261                         })
262                 );
263         }
264
265         // Create handler for key events
266         var keyHandler = this.createKeyHandler(editor);
267
268         // Getter for key handler
269         this.getKeyHandler = function() {
270                 return keyHandler;
271         };
272
273         // Updates the editor UI after the window has been resized
274         mxEvent.addListener(window, 'resize', mxUtils.bind(this, function() {
275                 this.refresh();
276                 graph.sizeDidChange();
277                 this.editor.outline.update(false);
278                 this.editor.outline.outline.sizeDidChange();
279         }));
280
281         // Updates action and menu states
282         this.init();
283         this.open();
284 };
285
286 /**
287  * Specifies the size of the split bar.
288  */
289 EditorUi.prototype.splitSize = (mxClient.IS_TOUCH) ? 16 : 8;
290
291 /**
292  * Specifies the height of the menubar. Default is 34.
293  */
294 EditorUi.prototype.menubarHeight = 33;
295
296 /**
297  * Specifies the height of the toolbar. Default is 36.
298  */
299 EditorUi.prototype.toolbarHeight = 36;
300
301 /**
302  * Specifies the height of the footer. Default is 28.
303  */
304 EditorUi.prototype.footerHeight = 28;
305
306 /**
307  * Specifies the position of the horizontal split bar. Default is 212.
308  */
309 EditorUi.prototype.hsplitPosition = 204;
310
311 /**
312  * Specifies the position of the vertical split bar. Default is 190.
313  */
314 EditorUi.prototype.vsplitPosition = 190;
315
316 /**
317  * Installs the listeners to update the action states.
318  */
319 EditorUi.prototype.init = function() {
320         // Updates action states
321         this.addUndoListener();
322         this.addSelectionListener();
323
324         // Overrides clipboard to update paste action state
325         var paste = this.actions.get('paste');
326
327         var updatePaste = function() {
328                 paste.setEnabled(!mxClipboard.isEmpty());
329         };
330
331         var mxClipboardCut = mxClipboard.cut;
332         mxClipboard.cut = function() {
333                 mxClipboardCut.apply(this, arguments);
334                 updatePaste();
335         };
336
337         var mxClipboardCopy = mxClipboard.copy;
338         mxClipboard.copy = function() {
339                 mxClipboardCopy.apply(this, arguments);
340                 updatePaste();
341         };
342 };
343
344 /**
345  * Hook for allowing selection and context menu for certain events.
346  */
347 EditorUi.prototype.isSelectionAllowed = function(evt) {
348         return false;
349 };
350
351 /**
352  * Opens the current diagram via the window.opener if one exists.
353  */
354 EditorUi.prototype.open = function() {
355         // Cross-domain window access is not allowed in FF, so if we
356         // were opened from another domain then this will fail.
357         try {
358                 if (window.opener != null && window.opener.openFile != null) {
359                         window.opener.openFile.setConsumer(mxUtils.bind(this, function(xml,
360                                 filename) {
361                                 try {
362                                         var doc = mxUtils.parseXml(xml);
363                                         this.editor.setGraphXml(doc.documentElement);
364                                         this.editor.modified = false;
365                                         this.editor.undoManager.clear();
366
367                                         if (filename != null) {
368                                                 this.editor.filename = filename;
369                                         }
370                                 } catch (e) {
371                                         mxUtils.alert(mxResources.get('invalidOrMissingFile') + ': ' + e.message);
372                                 }
373                         }));
374                 }
375         } catch (e) {
376                 // ignore
377         }
378 };
379
380 /**
381  * Giuseppe Careri
382  * Opens the current diagram via string.
383  */
384 EditorUi.prototype.openString = function(xml, filename, data) {
385         try {
386                 var doc = mxUtils.parseXml(xml);
387                 this.editor.setGraphXml(doc.documentElement);
388                 this.editor.modified = false;
389                 this.editor.undoManager.clear();
390
391                 if (filename != null) {
392                         this.editor.filename = filename;
393                 }
394
395                 if (data != null) {
396                         this.editor.data = data;
397                 }
398         } catch (e) {
399                 mxUtils.alert(mxResources.get('invalidOrMissingFile') + ': ' + e.message);
400         }
401 };
402
403 /**
404  * Saves As the current graph under the given project name.
405  */
406 EditorUi.prototype.new = function(name) {
407         var editor = this.editor;
408
409         if (name != null) {
410                 var xml = mxUtils.getPrettyXml(this.editor.getGraphXml());
411                 try {
412                         if (useLocalStorage) {
413                                 if (localStorage.getItem(name) != null &&
414                                         !mxUtils.confirm(mxResources.get('replace', [name]))) {
415                                         return;
416                                 }
417
418                                 localStorage.setItem(name, xml);
419                                 this.editor.setStatus(mxResources.get('saved') + ' ' + new Date());
420                         } else {
421                                 console.log(xml.length);
422                                 console.log(MAX_REQUEST_SIZE);
423                                 if (xml.length < MAX_REQUEST_SIZE) {
424                                         xml = encodeURIComponent(xml);
425                                         name = encodeURIComponent(name);
426
427                                         var xhr = new XMLHttpRequest();
428                                         xhr.open("POST", SAVE_URL, true);
429                                         xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
430                                         xhr.setRequestHeader('Authorization', 'Bearer ' + editor.data.token);
431                                         xhr.onload = function(e) {
432                                                 if (xhr.readyState === 4) {
433                                                         if (xhr.status === 201) {
434                                                                 editor.setStatus('Project ' + JSON.parse(xhr.response).name +
435                                                                         ' is opening in new window!');
436                                                                 setTimeout(function() {
437                                                                         window.open('square/project/' + JSON.parse(xhr.response).id,
438                                                                                 '_blank');
439                                                                 }, 1500);
440                                                         } else {
441                                                                 editor.setStatus('Error creating project: ' +
442                                                                         JSON.parse(xhr.response).errors[0].message);
443                                                         }
444                                                 }
445                                         };
446                                         xhr.onerror = function(e) {
447                                                 mxUtils.alert(xhr.statusText);
448                                         };
449                                         xhr.send('description=project_new&name=' + name);
450                                 } else {
451                                         mxUtils.alert(mxResources.get('drawingTooLarge'));
452                                         mxUtils.popup(xml);
453
454                                         return;
455                                 }
456                         }
457
458                         this.editor.filename = name;
459                         this.editor.modified = false;
460                 } catch (e) {
461                         this.editor.setStatus('Error creating project');
462                 }
463         } else {
464                 this.editor.setStatus('Error creating project');
465         }
466 };
467
468
469 /**
470  * Saves the current graph under the given project name.
471  */
472 EditorUi.prototype.save = function(name) {
473         var editor = this.editor;
474
475         if (name != null) {
476                 var xml = mxUtils.getPrettyXml(this.editor.getGraphXml());
477                 try {
478                         if (useLocalStorage) {
479                                 if (localStorage.getItem(name) != null &&
480                                         !mxUtils.confirm(mxResources.get('replace', [name]))) {
481                                         return;
482                                 }
483
484                                 localStorage.setItem(name, xml);
485                                 this.editor.setStatus(mxResources.get('saved') + ' ' + new Date());
486                         } else {
487                                 console.log(xml.length);
488                                 console.log(MAX_REQUEST_SIZE);
489                                 if (xml.length < MAX_REQUEST_SIZE) {
490                                         xml = encodeURIComponent(xml);
491
492                                         var xhr = new XMLHttpRequest();
493                                         xhr.open("PUT", SAVE_URL + this.editor.data.id, true);
494                                         xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
495                                         xhr.setRequestHeader('Authorization', 'Bearer ' + editor.data.token);
496                                         xhr.onload = function(e) {
497                                                 if (xhr.readyState === 4) {
498                                                         if (xhr.status === 200) {
499                                                                 editor.setStatus('Updated at ' + JSON.parse(xhr.response).updatedAt +
500                                                                         ' (' + xhr.status + ' ' + xhr.statusText + ')');
501                                                         } else {
502                                                                 editor.setStatus('Error saving project: ' +
503                                                                         JSON.parse(xhr.response).errors[0].message);
504                                                         }
505                                                 }
506                                         };
507                                         xhr.onerror = function(e) {
508                                                 mxUtils.alert(xhr.statusText);
509                                         };
510                                         xhr.send('preproduction=' + xml);
511                                 } else {
512                                         mxUtils.alert(mxResources.get('drawingTooLarge'));
513                                         mxUtils.popup(xml);
514
515                                         return;
516                                 }
517                         }
518
519                         this.editor.filename = name;
520                         this.editor.modified = false;
521                 } catch (e) {
522                         this.editor.setStatus('Error saving file');
523                 }
524         } else {
525                 this.editor.setStatus('Error saving file');
526         }
527 };
528
529 /**
530  * Saves As the current graph under the given project name.
531  */
532 EditorUi.prototype.saveAs = function(name) {
533         var editor = this.editor;
534
535         if (name != null) {
536                 var xml = mxUtils.getPrettyXml(this.editor.getGraphXml());
537                 try {
538                         if (useLocalStorage) {
539                                 if (localStorage.getItem(name) != null &&
540                                         !mxUtils.confirm(mxResources.get('replace', [name]))) {
541                                         return;
542                                 }
543
544                                 localStorage.setItem(name, xml);
545                                 this.editor.setStatus(mxResources.get('saved') + ' ' + new Date());
546                         } else {
547                                 console.log(xml.length);
548                                 console.log(MAX_REQUEST_SIZE);
549                                 if (xml.length < MAX_REQUEST_SIZE) {
550                                         xml = encodeURIComponent(xml);
551                                         name = encodeURIComponent(name);
552
553                                         var xhr = new XMLHttpRequest();
554                                         xhr.open("POST", SAVE_URL, true);
555                                         xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
556                                         xhr.setRequestHeader('Authorization', 'Bearer ' + editor.data.token);
557                                         xhr.onload = function(e) {
558                                                 if (xhr.readyState === 4) {
559                                                         if (xhr.status === 201) {
560                                                                 editor.setStatus('Project ' + JSON.parse(xhr.response).name +
561                                                                         ' is opening in new window!');
562                                                                 setTimeout(function() {
563                                                                         window.open('square/project/' + JSON.parse(xhr.response).id,
564                                                                                 '_blank');
565                                                                 }, 1500);
566                                                         } else {
567                                                                 editor.setStatus('Error cloning project: ' +
568                                                                         JSON.parse(xhr.response).errors[0].message);
569                                                         }
570                                                 }
571                                         };
572                                         xhr.onerror = function(e) {
573                                                 mxUtils.alert(xhr.statusText);
574                                         };
575                                         xhr.send('description=project_cloned&name=' + name +
576                                                 '&preproduction=' + xml + '&production=' + xml);
577                                 } else {
578                                         mxUtils.alert(mxResources.get('drawingTooLarge'));
579                                         mxUtils.popup(xml);
580
581                                         return;
582                                 }
583                         }
584
585                         this.editor.filename = name;
586                         this.editor.modified = false;
587                 } catch (e) {
588                         this.editor.setStatus('Error saving file');
589                 }
590         } else {
591                 this.editor.setStatus('Error saving file');
592         }
593 };
594
595 /**
596  * Plush the current graph under the given project name.
597  */
598 EditorUi.prototype.validate = function(a, b) {
599         var graph = this.editor.graph;
600         for (var a = a != null ? a : graph.model.getRoot(), b = b != null ? b : {}, c = true, d = graph.model.getChildCount(a), e = 0; e < d; e++) {
601                 var f = graph.model.getChildAt(a, e),
602                         g = b;
603                 graph.isValidRoot(f) && (g = {});
604                 g = this.validate(f, g);
605                 g != null ? graph.setCellWarning(f, g.replace(/\n/g, "<br>")) : graph.setCellWarning(f, null);
606                 c = c && g == null
607         }
608
609         d = "";
610
611         //--- Giuseppe Careri - Modify 12/12/2013 - giuseppe.careri@gmail.com
612         if (graph.model.isVertex(a)) {
613                 switch (a.value.nodeName) {
614                         case 'start':
615                                 d = this.validateBlock('start') ? '' : "Only one Start block is allowed" + "\n";
616                                 break;
617                         case 'finally':
618                                 d = this.validateBlock('finally') ? '' : "Only one Finally block is allowed" + "\n";
619                                 break;
620                         case 'playback':
621                         case 'background':
622                         case 'menu':
623                                 d = (a.value.getAttribute('file_id') > 0) ? "" : mxResources.get('noAudioSelected') + "\n";
624                                 break;
625                         case 'getdigits':
626                                 d = (a.value.getAttribute('file_id') > 0) ? "" : mxResources.get('noAudioSelected') + "\n";
627                                 d += (a.value.getAttribute('variable_id') > 0) ? "" : mxResources.get('noVariableSelected') + "\n";
628                                 break;
629                         case 'vswitch':
630                                 d = (a.value.getAttribute('variable') !== "") ? "" : mxResources.get('noVariableSelected') + "\n";
631                                 break;
632                         case 'database':
633                                 d = (a.value.getAttribute('database_id') > 0) ? "" : mxResources.get('noDatabaseSelected') + "\n";
634                                 break;
635                         case 'set':
636                         case 'math':
637                                 d = (a.value.getAttribute('variable_id') > 0) ? "" : mxResources.get('noVariableSelected') + "\n";
638                                 break;
639                         case 'gotoc':
640                                 d = (a.value.getAttribute('extension') !== "") ? "" : mxResources.get('noExtensionSelected') + "\n";
641                                 break;
642                         case 'system':
643                         case 'agi':
644                                 d = (a.value.getAttribute('command') !== "") ? "" : mxResources.get('noCommandSelected') + "\n";
645                                 break;
646                         case 'gotoif':
647                                 d = (a.value.getAttribute('condition') !== "") ? "" : mxResources.get('noConditionSelected') + "\n";
648                                 break;
649                         case 'saydigits':
650                                 d = (a.value.getAttribute('digits') !== "") ? "" : mxResources.get('noDigitsSelected') + "\n";
651                                 break;
652                         case 'saynumber':
653                                 d = (a.value.getAttribute('number') !== "") ? "" : mxResources.get('noNumberSelected') + "\n";
654                                 break;
655                         case 'sayphonetic':
656                                 d = (a.value.getAttribute('text') !== "") ? "" : mxResources.get('noTextSelected') + "\n";
657                                 break;
658                         case 'record':
659                                 d = (a.value.getAttribute('filename') !== "") ? "" : mxResources.get('noFileSelected') + "\n";
660                                 break;
661                         case 'goal':
662                                 d = (a.value.getAttribute('goalname') !== "") ? "" : mxResources.get('noGoalSelected') + "\n";
663                                 break;
664                         case 'ext_dial':
665                                 d = (a.value.getAttribute('phone') !== "") ? "" : mxResources.get('noIdentifierSelected') + "\n";
666                                 break;
667                         case 'dial':
668                                 d = (a.value.getAttribute('sip_id') > 0) ? "" : mxResources.get('noIdentifierSelected') + "\n";
669                                 break;
670                         case 'queue':
671                                 d = (a.value.getAttribute('queue_id') !== "") ? "" : mxResources.get('noQueueSelected') + "\n";
672                                 break;
673                         case 'callback':
674                                 d = (a.value.getAttribute('list_id') > 0) ? "" : mxResources.get('noListSelected') + "\n";
675                                 break;
676                         case 'gotoiftime':
677                                 d = (a.value.getAttribute('interval_id') > 0) ? "" : mxResources.get('noIntervalSelected') + "\n";
678                                 break;
679                         case 'gotoifmultitime':
680                                 d = (a.value.getAttribute('interval_id') != "") ? "" : mxResources.get('noIntervalSelected') + "\n";
681                                 break;
682                         case 'voicemail':
683                                 d = (a.value.getAttribute('context') !== "") ? "" : mxResources.get('noContextSelected') + "\n";
684                                 d += (a.value.getAttribute('boxnumber') !== "") ? "" : mxResources.get('noBoxNumberSelected') + "\n";
685                                 break;
686                         case 'subproject':
687                                 d = (a.value.getAttribute('project_id') > 0) ? "" : mxResources.get('noProjectSelected') + "\n";
688                                 break;
689                         case 'tts':
690                                 d = (a.value.getAttribute('text') !== "") ? "" : mxResources.get('noTextSelected') + "\n";
691                                 break;
692                         case 'ispeechtts':
693                                 d = (a.value.getAttribute('text') !== "") ? "" : mxResources.get('noTextSelected') + "\n";
694                                 d += (a.value.getAttribute('key') !== "") ? "" : mxResources.get('noKeySelected') + "\n";
695                                 break;
696                         case 'ispeechasr':
697                                 d = (a.value.getAttribute('key') !== "") ? "" : mxResources.get('noKeySelected') + "\n";
698                                 break;
699                 }
700         }
701
702         if (graph.model.isEdge(a)) {
703                 var z = graph.model.getCell(a.source.getId());
704                 switch (z.value.nodeName) {
705                         case 'background':
706                                 var patt = /^[0-9]{1,45}$|^[i]{1}$|^[t]{1}|^[#]{1}|^[*]{1}$/;
707                                 d = d + ((patt.test(String(a.getValue()))) ? "" : mxResources.get('numberOrInvalidOrTimeout') + "\n");
708                                 break;
709                         case 'menu':
710                                 var patt = /^[0-9]{1,45}$|^[-]{1}$|^[i]{1}$|^[t]{1}|^[#]{1}|^[*]{1}$/;
711                                 var str = String(a.getValue());
712                                 var res = str.split(",");
713                                 res.forEach(function(entry) {
714                                         d = d + ((patt.test(entry)) ? "" : mxResources.get('numberOrAll') + "\n");
715                                 });
716                                 break;
717                         case 'getdigits':
718                                 var patt = /^(x|i|-)$/;
719                                 var str = String(a.getValue());
720                                 d = d + ((patt.test(str)) ? "" : mxResources.get('numberOrInvalidOrDefault') + "\n");
721                                 break;
722                         case 'gotoif':
723                         case 'gotoiftime':
724                         case 'gotoifmultitime':
725                                 var patt = /^(true){1}$|^(false){1}$/;
726                                 d = d + ((patt.test(String(a.getValue()))) ? "" : mxResources.get('trueOrFalse') + "\n");
727                                 break;
728
729                 }
730         }
731         // -----------------------------------------------------------------
732
733
734         graph.isCellCollapsed(a) && !c && (d = d + ((mxResources.get(graph.containsValidationErrorsResource) || graph.containsValidationErrorsResource) + "\n"));
735         d = graph.model.isEdge(a) ?
736                 d + (graph.getEdgeValidationError(a, graph.model.getTerminal(a, true), graph.model.getTerminal(a, false)) || "") : d + (graph.getCellValidationError(a) || "");
737         e = graph.validateCell(a, b);
738         e != null && (d = d + e);
739         graph.model.getParent(a) == null && graph.view.validate();
740         return d.length > 0 || !c ? d : null
741 };
742 EditorUi.prototype.validateBlock = function(blockName) {
743         var graph = this.editor.graph;
744         var parent = graph.getDefaultParent();
745         var count = 0;
746         // var model = graph.getModel();
747         //We can also use model.getChildren(parent)
748         parent.children.forEach(function(n) {
749                 if (graph.model.isVertex(n) && n.value.nodeName == blockName) {
750                         count++;
751                 }
752         });
753         return count > 1 ? false : true;
754 }
755 EditorUi.prototype.publish = function(name) {
756         var editor = this.editor;
757
758         if (name != null) {
759                 var response = this.validate();
760                 if (response === null) {
761
762                         var xml = mxUtils.getPrettyXml(this.editor.getGraphXml());
763                         try {
764                                 if (useLocalStorage) {
765                                         if (localStorage.getItem(name) != null &&
766                                                 !mxUtils.confirm(mxResources.get('replace', [name]))) {
767                                                 return;
768                                         }
769
770                                         localStorage.setItem(name, xml);
771                                         this.editor.setStatus(mxResources.get('saved') + ' ' + new Date());
772                                 } else {
773                                         console.log(xml.length);
774                                         console.log(MAX_REQUEST_SIZE);
775                                         if (xml.length < MAX_REQUEST_SIZE) {
776                                                 xml = encodeURIComponent(xml);
777
778                                                 var xhr = new XMLHttpRequest();
779                                                 xhr.open("PUT", SAVE_URL + this.editor.data.id, true);
780                                                 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
781                                                 xhr.setRequestHeader('Authorization', 'Bearer ' + editor.data.token);
782                                                 xhr.onload = function(e) {
783                                                         if (xhr.readyState === 4) {
784                                                                 if (xhr.status === 200) {
785                                                                         editor.setStatus('Published at ' + JSON.parse(xhr.response).updatedAt +
786                                                                                 ' (' + xhr.status + ' ' + xhr.statusText + ')');
787                                                                 } else {
788                                                                         editor.setStatus('Error saving project: ' +
789                                                                                 JSON.parse(xhr.response).errors[0].message);
790                                                                 }
791                                                         }
792                                                 };
793                                                 xhr.onerror = function(e) {
794                                                         mxUtils.alert(xhr.statusText);
795                                                 };
796                                                 xhr.send('preproduction=' + xml + '&production=' + xml);
797                                         } else {
798                                                 mxUtils.alert(mxResources.get('drawingTooLarge'));
799                                                 mxUtils.popup(xml);
800
801                                                 return;
802                                         }
803                                 }
804
805                                 this.editor.filename = name;
806                                 this.editor.modified = false;
807                         } catch (e) {
808                                 this.editor.setStatus('Error publishing file');
809                         }
810                 } else {
811                         editor.setStatus('Error publishing project: ');
812                 }
813         } else {
814                 this.editor.setStatus('Error publishing file');
815         }
816 };
817
818
819 /**
820  * Create variable
821  */
822 EditorUi.prototype.variable = function(name) {
823         var editor = this.editor;
824
825         if (name != null) {
826                 try {
827
828                         var xhr = new XMLHttpRequest();
829                         xhr.open("POST", VARIABLE_URL, true);
830                         xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
831                         xhr.setRequestHeader('Authorization', 'Bearer ' + editor.data.token);
832                         xhr.onload = function(e) {
833                                 if (xhr.readyState === 4) {
834                                         if (xhr.status === 201) {
835                                                 editor.setStatus('Variable created at ' + JSON.parse(xhr.response).createdAt +
836                                                         ' (' + xhr.status + ' ' + xhr.statusText + ')');
837                                         } else {
838                                                 editor.setStatus('Error cloning project: ' +
839                                                         JSON.parse(xhr.response).errors[0].message);
840                                         }
841                                 }
842                         };
843                         xhr.onerror = function(e) {
844                                 mxUtils.alert(xhr.statusText);
845                         };
846                         xhr.send('name=' + name);
847                 } catch (e) {
848                         this.editor.setStatus('Error creating variable');
849                 }
850         } else {
851                 this.editor.setStatus('Error creating variable');
852         }
853 };
854
855 /**
856  * Returns the URL for a copy of this editor with no state.
857  */
858 EditorUi.prototype.getUrl = function(pathname) {
859         var href = (pathname != null) ? pathname : window.location.pathname;
860         var parms = (href.indexOf('?') > 0) ? 1 : 0;
861
862         // Removes template URL parameter for new blank diagram
863         for (var key in urlParams) {
864                 if (parms == 0) {
865                         href += '?';
866                 } else {
867                         href += '&';
868                 }
869
870                 href += key + '=' + urlParams[key];
871                 parms++;
872         }
873
874         return href;
875 };
876
877 /**
878  * Updates the states of the given undo/redo items.
879  */
880 EditorUi.prototype.addUndoListener = function() {
881         var undo = this.actions.get('undo');
882         var redo = this.actions.get('redo');
883
884         var undoMgr = this.editor.undoManager;
885
886         var undoListener = function() {
887                 undo.setEnabled(undoMgr.canUndo());
888                 redo.setEnabled(undoMgr.canRedo());
889         };
890
891         undoMgr.addListener(mxEvent.ADD, undoListener);
892         undoMgr.addListener(mxEvent.UNDO, undoListener);
893         undoMgr.addListener(mxEvent.REDO, undoListener);
894         undoMgr.addListener(mxEvent.CLEAR, undoListener);
895
896         // Updates the button states once
897         undoListener();
898 };
899
900 /**
901  * Updates the states of the given toolbar items based on the selection.
902  */
903 EditorUi.prototype.addSelectionListener = function() {
904         var selectionListener = mxUtils.bind(this, function() {
905                 var graph = this.editor.graph;
906                 var selected = !graph.isSelectionEmpty();
907                 var vertexSelected = false;
908                 var edgeSelected = false;
909
910                 var cells = graph.getSelectionCells();
911
912                 if (cells != null) {
913                         for (var i = 0; i < cells.length; i++) {
914                                 var cell = cells[i];
915
916                                 if (graph.getModel().isEdge(cell)) {
917                                         edgeSelected = true;
918                                 }
919
920                                 if (graph.getModel().isVertex(cell)) {
921                                         vertexSelected = true;
922                                 }
923
924                                 if (edgeSelected && vertexSelected) {
925                                         break;
926                                 }
927                         }
928                 }
929
930                 // Updates action states
931                 var actions = ['cut', 'copy', 'delete', 'duplicate', 'bold', 'italic',
932                         'style', 'underline', 'toFront', 'toBack', 'dashed', 'rounded',
933                         'shadow',
934                         'tilt', 'autosize'
935                 ];
936
937                 for (var i = 0; i < actions.length; i++) {
938                         this.actions.get(actions[i]).setEnabled(selected);
939                 }
940
941                 this.actions.get('curved').setEnabled(edgeSelected);
942                 this.actions.get('rotation').setEnabled(vertexSelected);
943                 this.actions.get('wordWrap').setEnabled(vertexSelected);
944                 this.actions.get('group').setEnabled(graph.getSelectionCount() > 1);
945                 this.actions.get('ungroup').setEnabled(graph.getSelectionCount() == 1 &&
946                         graph.getModel().getChildCount(graph.getSelectionCell()) > 0);
947                 var oneVertexSelected = vertexSelected && graph.getSelectionCount() == 1;
948                 this.actions.get('removeFromGroup').setEnabled(oneVertexSelected &&
949                         graph.getModel().isVertex(graph.getModel().getParent(graph.getSelectionCell()))
950                 );
951
952                 // Updates menu states
953                 var menus = ['fontFamily', 'fontSize', 'alignment', 'position', 'text',
954                         'format',
955                         'arrange', 'linewidth', 'spacing'
956                 ];
957
958                 for (var i = 0; i < menus.length; i++) {
959                         this.menus.get(menus[i]).setEnabled(selected);
960                 }
961
962                 menus = ['line', 'lineend', 'linestart'];
963
964                 for (var i = 0; i < menus.length; i++) {
965                         this.menus.get(menus[i]).setEnabled(edgeSelected);
966                 }
967
968                 this.actions.get('setAsDefaultEdge').setEnabled(edgeSelected);
969
970                 this.menus.get('align').setEnabled(graph.getSelectionCount() > 1);
971                 this.menus.get('direction').setEnabled(vertexSelected || (edgeSelected &&
972                         graph.isLoop(graph.view.getState(graph.getSelectionCell()))));
973                 this.menus.get('navigation').setEnabled(graph.foldingEnabled && ((graph.view
974                                 .currentRoot != null) ||
975                         (graph.getSelectionCount() == 1 && graph.isValidRoot(graph.getSelectionCell()))
976                 ));
977                 this.actions.get('home').setEnabled(graph.view.currentRoot != null);
978                 this.actions.get('exitGroup').setEnabled(graph.view.currentRoot != null);
979                 var groupEnabled = graph.getSelectionCount() == 1 && graph.isValidRoot(
980                         graph.getSelectionCell());
981                 this.actions.get('enterGroup').setEnabled(groupEnabled);
982                 this.actions.get('expand').setEnabled(groupEnabled);
983                 this.actions.get('collapse').setEnabled(groupEnabled);
984                 this.actions.get('editLink').setEnabled(graph.getSelectionCount() == 1);
985                 this.actions.get('openLink').setEnabled(graph.getSelectionCount() == 1 &&
986                         graph.getLinkForCell(graph.getSelectionCell()) != null);
987         });
988
989         this.editor.graph.getSelectionModel().addListener(mxEvent.CHANGE,
990                 selectionListener);
991         selectionListener();
992 };
993
994 /**
995  * Refreshes the viewport.
996  */
997 EditorUi.prototype.refresh = function() {
998         var quirks = mxClient.IS_IE && (document.documentMode == null || document.documentMode ==
999                 5);
1000         var w = this.container.clientWidth;
1001         var h = this.container.clientHeight;
1002
1003         if (this.container == document.body) {
1004                 w = document.body.clientWidth || document.documentElement.clientWidth;
1005                 h = (quirks) ? document.body.clientHeight || document.documentElement.clientHeight :
1006                         document.documentElement.clientHeight;
1007         }
1008
1009         var effHsplitPosition = Math.max(0, Math.min(this.hsplitPosition, w - this.splitSize -
1010                 20));
1011         var effVsplitPosition = Math.max(0, Math.min(this.vsplitPosition, h - this.menubarHeight -
1012                 this.toolbarHeight - this.footerHeight - this.splitSize - 1));
1013
1014         this.menubarContainer.style.height = this.menubarHeight + 'px';
1015         this.toolbarContainer.style.top = this.menubarHeight + 'px';
1016         this.toolbarContainer.style.height = this.toolbarHeight + 'px';
1017
1018         var tmp = this.menubarHeight + this.toolbarHeight;
1019
1020         if (!mxClient.IS_QUIRKS) {
1021                 tmp += 1;
1022         }
1023
1024         this.sidebarContainer.style.top = tmp + 'px';
1025         this.sidebarContainer.style.width = effHsplitPosition + 'px';
1026         this.outlineContainer.style.width = effHsplitPosition + 'px';
1027         this.outlineContainer.style.height = effVsplitPosition + 'px';
1028         this.outlineContainer.style.bottom = this.footerHeight + 'px';
1029         this.diagramContainer.style.left = (effHsplitPosition + this.splitSize) +
1030                 'px';
1031         this.diagramContainer.style.top = this.sidebarContainer.style.top;
1032         this.footerContainer.style.height = this.footerHeight + 'px';
1033         this.footerContainer.style.display = 'none';
1034         this.hsplit.style.top = this.sidebarContainer.style.top;
1035         this.hsplit.style.bottom = this.outlineContainer.style.bottom;
1036         this.hsplit.style.left = effHsplitPosition + 'px';
1037         this.vsplit.style.width = this.sidebarContainer.style.width;
1038         this.vsplit.style.bottom = (effVsplitPosition + this.footerHeight) + 'px';
1039
1040         if (quirks) {
1041                 this.menubarContainer.style.width = w + 'px';
1042                 this.toolbarContainer.style.width = this.menubarContainer.style.width;
1043                 var sidebarHeight = (h - effVsplitPosition - this.splitSize - this.footerHeight -
1044                         this.menubarHeight - this.toolbarHeight);
1045                 this.sidebarContainer.style.height = sidebarHeight + 'px';
1046                 this.diagramContainer.style.width = (w - effHsplitPosition - this.splitSize) +
1047                         'px';
1048                 var diagramHeight = (h - this.footerHeight - this.menubarHeight - this.toolbarHeight);
1049                 this.diagramContainer.style.height = diagramHeight + 'px';
1050                 this.footerContainer.style.width = this.menubarContainer.style.width;
1051                 this.hsplit.style.height = diagramHeight + 'px';
1052         } else {
1053                 this.sidebarContainer.style.bottom = (effVsplitPosition + this.splitSize +
1054                         this.footerHeight) + 'px';
1055                 this.diagramContainer.style.bottom = this.outlineContainer.style.bottom;
1056         }
1057 };
1058
1059 /**
1060  * Creates the required containers.
1061  */
1062 EditorUi.prototype.createDivs = function() {
1063         this.menubarContainer = this.createDiv('geMenubarContainer');
1064         this.toolbarContainer = this.createDiv('geToolbarContainer');
1065         this.sidebarContainer = this.createDiv('geSidebarContainer');
1066         this.outlineContainer = this.createDiv('geOutlineContainer');
1067         this.diagramContainer = this.createDiv('geDiagramContainer');
1068         this.footerContainer = this.createDiv('geFooterContainer');
1069         this.hsplit = this.createDiv('geHsplit');
1070         this.vsplit = this.createDiv('geVsplit');
1071
1072         // Sets static style for containers
1073         this.menubarContainer.style.top = '0px';
1074         this.menubarContainer.style.left = '0px';
1075         this.menubarContainer.style.right = '0px';
1076         this.toolbarContainer.style.left = '0px';
1077         this.toolbarContainer.style.right = '0px';
1078         this.sidebarContainer.style.left = '0px';
1079         this.outlineContainer.style.left = '0px';
1080         this.diagramContainer.style.right = '0px';
1081         this.footerContainer.style.left = '0px';
1082         this.footerContainer.style.right = '0px';
1083         this.footerContainer.style.bottom = '0px';
1084         this.vsplit.style.left = '0px';
1085         this.vsplit.style.height = this.splitSize + 'px';
1086         this.hsplit.style.width = this.splitSize + 'px';
1087 };
1088
1089 /**
1090  * Creates the required containers.
1091  */
1092 EditorUi.prototype.createUi = function() {
1093         // Creates menubar
1094         this.menubar = this.menus.createMenubar(this.createDiv('geMenubar'));
1095         this.menubarContainer.appendChild(this.menubar.container);
1096
1097         // Creates toolbar
1098         this.toolbar = this.createToolbar(this.createDiv('geToolbar'));
1099         this.toolbarContainer.appendChild(this.toolbar.container);
1100
1101         // Creates the sidebar
1102         this.sidebar = this.createSidebar(this.sidebarContainer);
1103
1104         // Creates the footer
1105         this.footerContainer.appendChild(this.createFooter());
1106
1107         // Adds status bar in menubar
1108         this.statusContainer = this.createStatusContainer();
1109
1110         // Connects the status bar to the editor status
1111         this.editor.addListener('statusChanged', mxUtils.bind(this, function() {
1112                 this.setStatusText(this.editor.getStatus());
1113         }));
1114
1115         this.setStatusText(this.editor.getStatus());
1116         this.menubar.container.appendChild(this.statusContainer);
1117
1118         // Inserts into DOM
1119         this.container.appendChild(this.menubarContainer);
1120         this.container.appendChild(this.toolbarContainer);
1121         this.container.appendChild(this.sidebarContainer);
1122         this.container.appendChild(this.outlineContainer);
1123         this.container.appendChild(this.diagramContainer);
1124         this.container.appendChild(this.footerContainer);
1125         this.container.appendChild(this.hsplit);
1126         this.container.appendChild(this.vsplit);
1127
1128         // HSplit
1129         this.addSplitHandler(this.hsplit, true, 0, mxUtils.bind(this, function(
1130                 value) {
1131                 this.hsplitPosition = value;
1132                 this.refresh();
1133                 this.editor.graph.sizeDidChange();
1134                 this.editor.outline.update(false);
1135                 this.editor.outline.outline.sizeDidChange();
1136         }));
1137
1138         // VSplit
1139         this.addSplitHandler(this.vsplit, false, this.footerHeight, mxUtils.bind(
1140                 this,
1141                 function(value) {
1142                         this.vsplitPosition = value;
1143                         this.refresh();
1144                         this.editor.outline.update(false);
1145                         this.editor.outline.outline.sizeDidChange();
1146                 }));
1147 };
1148
1149 /**
1150  * Creates a new toolbar for the given container.
1151  */
1152 EditorUi.prototype.createStatusContainer = function() {
1153         var container = document.createElement('a');
1154         container.className = 'geItem geStatus';
1155
1156         return container;
1157 };
1158
1159 /**
1160  * Creates a new toolbar for the given container.
1161  */
1162 EditorUi.prototype.createStatusContainer = function() {
1163         var container = document.createElement('a');
1164         container.className = 'geItem geStatus';
1165
1166         return container;
1167 };
1168
1169 /**
1170  * Creates a new toolbar for the given container.
1171  */
1172 EditorUi.prototype.setStatusText = function(value) {
1173         this.statusContainer.innerHTML = value;
1174 };
1175
1176 /**
1177  * Creates a new toolbar for the given container.
1178  */
1179 EditorUi.prototype.createToolbar = function(container) {
1180         return new Toolbar(this, container);
1181 };
1182
1183 /**
1184  * Creates a new sidebar for the given container.
1185  */
1186 EditorUi.prototype.createSidebar = function(container) {
1187         return new Sidebar(this, container);
1188 };
1189
1190 /**
1191  * Creates and returns a new footer.
1192  */
1193 EditorUi.prototype.createFooter = function() {
1194         return this.createDiv('geFooter');
1195 };
1196
1197 /**
1198  * Creates the actual toolbar for the toolbar container.
1199  */
1200 EditorUi.prototype.createDiv = function(classname) {
1201         var elt = document.createElement('div');
1202         elt.className = classname;
1203
1204         return elt;
1205 };
1206
1207 /**
1208  * Creates the custom header.
1209  */
1210 EditorUi.prototype.createHeader = function(classname) {
1211         var elt = document.createElement(classname);
1212         return elt;
1213 };
1214
1215 /**
1216  * Creates the custom header.
1217  */
1218 EditorUi.prototype.createForm = function(classname) {
1219         var elt = document.createElement('form');
1220         elt.className = classname;
1221
1222         return elt;
1223 };
1224
1225 /**
1226  * Updates the states of the given undo/redo items.
1227  */
1228 EditorUi.prototype.addSplitHandler = function(elt, horizontal, dx, onChange) {
1229         var start = null;
1230         var initial = null;
1231
1232         function getValue() {
1233                 return parseInt(((horizontal) ? elt.style.left : elt.style.bottom));
1234         };
1235
1236         function moveHandler(evt) {
1237                 if (start != null) {
1238                         var pt = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt));
1239                         onChange(Math.max(0, initial + ((horizontal) ? (pt.x - start.x) : (start.y -
1240                                 pt.y)) - dx));
1241                         mxEvent.consume(evt);
1242                 }
1243         };
1244
1245         function dropHandler(evt) {
1246                 moveHandler(evt);
1247                 start = null;
1248                 initial = null;
1249         };
1250
1251         mxEvent.addGestureListeners(elt, function(evt) {
1252                 start = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt));
1253                 initial = getValue();
1254                 mxEvent.consume(evt);
1255         });
1256
1257         mxEvent.addListener(document, 'mousemove', moveHandler);
1258         mxEvent.addListener(document, 'touchmove', moveHandler);
1259         mxEvent.addListener(document, 'mouseup', dropHandler);
1260         mxEvent.addListener(document, 'touchend', dropHandler);
1261 };
1262
1263 /**
1264  * Displays a print dialog.
1265  */
1266 EditorUi.prototype.showDialog = function(elt, w, h, modal, closable, onClose) {
1267         this.hideDialog();
1268         this.dialog = new Dialog(this, elt, w, (mxClient.IS_VML) ? h - 12 : h,
1269                 modal,
1270                 closable, onClose);
1271 };
1272
1273 /**
1274  * Displays a print dialog.
1275  */
1276 EditorUi.prototype.hideDialog = function() {
1277         if (this.dialog != null) {
1278                 this.dialog.close();
1279                 this.dialog = null;
1280                 this.editor.graph.container.focus();
1281         }
1282 };
1283
1284 /**
1285  * Adds the label menu items to the given menu and parent.
1286  */
1287 EditorUi.prototype.saveFile = function(forceDialog) {
1288         if (!forceDialog && this.editor.filename != null) {
1289                 this.save(this.editor.getOrCreateFilename());
1290         } else {
1291                 this.showDialog(new SaveDialog(this).container, 300, 100, true, true);
1292         }
1293 };
1294
1295 /**
1296  * Adds the label menu items to the given menu and parent.
1297  */
1298 EditorUi.prototype.publishFile = function(forceDialog) {
1299         if (!forceDialog && this.editor.filename != null) {
1300                 this.publish(this.editor.getOrCreateFilename());
1301         } else {
1302                 //this.showDialog(new SaveDialog(this).container, 300, 100, true, true);
1303         }
1304 };
1305
1306 /**
1307  * Executes the given layout.
1308  */
1309 EditorUi.prototype.executeLayout = function(layout, animate, ignoreChildCount) {
1310         var graph = this.editor.graph;
1311         var cell = graph.getSelectionCell();
1312
1313         // Allow global overridding of animation
1314         animate = this.animate != null ? this.animate : animate;
1315
1316         graph.getModel().beginUpdate();
1317         try {
1318                 layout.execute(graph.getDefaultParent(), cell);
1319         } catch (e) {
1320                 throw e;
1321         } finally {
1322                 // Animates the changes in the graph model except
1323                 // for Camino, where animation is too slow
1324                 if (animate && navigator.userAgent.indexOf('Camino') < 0) {
1325                         // New API for animating graph layout results asynchronously
1326                         var morph = new mxMorphing(graph);
1327                         morph.addListener(mxEvent.DONE, mxUtils.bind(this, function() {
1328                                 graph.getModel().endUpdate();
1329                         }));
1330
1331                         morph.startAnimation();
1332                 } else {
1333                         graph.getModel().endUpdate();
1334                 }
1335         }
1336 };
1337
1338 /**
1339  * Creates the keyboard event handler for the current graph and history.
1340  */
1341 EditorUi.prototype.createKeyHandler = function(editor) {
1342         var graph = this.editor.graph;
1343         var keyHandler = new mxKeyHandler(graph);
1344
1345         // Routes command-key to control-key on Mac
1346         keyHandler.isControlDown = function(evt) {
1347                 return mxEvent.isControlDown(evt) || (mxClient.IS_MAC && evt.metaKey);
1348         };
1349
1350         // Helper function to move cells with the cursor keys
1351         function nudge(keyCode) {
1352                 if (!graph.isSelectionEmpty()) {
1353                         var dx = 0;
1354                         var dy = 0;
1355
1356                         if (keyCode == 37) {
1357                                 dx = -1;
1358                         } else if (keyCode == 38) {
1359                                 dy = -1;
1360                         } else if (keyCode == 39) {
1361                                 dx = 1;
1362                         } else if (keyCode == 40) {
1363                                 dy = 1;
1364                         }
1365
1366                         graph.moveCells(graph.getSelectionCells(), dx, dy);
1367                         graph.scrollCellToVisible(graph.getSelectionCell());
1368                 }
1369         };
1370
1371         // Binds keystrokes to actions
1372         var bindAction = mxUtils.bind(this, function(code, control, key, shift) {
1373                 var action = this.actions.get(key);
1374
1375                 if (action != null) {
1376                         var f = function() {
1377                                 if (action.enabled) {
1378                                         action.funct();
1379                                 }
1380                         };
1381
1382                         if (control) {
1383                                 if (shift) {
1384                                         keyHandler.bindControlShiftKey(code, f);
1385                                 } else {
1386                                         keyHandler.bindControlKey(code, f);
1387                                 }
1388                         } else {
1389                                 if (shift) {
1390                                         keyHandler.bindShiftKey(code, f);
1391                                 } else {
1392                                         keyHandler.bindKey(code, f);
1393                                 }
1394                         }
1395                 }
1396         });
1397
1398         var ui = this;
1399         var keyHandleEscape = keyHandler.escape;
1400         keyHandler.escape = function(evt) {
1401                 ui.hideDialog();
1402                 keyHandleEscape.apply(this, arguments);
1403         };
1404
1405         // Ignores enter keystroke. Remove this line if you want the
1406         // enter keystroke to stop editing.
1407         keyHandler.enter = function() {};
1408         keyHandler.bindKey(8, function() {
1409                 graph.foldCells(true);
1410         }); // Backspace
1411         keyHandler.bindKey(13, function() {
1412                 graph.foldCells(false);
1413         }); // Enter
1414         keyHandler.bindKey(33, function() {
1415                 graph.exitGroup();
1416         }); // Page Up
1417         keyHandler.bindKey(34, function() {
1418                 graph.enterGroup();
1419         }); // Page Down
1420         keyHandler.bindKey(36, function() {
1421                 graph.home();
1422         }); // Home
1423         keyHandler.bindKey(35, function() {
1424                 graph.refresh();
1425         }); // End
1426         keyHandler.bindKey(37, function() {
1427                 nudge(37);
1428         }); // Left arrow
1429         keyHandler.bindKey(38, function() {
1430                 nudge(38);
1431         }); // Up arrow
1432         keyHandler.bindKey(39, function() {
1433                 nudge(39);
1434         }); // Right arrow
1435         keyHandler.bindKey(40, function() {
1436                 nudge(40);
1437         }); // Down arrow
1438         keyHandler.bindKey(113, function() {
1439                 graph.startEditingAtCell();
1440         });
1441         bindAction(46, false, 'delete'); // Delete
1442         bindAction(82, true, 'tilt'); // Ctrl+R
1443         bindAction(83, true, 'save'); // Ctrl+S
1444         bindAction(83, true, 'saveAs', true); // Ctrl+Shift+S
1445         bindAction(107, false, 'zoomIn'); // Add
1446         bindAction(109, false, 'zoomOut'); // Subtract
1447         bindAction(65, true, 'selectAll'); // Ctrl+A
1448         bindAction(86, true, 'selectVertices', true); // Ctrl+Shift+V
1449         bindAction(69, true, 'selectEdges', true); // Ctrl+Shift+E
1450         bindAction(69, true, 'export'); // Ctrl+Shift+E
1451         bindAction(66, true, 'toBack'); // Ctrl+B
1452         bindAction(70, true, 'toFront'); // Ctrl+F
1453         bindAction(68, true, 'duplicate'); // Ctrl+D
1454         bindAction(90, true, 'undo'); // Ctrl+Z
1455         bindAction(89, true, 'redo'); // Ctrl+Y
1456         bindAction(88, true, 'cut'); // Ctrl+X
1457         bindAction(67, true, 'copy'); // Ctrl+C
1458         bindAction(81, true, 'connect'); // Ctrl+Q
1459         bindAction(86, true, 'paste'); // Ctrl+V
1460         bindAction(71, true, 'group'); // Ctrl+G
1461         bindAction(71, true, 'grid', true); // Ctrl+Shift+G
1462         bindAction(85, true, 'ungroup'); // Ctrl+U
1463         bindAction(112, false, 'about'); // F1
1464         bindAction(80, true, 'publish', true); // Ctrl+Shift+P
1465
1466         return keyHandler;
1467 };
1468
1469 /**
1470  * Giuseppe Careri
1471  * Creates a new toolbar for the given container.
1472  */
1473 EditorUi.prototype.createMultiplicities = function(graph, cells, source,
1474         target) {
1475         for (var i = 0; i < cells.length; i++) {
1476                 graph.push(new mxMultiplicity(true, cells[i], null, null, source[i][0],
1477                         source[i][1], null, mxResources.get(cells[i]) + ' must have ' + source[i][0] + ' outcoming edge.', null));
1478                 graph.push(new mxMultiplicity(false, cells[i], null, null, target[i][0],
1479                         target[i][1], null, mxResources.get(cells[i]) + ' must have ' + target[i][0] + ' incoming edge.', null));
1480         };
1481 };