Commit a16337a47889d4af376111983a2dcce7520d42f0

Authored by xp.Huang
2 parents 426b4948 f5997cea

Merge branch 'ww' into 'main'

Ww

See merge request huang/thingskit-drawio!7

Too many changes to show.

To preserve performance only 5 of 8 files are displayed.

  1 +<component name="ProjectDictionaryState">
  2 + <dictionary name="WWN">
  3 + <words>
  4 + <w>layui</w>
  5 + </words>
  6 + </dictionary>
  7 +</component>
\ No newline at end of file
... ...
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="VcsDirectoryMappings">
  4 + <mapping directory="$PROJECT_DIR$" vcs="Git" />
  5 + </component>
  6 +</project>
\ No newline at end of file
... ...
... ... @@ -9,7 +9,7 @@
9 9 <meta name="Keywords" content="diagram, online, flow chart, flowchart maker, uml, erd">
10 10 <meta itemprop="name" content="diagrams.net - free flowchart maker and diagrams online">
11 11 <meta itemprop="description" content="diagrams.net is a free online diagramming application and flowchart maker . You can use it to create UML, entity relationship,
12   - org charts, BPMN and BPM, database schema and networks. Also possible are telecommunication network, workflow, flowcharts, maps overlays and GIS, electronic
  12 + org charts, BPMN and BPM, database schema and networks. Also possible are telecommunication network, workflow, flowcharts, maps overlays and GIS, electronic
13 13 circuit and social network diagrams.">
14 14 <meta itemprop="image" content="https://lh4.googleusercontent.com/-cLKEldMbT_E/Tx8qXDuw6eI/AAAAAAAAAAs/Ke0pnlk8Gpg/w500-h344-k/BPMN%2Bdiagram%2Brc2f.png">
15 15 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
... ... @@ -17,18 +17,25 @@
17 17 <meta name="mobile-web-app-capable" content="yes">
18 18 <meta name="theme-color" content="#d89000">
19 19 <script src="./plugins/axios.min.js"></script>
20   - <script src="./js/jquery/jquery-3.3.1.min.js"></script>
21   - <script src="./js/jquery.easyui.min.js"></script>
  20 +<!-- <script src="./js/jquery/jquery-3.3.1.min.js"></script>-->
  21 +<!-- <script src="./js/jquery.easyui.min.js"></script>-->
22 22 <link rel="stylesheet" href="https://cdnjs.loli.net/ajax/libs/layui/2.6.8/css/layui.min.css" integrity="sha512-iQBJbsNHXUcgEIgWThd2dr8tOdKPvICwqjPEZYY81z3eMya44A5MiAqfWSCh+Ee1YzNYkdrI982Qhwgr8LEYOQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
23 23 <script src="https://cdnjs.loli.net/ajax/libs/layui/2.6.8/layui.min.js" integrity="sha512-EKrFvch3qTzLFQgjbcjpsRmF8T3UCtc9ojtMAu6dvvP+bV8qYUDOaQ84nwYCkSLT7lbqGoya/Kf+8fyCBE0vRg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
24   -
  24 +
  25 + <!-- 引入修改样式 -->
  26 + <link rel="stylesheet" href="./styles/formatChange.css">
  27 +
25 28 <!-- 引入select.zTree及相关依赖 -->
26   - <link rel="stylesheet" href="./styles/zTreeStyle.css" type="text/css">
27   - <link rel="stylesheet" href="./styles/jquery.select.zTree.v1.5.css" type="text/css">
28   - <script type="text/javascript" src="./js/jquery.ztree.core.min.js"></script>
29   - <script type="text/javascript" src="./js/jquery.ztree.exhide.min.js"></script>
30   - <script type="text/javascript" src="./js/jquery.select.zTree.v1.5.min.js"></script>
  29 +<!-- <link rel="stylesheet" href="./styles/zTreeStyle.css" type="text/css">-->
  30 +<!-- <link rel="stylesheet" href="./styles/jquery.select.zTree.v1.5.css" type="text/css">-->
  31 +<!-- <script type="text/javascript" src="./js/jquery.ztree.core.min.js"></script>-->
  32 +<!-- <script type="text/javascript" src="./js/jquery.ztree.exhide.min.js"></script>-->
  33 +<!-- <script type="text/javascript" src="./js/jquery.select.zTree.v1.5.min.js"></script>-->
  34 +
  35 +<!-- Axios -->
31 36 <script src="./plugins/defHttp.js"></script>
  37 +
  38 + <script src="./js/api/index.js"></script>
32 39
33 40 <script type="text/javascript">
34 41 /**
... ... @@ -54,27 +61,27 @@
54 61 {
55 62 var result = new Object();
56 63 var params = window.location.search.slice(1).split('&');
57   -
  64 +
58 65 for (var i = 0; i < params.length; i++)
59 66 {
60 67 var idx = params[i].indexOf('=');
61   -
  68 +
62 69 if (idx > 0)
63 70 {
64 71 result[params[i].substring(0, idx)] = params[i].substring(idx + 1);
65 72 }
66 73 }
67   -
  74 +
68 75 return result;
69 76 })();
70   -
  77 +
71 78 // Forces CDN caches by passing URL parameters via URL hash
72 79 if (window.location.hash != null && window.location.hash.substring(0, 2) == '#P')
73 80 {
74 81 try
75 82 {
76 83 urlParams = JSON.parse(decodeURIComponent(window.location.hash.substring(2)));
77   -
  84 +
78 85 if (urlParams.hash != null)
79 86 {
80 87 window.location.hash = urlParams.hash;
... ... @@ -85,7 +92,7 @@
85 92 // ignore
86 93 }
87 94 }
88   -
  95 +
89 96 // Global variable for desktop
90 97 var mxIsElectron = window && window.process && window.process.type;
91 98
... ... @@ -95,21 +102,21 @@
95 102 (function()
96 103 {
97 104 var proto = window.location.protocol;
98   -
  105 +
99 106 if (!mxIsElectron)
100 107 {
101 108 var host = window.location.host;
102   -
  109 +
103 110 // Redirects apex, drive and rt to www
104 111 if (host === 'draw.io' || host === 'rt.draw.io' || host === 'drive.draw.io')
105 112 {
106 113 host = 'www.draw.io';
107 114 }
108   -
  115 +
109 116 var href = proto + '//' + host + window.location.href.substring(
110 117 window.location.protocol.length +
111 118 window.location.host.length + 2);
112   -
  119 +
113 120 // Redirects if href changes
114 121 if (href != window.location.href)
115 122 {
... ... @@ -118,7 +125,7 @@
118 125 }
119 126 })();
120 127 }
121   -
  128 +
122 129 /**
123 130 * Adds meta tag to the page.
124 131 */
... ... @@ -127,15 +134,15 @@
127 134 try
128 135 {
129 136 var s = document.createElement('meta');
130   -
131   - if (name != null)
  137 +
  138 + if (name != null)
132 139 {
133 140 s.setAttribute('name', name);
134 141 }
135 142
136 143 s.setAttribute('content', content);
137   -
138   - if (httpEquiv != null)
  144 +
  145 + if (httpEquiv != null)
139 146 {
140 147 s.setAttribute('http-equiv', httpEquiv);
141 148 }
... ... @@ -148,14 +155,14 @@
148 155 // ignore
149 156 }
150 157 };
151   -
  158 +
152 159 /**
153 160 * Synchronously adds scripts to the page.
154 161 */
155 162 function mxscript(src, onLoad, id, dataAppKey, noWrite)
156 163 {
157 164 var defer = onLoad == null && !noWrite;
158   -
  165 +
159 166 if ((urlParams['dev'] != '1' && typeof document.createElement('canvas').getContext === "function") ||
160 167 onLoad != null || noWrite)
161 168 {
... ... @@ -168,16 +175,16 @@
168 175 {
169 176 s.setAttribute('id', id);
170 177 }
171   -
  178 +
172 179 if (dataAppKey != null)
173 180 {
174 181 s.setAttribute('data-app-key', dataAppKey);
175 182 }
176   -
  183 +
177 184 if (onLoad != null)
178 185 {
179 186 var r = false;
180   -
  187 +
181 188 s.onload = s.onreadystatechange = function()
182 189 {
183 190 if (!r && (!this.readyState || this.readyState == 'complete'))
... ... @@ -187,9 +194,9 @@
187 194 }
188 195 };
189 196 }
190   -
  197 +
191 198 var t = document.getElementsByTagName('script')[0];
192   -
  199 +
193 200 if (t != null)
194 201 {
195 202 t.parentNode.insertBefore(s, t);
... ... @@ -211,11 +218,11 @@
211 218 g.type = 'text/javascript';
212 219 g.async = true;
213 220 g.src = src;
214   -
  221 +
215 222 var s = document.getElementsByTagName('script')[0];
216 223 s.parentNode.insertBefore(g, s);
217 224 };
218   -
  225 +
219 226 /**
220 227 * Adds meta tags with application name (depends on offline URL parameter)
221 228 */
... ... @@ -230,10 +237,10 @@
230 237 mxmeta(null, 'default-src \'self\' \'unsafe-inline\'; connect-src \'self\' https://*.draw.io https://fonts.googleapis.com https://fonts.gstatic.com; img-src * data:; media-src *; font-src *; style-src-elem \'self\' \'unsafe-inline\' https://fonts.googleapis.com', 'Content-Security-Policy');
231 238 }
232 239 })();
233   -
  240 +
234 241 // Checks for local storage
235 242 var isLocalStorage = false;
236   -
  243 +
237 244 try
238 245 {
239 246 isLocalStorage = urlParams['local'] != '1' && typeof(localStorage) != 'undefined';
... ... @@ -244,15 +251,15 @@
244 251 }
245 252
246 253 var mxScriptsLoaded = false, mxWinLoaded = false;
247   -
  254 +
248 255 function checkAllLoaded()
249 256 {
250 257 if (mxScriptsLoaded && mxWinLoaded)
251 258 {
252   - App.main();
  259 + App.main();
253 260 }
254 261 };
255   -
  262 +
256 263 var t0 = new Date();
257 264
258 265 // Changes paths for local development environment
... ... @@ -260,18 +267,18 @@
260 267 {
261 268 // Used to request grapheditor/mxgraph sources in dev mode
262 269 var mxDevUrl = document.location.protocol + '//devhost.jgraph.com/drawio/src/main';
263   -
  270 +
264 271 // Used to request draw.io sources in dev mode
265 272 var drawDevUrl = document.location.protocol + '//devhost.jgraph.com/drawio/src/main/webapp/';
266 273 var geBasePath = drawDevUrl + '/js/grapheditor';
267 274 var mxBasePath = mxDevUrl + '/mxgraph';
268   -
  275 +
269 276 if (document.location.protocol == 'file:')
270 277 {
271 278 geBasePath = './js/grapheditor';
272 279 mxBasePath = './mxgraph';
273 280 drawDevUrl = './';
274   -
  281 +
275 282 // Forces includes for dev environment in node.js
276 283 mxForceIncludes = true;
277 284 }
... ... @@ -287,19 +294,19 @@
287 294 mxscript(drawDevUrl + 'js/diagramly/Init.js');
288 295 mxscript(geBasePath + '/Init.js');
289 296 mxscript(mxBasePath + '/mxClient.js');
290   -
  297 +
291 298 // Adds all JS code that depends on mxClient. This indirection via Devel.js is
292 299 // required in some browsers to make sure mxClient.js (and the files that it
293 300 // loads asynchronously) are available when the code loaded in Devel.js runs.
294 301 mxscript(drawDevUrl + 'js/diagramly/Devel.js');
295   -
  302 +
296 303 // Electron
297 304 if (mxIsElectron)
298 305 {
299 306 mxscript('js/diagramly/DesktopLibrary.js');
300 307 mxscript('js/diagramly/ElectronApp.js');
301 308 }
302   -
  309 +
303 310 mxscript(drawDevUrl + 'js/PostConfig.js');
304 311 }
305 312 else
... ... @@ -307,18 +314,18 @@
307 314 (function()
308 315 {
309 316 var hostName = window.location.hostname;
310   -
  317 +
311 318 // Supported domains are *.draw.io and the packaged version in Quip
312 319 var supportedDomain = (hostName.substring(hostName.length - 8, hostName.length) === '.draw.io') ||
313 320 (hostName.substring(hostName.length - 13, hostName.length) === '.diagrams.net');
314   -
  321 +
315 322 function loadAppJS()
316 323 {
317 324 mxscript('js/app.min.js', function()
318 325 {
319 326 mxScriptsLoaded = true;
320 327 checkAllLoaded();
321   -
  328 +
322 329 // Electron
323 330 if (mxIsElectron)
324 331 {
... ... @@ -345,7 +352,7 @@
345 352 }
346 353 });
347 354 };
348   -
  355 +
349 356 if (!supportedDomain || mxIsElectron)
350 357 {
351 358 mxscript('js/PreConfig.js', loadAppJS);
... ... @@ -361,7 +368,7 @@
361 368 window.onerror = function()
362 369 {
363 370 var status = document.getElementById('geStatus');
364   -
  371 +
365 372 if (status != null)
366 373 {
367 374 status.innerHTML = 'Page could not be loaded. Please try refreshing.';
... ... @@ -461,7 +468,7 @@ if (navigator.userAgent != null && navigator.userAgent.toLowerCase().
461 468 {
462 469 // Redirects old Electron app to latest version
463 470 var div = document.getElementById('geInfo');
464   -
  471 +
465 472 if (div != null)
466 473 {
467 474 div.innerHTML = '<center><h2>You are using an out of date version of this app.<br>Please download the latest version ' +
... ...
  1 +class ConfigurationNodeApi {
  2 + /**
  3 + * @description 获取组态信息
  4 + * @param {'CONFIGURE' | 'CONTENT' | 'NODE'} levelType - 组态资源类型
  5 + * @param {string} levelId - 组态资源ID
  6 + */
  7 + static getConfigurationInfo(levelType, levelId) {
  8 + return defHttp.get(`/yt/configuration/node/${levelType}/${levelId}`)
  9 + }
  10 +
  11 + /**
  12 + * @description 获取组织节点树
  13 + * @returns
  14 + */
  15 + static getOrgTree() {
  16 + return defHttp.get('/yt/organization/me/list')
  17 + }
  18 +
  19 + /**
  20 + * @description 通过设备ID 获取 设备属性
  21 + * @param tbDeviceId
  22 + * @returns {Promise<*>}
  23 + */
  24 + static getDeviceAttr(tbDeviceId) {
  25 + return defHttp.get(`/plugins/telemetry/DEVICE/${tbDeviceId}/keys/timeseries`)
  26 + }
  27 +
  28 + /**
  29 + * @description 获取组织下的设备
  30 + * @param {'DIRECT_CONNECTION' | 'GATEWAY' | 'SENDOR'} deviceType - 'DIRECT_CONNECTION' 直连设备 'GATEWAY' 网关设备 'SENDOR' 传感器
  31 + * @param {string} orgId - 组织ID
  32 + * @returns {Promise<*>}
  33 + */
  34 + static getDeviceUnderTheOrg(deviceType, orgId) {
  35 + return defHttp.get(`/yt/device/list/${deviceType}?organizationId=${orgId}`)
  36 + }
  37 +
  38 + /**
  39 + * @description 查询设备的子设备
  40 + * @param deviceId 设备ID
  41 + * @returns {Promise<*>}
  42 + */
  43 + static getDeviceChildDevice(deviceId) {
  44 + return defHttp.get(`/yt/device/relation?page=1&pageSize=10&fromId=${deviceId}`)
  45 + }
  46 +
  47 + /**
  48 + * @description 查询所有主设备列表
  49 + * @param orgId
  50 + * @returns {Promise<*>}
  51 + */
  52 + static getMasterDevice(orgId) {
  53 + return defHttp.get(`/yt/device/list/master/${orgId}`)
  54 + }
  55 +
  56 + /**
  57 + * @description 查询所有从设备
  58 + * @param orgId
  59 + * @param masterDeviceId
  60 + * @returns {Promise<*>}
  61 + */
  62 + static getSlaveDevice(orgId, masterDeviceId) {
  63 + return defHttp.get(`/yt/device/list/slave/${orgId}?masterId=${masterDeviceId}`)
  64 + }
  65 +
  66 + /**
  67 + * @description 编辑数据交互
  68 + */
  69 + static updateNodeEvent(data) {
  70 + return defHttp.post('/yt/configuration/node/event', data)
  71 + }
  72 +
  73 + /**
  74 + * @description 编辑动画效果
  75 + * @param {*} data
  76 + * @returns
  77 + */
  78 + static updateNodeAct(data) {
  79 + return defHttp.post('/yt/configuration/node/act', data)
  80 + }
  81 +}
... ...
... ... @@ -16,14 +16,14 @@ App = function(editor, container, lightbox)
16 16 EditorUi.call(this, editor, container, (lightbox != null) ? lightbox :
17 17 (urlParams['lightbox'] == '1' || (uiTheme == 'min' &&
18 18 urlParams['chrome'] != '0')));
19   -
  19 +
20 20 // Logs unloading of window with modifications for Google Drive file
21 21 if (!mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp)
22 22 {
23 23 window.onunload = mxUtils.bind(this, function()
24 24 {
25 25 var file = this.getCurrentFile();
26   -
  26 +
27 27 if (file != null && file.isModified())
28 28 {
29 29 var evt = {category: 'DISCARD-FILE-' + file.getHash(),
... ... @@ -39,7 +39,7 @@ App = function(editor, container, lightbox)
39 39 '-change_' + ((file.lastChanged != null) ? Math.round((Date.now() - file.lastChanged.getTime()) / 1000) : 'x') +
40 40 '-alive_' + Math.round((Date.now() - App.startTime.getTime()) / 1000),
41 41 label: (file.sync != null) ? ('client_' + file.sync.clientId) : 'nosync'};
42   -
  42 +
43 43 if (file.constructor == DriveFile && file.desc != null && this.drive != null)
44 44 {
45 45 evt.label += ((this.drive.user != null) ? ('-user_' + this.drive.user.id) : '-nouser') + '-rev_' +
... ... @@ -56,7 +56,7 @@ App = function(editor, container, lightbox)
56 56 this.editor.addListener('autosaveChanged', mxUtils.bind(this, function()
57 57 {
58 58 var file = this.getCurrentFile();
59   -
  59 +
60 60 if (file != null)
61 61 {
62 62 EditorUi.logEvent({category: ((this.editor.autosave) ? 'ON' : 'OFF') +
... ... @@ -64,7 +64,7 @@ App = function(editor, container, lightbox)
64 64 label: 'autosave_' + ((this.editor.autosave) ? 'on' : 'off')});
65 65 }
66 66 }));
67   -
  67 +
68 68 // Pre-fetches images
69 69 if (mxClient.IS_SVG)
70 70 {
... ... @@ -75,7 +75,7 @@ App = function(editor, container, lightbox)
75 75 var img = new Image();
76 76 img.src = mxGraph.prototype.warningImage.src;
77 77 }
78   -
  78 +
79 79 // Global helper method to deal with popup blockers
80 80 window.openWindow = mxUtils.bind(this, function(url, pre, fallback)
81 81 {
... ... @@ -84,9 +84,9 @@ App = function(editor, container, lightbox)
84 84 fallback();
85 85 return;
86 86 }
87   -
  87 +
88 88 var wnd = null;
89   -
  89 +
90 90 try
91 91 {
92 92 wnd = window.open(url);
... ... @@ -95,7 +95,7 @@ App = function(editor, container, lightbox)
95 95 {
96 96 // ignore
97 97 }
98   -
  98 +
99 99 if (wnd == null || wnd === undefined)
100 100 {
101 101 this.showDialog(new PopupDialog(this, url, pre, fallback).container, 320, 140, true, true);
... ... @@ -118,7 +118,7 @@ App = function(editor, container, lightbox)
118 118 {
119 119 window.openFile.cancel(true);
120 120 }
121   -
  121 +
122 122 this.handleError(message);
123 123 });
124 124
... ... @@ -127,7 +127,7 @@ App = function(editor, container, lightbox)
127 127 {
128 128 this.addFileDropHandler([document]);
129 129 }
130   -
  130 +
131 131 // Process the queue for waiting plugins
132 132 if (App.DrawPlugins != null)
133 133 {
... ... @@ -150,7 +150,7 @@ App = function(editor, container, lightbox)
150 150 this.initializeEmbedMode();
151 151 }
152 152 }
153   -
  153 +
154 154 // Installs global callback for plugins
155 155 window.Draw.loadPlugin = mxUtils.bind(this, function(callback)
156 156 {
... ... @@ -164,7 +164,7 @@ App = function(editor, container, lightbox)
164 164 this.initializeEmbedMode();
165 165 }
166 166 });
167   -
  167 +
168 168 //Set a timeout in case a plugin doesn't load quickly or doesn't load at all
169 169 setTimeout(mxUtils.bind(this, function()
170 170 {
... ... @@ -292,7 +292,7 @@ App.PUSHER_CLUSTER = 'eu';
292 292 App.PUSHER_URL = 'https://js.pusher.com/7.0.3/pusher.min.js';
293 293
294 294 /**
295   - * SimplePeer library
  295 + * SimplePeer library
296 296 */
297 297 App.SIMPLE_PEER_URL = window.DRAWIO_BASE_URL + '/js/simplepeer/simplepeer9.10.0.min.js';
298 298
... ... @@ -300,11 +300,11 @@ App.PUSHER_URL = 'https://js.pusher.com/7.0.3/pusher.min.js';
300 300 * Google APIs to load. The realtime API is needed to notify collaborators of conversion
301 301 * of the realtime files, but after Dec 11 it's read-only and hence no longer needed.
302 302 */
303   -App.GOOGLE_APIS = 'drive-share';
  303 +App.GOOGLE_APIS = 'drive-share';
304 304
305 305 /**
306 306 * Function: authorize
307   - *
  307 + *
308 308 * Authorizes the client, gets the userId and calls <open>.
309 309 */
310 310 App.startTime = new Date();
... ... @@ -316,7 +316,7 @@ App.startTime = new Date();
316 316 App.pluginRegistry = {'4xAKTrabTpTzahoLthkwPNUn': 'plugins/explore.js',
317 317 'ex': 'plugins/explore.js', 'p1': 'plugins/p1.js',
318 318 'ac': 'plugins/connect.js', 'acj': 'plugins/connectJira.js',
319   - 'ac148': 'plugins/cConf-1-4-8.js', 'ac148cmnt': 'plugins/cConf-comments.js',
  319 + 'ac148': 'plugins/cConf-1-4-8.js', 'ac148cmnt': 'plugins/cConf-comments.js',
320 320 'voice': 'plugins/voice.js',
321 321 'tips': 'plugins/tooltips.js', 'svgdata': 'plugins/svgdata.js',
322 322 'electron': 'plugins/electron.js',
... ... @@ -360,7 +360,7 @@ App.publicPlugin = [
360 360 App.loadScripts = function(scripts, onload)
361 361 {
362 362 var n = scripts.length;
363   -
  363 +
364 364 for (var i = 0; i < scripts.length; i++)
365 365 {
366 366 mxscript(scripts[i], function()
... ... @@ -375,34 +375,34 @@ App.loadScripts = function(scripts, onload)
375 375
376 376 /**
377 377 * Function: getStoredMode
378   - *
  378 + *
379 379 * Returns the current mode.
380 380 */
381 381 App.getStoredMode = function()
382 382 {
383 383 var mode = null;
384   -
  384 +
385 385 if (mode == null && isLocalStorage)
386 386 {
387 387 mode = localStorage.getItem('.mode');
388 388 }
389   -
  389 +
390 390 if (mode == null && typeof(Storage) != 'undefined')
391 391 {
392 392 var cookies = document.cookie.split(";");
393   -
  393 +
394 394 for (var i = 0; i < cookies.length; i++)
395 395 {
396 396 // Removes spaces around cookie
397 397 var cookie = mxUtils.trim(cookies[i]);
398   -
  398 +
399 399 if (cookie.substring(0, 5) == 'MODE=')
400 400 {
401 401 mode = cookie.substring(5);
402 402 break;
403 403 }
404 404 }
405   -
  405 +
406 406 if (mode != null && isLocalStorage)
407 407 {
408 408 // Moves to local storage
... ... @@ -412,7 +412,7 @@ App.getStoredMode = function()
412 412 localStorage.setItem('.mode', mode);
413 413 }
414 414 }
415   -
  415 +
416 416 return mode;
417 417 };
418 418
... ... @@ -430,16 +430,16 @@ App.getStoredMode = function()
430 430 {
431 431 urlParams['mode'] = 'dropbox';
432 432 }
433   -
  433 +
434 434 App.mode = urlParams['mode'];
435 435 }
436   -
  436 +
437 437 if (App.mode == null)
438 438 {
439 439 // Stored mode overrides preferred mode
440 440 App.mode = App.getStoredMode();
441 441 }
442   -
  442 +
443 443 /**
444 444 * Lazy loading backends.
445 445 */
... ... @@ -474,7 +474,7 @@ App.getStoredMode = function()
474 474 window.DriveClient = null;
475 475 }
476 476 }
477   -
  477 +
478 478 // Loads dropbox for all browsers but IE8 and below (no CORS) if not disabled or if enabled and in embed mode
479 479 // KNOWN: Picker does not work in IE11 (https://dropbox.zendesk.com/requests/1650781)
480 480 if (typeof window.DropboxClient === 'function')
... ... @@ -490,7 +490,7 @@ App.getStoredMode = function()
490 490 {
491 491 // Must load this after the dropbox SDK since they use the same namespace
492 492 mxscript(App.DROPINS_URL, null, 'dropboxjs', App.DROPBOX_APPKEY, true);
493   - });
  493 + });
494 494 }
495 495 else if (urlParams['chrome'] == '0')
496 496 {
... ... @@ -503,7 +503,7 @@ App.getStoredMode = function()
503 503 window.DropboxClient = null;
504 504 }
505 505 }
506   -
  506 +
507 507 // Loads OneDrive for all browsers but IE6/IOS if not disabled or if enabled and in embed mode
508 508 if (typeof window.OneDriveClient === 'function')
509 509 {
... ... @@ -528,7 +528,7 @@ App.getStoredMode = function()
528 528 window.OneDriveClient = null;
529 529 }
530 530 }
531   -
  531 +
532 532 // Loads Trello for all browsers but < IE10 if not disabled or if enabled and in embed mode
533 533 if (typeof window.TrelloClient === 'function')
534 534 {
... ... @@ -584,7 +584,7 @@ App.clearServiceWorker = function(success)
584 584
585 585 /**
586 586 * Program flow starts here.
587   - *
  587 + *
588 588 * Optional callback is called with the app instance.
589 589 */
590 590 let defHttp;
... ... @@ -613,28 +613,28 @@ App.main = function(callback, createUi)
613 613 /aj\.draw\.io$/.test(window.location.hostname)))
614 614 {
615 615 document.body.innerHTML = '<div style="margin-top:10%;text-align:center;">Stand-alone mode not allowed for this domain.</div>';
616   -
  616 +
617 617 return;
618 618 }
619   -
  619 +
620 620 // Removes info text in embed mode
621 621 if (urlParams['embed'] == '1' || urlParams['lightbox'] == '1')
622 622 {
623 623 var geInfo = document.getElementById('geInfo');
624   -
  624 +
625 625 if (geInfo != null)
626 626 {
627 627 geInfo.parentNode.removeChild(geInfo);
628 628 }
629 629 }
630   -
  630 +
631 631 // Redirects to the latest AWS icons
632 632 if (document.referrer != null && urlParams['libs'] == 'aws3' &&
633 633 document.referrer.substring(0, 42) == 'https://aws.amazon.com/architecture/icons/')
634 634 {
635 635 urlParams['libs'] = 'aws4';
636 636 }
637   -
  637 +
638 638 if (window.mxscript != null)
639 639 {
640 640 // Checks for script content changes to avoid CSP errors in production
... ... @@ -642,7 +642,7 @@ App.main = function(callback, createUi)
642 642 CryptoJS != null && App.mode != App.MODE_DROPBOX && App.mode != App.MODE_TRELLO)
643 643 {
644 644 var scripts = document.getElementsByTagName('script');
645   -
  645 +
646 646 // Checks bootstrap script
647 647 if (scripts != null && scripts.length > 0)
648 648 {
... ... @@ -655,13 +655,14 @@ App.main = function(callback, createUi)
655 655 alert('[Dev] Bootstrap script change requires update of CSP');
656 656 }
657 657 }
658   -
  658 +
659 659 // Checks main script
660 660 if (scripts != null && scripts.length > 1)
661 661 {
662 662 var content = mxUtils.getTextContent(scripts[scripts.length - 1]);
663   -
664   - if (CryptoJS.MD5(content).toString() != 'd53805dd6f0bbba2da4966491ca0a505')
  663 +
  664 + // if (CryptoJS.MD5(content).toString() != 'd53805dd6f0bbba2da4966491ca0a505')
  665 + if (CryptoJS.MD5(content).toString() != '4559a92176e60ec27e523711c8ea01ac')
665 666 {
666 667 console.log('Change main script MD5 in the previous line:', CryptoJS.MD5(content).toString());
667 668 alert('[Dev] Main script change requires update of CSP');
... ... @@ -697,7 +698,7 @@ App.main = function(callback, createUi)
697 698 console.error(e);
698 699 }
699 700 }
700   -
  701 +
701 702 // Loads Pusher API
702 703 if (('ArrayBuffer' in window) && !mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp &&
703 704 DrawioFile.SYNC == 'auto' && (urlParams['embed'] != '1' ||
... ... @@ -707,20 +708,20 @@ App.main = function(callback, createUi)
707 708 {
708 709 // TODO: Check if async loading is fast enough
709 710 mxscript(App.PUSHER_URL);
710   -
  711 +
711 712 if (urlParams['fast-sync'] == '1')
712 713 {
713 714 mxscript(App.SIMPLE_PEER_URL);
714 715 }
715 716 }
716   -
  717 +
717 718 // Loads plugins
718 719 if (urlParams['plugins'] != '0' && urlParams['offline'] != '1')
719 720 {
720 721 // mxSettings is not yet initialized in configure mode, redirect parameter
721 722 // to p URL parameter in caller for plugins in embed mode
722 723 var plugins = (mxSettings.settings != null) ? mxSettings.getPlugins() : null;
723   -
  724 +
724 725 // Configured plugins in embed mode with configure=1 URL should be loaded so we
725 726 // look ahead here and parse the config to fetch the list of custom plugins
726 727 if (mxSettings.settings == null && isLocalStorage && typeof(JSON) !== 'undefined')
... ... @@ -728,7 +729,7 @@ App.main = function(callback, createUi)
728 729 try
729 730 {
730 731 var temp = JSON.parse(localStorage.getItem(mxSettings.key));
731   -
  732 +
732 733 if (temp != null)
733 734 {
734 735 plugins = temp.plugins;
... ... @@ -748,15 +749,15 @@ App.main = function(callback, createUi)
748 749 // Mapping from key to URL in App.plugins
749 750 App.loadPlugins(temp.split(';'));
750 751 }
751   -
  752 +
752 753 if (plugins != null && plugins.length > 0 && urlParams['plugins'] != '0')
753 754 {
754   - // Loading plugins inside the asynchronous block below stops the page from loading so a
  755 + // Loading plugins inside the asynchronous block below stops the page from loading so a
755 756 // hardcoded message for the warning dialog is used since the resources are loadd below
756 757 var warning = 'The page has requested to load the following plugin(s):\n \n {1}\n \n Would you like to load these plugin(s) now?\n \n NOTE : Only allow plugins to run if you fully understand the security implications of doing so.\n';
757 758 var tmp = window.location.protocol + '//' + window.location.host;
758 759 var local = true;
759   -
  760 +
760 761 for (var i = 0; i < plugins.length && local; i++)
761 762 {
762 763 if (plugins[i].charAt(0) != '/' && plugins[i].substring(0, tmp.length) != tmp)
... ... @@ -764,7 +765,7 @@ App.main = function(callback, createUi)
764 765 local = false;
765 766 }
766 767 }
767   -
  768 +
768 769 if (local || mxUtils.confirm(mxResources.replacePlaceholders(warning, [plugins.join('\n')]).replace(/\\n/g, '\n')))
769 770 {
770 771 for (var i = 0; i < plugins.length; i++)
... ... @@ -775,12 +776,12 @@ App.main = function(callback, createUi)
775 776 {
776 777 App.pluginsLoaded[plugins[i]] = true;
777 778 App.embedModePluginsCount++;
778   -
  779 +
779 780 if (plugins[i].charAt(0) == '/')
780 781 {
781 782 plugins[i] = PLUGINS_BASE_PATH + plugins[i];
782 783 }
783   -
  784 +
784 785 mxscript(plugins[i]);
785 786 }
786 787 }
... ... @@ -792,7 +793,7 @@ App.main = function(callback, createUi)
792 793 }
793 794 }
794 795 }
795   -
  796 +
796 797 // Loads gapi for all browsers but IE8 and below if not disabled or if enabled and in embed mode
797 798 // Special case: Cannot load in asynchronous code below
798 799 if (typeof window.DriveClient === 'function' &&
... ... @@ -808,7 +809,7 @@ App.main = function(callback, createUi)
808 809 window.DriveClient = null;
809 810 }
810 811 }
811   -
  812 +
812 813 /**
813 814 * Asynchronous MathJax extension.
814 815 */
... ... @@ -827,7 +828,7 @@ App.main = function(callback, createUi)
827 828 {
828 829 // Adds bundle text to resources
829 830 mxResources.parse(xhr[0].getText());
830   -
  831 +
831 832 // Configuration mode
832 833 if (isLocalStorage && localStorage != null && window.location.hash != null &&
833 834 window.location.hash.substring(0, 9) == '#_CONFIG_')
... ... @@ -835,12 +836,12 @@ App.main = function(callback, createUi)
835 836 try
836 837 {
837 838 var trustedPlugins = {};
838   -
  839 +
839 840 for (var key in App.pluginRegistry)
840 841 {
841 842 trustedPlugins[App.pluginRegistry[key]] = true;
842 843 }
843   -
  844 +
844 845 // Only allows trusted plugins
845 846 function checkPlugins(plugins)
846 847 {
... ... @@ -854,46 +855,46 @@ App.main = function(callback, createUi)
854 855 }
855 856 }
856 857 }
857   -
  858 +
858 859 return true;
859 860 };
860   -
  861 +
861 862 var value = JSON.parse(Graph.decompress(window.location.hash.substring(9)));
862 863
863 864 if (value != null && checkPlugins(value.plugins))
864 865 {
865 866 EditorUi.debug('Setting configuration', JSON.stringify(value));
866   -
  867 +
867 868 if (value.merge != null)
868 869 {
869 870 var temp = localStorage.getItem(Editor.configurationKey);
870   -
  871 +
871 872 if (temp != null)
872 873 {
873   -
  874 +
874 875 try
875 876 {
876 877 var config = JSON.parse(temp);
877   -
  878 +
878 879 for (var key in value.merge)
879 880 {
880 881 config[key] = value.merge[key];
881 882 }
882   -
883   - value = config;
  883 +
  884 + value = config;
884 885 }
885 886 catch (e)
886 887 {
887 888 window.location.hash = '';
888 889 alert(e);
889   - }
  890 + }
890 891 }
891 892 else
892 893 {
893 894 value = value.merge;
894 895 }
895 896 }
896   -
  897 +
897 898 if (confirm(mxResources.get('configLinkWarn')) &&
898 899 confirm(mxResources.get('configLinkConfirm')))
899 900 {
... ... @@ -911,21 +912,21 @@ App.main = function(callback, createUi)
911 912 alert(e);
912 913 }
913 914 }
914   -
  915 +
915 916 // Prepares themes with mapping from old default-style to old XML file
916 917 if (xhr.length > 1)
917 918 {
918 919 Graph.prototype.defaultThemes['default-style2'] = xhr[1].getDocumentElement();
919 920 Graph.prototype.defaultThemes['darkTheme'] = xhr[1].getDocumentElement();
920 921 }
921   -
  922 +
922 923 // Main
923 924 function realMain()
924 925 {
925 926 var ui = (createUi != null) ? createUi() : new App(new Editor(
926 927 urlParams['chrome'] == '0' || uiTheme == 'min',
927 928 null, null, null, urlParams['chrome'] != '0'));
928   -
  929 +
929 930 if (window.mxscript != null)
930 931 {
931 932 // Loads dropbox for all browsers but IE8 and below (no CORS) if not disabled or if enabled and in embed mode
... ... @@ -950,7 +951,7 @@ App.main = function(callback, createUi)
950 951 {
951 952 window.DropboxClient = null;
952 953 }
953   -
  954 +
954 955 // Loads OneDrive for all browsers but IE6/IOS if not disabled or if enabled and in embed mode
955 956 if (typeof window.OneDriveClient === 'function' &&
956 957 (typeof OneDrive === 'undefined' && window.DrawOneDriveClientCallback != null &&
... ... @@ -966,7 +967,7 @@ App.main = function(callback, createUi)
966 967 {
967 968 window.OneDriveClient = null;
968 969 }
969   -
  970 +
970 971 // Loads Trello for all browsers but < IE10 if not disabled or if enabled and in embed mode
971 972 if (typeof window.TrelloClient === 'function' && !mxClient.IS_IE11 &&
972 973 typeof window.Trello === 'undefined' && window.DrawTrelloClientCallback != null &&
... ... @@ -987,28 +988,28 @@ App.main = function(callback, createUi)
987 988 {
988 989 window.TrelloClient = null;
989 990 }
990   -
  991 +
991 992 }
992   -
  993 +
993 994 if (callback != null)
994 995 {
995 996 callback(ui);
996 997 }
997   -
  998 +
998 999 /**
999 1000 * For developers only
1000 1001 */
1001 1002 if (urlParams['chrome'] != '0' && urlParams['test'] == '1')
1002 1003 {
1003 1004 EditorUi.debug('App.start', [ui, (new Date().getTime() - t0.getTime()) + 'ms']);
1004   -
  1005 +
1005 1006 if (urlParams['export'] != null)
1006 1007 {
1007 1008 EditorUi.debug('Export:', EXPORT_URL);
1008 1009 }
1009 1010 }
1010 1011 };
1011   -
  1012 +
1012 1013 if (urlParams['dev'] == '1' || EditorUi.isElectronApp) //TODO check if we can remove these scripts loading from index.html
1013 1014 {
1014 1015 realMain();
... ... @@ -1022,11 +1023,11 @@ App.main = function(callback, createUi)
1022 1023 }, function(xhr)
1023 1024 {
1024 1025 var st = document.getElementById('geStatus');
1025   -
  1026 +
1026 1027 if (st != null)
1027 1028 {
1028 1029 st.innerHTML = 'Error loading page. <a>Please try refreshing.</a>';
1029   -
  1030 +
1030 1031 // Tries reload with default resources in case any language resources were not available
1031 1032 st.getElementsByTagName('a')[0].onclick = function()
1032 1033 {
... ... @@ -1047,11 +1048,11 @@ App.main = function(callback, createUi)
1047 1048 {
1048 1049 document.body.style.backgroundColor = (uiTheme == 'dark' ||
1049 1050 mxSettings.settings.darkMode) ? Editor.darkColor : '#ffffff';
1050   -
  1051 +
1051 1052 if (mxSettings.settings.autosaveDelay != null)
1052 1053 {
1053 1054 var val = parseInt(mxSettings.settings.autosaveDelay);
1054   -
  1055 +
1055 1056 if (!isNaN(val) && val > 0)
1056 1057 {
1057 1058 DrawioFile.prototype.autosaveDelay = val;
... ... @@ -1062,11 +1063,11 @@ App.main = function(callback, createUi)
1062 1063 EditorUi.debug('Invalid autosaveDelay', val);
1063 1064 }
1064 1065 }
1065   -
  1066 +
1066 1067 if (mxSettings.settings.defaultEdgeLength != null)
1067 1068 {
1068 1069 var val = parseInt(mxSettings.settings.defaultEdgeLength);
1069   -
  1070 +
1070 1071 if (!isNaN(val) && val > 0)
1071 1072 {
1072 1073 Graph.prototype.defaultEdgeLength = val;
... ... @@ -1093,7 +1094,7 @@ App.main = function(callback, createUi)
1093 1094 for (var i = 0; i < Menus.prototype.defaultFonts.length; i++)
1094 1095 {
1095 1096 var value = Menus.prototype.defaultFonts[i];
1096   -
  1097 +
1097 1098 if (typeof value !== 'string' &&
1098 1099 value.fontFamily != null &&
1099 1100 value.fontUrl != null)
... ... @@ -1102,7 +1103,7 @@ App.main = function(callback, createUi)
1102 1103 }
1103 1104 }
1104 1105 }
1105   -
  1106 +
1106 1107 // Adds required resources (disables loading of fallback properties, this can only
1107 1108 // be used if we know that all keys are defined in the language specific file)
1108 1109 mxResources.loadDefaultBundle = false;
... ... @@ -1114,7 +1115,7 @@ App.main = function(callback, createUi)
1114 1115 if (urlParams['configure'] == '1')
1115 1116 {
1116 1117 var op = window.opener || window.parent;
1117   -
  1118 +
1118 1119 var configHandler = function(evt)
1119 1120 {
1120 1121 if (evt.source == op)
... ... @@ -1122,7 +1123,7 @@ App.main = function(callback, createUi)
1122 1123 try
1123 1124 {
1124 1125 var data = JSON.parse(evt.data);
1125   -
  1126 +
1126 1127 if (data != null && data.action == 'configure')
1127 1128 {
1128 1129 mxEvent.removeListener(window, 'message', configHandler);
... ... @@ -1140,7 +1141,7 @@ App.main = function(callback, createUi)
1140 1141 }
1141 1142 }
1142 1143 };
1143   -
  1144 +
1144 1145 // Receives XML message from opener and puts it into the graph
1145 1146 mxEvent.addListener(window, 'message', configHandler);
1146 1147 op.postMessage(JSON.stringify({event: 'configure'}), '*');
... ... @@ -1166,18 +1167,18 @@ App.main = function(callback, createUi)
1166 1167 }
1167 1168 }
1168 1169 }
1169   -
  1170 +
1170 1171 // Loads configuration from local storage
1171 1172 if (isLocalStorage && localStorage != null && urlParams['embed'] != '1')
1172 1173 {
1173 1174 var configData = localStorage.getItem(Editor.configurationKey);
1174   -
  1175 +
1175 1176 if (configData != null)
1176 1177 {
1177 1178 try
1178 1179 {
1179 1180 configData = JSON.parse(configData);
1180   -
  1181 +
1181 1182 if (configData != null)
1182 1183 {
1183 1184 EditorUi.debug('Using local configuration', configData);
... ... @@ -1195,7 +1196,7 @@ App.main = function(callback, createUi)
1195 1196 }
1196 1197 }
1197 1198 }
1198   -
  1199 +
1199 1200 doMain();
1200 1201 }
1201 1202 };
... ... @@ -1209,7 +1210,7 @@ mxUtils.extend(App, EditorUi);
1209 1210 App.prototype.defaultUserPicture = IMAGE_PATH + '/default-user.jpg';
1210 1211
1211 1212 /**
1212   - *
  1213 + *
1213 1214 */
1214 1215 App.prototype.shareImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowOTgwMTE3NDA3MjA2ODExODhDNkFGMDBEQkQ0RTgwOSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxMjU2NzdEMTcwRDIxMUUxQjc0MDkxRDhCNUQzOEFGRCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxMjU2NzdEMDcwRDIxMUUxQjc0MDkxRDhCNUQzOEFGRCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowNjgwMTE3NDA3MjA2ODExODcxRkM4MUY1OTFDMjQ5OCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowNzgwMTE3NDA3MjA2ODExODhDNkFGMDBEQkQ0RTgwOSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PrM/fs0AAADgSURBVHjaYmDAA/7//88MwgzkAKDGFiD+BsQ/QWxSNaf9RwN37twpI8WAS+gGfP78+RpQSoRYA36iG/D379+vQClNdLVMOMz4gi7w79+/n0CKg1gD9qELvH379hzIHGK9oA508ieY8//8+fO5rq4uFCilRKwL1JmYmNhhHEZGRiZ+fn6Q2meEbDYG4u3/cYCfP38uA7kOm0ZOIJ7zn0jw48ePPiDFhmzArv8kgi9fvuwB+w5qwH9ykjswbFSZyM4sEMDPBDTlL5BxkFSd7969OwZ2BZKYGhDzkmjOJ4AAAwBhpRqGnEFb8QAAAABJRU5ErkJggg==';
1215 1216
... ... @@ -1253,7 +1254,7 @@ App.prototype.compactMode = false;
1253 1254 *
1254 1255 */
1255 1256 App.prototype.fullscreenMode = false;
1256   -
  1257 +
1257 1258 /**
1258 1259 * Overriden UI settings depending on mode.
1259 1260 */
... ... @@ -1275,7 +1276,7 @@ App.initPluginCallback = function()
1275 1276 {
1276 1277 // Workaround for need to load plugins now but wait for UI instance
1277 1278 App.DrawPlugins = [];
1278   -
  1279 +
1279 1280 // Global entry point for plugins is Draw.loadPlugin. This is the only
1280 1281 // long-term supported solution for access to the EditorUi instance.
1281 1282 window.Draw = new Object();
... ... @@ -1287,7 +1288,7 @@ App.initPluginCallback = function()
1287 1288 };
1288 1289
1289 1290 /**
1290   - *
  1291 + *
1291 1292 */
1292 1293 App.pluginsLoaded = {};
1293 1294 App.embedModePluginsCount = 0;
... ... @@ -1306,14 +1307,14 @@ App.loadPlugins = function(plugins, useInclude)
1306 1307 try
1307 1308 {
1308 1309 var url = PLUGINS_BASE_PATH + App.pluginRegistry[plugins[i]];
1309   -
  1310 +
1310 1311 if (url != null)
1311 1312 {
1312 1313 if (App.pluginsLoaded[url] == null)
1313 1314 {
1314 1315 App.pluginsLoaded[url] = true;
1315 1316 App.embedModePluginsCount++;
1316   -
  1317 +
1317 1318 if (typeof window.drawDevUrl === 'undefined')
1318 1319 {
1319 1320 if (useInclude)
... ... @@ -1366,7 +1367,7 @@ App.prototype.initializeEmbedMode = function()
1366 1367 {
1367 1368 this.showBanner('EmbedDeprecationFooter', 'app.diagrams.net will stop working for embed mode. Please use embed.diagrams.net.');
1368 1369 }
1369   -
  1370 +
1370 1371 if (App.embedModePluginsCount > 0 || this.initEmbedDone)
1371 1372 {
1372 1373 return; //Wait for plugins to load, or this is a duplicate call due to timeout
... ... @@ -1375,7 +1376,7 @@ App.prototype.initializeEmbedMode = function()
1375 1376 {
1376 1377 this.initEmbedDone = true;
1377 1378 }
1378   -
  1379 +
1379 1380 EditorUi.prototype.initializeEmbedMode.apply(this, arguments);
1380 1381 }
1381 1382 };
... ... @@ -1413,7 +1414,7 @@ App.prototype.init = function()
1413 1414
1414 1415 /**
1415 1416 * Holds the listener for description changes.
1416   - */
  1417 + */
1417 1418 this.descriptorChangedListener = mxUtils.bind(this, this.descriptorChanged);
1418 1419
1419 1420 /**
... ... @@ -1423,7 +1424,7 @@ App.prototype.init = function()
1423 1424 mxClient.IS_IE11 || mxClient.IS_EDGE) &&
1424 1425 (urlParams['gh'] != '0' && (urlParams['embed'] != '1' ||
1425 1426 urlParams['gh'] == '1')) ? new GitHubClient(this) : null;
1426   -
  1427 +
1427 1428 if (this.gitHub != null)
1428 1429 {
1429 1430 this.gitHub.addListener('userChanged', mxUtils.bind(this, function()
... ... @@ -1432,7 +1433,7 @@ App.prototype.init = function()
1432 1433 this.restoreLibraries();
1433 1434 }));
1434 1435 }
1435   -
  1436 +
1436 1437 /**
1437 1438 * Creates gitlab client.
1438 1439 */
... ... @@ -1466,13 +1467,13 @@ App.prototype.init = function()
1466 1467 * Holds the x-coordinate of the point.
1467 1468 */
1468 1469 this.oneDrive = new OneDriveClient(this);
1469   -
  1470 +
1470 1471 this.oneDrive.addListener('userChanged', mxUtils.bind(this, function()
1471 1472 {
1472 1473 this.updateUserElement();
1473 1474 this.restoreLibraries();
1474 1475 }));
1475   -
  1476 +
1476 1477 // Notifies listeners of new client
1477 1478 this.fireEvent(new mxEventObject('clientLoaded', 'client', this.oneDrive));
1478 1479 }
... ... @@ -1500,14 +1501,14 @@ App.prototype.init = function()
1500 1501 try
1501 1502 {
1502 1503 this.trello = new TrelloClient(this);
1503   -
  1504 +
1504 1505 //TODO we have no user info from Trello so we don't set a user
1505 1506 this.trello.addListener('userChanged', mxUtils.bind(this, function()
1506 1507 {
1507 1508 this.updateUserElement();
1508 1509 this.restoreLibraries();
1509 1510 }));
1510   -
  1511 +
1511 1512 // Notifies listeners of new client
1512 1513 this.fireEvent(new mxEventObject('clientLoaded', 'client', this.trello));
1513 1514 }
... ... @@ -1543,22 +1544,22 @@ App.prototype.init = function()
1543 1544 var doInit = mxUtils.bind(this, function()
1544 1545 {
1545 1546 this.drive = new DriveClient(this);
1546   -
  1547 +
1547 1548 this.drive.addListener('userChanged', mxUtils.bind(this, function()
1548 1549 {
1549 1550 this.updateUserElement();
1550 1551 this.restoreLibraries();
1551 1552 this.checkLicense();
1552 1553 }))
1553   -
  1554 +
1554 1555 // Notifies listeners of new client
1555 1556 this.fireEvent(new mxEventObject('clientLoaded', 'client', this.drive));
1556 1557 });
1557   -
  1558 +
1558 1559 if (window.DrawGapiClientCallback != null)
1559 1560 {
1560 1561 gapi.load(((urlParams['picker'] != '0') ? 'picker,': '') + App.GOOGLE_APIS, doInit);
1561   -
  1562 +
1562 1563 /**
1563 1564 * Clears any callbacks.
1564 1565 */
... ... @@ -1574,7 +1575,7 @@ App.prototype.init = function()
1574 1575 window.DrawGapiClientCallback = initDriveClient;
1575 1576 }
1576 1577 });
1577   -
  1578 +
1578 1579 initDriveClient();
1579 1580 }
1580 1581
... ... @@ -1591,20 +1592,20 @@ App.prototype.init = function()
1591 1592 * Clears dropbox client callback.
1592 1593 */
1593 1594 window.DrawDropboxClientCallback = null;
1594   -
  1595 +
1595 1596 /**
1596 1597 * Holds the x-coordinate of the point.
1597 1598 */
1598 1599 try
1599 1600 {
1600 1601 this.dropbox = new DropboxClient(this);
1601   -
  1602 +
1602 1603 this.dropbox.addListener('userChanged', mxUtils.bind(this, function()
1603 1604 {
1604 1605 this.updateUserElement();
1605 1606 this.restoreLibraries();
1606 1607 }));
1607   -
  1608 +
1608 1609 // Notifies listeners of new client
1609 1610 this.fireEvent(new mxEventObject('clientLoaded', 'client', this.dropbox));
1610 1611 }
... ... @@ -1647,7 +1648,7 @@ App.prototype.init = function()
1647 1648 {
1648 1649 this.mode = App.mode;
1649 1650 }
1650   -
  1651 +
1651 1652 // Add to Home Screen dialog for mobile devices
1652 1653 if ('serviceWorker' in navigator && !this.editor.isChromelessView() &&
1653 1654 (mxClient.IS_ANDROID || mxClient.IS_IOS))
... ... @@ -1660,7 +1661,7 @@ App.prototype.init = function()
1660 1661 });
1661 1662 }));
1662 1663 }
1663   -
  1664 +
1664 1665 if (!mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp && !this.isOffline() &&
1665 1666 !mxClient.IS_ANDROID && !mxClient.IS_IOS && urlParams['open'] == null &&
1666 1667 (!this.editor.chromeless || this.editor.editable))
... ... @@ -1669,7 +1670,7 @@ App.prototype.init = function()
1669 1670 {
1670 1671 var file = this.getCurrentFile();
1671 1672 var mode = (file != null) ? file.getMode() : null;
1672   -
  1673 +
1673 1674 if (urlParams['extAuth'] != '1' && (mode == App.MODE_DEVICE || mode == App.MODE_BROWSER))
1674 1675 {
1675 1676 //关闭桌面弹出广告
... ... @@ -1683,38 +1684,38 @@ App.prototype.init = function()
1683 1684 }
1684 1685 }));
1685 1686 }
1686   -
  1687 +
1687 1688 if (!mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp && urlParams['embed'] != '1' && DrawioFile.SYNC == 'auto' &&
1688 1689 urlParams['local'] != '1' && urlParams['stealth'] != '1' && !this.isOffline() &&
1689 1690 (!this.editor.chromeless || this.editor.editable))
1690 1691 {
1691 1692 // Checks if the cache is alive
1692 1693 var acceptResponse = true;
1693   -
  1694 +
1694 1695 var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
1695 1696 {
1696 1697 acceptResponse = false;
1697   -
  1698 +
1698 1699 // Switches to manual sync if cache cannot be reached
1699 1700 DrawioFile.SYNC = 'manual';
1700   -
  1701 +
1701 1702 var file = this.getCurrentFile();
1702   -
  1703 +
1703 1704 if (file != null && file.sync != null)
1704 1705 {
1705 1706 file.sync.destroy();
1706 1707 file.sync = null;
1707   -
  1708 +
1708 1709 var status = mxUtils.htmlEntities(mxResources.get('timeout'));
1709 1710 this.editor.setStatus('<div title="'+ status +
1710 1711 '" class="geStatusAlert">' + status + '</div>');
1711 1712 }
1712   -
  1713 +
1713 1714 EditorUi.logEvent({category: 'TIMEOUT-CACHE-CHECK', action: 'timeout', label: 408});
1714 1715 }), Editor.cacheTimeout);
1715   -
  1716 +
1716 1717 var t0 = new Date().getTime();
1717   -
  1718 +
1718 1719 mxUtils.get(EditorUi.cacheUrl + '?alive', mxUtils.bind(this, function(req)
1719 1720 {
1720 1721 window.clearTimeout(timeoutThread);
... ... @@ -1735,7 +1736,7 @@ App.prototype.init = function()
1735 1736 this.buttonContainer.style.paddingRight = '48px';
1736 1737 this.buttonContainer.style.position = 'absolute';
1737 1738 this.buttonContainer.style.right = '0px';
1738   -
  1739 +
1739 1740 this.menubar.container.appendChild(this.buttonContainer);
1740 1741 }
1741 1742
... ... @@ -1746,21 +1747,21 @@ App.prototype.init = function()
1746 1747 this.toggleElement.click();
1747 1748 this.toggleElement.style.display = 'none';
1748 1749 }
1749   -
  1750 +
1750 1751 this.icon = document.createElement('img');
1751 1752 this.icon.setAttribute('src', IMAGE_PATH + '/logo-flat-small.png');
1752 1753 this.icon.setAttribute('title', mxResources.get('draw.io'));
1753 1754 this.icon.style.padding = urlParams['atlas'] == '1'? '7px' : '6px';
1754 1755 this.icon.style.cursor = 'pointer';
1755   -
  1756 +
1756 1757 mxEvent.addListener(this.icon, 'click', mxUtils.bind(this, function(evt)
1757 1758 {
1758 1759 this.appIconClicked(evt);
1759 1760 }));
1760   -
  1761 +
1761 1762 this.menubar.container.insertBefore(this.icon, this.menubar.container.firstChild);
1762 1763 }
1763   -
  1764 +
1764 1765 if (this.editor.graph.isViewer())
1765 1766 {
1766 1767 this.initializeViewerMode();
... ... @@ -1820,22 +1821,22 @@ App.prototype.sanityCheck = function()
1820 1821 '-change_' + ((file.lastChanged != null) ? Math.round((Date.now() - file.lastChanged.getTime()) / 1000) : 'x')+
1821 1822 '-alive_' + Math.round((Date.now() - App.startTime.getTime()) / 1000),
1822 1823 label: (file.sync != null) ? ('client_' + file.sync.clientId) : 'nosync'};
1823   -
  1824 +
1824 1825 if (file.constructor == DriveFile && file.desc != null && this.drive != null)
1825 1826 {
1826 1827 evt.label += ((this.drive.user != null) ? ('-user_' + this.drive.user.id) : '-nouser') + '-rev_' +
1827 1828 file.desc.headRevisionId + '-mod_' + file.desc.modifiedDate + '-size_' + file.getSize() +
1828 1829 '-mime_' + file.desc.mimeType;
1829 1830 }
1830   -
  1831 +
1831 1832 EditorUi.logEvent(evt);
1832 1833
1833 1834 var msg = mxResources.get('ensureDataSaved');
1834   -
  1835 +
1835 1836 if (file.lastSaved != null)
1836 1837 {
1837 1838 var str = this.timeSince(file.lastSaved);
1838   -
  1839 +
1839 1840 if (str == null)
1840 1841 {
1841 1842 str = mxResources.get('lessThanAMinute');
... ... @@ -1843,7 +1844,7 @@ App.prototype.sanityCheck = function()
1843 1844
1844 1845 msg = mxResources.get('lastSaved', [str]);
1845 1846 }
1846   -
  1847 +
1847 1848 // Resets possible stale state
1848 1849 this.spinner.stop();
1849 1850
... ... @@ -1889,7 +1890,7 @@ App.prototype.getPusher = function()
1889 1890 encrypted: true
1890 1891 });
1891 1892 }
1892   -
  1893 +
1893 1894 return this.pusher;
1894 1895 };
1895 1896
... ... @@ -1942,14 +1943,14 @@ App.prototype.showRatingBanner = function()
1942 1943 mxUtils.setPrefixedStyle(banner.style, 'transform', 'translate(-50%,120%)');
1943 1944 mxUtils.setPrefixedStyle(banner.style, 'transition', 'all 1s ease');
1944 1945 banner.className = 'geBtn gePrimaryBtn';
1945   -
  1946 +
1946 1947 var img = document.createElement('img');
1947 1948 img.setAttribute('src', Dialog.prototype.closeImage);
1948 1949 img.setAttribute('title', mxResources.get('close'));
1949 1950 img.setAttribute('border', '0');
1950 1951 img.style.cssText = 'position:absolute;right:10px;top:12px;filter:invert(1);padding:6px;margin:-6px;cursor:default;';
1951 1952 banner.appendChild(img);
1952   -
  1953 +
1953 1954 var star = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZ' +
1954 1955 'XdvcmtzIENTM5jWRgMAAAQRdEVYdFhNTDpjb20uYWRvYmUueG1wADw/eHBhY2tldCBiZWdpbj0iICAgIiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8i' +
1955 1956 'IHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDQuMS1jMDM0IDQ2LjI3Mjk3NiwgU2F0IEphbiAyNyAyMDA3IDIyOjExOjQxICAgICAgICAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDI' +
... ... @@ -1967,7 +1968,7 @@ App.prototype.showRatingBanner = function()
1967 1968
1968 1969 mxUtils.write(banner, 'Please rate us');
1969 1970 document.body.appendChild(banner);
1970   -
  1971 +
1971 1972 var star1 = document.createElement('img');
1972 1973 star1.setAttribute('border', '0');
1973 1974 star1.setAttribute('align', 'absmiddle');
... ... @@ -1975,7 +1976,7 @@ App.prototype.showRatingBanner = function()
1975 1976 star1.setAttribute('style', 'margin-top:-6px;cursor:pointer;margin-left:8px;');
1976 1977 star1.setAttribute('src', star);
1977 1978 banner.appendChild(star1);
1978   -
  1979 +
1979 1980 var star2 = document.createElement('img');
1980 1981 star2.setAttribute('border', '0');
1981 1982 star2.setAttribute('align', 'absmiddle');
... ... @@ -1983,7 +1984,7 @@ App.prototype.showRatingBanner = function()
1983 1984 star2.setAttribute('style', 'margin-top:-6px;margin-left:3px;cursor:pointer;');
1984 1985 star2.setAttribute('src', star);
1985 1986 banner.appendChild(star2);
1986   -
  1987 +
1987 1988 var star3 = document.createElement('img');
1988 1989 star3.setAttribute('border', '0');
1989 1990 star3.setAttribute('align', 'absmiddle');
... ... @@ -1991,7 +1992,7 @@ App.prototype.showRatingBanner = function()
1991 1992 star3.setAttribute('style', 'margin-top:-6px;margin-left:3px;cursor:pointer;');
1992 1993 star3.setAttribute('src', star);
1993 1994 banner.appendChild(star3);
1994   -
  1995 +
1995 1996 var star4 = document.createElement('img');
1996 1997 star4.setAttribute('border', '0');
1997 1998 star4.setAttribute('align', 'absmiddle');
... ... @@ -1999,16 +2000,16 @@ App.prototype.showRatingBanner = function()
1999 2000 star4.setAttribute('style', 'margin-top:-6px;margin-left:3px;cursor:pointer;');
2000 2001 star4.setAttribute('src', star);
2001 2002 banner.appendChild(star4);
2002   -
  2003 +
2003 2004 this.bannerShowing = true;
2004   -
  2005 +
2005 2006 var onclose = mxUtils.bind(this, function()
2006 2007 {
2007 2008 if (banner.parentNode != null)
2008 2009 {
2009 2010 banner.parentNode.removeChild(banner);
2010 2011 this.bannerShowing = false;
2011   -
  2012 +
2012 2013 this['hideBanner' + 'ratingFooter'] = true;
2013 2014
2014 2015 if (isLocalStorage && mxSettings.settings != null)
... ... @@ -2018,7 +2019,7 @@ App.prototype.showRatingBanner = function()
2018 2019 }
2019 2020 }
2020 2021 });
2021   -
  2022 +
2022 2023 mxEvent.addListener(img, 'click', mxUtils.bind(this, function(e)
2023 2024 {
2024 2025 mxEvent.consume(e);
... ... @@ -2049,32 +2050,32 @@ App.prototype.showRatingBanner = function()
2049 2050 var hide = mxUtils.bind(this, function()
2050 2051 {
2051 2052 mxUtils.setPrefixedStyle(banner.style, 'transform', 'translate(-50%,120%)');
2052   -
  2053 +
2053 2054 window.setTimeout(mxUtils.bind(this, function()
2054 2055 {
2055 2056 onclose();
2056 2057 }), 1000);
2057 2058 });
2058   -
  2059 +
2059 2060 window.setTimeout(mxUtils.bind(this, function()
2060 2061 {
2061 2062 mxUtils.setPrefixedStyle(banner.style, 'transform', 'translate(-50%,0%)');
2062 2063 }), 500);
2063   -
  2064 +
2064 2065 window.setTimeout(hide, 60000);
2065 2066 }
2066 2067 };
2067 2068
2068 2069 /**
2069 2070 * Checks license in the case of Google Drive storage.
2070   - * IMPORTANT: Do not change this function without consulting
  2071 + * IMPORTANT: Do not change this function without consulting
2071 2072 * the privacy lead. No personal information must be sent.
2072 2073 */
2073 2074 App.prototype.checkLicense = function()
2074 2075 {
2075 2076 var driveUser = this.drive.getUser();
2076 2077 var email = (driveUser != null) ? driveUser.email : null;
2077   -
  2078 +
2078 2079 if (!this.isOffline() && !this.editor.chromeless && email != null && driveUser.id != null)
2079 2080 {
2080 2081 // Only the domain and hashed user ID are transmitted. This code was reviewed and deemed
... ... @@ -2082,9 +2083,9 @@ App.prototype.checkLicense = function()
2082 2083 var at = email.lastIndexOf('@');
2083 2084 var domain = (at >= 0) ? email.substring(at + 1) : '';
2084 2085 var userId = Editor.crc32(driveUser.id);
2085   -
  2086 +
2086 2087 // Timestamp is workaround for cached response in certain environments
2087   - mxUtils.post('/license', 'domain=' + encodeURIComponent(domain) + '&id=' + encodeURIComponent(userId) +
  2088 + mxUtils.post('/license', 'domain=' + encodeURIComponent(domain) + '&id=' + encodeURIComponent(userId) +
2088 2089 '&ts=' + new Date().getTime(),
2089 2090 mxUtils.bind(this, function(req)
2090 2091 {
... ... @@ -2093,11 +2094,11 @@ App.prototype.checkLicense = function()
2093 2094 if (req.getStatus() >= 200 && req.getStatus() <= 299)
2094 2095 {
2095 2096 var value = req.getText();
2096   -
  2097 +
2097 2098 if (value.length > 0)
2098 2099 {
2099 2100 var lic = JSON.parse(value);
2100   -
  2101 +
2101 2102 if (lic != null)
2102 2103 {
2103 2104 this.handleLicense(lic, domain);
... ... @@ -2125,12 +2126,12 @@ App.prototype.handleLicense = function(lic, domain)
2125 2126 };
2126 2127
2127 2128 /**
2128   - *
  2129 + *
2129 2130 */
2130 2131 App.prototype.getEditBlankXml = function()
2131 2132 {
2132 2133 var file = this.getCurrentFile();
2133   -
  2134 +
2134 2135 if (file != null && this.editor.isChromelessView() && this.editor.graph.isLightboxView())
2135 2136 {
2136 2137 return file.getData();
... ... @@ -2159,7 +2160,7 @@ App.prototype.addRecent = function(entry)
2159 2160 if (isLocalStorage && localStorage != null)
2160 2161 {
2161 2162 var recent = this.getRecent();
2162   -
  2163 +
2163 2164 if (recent == null)
2164 2165 {
2165 2166 recent = [];
... ... @@ -2174,7 +2175,7 @@ App.prototype.addRecent = function(entry)
2174 2175 }
2175 2176 }
2176 2177 }
2177   -
  2178 +
2178 2179 if (recent != null)
2179 2180 {
2180 2181 recent.unshift(entry);
... ... @@ -2194,7 +2195,7 @@ App.prototype.getRecent = function()
2194 2195 try
2195 2196 {
2196 2197 var recent = localStorage.getItem('.recent');
2197   -
  2198 +
2198 2199 if (recent != null)
2199 2200 {
2200 2201 return JSON.parse(recent);
... ... @@ -2204,7 +2205,7 @@ App.prototype.getRecent = function()
2204 2205 {
2205 2206 // ignore
2206 2207 }
2207   -
  2208 +
2208 2209 return null;
2209 2210 }
2210 2211 };
... ... @@ -2239,7 +2240,7 @@ App.prototype.onBeforeUnload = function()
2239 2240 else
2240 2241 {
2241 2242 var file = this.getCurrentFile();
2242   -
  2243 +
2243 2244 if (file != null)
2244 2245 {
2245 2246 // KNOWN: Message is ignored by most browsers
... ... @@ -2263,7 +2264,7 @@ App.prototype.onBeforeUnload = function()
2263 2264
2264 2265 /**
2265 2266 * Translates this point by the given vector.
2266   - *
  2267 + *
2267 2268 * @param {number} dx X-coordinate of the translation.
2268 2269 * @param {number} dy Y-coordinate of the translation.
2269 2270 */
... ... @@ -2273,18 +2274,18 @@ App.prototype.updateDocumentTitle = function()
2273 2274 {
2274 2275 var title = this.editor.appName;
2275 2276 var file = this.getCurrentFile();
2276   -
  2277 +
2277 2278 if (this.isOfflineApp())
2278 2279 {
2279 2280 title += ' app';
2280 2281 }
2281   -
  2282 +
2282 2283 if (file != null)
2283 2284 {
2284 2285 var filename = (file.getTitle() != null) ? file.getTitle() : this.defaultFilename;
2285 2286 title = filename + ' - ' + title;
2286 2287 }
2287   -
  2288 +
2288 2289 if (document.title != title)
2289 2290 {
2290 2291 document.title = title;
... ... @@ -2301,32 +2302,32 @@ App.prototype.updateDocumentTitle = function()
2301 2302 App.prototype.getThumbnail = function(width, fn)
2302 2303 {
2303 2304 var result = false;
2304   -
  2305 +
2305 2306 try
2306 2307 {
2307 2308 var acceptResponse = true;
2308   -
  2309 +
2309 2310 var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
2310 2311 {
2311 2312 acceptResponse = false;
2312 2313 fn(null);
2313 2314 }), this.timeout);
2314   -
  2315 +
2315 2316 var success = mxUtils.bind(this, function(canvas)
2316 2317 {
2317 2318 window.clearTimeout(timeoutThread);
2318   -
  2319 +
2319 2320 if (acceptResponse)
2320 2321 {
2321 2322 fn(canvas);
2322 2323 }
2323 2324 });
2324   -
  2325 +
2325 2326 if (this.thumbImageCache == null)
2326 2327 {
2327 2328 this.thumbImageCache = new Object();
2328 2329 }
2329   -
  2330 +
2330 2331 var graph = this.editor.graph;
2331 2332 var bgImg = graph.backgroundImage;
2332 2333
... ... @@ -2362,15 +2363,15 @@ App.prototype.getThumbnail = function(width, fn)
2362 2363 {
2363 2364 return 1;
2364 2365 }
2365   -
  2366 +
2366 2367 return graphGetGlobalVariable.apply(this, arguments);
2367 2368 };
2368   -
  2369 +
2369 2370 graph.getGlobalVariable = graphGetGlobalVariable;
2370 2371 document.body.appendChild(graph.container);
2371 2372 graph.model.setRoot(page.root);
2372 2373 }
2373   -
  2374 +
2374 2375 // Uses client-side canvas export
2375 2376 if (mxClient.IS_CHROMEAPP || this.useCanvasForExport)
2376 2377 {
... ... @@ -2388,7 +2389,7 @@ App.prototype.getThumbnail = function(width, fn)
2388 2389 {
2389 2390 canvas = null;
2390 2391 }
2391   -
  2392 +
2392 2393 success(canvas);
2393 2394 }), width, this.thumbImageCache, '#ffffff', function()
2394 2395 {
... ... @@ -2396,7 +2397,7 @@ App.prototype.getThumbnail = function(width, fn)
2396 2397 success();
2397 2398 }, null, null, null, null, null, null, graph, null, null, null,
2398 2399 null, 'diagram', null);
2399   -
  2400 +
2400 2401 result = true;
2401 2402 }
2402 2403 else if (this.canvasSupported && this.getCurrentFile() != null)
... ... @@ -2418,33 +2419,33 @@ App.prototype.getThumbnail = function(width, fn)
2418 2419
2419 2420 // Limits scale to 1 or 2 * width / height
2420 2421 scale = Math.min(1, Math.min((width * 3) / (bounds.height * 4), scale));
2421   -
  2422 +
2422 2423 var x0 = Math.floor(bounds.x);
2423 2424 var y0 = Math.floor(bounds.y);
2424   -
  2425 +
2425 2426 canvas.setAttribute('width', Math.ceil(scale * (bounds.width + 4)));
2426 2427 canvas.setAttribute('height', Math.ceil(scale * (bounds.height + 4)));
2427   -
  2428 +
2428 2429 var ctx = canvas.getContext('2d');
2429   -
  2430 +
2430 2431 // Configures the canvas
2431 2432 ctx.scale(scale, scale);
2432 2433 ctx.translate(-x0, -y0);
2433   -
  2434 +
2434 2435 // Paint white background instead of transparent
2435 2436 var bg = graph.background;
2436   -
  2437 +
2437 2438 if (bg == null || bg == '' || bg == mxConstants.NONE)
2438 2439 {
2439 2440 bg = '#ffffff';
2440 2441 }
2441   -
  2442 +
2442 2443 // Paints background
2443 2444 ctx.save();
2444 2445 ctx.fillStyle = bg;
2445 2446 ctx.fillRect(x0, y0, Math.ceil(bounds.width + 4), Math.ceil(bounds.height + 4));
2446 2447 ctx.restore();
2447   -
  2448 +
2448 2449 // Paints background image
2449 2450 if (bgImg != null)
2450 2451 {
... ... @@ -2454,9 +2455,9 @@ App.prototype.getThumbnail = function(width, fn)
2454 2455 ctx.drawImage(img, bgImg.x * scale, bgImg.y * scale,
2455 2456 bgImg.width * scale, bgImg.height * scale);
2456 2457 }
2457   -
  2458 +
2458 2459 var htmlCanvas = new mxJsCanvas(canvas);
2459   -
  2460 +
2460 2461 // NOTE: htmlCanvas passed into async canvas is only used for image
2461 2462 // and canvas caching (canvas caching not used in this case as we do
2462 2463 // not render text). To reuse that cache via the thumbImageCache we
... ... @@ -2465,7 +2466,7 @@ App.prototype.getThumbnail = function(width, fn)
2465 2466 // LATER: Is clear thumbImageCache needed if file changes?
2466 2467 var asynCanvas = new mxAsyncCanvas(this.thumbImageCache);
2467 2468 htmlCanvas.images = this.thumbImageCache.images;
2468   -
  2469 +
2469 2470 // Render graph
2470 2471 var imgExport = new mxImageExport();
2471 2472
... ... @@ -2480,20 +2481,20 @@ App.prototype.getThumbnail = function(width, fn)
2480 2481 canvas.restore();
2481 2482 }
2482 2483 };
2483   -
  2484 +
2484 2485 imgExport.drawText = function(state, canvas)
2485 2486 {
2486 2487 // No text output for thumbnails
2487 2488 };
2488   -
  2489 +
2489 2490 imgExport.drawState(graph.getView().getState(graph.model.root), asynCanvas);
2490   -
  2491 +
2491 2492 asynCanvas.finish(mxUtils.bind(this, function()
2492 2493 {
2493 2494 try
2494 2495 {
2495 2496 imgExport.drawState(graph.getView().getState(graph.model.root), htmlCanvas);
2496   -
  2497 +
2497 2498 // Removes temporary graph from DOM
2498 2499 if (graph != this.editor.graph && graph.container.parentNode != null)
2499 2500 {
... ... @@ -2507,32 +2508,32 @@ App.prototype.getThumbnail = function(width, fn)
2507 2508
2508 2509 success(canvas);
2509 2510 }));
2510   -
  2511 +
2511 2512 result = true;
2512 2513 }
2513 2514 }
2514 2515 catch (e)
2515 2516 {
2516 2517 result = false;
2517   -
  2518 +
2518 2519 // Removes temporary graph from DOM
2519 2520 if (graph != null && graph != this.editor.graph && graph.container.parentNode != null)
2520 2521 {
2521 2522 graph.container.parentNode.removeChild(graph.container);
2522 2523 }
2523 2524 }
2524   -
  2525 +
2525 2526 if (!result)
2526 2527 {
2527 2528 window.clearTimeout(timeoutThread);
2528 2529 }
2529   -
  2530 +
2530 2531 return result;
2531 2532 };
2532 2533
2533 2534 /**
2534 2535 * Translates this point by the given vector.
2535   - *
  2536 + *
2536 2537 * @param {number} dx X-coordinate of the translation.
2537 2538 * @param {number} dy Y-coordinate of the translation.
2538 2539 */
... ... @@ -2545,26 +2546,26 @@ App.prototype.createBackground = function()
2545 2546 bg.style.top = '0px';
2546 2547 bg.style.bottom = '0px';
2547 2548 bg.style.right = '0px';
2548   -
  2549 +
2549 2550 mxUtils.setOpacity(bg, 100);
2550   -
  2551 +
2551 2552 return bg;
2552 2553 };
2553 2554
2554 2555 /**
2555 2556 * Translates this point by the given vector.
2556   - *
  2557 + *
2557 2558 * @param {number} dx X-coordinate of the translation.
2558 2559 * @param {number} dy Y-coordinate of the translation.
2559 2560 */
2560 2561 (function()
2561 2562 {
2562 2563 var editorUiSetMode = EditorUi.prototype.setMode;
2563   -
  2564 +
2564 2565 App.prototype.setMode = function(mode, remember)
2565 2566 {
2566 2567 editorUiSetMode.apply(this, arguments);
2567   -
  2568 +
2568 2569 // Note: UseLocalStorage affects the file dialogs
2569 2570 // and should not be modified if mode is undefined
2570 2571 if (this.mode != null)
... ... @@ -2576,7 +2577,7 @@ App.prototype.createBackground = function()
2576 2577 {
2577 2578 var file = this.getCurrentFile();
2578 2579 mode = (file != null) ? file.getMode() : mode;
2579   -
  2580 +
2580 2581 if (mode == App.MODE_GOOGLE)
2581 2582 {
2582 2583 this.appIcon.setAttribute('title', mxResources.get('openIt', [mxResources.get('googleDrive')]));
... ... @@ -2598,7 +2599,7 @@ App.prototype.createBackground = function()
2598 2599 this.appIcon.style.cursor = (mode == App.MODE_DEVICE) ? 'pointer' : 'default';
2599 2600 }
2600 2601 }
2601   -
  2602 +
2602 2603 if (remember)
2603 2604 {
2604 2605 try
... ... @@ -2624,7 +2625,7 @@ App.prototype.createBackground = function()
2624 2625
2625 2626 /**
2626 2627 * Function: authorize
2627   - *
  2628 + *
2628 2629 * Authorizes the client, gets the userId and calls <open>.
2629 2630 */
2630 2631 App.prototype.appIconClicked = function(evt)
... ... @@ -2637,7 +2638,7 @@ App.prototype.appIconClicked = function(evt)
2637 2638 {
2638 2639 var file = this.getCurrentFile();
2639 2640 var mode = (file != null) ? file.getMode() : null;
2640   -
  2641 +
2641 2642 if (mode == App.MODE_GOOGLE)
2642 2643 {
2643 2644 if (file != null && file.desc != null && file.desc.parents != null &&
... ... @@ -2661,12 +2662,12 @@ App.prototype.appIconClicked = function(evt)
2661 2662 {
2662 2663 var url = file.meta.webUrl;
2663 2664 var name = encodeURIComponent(file.meta.name);
2664   -
  2665 +
2665 2666 if (url.substring(url.length - name.length, url.length) == name)
2666 2667 {
2667 2668 url = url.substring(0, url.length - name.length);
2668 2669 }
2669   -
  2670 +
2670 2671 this.openLink(url);
2671 2672 }
2672 2673 else
... ... @@ -2679,12 +2680,12 @@ App.prototype.appIconClicked = function(evt)
2679 2680 if (file != null && file.stat != null && file.stat.path_display != null)
2680 2681 {
2681 2682 var url = 'https://www.dropbox.com/home/Apps/drawio' + file.stat.path_display;
2682   -
  2683 +
2683 2684 if (!mxEvent.isShiftDown(evt))
2684 2685 {
2685 2686 url = url.substring(0, url.length - file.stat.name.length);
2686 2687 }
2687   -
  2688 +
2688 2689 this.openLink(url);
2689 2690 }
2690 2691 else
... ... @@ -2723,13 +2724,13 @@ App.prototype.appIconClicked = function(evt)
2723 2724 this.openLink('https://get.draw.io/');
2724 2725 }
2725 2726 }
2726   -
  2727 +
2727 2728 mxEvent.consume(evt);
2728 2729 };
2729 2730
2730 2731 /**
2731 2732 * Function: authorize
2732   - *
  2733 + *
2733 2734 * Authorizes the client, gets the userId and calls <open>.
2734 2735 */
2735 2736 App.prototype.clearMode = function()
... ... @@ -2748,31 +2749,31 @@ App.prototype.clearMode = function()
2748 2749
2749 2750 /**
2750 2751 * Translates this point by the given vector.
2751   - *
  2752 + *
2752 2753 * @param {number} dx X-coordinate of the translation.
2753 2754 * @param {number} dy Y-coordinate of the translation.
2754 2755 */
2755 2756 App.prototype.getDiagramId = function()
2756 2757 {
2757 2758 var id = window.location.hash;
2758   -
  2759 +
2759 2760 // Strips the hash sign
2760 2761 if (id != null && id.length > 0)
2761 2762 {
2762 2763 id = id.substring(1);
2763 2764 }
2764   -
  2765 +
2765 2766 // Workaround for Trello client appending data after hash
2766 2767 if (id != null && id.length > 1 && id.charAt(0) == 'T')
2767 2768 {
2768 2769 var idx = id.indexOf('#');
2769   -
  2770 +
2770 2771 if (idx > 0)
2771 2772 {
2772 2773 id = id.substring(0, idx);
2773 2774 }
2774 2775 }
2775   -
  2776 +
2776 2777 return id;
2777 2778 };
2778 2779
... ... @@ -2792,12 +2793,12 @@ App.prototype.open = function()
2792 2793 if (window.opener != null)
2793 2794 {
2794 2795 var value = urlParams['create'];
2795   -
  2796 +
2796 2797 if (value != null)
2797 2798 {
2798 2799 value = decodeURIComponent(value);
2799 2800 }
2800   -
  2801 +
2801 2802 if (value != null && value.length > 0 && value.substring(0, 7) != 'http://' &&
2802 2803 value.substring(0, 8) != 'https://')
2803 2804 {
... ... @@ -2809,12 +2810,12 @@ App.prototype.open = function()
2809 2810 window.opener.openFile.setConsumer(mxUtils.bind(this, function(xml, filename, temp)
2810 2811 {
2811 2812 this.spinner.stop();
2812   -
  2813 +
2813 2814 if (filename == null)
2814 2815 {
2815 2816 var title = urlParams['title'];
2816 2817 temp = true;
2817   -
  2818 +
2818 2819 if (title != null)
2819 2820 {
2820 2821 filename = decodeURIComponent(title);
... ... @@ -2824,15 +2825,15 @@ App.prototype.open = function()
2824 2825 filename = this.defaultFilename;
2825 2826 }
2826 2827 }
2827   -
  2828 +
2828 2829 // Replaces PNG with XML extension
2829 2830 var dot = (!this.useCanvasForExport) ? filename.substring(filename.length - 4) == '.png' : -1;
2830   -
  2831 +
2831 2832 if (dot > 0)
2832 2833 {
2833 2834 filename = filename.substring(0, filename.length - 4) + '.drawio';
2834 2835 }
2835   -
  2836 +
2836 2837 this.fileLoaded((mxClient.IS_IOS) ?
2837 2838 new StorageFile(this, xml, filename) :
2838 2839 new LocalFile(this, xml, filename, temp));
... ... @@ -2856,7 +2857,7 @@ App.prototype.loadGapi = function(then)
2856 2857
2857 2858 /**
2858 2859 * Main function. Program starts here.
2859   - *
  2860 + *
2860 2861 * @param {number} dx X-coordinate of the translation.
2861 2862 * @param {number} dy Y-coordinate of the translation.
2862 2863 */
... ... @@ -2875,9 +2876,9 @@ App.prototype.load = function()
2875 2876 {
2876 2877 // ignores invalid state args
2877 2878 }
2878   -
  2879 +
2879 2880 this.editor.graph.setEnabled(this.getCurrentFile() != null);
2880   -
  2881 +
2881 2882 // Passes the userId from the state parameter to the client
2882 2883 if ((window.location.hash == null || window.location.hash.length == 0) &&
2883 2884 this.drive != null && this.stateArg != null && this.stateArg.userId != null)
... ... @@ -2900,7 +2901,7 @@ App.prototype.load = function()
2900 2901 {
2901 2902 this.mode = null;
2902 2903 }
2903   -
  2904 +
2904 2905 this.start();
2905 2906 }
2906 2907 else
... ... @@ -2916,7 +2917,7 @@ App.prototype.load = function()
2916 2917 else
2917 2918 {
2918 2919 this.restoreLibraries();
2919   -
  2920 +
2920 2921 if (urlParams['gapi'] == '1')
2921 2922 {
2922 2923 this.loadGapi(function() {});
... ... @@ -2938,17 +2939,17 @@ App.prototype.showRefreshDialog = function(title, message)
2938 2939 mxResources.get('refresh'), mxUtils.bind(this, function()
2939 2940 {
2940 2941 var file = this.getCurrentFile();
2941   -
  2942 +
2942 2943 if (file != null)
2943 2944 {
2944 2945 file.setModified(false);
2945 2946 }
2946   -
  2947 +
2947 2948 this.spinner.spin(document.body, mxResources.get('connecting'));
2948 2949 this.editor.graph.setEnabled(false);
2949 2950 window.location.reload();
2950 2951 }), null, null, null, null, null, 340, 180);
2951   -
  2952 +
2952 2953 // Adds important notice to dialog
2953 2954 if (this.dialog != null && this.dialog.container != null)
2954 2955 {
... ... @@ -2975,7 +2976,7 @@ App.prototype.showAlert = function(message)
2975 2976 {
2976 2977 var div = document.createElement('div');
2977 2978 div.className = 'geAlert';
2978   - div.style.zIndex = 2e9;
  2979 + div.style.zIndex = 2e9;
2979 2980 div.style.left = '50%';
2980 2981 div.style.top = '-100%';
2981 2982 //Limit width to 80% max with word wrapping
... ... @@ -2984,9 +2985,9 @@ App.prototype.showAlert = function(message)
2984 2985 div.style.whiteSpace = 'pre-wrap';
2985 2986 mxUtils.setPrefixedStyle(div.style, 'transform', 'translate(-50%,0%)');
2986 2987 mxUtils.setPrefixedStyle(div.style, 'transition', 'all 1s ease');
2987   -
  2988 +
2988 2989 div.innerHTML = message;
2989   -
  2990 +
2990 2991 var close = document.createElement('a');
2991 2992 close.className = 'geAlertLink';
2992 2993 close.style.textAlign = 'right';
... ... @@ -2995,7 +2996,7 @@ App.prototype.showAlert = function(message)
2995 2996 close.setAttribute('title', mxResources.get('close'));
2996 2997 close.innerHTML = mxResources.get('close');
2997 2998 div.appendChild(close);
2998   -
  2999 +
2999 3000 mxEvent.addListener(close, 'click', function(evt)
3000 3001 {
3001 3002 if (div.parentNode != null)
... ... @@ -3004,21 +3005,21 @@ App.prototype.showAlert = function(message)
3004 3005 mxEvent.consume(evt);
3005 3006 }
3006 3007 });
3007   -
  3008 +
3008 3009 document.body.appendChild(div);
3009   -
  3010 +
3010 3011 // Delayed to get smoother animation after DOM rendering
3011 3012 window.setTimeout(function()
3012 3013 {
3013 3014 div.style.top = '30px';
3014 3015 }, 10);
3015   -
  3016 +
3016 3017 // Fades out the alert after 15 secs
3017 3018 window.setTimeout(function()
3018 3019 {
3019 3020 mxUtils.setPrefixedStyle(div.style, 'transition', 'all 2s ease');
3020 3021 div.style.opacity = '0';
3021   -
  3022 +
3022 3023 window.setTimeout(function()
3023 3024 {
3024 3025 if (div.parentNode != null)
... ... @@ -3032,7 +3033,7 @@ App.prototype.showAlert = function(message)
3032 3033
3033 3034 /**
3034 3035 * Translates this point by the given vector.
3035   - *
  3036 + *
3036 3037 * @param {number} dx X-coordinate of the translation.
3037 3038 * @param {number} dy Y-coordinate of the translation.
3038 3039 */
... ... @@ -3042,7 +3043,7 @@ App.prototype.start = function()
3042 3043 {
3043 3044 this.bg.parentNode.removeChild(this.bg);
3044 3045 }
3045   -
  3046 +
3046 3047 this.restoreLibraries();
3047 3048 this.spinner.stop();
3048 3049
... ... @@ -3050,7 +3051,7 @@ App.prototype.start = function()
3050 3051 {
3051 3052 // Handles all errors
3052 3053 var ui = this;
3053   -
  3054 +
3054 3055 window.onerror = function(message, url, linenumber, colno, err)
3055 3056 {
3056 3057 // Ignores Grammarly error [1344]
... ... @@ -3062,7 +3063,7 @@ App.prototype.start = function()
3062 3063 null, null, null, null, true);
3063 3064 }
3064 3065 };
3065   -
  3066 +
3066 3067 // Listens to changes of the hash if not in embed or client mode
3067 3068 if (urlParams['client'] != '1' && urlParams['embed'] != '1')
3068 3069 {
... ... @@ -3075,7 +3076,7 @@ App.prototype.start = function()
3075 3076 {
3076 3077 var file = this.getCurrentFile();
3077 3078 EditorUi.debug('storage event', [evt], [file]);
3078   -
  3079 +
3079 3080 if (file != null && evt.key == '.draft-alive-check' && evt.newValue != null && file.draftId != null)
3080 3081 {
3081 3082 this.draftAliveCheck = evt.newValue;
... ... @@ -3095,7 +3096,7 @@ App.prototype.start = function()
3095 3096 {
3096 3097 // ignore
3097 3098 }
3098   -
  3099 +
3099 3100 mxEvent.addListener(window, 'hashchange', mxUtils.bind(this, function(evt)
3100 3101 {
3101 3102 try
... ... @@ -3123,7 +3124,7 @@ App.prototype.start = function()
3123 3124 }
3124 3125 }));
3125 3126 }
3126   -
  3127 +
3127 3128 // Descriptor for CSV import
3128 3129 if ((window.location.hash == null || window.location.hash.length <= 1) && urlParams['desc'] != null)
3129 3130 {
... ... @@ -3162,9 +3163,9 @@ App.prototype.start = function()
3162 3163 {
3163 3164 xml = Editor.extractGraphModelFromPng(xml);
3164 3165 }
3165   -
  3166 +
3166 3167 var title = urlParams['title'];
3167   -
  3168 +
3168 3169 if (title != null)
3169 3170 {
3170 3171 title = decodeURIComponent(title);
... ... @@ -3173,9 +3174,9 @@ App.prototype.start = function()
3173 3174 {
3174 3175 title = this.defaultFilename;
3175 3176 }
3176   -
  3177 +
3177 3178 var file = new LocalFile(this, xml, title, true);
3178   -
  3179 +
3179 3180 if (window.location.hash != null && window.location.hash.substring(0, 2) == '#P')
3180 3181 {
3181 3182 file.getHash = function()
... ... @@ -3183,17 +3184,17 @@ App.prototype.start = function()
3183 3184 return window.location.hash.substring(1);
3184 3185 };
3185 3186 }
3186   -
  3187 +
3187 3188 this.fileLoaded(file);
3188 3189 this.getCurrentFile().setModified(!this.editor.chromeless);
3189 3190 });
3190 3191
3191 3192 var parent = window.opener || window.parent;
3192   -
  3193 +
3193 3194 if (parent != window)
3194 3195 {
3195 3196 var value = urlParams['create'];
3196   -
  3197 +
3197 3198 if (value != null)
3198 3199 {
3199 3200 doLoadFile(parent[decodeURIComponent(value)]);
... ... @@ -3201,7 +3202,7 @@ App.prototype.start = function()
3201 3202 else
3202 3203 {
3203 3204 value = urlParams['data'];
3204   -
  3205 +
3205 3206 if (value != null)
3206 3207 {
3207 3208 doLoadFile(decodeURIComponent(value));
... ... @@ -3232,7 +3233,7 @@ App.prototype.start = function()
3232 3233 else
3233 3234 {
3234 3235 var waiting = false;
3235   -
  3236 +
3236 3237 // Checks if we're waiting for some asynchronous file to be loaded
3237 3238 // Cross-domain window access is not allowed in FF, so if we
3238 3239 // were opened from another domain then this will fail.
... ... @@ -3244,7 +3245,7 @@ App.prototype.start = function()
3244 3245 {
3245 3246 // ignore
3246 3247 }
3247   -
  3248 +
3248 3249 if (waiting)
3249 3250 {
3250 3251 // Spinner is stopped in App.open
... ... @@ -3253,7 +3254,7 @@ App.prototype.start = function()
3253 3254 else
3254 3255 {
3255 3256 var id = this.getDiagramId();
3256   -
  3257 +
3257 3258 if (EditorUi.enableDrafts && urlParams['mode'] == null &&
3258 3259 this.getServiceName() == 'draw.io' && (id == null || id.length == 0) &&
3259 3260 !this.editor.isChromelessView())
... ... @@ -3265,7 +3266,7 @@ App.prototype.start = function()
3265 3266 this.loadFile(id, null, null, mxUtils.bind(this, function()
3266 3267 {
3267 3268 var temp = decodeURIComponent(urlParams['viewbox'] || '');
3268   -
  3269 +
3269 3270 if (temp != '')
3270 3271 {
3271 3272 try
... ... @@ -3293,9 +3294,9 @@ App.prototype.start = function()
3293 3294 }
3294 3295 }
3295 3296 });
3296   -
  3297 +
3297 3298 var value = decodeURIComponent(urlParams['create'] || '');
3298   -
  3299 +
3299 3300 if ((window.location.hash == null || window.location.hash.length <= 1) &&
3300 3301 value != null && value.length > 0 && this.spinner.spin(document.body, mxResources.get('loading')))
3301 3302 {
... ... @@ -3307,20 +3308,20 @@ App.prototype.start = function()
3307 3308 window.location.search = this.getSearch(['create', 'title']);
3308 3309 };
3309 3310 });
3310   -
  3311 +
3311 3312 var showCreateDialog = mxUtils.bind(this, function(xml)
3312 3313 {
3313 3314 this.spinner.stop();
3314   -
  3315 +
3315 3316 // Resets mode for dialog - local file is only for preview
3316 3317 if (urlParams['splash'] != '0')
3317 3318 {
3318 3319 this.fileLoaded(new LocalFile(this, xml, null));
3319   -
  3320 +
3320 3321 this.editor.graph.setEnabled(false);
3321 3322 this.mode = urlParams['mode'];
3322 3323 var title = urlParams['title'];
3323   -
  3324 +
3324 3325 if (title != null)
3325 3326 {
3326 3327 title = decodeURIComponent(title);
... ... @@ -3329,16 +3330,16 @@ App.prototype.start = function()
3329 3330 {
3330 3331 title = this.defaultFilename;
3331 3332 }
3332   -
  3333 +
3333 3334 var serviceCount = this.getServiceCount(true);
3334   -
  3335 +
3335 3336 if (isLocalStorage)
3336 3337 {
3337 3338 serviceCount++;
3338 3339 }
3339   -
  3340 +
3340 3341 var rowLimit = (serviceCount <= 4) ? 2 : (serviceCount > 6 ? 4 : 3);
3341   -
  3342 +
3342 3343 var dlg = new CreateDialog(this, title, mxUtils.bind(this, function(filename, mode)
3343 3344 {
3344 3345 if (mode == null)
... ... @@ -3371,9 +3372,9 @@ App.prototype.start = function()
3371 3372 dlg.init();
3372 3373 }
3373 3374 });
3374   -
  3375 +
3375 3376 value = decodeURIComponent(value);
3376   -
  3377 +
3377 3378 if (value.substring(0, 7) != 'http://' && value.substring(0, 8) != 'https://')
3378 3379 {
3379 3380 // Cross-domain window access is not allowed in FF, so if we
... ... @@ -3420,7 +3421,7 @@ App.prototype.start = function()
3420 3421 window.history.replaceState(null, null, window.location.pathname +
3421 3422 this.getSearch(['state']));
3422 3423 }
3423   -
  3424 +
3424 3425 window.location.hash = 'G' + this.stateArg.ids[0];
3425 3426 }
3426 3427 }
... ... @@ -3433,7 +3434,7 @@ App.prototype.start = function()
3433 3434 window.history.replaceState(null, null, window.location.pathname +
3434 3435 this.getSearch(['state']));
3435 3436 }
3436   -
  3437 +
3437 3438 this.setMode(App.MODE_GOOGLE);
3438 3439
3439 3440 if (urlParams['splash'] == '0')
... ... @@ -3452,12 +3453,12 @@ App.prototype.start = function()
3452 3453 // Removes open URL parameter. Hash is also updated in Init to load client.
3453 3454 if (urlParams['open'] != null && window.history && window.history.replaceState)
3454 3455 {
3455   -
  3456 +
3456 3457 window.history.replaceState(null, null, window.location.pathname +
3457 3458 this.getSearch(['open', 'sketch']));
3458 3459 window.location.hash = urlParams['open'];
3459 3460 }
3460   -
  3461 +
3461 3462 done();
3462 3463 }
3463 3464 }
... ... @@ -3479,11 +3480,11 @@ App.prototype.loadDraft = function(xml, success)
3479 3480 window.setTimeout(mxUtils.bind(this, function()
3480 3481 {
3481 3482 var file = this.getCurrentFile();
3482   -
  3483 +
3483 3484 if (file != null)
3484 3485 {
3485 3486 file.fileChanged();
3486   -
  3487 +
3487 3488 if (success != null)
3488 3489 {
3489 3490 success();
... ... @@ -3512,14 +3513,14 @@ App.prototype.filterDrafts = function(filePath, guid, callback)
3512 3513 try
3513 3514 {
3514 3515 var key = items[i].key;
3515   -
  3516 +
3516 3517 if (key != null && key.substring(0, 7) == '.draft_')
3517 3518 {
3518 3519 var obj = JSON.parse(items[i].data);
3519   -
3520   - if (obj != null && obj.type == 'draft' && obj.aliveCheck != guid &&
  3520 +
  3521 + if (obj != null && obj.type == 'draft' && obj.aliveCheck != guid &&
3521 3522 ((filePath == null && obj.fileObject == null) ||
3522   - (obj.fileObject != null && obj.fileObject.path == filePath)))
  3523 + (obj.fileObject != null && obj.fileObject.path == filePath)))
3523 3524 {
3524 3525 obj.key = key;
3525 3526 drafts.push(obj);
... ... @@ -3551,7 +3552,7 @@ App.prototype.checkDrafts = function()
3551 3552 // Triggers storage event for other windows to mark active drafts
3552 3553 var guid = Editor.guid();
3553 3554 localStorage.setItem('.draft-alive-check', guid);
3554   -
  3555 +
3555 3556 window.setTimeout(mxUtils.bind(this, function()
3556 3557 {
3557 3558 localStorage.removeItem('.draft-alive-check');
... ... @@ -3568,14 +3569,14 @@ App.prototype.checkDrafts = function()
3568 3569 else if (drafts.length > 1)
3569 3570 {
3570 3571 var ts = new Date(drafts[0].modified);
3571   -
  3572 +
3572 3573 var dlg = new DraftDialog(this, (drafts.length > 1) ? mxResources.get('selectDraft') :
3573 3574 mxResources.get('draftFound', [ts.toLocaleDateString() + ' ' + ts.toLocaleTimeString()]),
3574 3575 (drafts.length > 1) ? null : drafts[0].data, mxUtils.bind(this, function(index)
3575 3576 {
3576 3577 this.hideDialog();
3577 3578 index = (index != '') ? index : 0;
3578   -
  3579 +
3579 3580 this.loadDraft(drafts[index].data, mxUtils.bind(this, function()
3580 3581 {
3581 3582 this.removeDatabaseItem(drafts[index].key);
... ... @@ -3583,12 +3584,12 @@ App.prototype.checkDrafts = function()
3583 3584 }), mxUtils.bind(this, function(index, success)
3584 3585 {
3585 3586 index = (index != '') ? index : 0;
3586   -
  3587 +
3587 3588 // Discard draft
3588 3589 this.confirm(mxResources.get('areYouSure'), null, mxUtils.bind(this, function()
3589 3590 {
3590 3591 this.removeDatabaseItem(drafts[index].key);
3591   -
  3592 +
3592 3593 if (success != null)
3593 3594 {
3594 3595 success();
... ... @@ -3627,7 +3628,7 @@ App.prototype.checkDrafts = function()
3627 3628
3628 3629 /**
3629 3630 * Translates this point by the given vector.
3630   - *
  3631 + *
3631 3632 * @param {number} dx X-coordinate of the translation.
3632 3633 * @param {number} dy Y-coordinate of the translation.
3633 3634 */
... ... @@ -3636,15 +3637,15 @@ App.prototype.showSplash = function(force)
3636 3637 //Splash dialog shouldn't be shownn when running without a file menu
3637 3638 if (urlParams['noFileMenu'] == '1')
3638 3639 {
3639   - return;
  3640 + return;
3640 3641 }
3641   -
  3642 +
3642 3643 var serviceCount = this.getServiceCount(true);
3643   -
  3644 +
3644 3645 var showSecondDialog = mxUtils.bind(this, function()
3645 3646 {
3646 3647 var dlg = new SplashDialog(this);
3647   -
  3648 +
3648 3649 this.showDialog(dlg.container, 340, (mxClient.IS_CHROMEAPP || EditorUi.isElectronApp) ? 200 : 230, true, true,
3649 3650 mxUtils.bind(this, function(cancel)
3650 3651 {
... ... @@ -3658,7 +3659,7 @@ App.prototype.showSplash = function(force)
3658 3659 }
3659 3660 }), true);
3660 3661 });
3661   -
  3662 +
3662 3663 if (this.editor.isChromelessView())
3663 3664 {
3664 3665 this.handleError({message: mxResources.get('noFileSelected')},
... ... @@ -3670,13 +3671,13 @@ App.prototype.showSplash = function(force)
3670 3671 else if (!mxClient.IS_CHROMEAPP && (this.mode == null || force))
3671 3672 {
3672 3673 var rowLimit = (serviceCount == 4) ? 2 : 3;
3673   -
  3674 +
3674 3675 var dlg = new StorageDialog(this, mxUtils.bind(this, function()
3675 3676 {
3676 3677 this.hideDialog();
3677 3678 showSecondDialog();
3678 3679 }), rowLimit);
3679   -
  3680 +
3680 3681 this.showDialog(dlg.container, (rowLimit < 3) ? 200 : 300,
3681 3682 ((serviceCount > 3) ? 320 : 210), true, false);
3682 3683 }
... ... @@ -3688,7 +3689,7 @@ App.prototype.showSplash = function(force)
3688 3689
3689 3690 /**
3690 3691 * Translates this point by the given vector.
3691   - *
  3692 + *
3692 3693 * @param {number} dx X-coordinate of the translation.
3693 3694 * @param {number} dy Y-coordinate of the translation.
3694 3695 */
... ... @@ -3696,7 +3697,7 @@ App.prototype.addLanguageMenu = function(elt, addLabel)
3696 3697 {
3697 3698 var img = null;
3698 3699 var langMenu = this.menus.get('language');
3699   -
  3700 +
3700 3701 if (langMenu != null)
3701 3702 {
3702 3703 img = document.createElement('div');
... ... @@ -3706,7 +3707,7 @@ App.prototype.addLanguageMenu = function(elt, addLabel)
3706 3707 img.style.cursor = 'pointer';
3707 3708 img.style.bottom = '20px';
3708 3709 img.style.right = '20px';
3709   -
  3710 +
3710 3711 if (addLabel)
3711 3712 {
3712 3713 img.style.direction = 'rtl';
... ... @@ -3719,11 +3720,11 @@ App.prototype.addLanguageMenu = function(elt, addLabel)
3719 3720 label.style.margin = '5px 24px 0 0';
3720 3721 label.style.color = 'gray';
3721 3722 label.style.userSelect = 'none';
3722   -
  3723 +
3723 3724 mxUtils.write(label, mxResources.get('language'));
3724 3725 img.appendChild(label);
3725 3726 }
3726   -
  3727 +
3727 3728 mxEvent.addListener(img, 'click', mxUtils.bind(this, function(evt)
3728 3729 {
3729 3730 this.editor.graph.popupMenuHandler.hideMenu();
... ... @@ -3732,24 +3733,24 @@ App.prototype.addLanguageMenu = function(elt, addLabel)
3732 3733 menu.smartSeparators = true;
3733 3734 menu.showDisabled = true;
3734 3735 menu.autoExpand = true;
3735   -
  3736 +
3736 3737 // Disables autoexpand and destroys menu when hidden
3737 3738 menu.hideMenu = mxUtils.bind(this, function()
3738 3739 {
3739 3740 mxPopupMenu.prototype.hideMenu.apply(menu, arguments);
3740 3741 menu.destroy();
3741 3742 });
3742   -
  3743 +
3743 3744 var offset = mxUtils.getOffset(img);
3744 3745 menu.popup(offset.x, offset.y + img.offsetHeight, null, evt);
3745   -
  3746 +
3746 3747 // Allows hiding by clicking on document
3747 3748 this.setCurrentMenu(menu);
3748 3749 }));
3749   -
  3750 +
3750 3751 elt.appendChild(img);
3751 3752 }
3752   -
  3753 +
3753 3754 return img;
3754 3755 };
3755 3756
... ... @@ -3762,13 +3763,13 @@ App.prototype.loadFileSystemEntry = function(fileHandle, success, error)
3762 3763 {
3763 3764 this.handleError(e);
3764 3765 });
3765   -
  3766 +
3766 3767 try
3767 3768 {
3768 3769 fileHandle.getFile().then(mxUtils.bind(this, function(file)
3769 3770 {
3770 3771 var reader = new FileReader();
3771   -
  3772 +
3772 3773 reader.onload = mxUtils.bind(this, function(e)
3773 3774 {
3774 3775 try
... ... @@ -3776,12 +3777,12 @@ App.prototype.loadFileSystemEntry = function(fileHandle, success, error)
3776 3777 if (success != null)
3777 3778 {
3778 3779 var data = e.target.result;
3779   -
  3780 +
3780 3781 if (file.type == 'image/png')
3781 3782 {
3782 3783 data = this.extractGraphModelFromPng(data);
3783 3784 }
3784   -
  3785 +
3785 3786 success(new LocalFile(this, data, file.name, null, fileHandle, file));
3786 3787 }
3787 3788 else
... ... @@ -3794,9 +3795,9 @@ App.prototype.loadFileSystemEntry = function(fileHandle, success, error)
3794 3795 error(e);
3795 3796 }
3796 3797 });
3797   -
  3798 +
3798 3799 reader.onerror = error;
3799   -
  3800 +
3800 3801 if ((file.type.substring(0, 5) === 'image' ||
3801 3802 file.type === 'application/pdf') &&
3802 3803 file.type.substring(0, 9) !== 'image/svg')
... ... @@ -3822,24 +3823,24 @@ App.prototype.createFileSystemOptions = function(name)
3822 3823 {
3823 3824 var ext = [];
3824 3825 var temp = null;
3825   -
  3826 +
3826 3827 if (name != null)
3827 3828 {
3828 3829 var idx = name.lastIndexOf('.');
3829   -
  3830 +
3830 3831 if (idx > 0)
3831 3832 {
3832 3833 temp = name.substring(idx + 1);
3833 3834 }
3834 3835 }
3835   -
  3836 +
3836 3837 for (var i = 0; i < this.editor.diagramFileTypes.length; i++)
3837 3838 {
3838 3839 var obj = {description: mxResources.get(this.editor.diagramFileTypes[i].description) +
3839 3840 ((mxClient.IS_MAC) ? ' (.' + this.editor.diagramFileTypes[i].extension + ')' : ''),
3840 3841 accept: {}};
3841 3842 obj.accept[this.editor.diagramFileTypes[i].mimeType] = ['.' + this.editor.diagramFileTypes[i].extension];
3842   -
  3843 +
3843 3844 if (this.editor.diagramFileTypes[i].extension == temp)
3844 3845 {
3845 3846 ext.splice(0, 0, obj);
... ... @@ -3856,7 +3857,7 @@ App.prototype.createFileSystemOptions = function(name)
3856 3857 }
3857 3858 }
3858 3859 }
3859   -
  3860 +
3860 3861 // TODO: Specify default filename
3861 3862 return {types: ext, fileName: name};
3862 3863 };
... ... @@ -3873,9 +3874,9 @@ App.prototype.showSaveFilePicker = function(success, error, opts)
3873 3874 this.handleError(e);
3874 3875 }
3875 3876 });
3876   -
  3877 +
3877 3878 opts = (opts != null) ? opts : this.createFileSystemOptions();
3878   -
  3879 +
3879 3880 window.showSaveFilePicker(opts).then(mxUtils.bind(this, function(fileHandle)
3880 3881 {
3881 3882 if (fileHandle != null)
... ... @@ -3890,7 +3891,7 @@ App.prototype.showSaveFilePicker = function(success, error, opts)
3890 3891
3891 3892 /**
3892 3893 * Translates this point by the given vector.
3893   - *
  3894 + *
3894 3895 * @param {number} dx X-coordinate of the translation.
3895 3896 * @param {number} dy Y-coordinate of the translation.
3896 3897 */
... ... @@ -3899,7 +3900,7 @@ App.prototype.pickFile = function(mode)
3899 3900 try
3900 3901 {
3901 3902 mode = (mode != null) ? mode : this.mode;
3902   -
  3903 +
3903 3904 if (mode == App.MODE_GOOGLE)
3904 3905 {
3905 3906 if (this.drive != null && typeof(google) != 'undefined' && typeof(google.picker) != 'undefined')
... ... @@ -3914,7 +3915,7 @@ App.prototype.pickFile = function(mode)
3914 3915 else
3915 3916 {
3916 3917 var peer = this.getPeerForMode(mode);
3917   -
  3918 +
3918 3919 if (peer != null)
3919 3920 {
3920 3921 peer.pickFile();
... ... @@ -3938,17 +3939,17 @@ App.prototype.pickFile = function(mode)
3938 3939 }
3939 3940 else if (mode == App.MODE_DEVICE && Graph.fileSupport)
3940 3941 {
3941   - if (this.openFileInputElt == null)
  3942 + if (this.openFileInputElt == null)
3942 3943 {
3943 3944 var input = document.createElement('input');
3944 3945 input.setAttribute('type', 'file');
3945   -
  3946 +
3946 3947 mxEvent.addListener(input, 'change', mxUtils.bind(this, function()
3947 3948 {
3948 3949 if (input.files != null)
3949 3950 {
3950 3951 this.openFiles(input.files);
3951   -
  3952 +
3952 3953 // Resets input to force change event for
3953 3954 // same file (type reset required for IE)
3954 3955 input.type = '';
... ... @@ -3956,12 +3957,12 @@ App.prototype.pickFile = function(mode)
3956 3957 input.value = '';
3957 3958 }
3958 3959 }));
3959   -
  3960 +
3960 3961 input.style.display = 'none';
3961 3962 document.body.appendChild(input);
3962 3963 this.openFileInputElt = input;
3963 3964 }
3964   -
  3965 +
3965 3966 this.openFileInputElt.click();
3966 3967 }
3967 3968 else
... ... @@ -3970,26 +3971,26 @@ App.prototype.pickFile = function(mode)
3970 3971 window.openNew = this.getCurrentFile() != null && !this.isDiagramEmpty();
3971 3972 window.baseUrl = this.getUrl();
3972 3973 window.openKey = 'open';
3973   -
3974   - window.listBrowserFiles = mxUtils.bind(this, function(success, error)
  3974 +
  3975 + window.listBrowserFiles = mxUtils.bind(this, function(success, error)
3975 3976 {
3976 3977 StorageFile.listFiles(this, 'F', success, error);
3977 3978 });
3978   -
  3979 +
3979 3980 window.openBrowserFile = mxUtils.bind(this, function(title, success, error)
3980 3981 {
3981 3982 StorageFile.getFileContent(this, title, success, error);
3982 3983 });
3983   -
  3984 +
3984 3985 window.deleteBrowserFile = mxUtils.bind(this, function(title, success, error)
3985 3986 {
3986 3987 StorageFile.deleteFile(this, title, success, error);
3987 3988 });
3988   -
  3989 +
3989 3990 var prevValue = Editor.useLocalStorage;
3990 3991 Editor.useLocalStorage = (mode == App.MODE_BROWSER);
3991 3992 this.openFile();
3992   -
  3993 +
3993 3994 // Installs local handler for opened files in same window
3994 3995 window.openFile.setConsumer(mxUtils.bind(this, function(xml, filename)
3995 3996 {
... ... @@ -3997,19 +3998,19 @@ App.prototype.pickFile = function(mode)
3997 3998 {
3998 3999 // Replaces PNG with XML extension
3999 4000 var dot = !this.useCanvasForExport && filename.substring(filename.length - 4) == '.png';
4000   -
  4001 +
4001 4002 if (dot)
4002 4003 {
4003 4004 filename = filename.substring(0, filename.length - 4) + '.drawio';
4004 4005 }
4005   -
  4006 +
4006 4007 this.fileLoaded((mode == App.MODE_BROWSER) ?
4007 4008 new StorageFile(this, xml, filename) :
4008 4009 new LocalFile(this, xml, filename));
4009 4010 });
4010   -
  4011 +
4011 4012 var currentFile = this.getCurrentFile();
4012   -
  4013 +
4013 4014 if (currentFile == null || !currentFile.isModified())
4014 4015 {
4015 4016 doOpenFile();
... ... @@ -4020,16 +4021,16 @@ App.prototype.pickFile = function(mode)
4020 4021 mxResources.get('cancel'), mxResources.get('discardChanges'));
4021 4022 }
4022 4023 }));
4023   -
  4024 +
4024 4025 // Extends dialog close to show splash screen
4025 4026 var dlg = this.dialog;
4026 4027 var dlgClose = dlg.close;
4027   -
  4028 +
4028 4029 this.dialog.close = mxUtils.bind(this, function(cancel)
4029 4030 {
4030 4031 Editor.useLocalStorage = prevValue;
4031 4032 dlgClose.apply(dlg, arguments);
4032   -
  4033 +
4033 4034 if (this.getCurrentFile() == null)
4034 4035 {
4035 4036 this.showSplash();
... ... @@ -4046,14 +4047,14 @@ App.prototype.pickFile = function(mode)
4046 4047
4047 4048 /**
4048 4049 * Translates this point by the given vector.
4049   - *
  4050 + *
4050 4051 * @param {number} dx X-coordinate of the translation.
4051 4052 * @param {number} dy Y-coordinate of the translation.
4052 4053 */
4053 4054 App.prototype.pickLibrary = function(mode)
4054 4055 {
4055 4056 mode = (mode != null) ? mode : this.mode;
4056   -
  4057 +
4057 4058 if (mode == App.MODE_GOOGLE || mode == App.MODE_DROPBOX || mode == App.MODE_ONEDRIVE ||
4058 4059 mode == App.MODE_GITHUB || mode == App.MODE_GITLAB || mode == App.MODE_TRELLO)
4059 4060 {
... ... @@ -4063,7 +4064,7 @@ App.prototype.pickLibrary = function(mode)
4063 4064 ((mode == App.MODE_GITLAB) ? this.gitLab :
4064 4065 ((mode == App.MODE_TRELLO) ? this.trello :
4065 4066 this.dropbox))));
4066   -
  4067 +
4067 4068 if (peer != null)
4068 4069 {
4069 4070 peer.pickLibrary(mxUtils.bind(this, function(id, optionalFile)
... ... @@ -4086,7 +4087,7 @@ App.prototype.pickLibrary = function(mode)
4086 4087 peer.getLibrary(id, mxUtils.bind(this, function(file)
4087 4088 {
4088 4089 this.spinner.stop();
4089   -
  4090 +
4090 4091 try
4091 4092 {
4092 4093 this.loadLibrary(file);
... ... @@ -4106,11 +4107,11 @@ App.prototype.pickLibrary = function(mode)
4106 4107 }
4107 4108 else if (mode == App.MODE_DEVICE && Graph.fileSupport)
4108 4109 {
4109   - if (this.libFileInputElt == null)
  4110 + if (this.libFileInputElt == null)
4110 4111 {
4111 4112 var input = document.createElement('input');
4112 4113 input.setAttribute('type', 'file');
4113   -
  4114 +
4114 4115 mxEvent.addListener(input, 'change', mxUtils.bind(this, function()
4115 4116 {
4116 4117 if (input.files != null)
... ... @@ -4120,7 +4121,7 @@ App.prototype.pickLibrary = function(mode)
4120 4121 (mxUtils.bind(this, function(file)
4121 4122 {
4122 4123 var reader = new FileReader();
4123   -
  4124 +
4124 4125 reader.onload = mxUtils.bind(this, function(e)
4125 4126 {
4126 4127 try
... ... @@ -4132,54 +4133,54 @@ App.prototype.pickLibrary = function(mode)
4132 4133 this.handleError(e, mxResources.get('errorLoadingFile'));
4133 4134 }
4134 4135 });
4135   -
  4136 +
4136 4137 reader.readAsText(file);
4137 4138 }))(input.files[i]);
4138 4139 }
4139   -
  4140 +
4140 4141 // Resets input to force change event for same file (type reset required for IE)
4141 4142 input.type = '';
4142 4143 input.type = 'file';
4143 4144 input.value = '';
4144 4145 }
4145 4146 }));
4146   -
  4147 +
4147 4148 input.style.display = 'none';
4148 4149 document.body.appendChild(input);
4149 4150 this.libFileInputElt = input;
4150 4151 }
4151   -
  4152 +
4152 4153 this.libFileInputElt.click();
4153 4154 }
4154 4155 else
4155 4156 {
4156 4157 window.openNew = false;
4157 4158 window.openKey = 'open';
4158   -
4159   - window.listBrowserFiles = mxUtils.bind(this, function(success, error)
  4159 +
  4160 + window.listBrowserFiles = mxUtils.bind(this, function(success, error)
4160 4161 {
4161 4162 StorageFile.listFiles(this, 'L', success, error);
4162 4163 });
4163   -
  4164 +
4164 4165 window.openBrowserFile = mxUtils.bind(this, function(title, success, error)
4165 4166 {
4166 4167 StorageFile.getFileContent(this, title, success, error);
4167 4168 });
4168   -
  4169 +
4169 4170 window.deleteBrowserFile = mxUtils.bind(this, function(title, success, error)
4170 4171 {
4171 4172 StorageFile.deleteFile(this, title, success, error);
4172 4173 });
4173   -
  4174 +
4174 4175 var prevValue = Editor.useLocalStorage;
4175 4176 Editor.useLocalStorage = mode == App.MODE_BROWSER;
4176   -
  4177 +
4177 4178 // Closes dialog after open
4178 4179 window.openFile = new OpenFile(mxUtils.bind(this, function(cancel)
4179 4180 {
4180 4181 this.hideDialog(cancel);
4181 4182 }));
4182   -
  4183 +
4183 4184 window.openFile.setConsumer(mxUtils.bind(this, function(xml, filename)
4184 4185 {
4185 4186 try
... ... @@ -4205,7 +4206,7 @@ App.prototype.pickLibrary = function(mode)
4205 4206
4206 4207 /**
4207 4208 * Translates this point by the given vector.
4208   - *
  4209 + *
4209 4210 * @param {number} dx X-coordinate of the translation.
4210 4211 * @param {number} dy Y-coordinate of the translation.
4211 4212 */
... ... @@ -4217,25 +4218,25 @@ App.prototype.saveLibrary = function(name, images, file, mode, noSpin, noReload,
4217 4218 noSpin = (noSpin != null) ? noSpin : false;
4218 4219 noReload = (noReload != null) ? noReload : false;
4219 4220 var xml = this.createLibraryDataFromImages(images);
4220   -
  4221 +
4221 4222 var error = mxUtils.bind(this, function(resp)
4222 4223 {
4223 4224 this.spinner.stop();
4224   -
  4225 +
4225 4226 if (fn != null)
4226 4227 {
4227 4228 fn();
4228 4229 }
4229   -
  4230 +
4230 4231 this.handleError(resp, (resp != null) ? mxResources.get('errorSavingFile') : null);
4231 4232 });
4232   -
  4233 +
4233 4234 // Handles special case for local libraries
4234 4235 if (file == null && mode == App.MODE_DEVICE)
4235 4236 {
4236 4237 file = new LocalLibrary(this, xml, name);
4237 4238 }
4238   -
  4239 +
4239 4240 if (file == null)
4240 4241 {
4241 4242 this.pickFolder(mode, mxUtils.bind(this, function(folderId)
... ... @@ -4299,7 +4300,7 @@ App.prototype.saveLibrary = function(name, images, file, mode, noSpin, noReload,
4299 4300 var fn = mxUtils.bind(this, function()
4300 4301 {
4301 4302 var file = new StorageLibrary(this, xml, name);
4302   -
  4303 +
4303 4304 // Inserts data into local storage
4304 4305 file.saveFile(name, false, mxUtils.bind(this, function()
4305 4306 {
... ... @@ -4307,7 +4308,7 @@ App.prototype.saveLibrary = function(name, images, file, mode, noSpin, noReload,
4307 4308 this.libraryLoaded(file, images);
4308 4309 }), error);
4309 4310 });
4310   -
  4311 +
4311 4312 if (localStorage.getItem(name) == null)
4312 4313 {
4313 4314 fn();
... ... @@ -4326,30 +4327,30 @@ App.prototype.saveLibrary = function(name, images, file, mode, noSpin, noReload,
4326 4327 else if (noSpin || this.spinner.spin(document.body, mxResources.get('saving')))
4327 4328 {
4328 4329 file.setData(xml);
4329   -
  4330 +
4330 4331 var doSave = mxUtils.bind(this, function()
4331 4332 {
4332 4333 file.save(true, mxUtils.bind(this, function(resp)
4333 4334 {
4334 4335 this.spinner.stop();
4335 4336 this.hideDialog(true);
4336   -
  4337 +
4337 4338 if (!noReload)
4338 4339 {
4339 4340 this.libraryLoaded(file, images);
4340 4341 }
4341   -
  4342 +
4342 4343 if (fn != null)
4343 4344 {
4344 4345 fn();
4345 4346 }
4346 4347 }), error);
4347 4348 });
4348   -
  4349 +
4349 4350 if (name != file.getTitle())
4350 4351 {
4351 4352 var oldHash = file.getHash();
4352   -
  4353 +
4353 4354 file.rename(name, mxUtils.bind(this, function(resp)
4354 4355 {
4355 4356 // Change hash in stored settings
... ... @@ -4358,12 +4359,12 @@ App.prototype.saveLibrary = function(name, images, file, mode, noSpin, noReload,
4358 4359 mxSettings.removeCustomLibrary(oldHash);
4359 4360 mxSettings.addCustomLibrary(file.getHash());
4360 4361 }
4361   -
  4362 +
4362 4363 // Workaround for library files changing hash so
4363 4364 // the old library cannot be removed from the
4364 4365 // sidebar using the updated file in libraryLoaded
4365 4366 this.removeLibrarySidebar(oldHash);
4366   -
  4367 +
4367 4368 doSave();
4368 4369 }), error)
4369 4370 }
... ... @@ -4385,7 +4386,7 @@ App.prototype.saveLibrary = function(name, images, file, mode, noSpin, noReload,
4385 4386 App.prototype.saveFile = function(forceDialog, success)
4386 4387 {
4387 4388 var file = this.getCurrentFile();
4388   -
  4389 +
4389 4390 if (file != null)
4390 4391 {
4391 4392 // FIXME: Invoke for local files
... ... @@ -4395,7 +4396,7 @@ App.prototype.saveFile = function(forceDialog, success)
4395 4396 {
4396 4397 file.removeDraft();
4397 4398 }
4398   -
  4399 +
4399 4400 if (this.getCurrentFile() != file && !file.isModified())
4400 4401 {
4401 4402 // Workaround for possible status update while save as dialog is showing
... ... @@ -4409,13 +4410,13 @@ App.prototype.saveFile = function(forceDialog, success)
4409 4410 this.editor.setStatus('');
4410 4411 }
4411 4412 }
4412   -
  4413 +
4413 4414 if (success != null)
4414 4415 {
4415 4416 success();
4416 4417 }
4417 4418 });
4418   -
  4419 +
4419 4420 if (!forceDialog && file.getTitle() != null && file.invalidFileHandle == null && this.mode != null)
4420 4421 {
4421 4422 this.save(file.getTitle(), done);
... ... @@ -4437,14 +4438,14 @@ App.prototype.saveFile = function(forceDialog, success)
4437 4438 var allowTab = !mxClient.IS_IOS || !navigator.standalone;
4438 4439 var prev = this.mode;
4439 4440 var serviceCount = this.getServiceCount(true);
4440   -
  4441 +
4441 4442 if (isLocalStorage)
4442 4443 {
4443 4444 serviceCount++;
4444 4445 }
4445   -
  4446 +
4446 4447 var rowLimit = (serviceCount <= 4) ? 2 : (serviceCount > 6 ? 4 : 3);
4447   -
  4448 +
4448 4449 var dlg = new CreateDialog(this, filename, mxUtils.bind(this, function(name, mode, input)
4449 4450 {
4450 4451 if (name != null && name.length > 0)
... ... @@ -4460,7 +4461,7 @@ App.prototype.saveFile = function(forceDialog, success)
4460 4461 {
4461 4462 input.value = name.split('.').slice(0, -1).join('.');
4462 4463 input.focus();
4463   -
  4464 +
4464 4465 if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5)
4465 4466 {
4466 4467 input.select();
... ... @@ -4474,7 +4475,7 @@ App.prototype.saveFile = function(forceDialog, success)
4474 4475 else
4475 4476 {
4476 4477 this.hideDialog();
4477   -
  4478 +
4478 4479 if (prev == null && mode == App.MODE_DEVICE)
4479 4480 {
4480 4481 if (file != null && EditorUi.nativeFileSupport)
... ... @@ -4513,7 +4514,7 @@ App.prototype.saveFile = function(forceDialog, success)
4513 4514 {
4514 4515 window.openFile = null;
4515 4516 });
4516   -
  4517 +
4517 4518 // Do not use a filename to use undefined mode
4518 4519 window.openFile.setData(this.getFileData(true));
4519 4520 this.openLink(this.getUrl(window.location.pathname), null, true);
... ... @@ -4547,7 +4548,7 @@ App.prototype.saveFile = function(forceDialog, success)
4547 4548
4548 4549 /**
4549 4550 * Translates this point by the given vector.
4550   - *
  4551 + *
4551 4552 * @param {number} dx X-coordinate of the translation.
4552 4553 * @param {number} dy Y-coordinate of the translation.
4553 4554 */
... ... @@ -4559,7 +4560,7 @@ App.prototype.loadTemplate = function(url, onload, onerror, templateFilename, as
4559 4560 var isVisioFilename = /(\.v(dx|sdx?))($|\?)/i.test(filterFn) ||
4560 4561 /(\.vs(x|sx?))($|\?)/i.test(filterFn);
4561 4562 var binary = /\.png$/i.test(filterFn) || /\.pdf$/i.test(filterFn);
4562   -
  4563 +
4563 4564 if (!this.editor.isCorsEnabledForUrl(realUrl))
4564 4565 {
4565 4566 base64 = binary || isVisioFilename;
... ... @@ -4574,7 +4575,7 @@ App.prototype.loadTemplate = function(url, onload, onerror, templateFilename, as
4574 4575 {
4575 4576 var data = (!base64) ? responseData : ((window.atob && !mxClient.IS_IE && !mxClient.IS_IE11) ?
4576 4577 atob(responseData) : Base64.decode(responseData));
4577   -
  4578 +
4578 4579 if (isVisioFilename || this.isVisioData(data))
4579 4580 {
4580 4581 // Adds filename to control converter code
... ... @@ -4589,7 +4590,7 @@ App.prototype.loadTemplate = function(url, onload, onerror, templateFilename, as
4589 4590 filterFn = this.isRemoteVisioData(data) ? 'raw.vsd' : 'raw.vsdx';
4590 4591 }
4591 4592 }
4592   -
  4593 +
4593 4594 this.importVisio(this.base64ToBlob(responseData.substring(responseData.indexOf(',') + 1)), function(xml)
4594 4595 {
4595 4596 onload(xml);
... ... @@ -4630,7 +4631,7 @@ App.prototype.loadTemplate = function(url, onload, onerror, templateFilename, as
4630 4631 {
4631 4632 data = Editor.extractGraphModelFromPng(responseData);
4632 4633 }
4633   -
  4634 +
4634 4635 onload(data);
4635 4636 }
4636 4637 }
... ... @@ -4644,7 +4645,7 @@ App.prototype.loadTemplate = function(url, onload, onerror, templateFilename, as
4644 4645
4645 4646 /**
4646 4647 * Translates this point by the given vector.
4647   - *
  4648 + *
4648 4649 * @param {number} dx X-coordinate of the translation.
4649 4650 * @param {number} dy Y-coordinate of the translation.
4650 4651 */
... ... @@ -4682,7 +4683,7 @@ App.prototype.getPeerForMode = function(mode)
4682 4683
4683 4684 /**
4684 4685 * Translates this point by the given vector.
4685   - *
  4686 + *
4686 4687 * @param {number} dx X-coordinate of the translation.
4687 4688 * @param {number} dy Y-coordinate of the translation.
4688 4689 */
... ... @@ -4693,16 +4694,16 @@ App.prototype.createFile = function(title, data, libs, mode, done, replace, fold
4693 4694 if (title != null && this.spinner.spin(document.body, mxResources.get('inserting')))
4694 4695 {
4695 4696 data = (data != null) ? data : this.emptyDiagramXml;
4696   -
  4697 +
4697 4698 var complete = mxUtils.bind(this, function()
4698 4699 {
4699 4700 this.spinner.stop();
4700 4701 });
4701   -
  4702 +
4702 4703 var error = mxUtils.bind(this, function(resp)
4703 4704 {
4704 4705 complete();
4705   -
  4706 +
4706 4707 if (resp == null && this.getCurrentFile() == null && this.dialog == null)
4707 4708 {
4708 4709 this.showSplash();
... ... @@ -4712,7 +4713,7 @@ App.prototype.createFile = function(title, data, libs, mode, done, replace, fold
4712 4713 this.handleError(resp);
4713 4714 }
4714 4715 });
4715   -
  4716 +
4716 4717 try
4717 4718 {
4718 4719 if (mode == App.MODE_GOOGLE && this.drive != null)
... ... @@ -4721,7 +4722,7 @@ App.prototype.createFile = function(title, data, libs, mode, done, replace, fold
4721 4722 {
4722 4723 folderId = this.stateArg.folderId;
4723 4724 }
4724   -
  4725 +
4725 4726 this.drive.insertFile(title, data, folderId, mxUtils.bind(this, function(file)
4726 4727 {
4727 4728 complete();
... ... @@ -4779,11 +4780,11 @@ App.prototype.createFile = function(title, data, libs, mode, done, replace, fold
4779 4780 else if (!tempFile && mode == App.MODE_DEVICE && EditorUi.nativeFileSupport)
4780 4781 {
4781 4782 complete();
4782   -
  4783 +
4783 4784 this.showSaveFilePicker(mxUtils.bind(this, function(fileHandle, desc)
4784 4785 {
4785 4786 var file = new LocalFile(this, data, desc.name, null, fileHandle, desc);
4786   -
  4787 +
4787 4788 file.saveFile(desc.name, false, mxUtils.bind(this, function()
4788 4789 {
4789 4790 this.fileCreated(file, libs, replace, done, clibs);
... ... @@ -4812,14 +4813,14 @@ App.prototype.createFile = function(title, data, libs, mode, done, replace, fold
4812 4813
4813 4814 /**
4814 4815 * Translates this point by the given vector.
4815   - *
  4816 + *
4816 4817 * @param {number} dx X-coordinate of the translation.
4817 4818 * @param {number} dy Y-coordinate of the translation.
4818 4819 */
4819 4820 App.prototype.fileCreated = function(file, libs, replace, done, clibs)
4820 4821 {
4821 4822 var url = window.location.pathname;
4822   -
  4823 +
4823 4824 if (libs != null && libs.length > 0)
4824 4825 {
4825 4826 url += '?libs=' + libs;
... ... @@ -4829,7 +4830,7 @@ App.prototype.fileCreated = function(file, libs, replace, done, clibs)
4829 4830 {
4830 4831 url += '?clibs=' + clibs;
4831 4832 }
4832   -
  4833 +
4833 4834 url = this.getUrl(url);
4834 4835
4835 4836 // Always opens a new tab for local files to avoid losing changes
... ... @@ -4848,7 +4849,7 @@ App.prototype.fileCreated = function(file, libs, replace, done, clibs)
4848 4849 var redirect = window.location.protocol + '//' + window.location.hostname + url;
4849 4850 var node = dataNode;
4850 4851 var graph = null;
4851   -
  4852 +
4852 4853 // Handles special case where SVG files need a rendered graph to be saved
4853 4854 if (dataNode != null && /\.svg$/i.test(file.getTitle()))
4854 4855 {
... ... @@ -4856,7 +4857,7 @@ App.prototype.fileCreated = function(file, libs, replace, done, clibs)
4856 4857 document.body.appendChild(graph.container);
4857 4858 node = this.decodeNodeIntoGraph(node, graph);
4858 4859 }
4859   -
  4860 +
4860 4861 file.setData(this.createFileData(dataNode, graph, file, redirect));
4861 4862
4862 4863 if (graph != null)
... ... @@ -4868,43 +4869,43 @@ App.prototype.fileCreated = function(file, libs, replace, done, clibs)
4868 4869 {
4869 4870 this.spinner.stop();
4870 4871 });
4871   -
  4872 +
4872 4873 var fn = mxUtils.bind(this, function()
4873 4874 {
4874 4875 complete();
4875   -
  4876 +
4876 4877 var currentFile = this.getCurrentFile();
4877   -
  4878 +
4878 4879 if (replace == null && currentFile != null)
4879 4880 {
4880 4881 replace = !currentFile.isModified() && currentFile.getMode() == null;
4881 4882 }
4882   -
  4883 +
4883 4884 var fn3 = mxUtils.bind(this, function()
4884 4885 {
4885 4886 window.openFile = null;
4886 4887 this.fileLoaded(file);
4887   -
  4888 +
4888 4889 if (replace)
4889 4890 {
4890 4891 file.addAllSavedStatus();
4891 4892 }
4892   -
  4893 +
4893 4894 if (libs != null)
4894 4895 {
4895 4896 this.sidebar.showEntries(libs);
4896 4897 }
4897   -
  4898 +
4898 4899 if (clibs != null)
4899 4900 {
4900 4901 var temp = [];
4901 4902 var tokens = clibs.split(';');
4902   -
  4903 +
4903 4904 for (var i = 0; i < tokens.length; i++)
4904 4905 {
4905 4906 temp.push(decodeURIComponent(tokens[i]));
4906 4907 }
4907   -
  4908 +
4908 4909 this.loadLibraries(temp);
4909 4910 }
4910 4911 });
... ... @@ -4926,7 +4927,7 @@ App.prototype.fileCreated = function(file, libs, replace, done, clibs)
4926 4927 {
4927 4928 done();
4928 4929 }
4929   -
  4930 +
4930 4931 // Opens the file in a new window
4931 4932 if (replace != null && !replace)
4932 4933 {
... ... @@ -4937,7 +4938,7 @@ App.prototype.fileCreated = function(file, libs, replace, done, clibs)
4937 4938 {
4938 4939 window.openFile = null;
4939 4940 });
4940   -
  4941 +
4941 4942 window.openFile.setData(file.getData(), file.getTitle(), file.getMode() == null);
4942 4943 }
4943 4944
... ... @@ -4945,7 +4946,7 @@ App.prototype.fileCreated = function(file, libs, replace, done, clibs)
4945 4946 {
4946 4947 done();
4947 4948 }
4948   -
  4949 +
4949 4950 window.openWindow(url, null, fn2);
4950 4951 }
4951 4952 else
... ... @@ -4953,7 +4954,7 @@ App.prototype.fileCreated = function(file, libs, replace, done, clibs)
4953 4954 fn2();
4954 4955 }
4955 4956 });
4956   -
  4957 +
4957 4958 // Updates data in memory for local files
4958 4959 if (file.constructor == LocalFile)
4959 4960 {
... ... @@ -5422,19 +5423,19 @@ App.prototype.loadFile = function (id, sameWindow, file, success, force) {
5422 5423 };
5423 5424 /**
5424 5425 * Translates this point by the given vector.
5425   - *
  5426 + *
5426 5427 * @param {number} dx X-coordinate of the translation.
5427 5428 * @param {number} dy Y-coordinate of the translation.
5428 5429 */
5429 5430 App.prototype.getLibraryStorageHint = function(file)
5430 5431 {
5431 5432 var tip = file.getTitle();
5432   -
  5433 +
5433 5434 if (file.constructor != LocalLibrary)
5434 5435 {
5435 5436 tip += '\n' + file.getHash();
5436 5437 }
5437   -
  5438 +
5438 5439 if (file.constructor == DriveLibrary)
5439 5440 {
5440 5441 tip += ' (' + mxResources.get('googleDrive') + ')';
... ... @@ -5502,7 +5503,7 @@ App.prototype.loadLibraries = function(libs, done)
5502 5503 {
5503 5504 this.loadedLibraries = new Object();
5504 5505 }
5505   -
  5506 +
5506 5507 // Ignores this library next time
5507 5508 var ignore = mxUtils.bind(this, function(id, keep)
5508 5509 {
... ... @@ -5533,20 +5534,20 @@ App.prototype.loadLibraries = function(libs, done)
5533 5534 }
5534 5535 }
5535 5536 }
5536   -
  5537 +
5537 5538 if (done != null)
5538 5539 {
5539 5540 done();
5540 5541 }
5541 5542 }
5542 5543 });
5543   -
  5544 +
5544 5545 if (libs != null)
5545 5546 {
5546 5547 for (var i = 0; i < libs.length; i++)
5547 5548 {
5548 5549 var name = encodeURIComponent(decodeURIComponent(libs[i]));
5549   -
  5550 +
5550 5551 (mxUtils.bind(this, function(id, index)
5551 5552 {
5552 5553 if (id != null && id.length > 0 && this.loadedLibraries[id] == null &&
... ... @@ -5555,23 +5556,23 @@ App.prototype.loadLibraries = function(libs, done)
5555 5556 // Waits for all libraries to load
5556 5557 this.loadedLibraries[id] = true;
5557 5558 waiting++;
5558   -
  5559 +
5559 5560 var onload = mxUtils.bind(this, function(file)
5560 5561 {
5561 5562 files[index] = file;
5562 5563 waiting--;
5563 5564 checkDone();
5564 5565 });
5565   -
  5566 +
5566 5567 var onerror = mxUtils.bind(this, function(keep)
5567 5568 {
5568 5569 ignore(id, keep);
5569 5570 waiting--;
5570 5571 checkDone();
5571 5572 });
5572   -
  5573 +
5573 5574 var service = id.substring(0, 1);
5574   -
  5575 +
5575 5576 if (service == 'L')
5576 5577 {
5577 5578 if (isLocalStorage || mxClient.IS_CHROMEAPP)
... ... @@ -5582,14 +5583,14 @@ App.prototype.loadLibraries = function(libs, done)
5582 5583 try
5583 5584 {
5584 5585 var name = decodeURIComponent(id.substring(1));
5585   -
  5586 +
5586 5587 StorageFile.getFileContent(this, name, mxUtils.bind(this, function(xml)
5587 5588 {
5588 5589 if (name == '.scratchpad' && xml == null)
5589 5590 {
5590 5591 xml = this.emptyLibraryXml;
5591 5592 }
5592   -
  5593 +
5593 5594 if (xml != null)
5594 5595 {
5595 5596 onload(new StorageLibrary(this, xml, name));
... ... @@ -5610,7 +5611,7 @@ App.prototype.loadLibraries = function(libs, done)
5610 5611 else if (service == 'U')
5611 5612 {
5612 5613 var url = decodeURIComponent(id.substring(1));
5613   -
  5614 +
5614 5615 if (!this.isOffline())
5615 5616 {
5616 5617 this.loadTemplate(url, mxUtils.bind(this, function(text)
... ... @@ -5633,16 +5634,16 @@ App.prototype.loadLibraries = function(libs, done)
5633 5634 else if (service == 'R')
5634 5635 {
5635 5636 var libDesc = decodeURIComponent(id.substring(1));
5636   -
  5637 +
5637 5638 try
5638 5639 {
5639 5640 libDesc = JSON.parse(libDesc);
5640 5641 var libObj = {
5641   - id: libDesc[0],
5642   - title: libDesc[1],
  5642 + id: libDesc[0],
  5643 + title: libDesc[1],
5643 5644 downloadUrl: libDesc[2]
5644 5645 }
5645   -
  5646 +
5646 5647 this.remoteInvoke('getFileContent', [libObj.downloadUrl], null, mxUtils.bind(this, function(libContent)
5647 5648 {
5648 5649 try
... ... @@ -5680,7 +5681,7 @@ App.prototype.loadLibraries = function(libs, done)
5680 5681 else
5681 5682 {
5682 5683 var peer = null;
5683   -
  5684 +
5684 5685 if (service == 'G')
5685 5686 {
5686 5687 if (this.drive != null && this.drive.user != null)
... ... @@ -5716,7 +5717,7 @@ App.prototype.loadLibraries = function(libs, done)
5716 5717 peer = this.oneDrive;
5717 5718 }
5718 5719 }
5719   -
  5720 +
5720 5721 if (peer != null)
5721 5722 {
5722 5723 peer.getLibrary(decodeURIComponent(id.substring(1)), mxUtils.bind(this, function(file)
... ... @@ -5742,7 +5743,7 @@ App.prototype.loadLibraries = function(libs, done)
5742 5743 }
5743 5744 }))(name, i);
5744 5745 }
5745   -
  5746 +
5746 5747 checkDone();
5747 5748 }
5748 5749 else
... ... @@ -5754,7 +5755,7 @@ App.prototype.loadLibraries = function(libs, done)
5754 5755
5755 5756 /**
5756 5757 * Translates this point by the given vector.
5757   - *
  5758 + *
5758 5759 * @param {number} dx X-coordinate of the translation.
5759 5760 * @param {number} dy Y-coordinate of the translation.
5760 5761 */
... ... @@ -5763,7 +5764,7 @@ App.prototype.updateButtonContainer = function()
5763 5764 if (this.buttonContainer != null)
5764 5765 {
5765 5766 var file = this.getCurrentFile();
5766   -
  5767 +
5767 5768 if (urlParams['embed'] == '1')
5768 5769 {
5769 5770 if (uiTheme == 'atlas' || urlParams['atlas'] == '1')
... ... @@ -5778,7 +5779,7 @@ App.prototype.updateButtonContainer = function()
5778 5779 this.buttonContainer.style.paddingTop = '6px';
5779 5780 }
5780 5781 }
5781   -
  5782 +
5782 5783 // Comments
5783 5784 if (this.commentsSupported() && urlParams['sketch'] != '1')
5784 5785 {
... ... @@ -5791,7 +5792,7 @@ App.prototype.updateButtonContainer = function()
5791 5792 'margin-right:4px;float:left;cursor:pointer;width:24px;height:24px;background-size:24px 24px;' +
5792 5793 'background-position:center center;background-repeat:no-repeat;background-image:' +
5793 5794 'url(' + Editor.commentImage + ');';
5794   -
  5795 +
5795 5796 if (uiTheme == 'atlas')
5796 5797 {
5797 5798 this.commentButton.style.marginRight = '10px';
... ... @@ -5809,14 +5810,14 @@ App.prototype.updateButtonContainer = function()
5809 5810 {
5810 5811 this.commentButton.style.marginTop = '-5px';
5811 5812 }
5812   -
  5813 +
5813 5814 mxEvent.addListener(this.commentButton, 'click', mxUtils.bind(this, function()
5814 5815 {
5815 5816 this.actions.get('comments').funct();
5816 5817 }));
5817   -
  5818 +
5818 5819 this.buttonContainer.appendChild(this.commentButton);
5819   -
  5820 +
5820 5821 if (uiTheme == 'dark' || uiTheme == 'atlas')
5821 5822 {
5822 5823 this.commentButton.style.filter = 'invert(100%)';
... ... @@ -5828,7 +5829,7 @@ App.prototype.updateButtonContainer = function()
5828 5829 this.commentButton.parentNode.removeChild(this.commentButton);
5829 5830 this.commentButton = null;
5830 5831 }
5831   -
  5832 +
5832 5833 // Share
5833 5834 if (urlParams['embed'] != '1' && this.getServiceName() == 'draw.io' &&
5834 5835 !mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp &&
... ... @@ -5851,27 +5852,27 @@ App.prototype.updateButtonContainer = function()
5851 5852 this.shareButton.style.minWidth = '0px';
5852 5853 this.shareButton.style.cssFloat = 'right';
5853 5854 this.shareButton.setAttribute('title', mxResources.get('share'));
5854   -
  5855 +
5855 5856 var icon = document.createElement('img');
5856 5857 icon.setAttribute('src', this.shareImage);
5857 5858 icon.setAttribute('align', 'absmiddle');
5858 5859 icon.style.marginRight = '4px';
5859 5860 icon.style.marginTop = '-3px';
5860 5861 this.shareButton.appendChild(icon);
5861   -
  5862 +
5862 5863 if (!Editor.isDarkMode() && uiTheme != 'atlas')
5863 5864 {
5864 5865 this.shareButton.style.color = 'black';
5865 5866 icon.style.filter = 'invert(100%)';
5866 5867 }
5867   -
  5868 +
5868 5869 mxUtils.write(this.shareButton, mxResources.get('share'));
5869   -
  5870 +
5870 5871 mxEvent.addListener(this.shareButton, 'click', mxUtils.bind(this, function()
5871 5872 {
5872 5873 this.actions.get('share').funct();
5873 5874 }));
5874   -
  5875 +
5875 5876 this.buttonContainer.appendChild(this.shareButton);
5876 5877 }
5877 5878 }
... ... @@ -5880,7 +5881,7 @@ App.prototype.updateButtonContainer = function()
5880 5881 this.shareButton.parentNode.removeChild(this.shareButton);
5881 5882 this.shareButton = null;
5882 5883 }
5883   -
  5884 +
5884 5885 //Fetch notifications
5885 5886 if (urlParams['extAuth'] != '1') //Disable notification with external auth (e.g, Teams app)
5886 5887 {
... ... @@ -5899,32 +5900,32 @@ App.prototype.fetchAndShowNotification = function(target, subtarget)
5899 5900 {
5900 5901 if (this.fetchingNotif)
5901 5902 {
5902   - return;
  5903 + return;
5903 5904 }
5904   -
  5905 +
5905 5906 target = target || 'online';
5906 5907 var cachedNotifKey = '.notifCache';
5907 5908 var cachedNotif = null;
5908   -
  5909 +
5909 5910 var processNotif = mxUtils.bind(this, function(notifs)
5910 5911 {
5911 5912 notifs = notifs.filter(function(notif)
5912 5913 {
5913   - return !notif.targets || notif.targets.indexOf(target) > -1 ||
  5914 + return !notif.targets || notif.targets.indexOf(target) > -1 ||
5914 5915 (subtarget != null && notif.targets.indexOf(subtarget) > -1);
5915 5916 });
5916   -
  5917 +
5917 5918 var lsReadFlag = target + 'NotifReadTS';
5918 5919 var lastRead = (localStorage != null) ? parseInt(localStorage.getItem(lsReadFlag)) : true;
5919   -
  5920 +
5920 5921 for (var i = 0; i < notifs.length; i++)
5921 5922 {
5922 5923 notifs[i].isNew = (!lastRead || notifs[i].timestamp > lastRead);
5923 5924 }
5924   -
  5925 +
5925 5926 this.showNotification(notifs, lsReadFlag);
5926 5927 });
5927   -
  5928 +
5928 5929 try
5929 5930 {
5930 5931 if (localStorage != null)
... ... @@ -5933,7 +5934,7 @@ App.prototype.fetchAndShowNotification = function(target, subtarget)
5933 5934 }
5934 5935 }
5935 5936 catch(e) {} //Ignore
5936   -
  5937 +
5937 5938 if (cachedNotif == null || cachedNotif.ts + 24 * 60 * 60 * 1000 < Date.now()) //Cache for one day
5938 5939 {
5939 5940 this.fetchingNotif = true;
... ... @@ -5943,7 +5944,7 @@ App.prototype.fetchAndShowNotification = function(target, subtarget)
5943 5944 if (req.getStatus() >= 200 && req.getStatus() <= 299)
5944 5945 {
5945 5946 var notifs = JSON.parse(req.getText());
5946   -
  5947 +
5947 5948 //Process and sort
5948 5949 notifs.sort(function(a, b)
5949 5950 {
... ... @@ -5954,8 +5955,8 @@ App.prototype.fetchAndShowNotification = function(target, subtarget)
5954 5955 {
5955 5956 localStorage.setItem(cachedNotifKey, JSON.stringify({ts: Date.now(), notifs: notifs}));
5956 5957 }
5957   -
5958   - this.fetchingNotif = false;
  5958 +
  5959 + this.fetchingNotif = false;
5959 5960 processNotif(notifs);
5960 5961 }
5961 5962 }));
... ... @@ -5990,19 +5991,19 @@ App.prototype.showNotification = function(notifs, lsReadFlag)
5990 5991 this.notificationBtn.style.display = 'none';
5991 5992 this.editor.fireEvent(new mxEventObject('statusChanged'));
5992 5993 }
5993   -
  5994 +
5994 5995 return;
5995 5996 }
5996   -
  5997 +
5997 5998 function shouldAnimate(newNotif)
5998 5999 {
5999 6000 var countEl = document.querySelector('.geNotification-count');
6000   -
  6001 +
6001 6002 if (countEl == null)
6002 6003 {
6003 6004 return;
6004 6005 }
6005   -
  6006 +
6006 6007 countEl.innerHTML = newNotif;
6007 6008 countEl.style.display = newNotif == 0? 'none' : '';
6008 6009 var notifBell = document.querySelector('.geNotification-bell');
... ... @@ -6010,28 +6011,28 @@ App.prototype.showNotification = function(notifs, lsReadFlag)
6010 6011 notifBell.className = 'geNotification-bell' + (newNotif == 0? ' geNotification-bellOff' : '');
6011 6012 document.querySelector('.geBell-rad').style.animation = newNotif == 0? 'none' : '';
6012 6013 }
6013   -
  6014 +
6014 6015 var markAllAsRead = mxUtils.bind(this, function()
6015 6016 {
6016 6017 this.notificationWin.style.display = 'none';
6017 6018 var unread = this.notificationWin.querySelectorAll('.circle.active');
6018   -
  6019 +
6019 6020 for (var i = 0; i < unread.length; i++)
6020 6021 {
6021 6022 unread[i].className = 'circle';
6022 6023 }
6023   -
  6024 +
6024 6025 if (isLocalStorage && notifs[0])
6025 6026 {
6026 6027 localStorage.setItem(lsReadFlag, notifs[0].timestamp);
6027 6028 }
6028 6029 });
6029   -
  6030 +
6030 6031 if (this.notificationBtn == null)
6031 6032 {
6032 6033 this.notificationBtn = document.createElement('div');
6033 6034 this.notificationBtn.className = 'geNotification-box';
6034   -
  6035 +
6035 6036 if (uiTheme == 'min')
6036 6037 {
6037 6038 this.notificationBtn.style.width = '30px';
... ... @@ -6041,11 +6042,11 @@ App.prototype.showNotification = function(notifs, lsReadFlag)
6041 6042 {
6042 6043 this.notificationBtn.style.top = '2px';
6043 6044 }
6044   -
  6045 +
6045 6046 var notifCount = document.createElement('span');
6046 6047 notifCount.className = 'geNotification-count';
6047 6048 this.notificationBtn.appendChild(notifCount);
6048   -
  6049 +
6049 6050 var notifBell = document.createElement('div');
6050 6051 notifBell.className = 'geNotification-bell';
6051 6052 notifBell.style.opacity = uiTheme == 'min'? '0.5' : '';
... ... @@ -6062,15 +6063,15 @@ App.prototype.showNotification = function(notifs, lsReadFlag)
6062 6063 bellPart.className = 'geBell-rad';
6063 6064 notifBell.appendChild(bellPart);
6064 6065 this.notificationBtn.appendChild(notifBell);
6065   -
  6066 +
6066 6067 //Add as first child such that it is the left-most one
6067 6068 this.buttonContainer.insertBefore(this.notificationBtn, this.buttonContainer.firstChild);
6068   -
  6069 +
6069 6070 this.notificationWin = document.createElement('div');
6070 6071 this.notificationWin.className = 'geNotifPanel';
6071 6072 this.notificationWin.style.display = 'none';
6072 6073 document.body.appendChild(this.notificationWin);
6073   -
  6074 +
6074 6075 var winHeader = document.createElement('div');
6075 6076 winHeader.className = 'header';
6076 6077 var winTitle = document.createElement('span');
... ... @@ -6082,7 +6083,7 @@ App.prototype.showNotification = function(notifs, lsReadFlag)
6082 6083 winClose.textContent = 'x';
6083 6084 winHeader.appendChild(winClose);
6084 6085 this.notificationWin.appendChild(winHeader);
6085   -
  6086 +
6086 6087 var winBody = document.createElement('div');
6087 6088 winBody.className = 'notifications clearfix';
6088 6089 var notifList = document.createElement('div');
... ... @@ -6090,7 +6091,7 @@ App.prototype.showNotification = function(notifs, lsReadFlag)
6090 6091 notifList.style.position = 'relative';
6091 6092 winBody.appendChild(notifList);
6092 6093 this.notificationWin.appendChild(winBody);
6093   -
  6094 +
6094 6095 mxEvent.addListener(this.notificationBtn, 'click', mxUtils.bind(this, function()
6095 6096 {
6096 6097 if (this.notificationWin.style.display == 'none')
... ... @@ -6107,17 +6108,17 @@ App.prototype.showNotification = function(notifs, lsReadFlag)
6107 6108 markAllAsRead();
6108 6109 }
6109 6110 }));
6110   -
  6111 +
6111 6112 mxEvent.addListener(winClose, 'click', markAllAsRead);
6112 6113 }
6113 6114 else
6114 6115 {
6115 6116 this.notificationBtn.style.display = ''; //In case it was hidden
6116 6117 }
6117   -
  6118 +
6118 6119 var newNotif = 0;
6119 6120 var notifListEl = document.getElementById('geNotifList');
6120   -
  6121 +
6121 6122 if (notifListEl == null)
6122 6123 {
6123 6124 return; //This shouldn't happen and no meaning of continuing
... ... @@ -6125,7 +6126,7 @@ App.prototype.showNotification = function(notifs, lsReadFlag)
6125 6126 else
6126 6127 {
6127 6128 notifListEl.innerHTML = '<div class="line"></div>';
6128   -
  6129 +
6129 6130 for (var i = 0; i < notifs.length; i++)
6130 6131 {
6131 6132 (function(editorUi, notif)
... ... @@ -6134,19 +6135,19 @@ App.prototype.showNotification = function(notifs, lsReadFlag)
6134 6135 {
6135 6136 newNotif++;
6136 6137 }
6137   -
  6138 +
6138 6139 var notifEl = document.createElement('div');
6139 6140 notifEl.className = 'notification';
6140 6141 var ts = new Date(notif.timestamp);
6141 6142 var str = editorUi.timeSince(ts);
6142   -
  6143 +
6143 6144 if (str == null)
6144 6145 {
6145 6146 str = mxResources.get('lessThanAMinute');
6146 6147 }
6147   -
6148   - notifEl.innerHTML = '<div class="circle' + (notif.isNew? ' active' : '') + '"></div><span class="time">' +
6149   - mxUtils.htmlEntities(mxResources.get('timeAgo', [str], '{1} ago')) + '</span>' +
  6148 +
  6149 + notifEl.innerHTML = '<div class="circle' + (notif.isNew? ' active' : '') + '"></div><span class="time">' +
  6150 + mxUtils.htmlEntities(mxResources.get('timeAgo', [str], '{1} ago')) + '</span>' +
6150 6151 '<p>' + mxUtils.htmlEntities(notif.content) + '</p>';
6151 6152 if (notif.link)
6152 6153 {
... ... @@ -6155,18 +6156,18 @@ App.prototype.showNotification = function(notifs, lsReadFlag)
6155 6156 window.open(notif.link, 'notifWin');
6156 6157 });
6157 6158 }
6158   -
  6159 +
6159 6160 notifListEl.appendChild(notifEl);
6160 6161 })(this, notifs[i]);
6161 6162 }
6162 6163 }
6163   -
  6164 +
6164 6165 shouldAnimate(newNotif);
6165 6166 };
6166 6167
6167 6168 /**
6168 6169 * Translates this point by the given vector.
6169   - *
  6170 + *
6170 6171 * @param {number} dx X-coordinate of the translation.
6171 6172 * @param {number} dy Y-coordinate of the translation.
6172 6173 */
... ... @@ -6176,12 +6177,12 @@ App.prototype.save = function(name, done)
6176 6177 if (file != null && this.spinner.spin(document.body, mxResources.get('saving')))
6177 6178 {
6178 6179 this.editor.setStatus('');
6179   -
  6180 +
6180 6181 if (this.editor.graph.isEditing())
6181 6182 {
6182 6183 this.editor.graph.stopEditing();
6183 6184 }
6184   -
  6185 +
6185 6186 var success = mxUtils.bind(this, function()
6186 6187 {
6187 6188 file.handleFileSuccess(true);
... ... @@ -6191,7 +6192,7 @@ App.prototype.save = function(name, done)
6191 6192 done();
6192 6193 }
6193 6194 });
6194   -
  6195 +
6195 6196 var error = mxUtils.bind(this, function(err)
6196 6197 {
6197 6198 if (file.isModified())
... ... @@ -6201,10 +6202,10 @@ App.prototype.save = function(name, done)
6201 6202 this.save(name, done);
6202 6203 }));
6203 6204 }
6204   -
  6205 +
6205 6206 file.handleFileError(err, err == null || err.name != 'AbortError');
6206 6207 });
6207   -
  6208 +
6208 6209 try
6209 6210 {
6210 6211 if (name == file.getTitle())
... ... @@ -6234,23 +6235,23 @@ App.prototype.pickFolder = function(mode, fn, enabled, direct, force)
6234 6235 {
6235 6236 enabled = (enabled != null) ? enabled : true;
6236 6237 var resume = this.spinner.pause();
6237   -
  6238 +
6238 6239 if (enabled && mode == App.MODE_GOOGLE && this.drive != null)
6239 6240 {
6240 6241 // Shows a save dialog
6241 6242 this.drive.pickFolder(mxUtils.bind(this, function(evt)
6242 6243 {
6243 6244 resume();
6244   -
  6245 +
6245 6246 if (evt.action == google.picker.Action.PICKED)
6246 6247 {
6247 6248 var folderId = null;
6248   -
  6249 +
6249 6250 if (evt.docs != null && evt.docs.length > 0 && evt.docs[0].type == 'folder')
6250 6251 {
6251 6252 folderId = evt.docs[0].id;
6252 6253 }
6253   -
  6254 +
6254 6255 fn(folderId);
6255 6256 }
6256 6257 }), force);
... ... @@ -6261,7 +6262,7 @@ App.prototype.pickFolder = function(mode, fn, enabled, direct, force)
6261 6262 {
6262 6263 var folderId = null;
6263 6264 resume();
6264   -
  6265 +
6265 6266 if (files != null && files.value != null && files.value.length > 0)
6266 6267 {
6267 6268 folderId = OneDriveFile.prototype.getIdOf(files.value[0]);
... ... @@ -6300,7 +6301,7 @@ App.prototype.pickFolder = function(mode, fn, enabled, direct, force)
6300 6301 };
6301 6302
6302 6303 /**
6303   - *
  6304 + *
6304 6305 */
6305 6306 App.prototype.exportFile = function(data, filename, mimeType, base64Encoded, mode, folderId)
6306 6307 {
... ... @@ -6330,12 +6331,12 @@ App.prototype.exportFile = function(data, filename, mimeType, base64Encoded, mod
6330 6331 // "File exported. Click here to open folder."
6331 6332 // this.editor.setStatus('<div class="geStatusMessage">' +
6332 6333 // mxResources.get('saved') + '</div>');
6333   -//
  6334 +//
6334 6335 // // Installs click handler for opening
6335 6336 // if (this.statusContainer != null)
6336 6337 // {
6337 6338 // var links = this.statusContainer.getElementsByTagName('div');
6338   -//
  6339 +//
6339 6340 // if (links.length > 0)
6340 6341 // {
6341 6342 // links[0].style.cursor = 'pointer';
... ... @@ -6349,7 +6350,7 @@ App.prototype.exportFile = function(data, filename, mimeType, base64Encoded, mod
6349 6350 // }));
6350 6351 // }
6351 6352 // }
6352   -
  6353 +
6353 6354 this.spinner.stop();
6354 6355 }), mxUtils.bind(this, function(resp)
6355 6356 {
... ... @@ -6425,7 +6426,7 @@ App.prototype.exportFile = function(data, filename, mimeType, base64Encoded, mod
6425 6426 {
6426 6427 localStorage.setItem(filename, data);
6427 6428 });
6428   -
  6429 +
6429 6430 if (localStorage.getItem(filename) == null)
6430 6431 {
6431 6432 fn();
... ... @@ -6439,14 +6440,14 @@ App.prototype.exportFile = function(data, filename, mimeType, base64Encoded, mod
6439 6440
6440 6441 /**
6441 6442 * Translates this point by the given vector.
6442   - *
  6443 + *
6443 6444 * @param {number} dx X-coordinate of the translation.
6444 6445 * @param {number} dy Y-coordinate of the translation.
6445 6446 */
6446 6447 App.prototype.descriptorChanged = function()
6447 6448 {
6448 6449 var file = this.getCurrentFile();
6449   -
  6450 +
6450 6451 if (file != null)
6451 6452 {
6452 6453 if (this.fname != null)
... ... @@ -6457,23 +6458,23 @@ App.prototype.descriptorChanged = function()
6457 6458 mxUtils.write(this.fname, filename);
6458 6459 this.fname.setAttribute('title', filename + ' - ' + mxResources.get('rename'));
6459 6460 }
6460   -
  6461 +
6461 6462 var graph = this.editor.graph;
6462 6463 var editable = file.isEditable() && !file.invalidChecksum;
6463   -
  6464 +
6464 6465 if (graph.isEnabled() && !editable)
6465 6466 {
6466 6467 graph.reset();
6467 6468 }
6468   -
  6469 +
6469 6470 graph.setEnabled(editable);
6470   -
  6471 +
6471 6472 // Ignores title and hash for revisions
6472 6473 if (urlParams['rev'] == null)
6473 6474 {
6474 6475 this.updateDocumentTitle();
6475 6476 var newHash = file.getHash();
6476   -
  6477 +
6477 6478 if (newHash.length > 0)
6478 6479 {
6479 6480 window.location.hash = newHash;
... ... @@ -6484,9 +6485,9 @@ App.prototype.descriptorChanged = function()
6484 6485 }
6485 6486 }
6486 6487 }
6487   -
  6488 +
6488 6489 this.updateUi();
6489   -
  6490 +
6490 6491 // Refresh if editable state has changed
6491 6492 if (this.format != null && (file == null ||
6492 6493 this.fileEditable != file.isEditable()) &&
... ... @@ -6495,7 +6496,7 @@ App.prototype.descriptorChanged = function()
6495 6496 this.format.refresh();
6496 6497 this.fileEditable = (file != null) ? file.isEditable() : null;
6497 6498 }
6498   -
  6499 +
6499 6500 this.fireEvent(new mxEventObject('fileDescriptorChanged', 'file', file));
6500 6501 };
6501 6502
... ... @@ -6505,7 +6506,7 @@ App.prototype.descriptorChanged = function()
6505 6506 App.prototype.showAuthDialog = function(peer, showRememberOption, fn, closeFn)
6506 6507 {
6507 6508 var resume = this.spinner.pause();
6508   -
  6509 +
6509 6510 this.showDialog(new AuthDialog(this, peer, showRememberOption, mxUtils.bind(this, function(remember)
6510 6511 {
6511 6512 try
... ... @@ -6529,7 +6530,7 @@ App.prototype.showAuthDialog = function(peer, showRememberOption, fn, closeFn)
6529 6530 {
6530 6531 closeFn(cancel);
6531 6532 }
6532   -
  6533 +
6533 6534 if (cancel && this.getCurrentFile() == null && this.dialog == null)
6534 6535 {
6535 6536 this.showSplash();
... ... @@ -6545,32 +6546,32 @@ App.prototype.showAuthDialog = function(peer, showRememberOption, fn, closeFn)
6545 6546 App.prototype.convertFile = function(url, filename, mimeType, extension, success, error, executeRequest, headers)
6546 6547 {
6547 6548 var name = filename;
6548   -
  6549 +
6549 6550 // SVG file extensions are valid and needed for image import
6550 6551 if (!/\.svg$/i.test(name))
6551 6552 {
6552 6553 name = name.substring(0, filename.lastIndexOf('.')) + extension;
6553 6554 }
6554   -
  6555 +
6555 6556 var gitHubUrl = false;
6556   -
  6557 +
6557 6558 if (this.gitHub != null && url.substring(0, this.gitHub.baseUrl.length) == this.gitHub.baseUrl)
6558 6559 {
6559 6560 gitHubUrl = true;
6560 6561 }
6561   -
  6562 +
6562 6563 // Workaround for wrong binary response with VSD(X) & VDX files
6563 6564 if (/\.v(dx|sdx?)$/i.test(filename) && Graph.fileSupport && new XMLHttpRequest().upload &&
6564 6565 typeof new XMLHttpRequest().responseType === 'string')
6565 6566 {
6566 6567 var req = new XMLHttpRequest();
6567 6568 req.open('GET', url, true);
6568   -
  6569 +
6569 6570 if (!gitHubUrl)
6570 6571 {
6571 6572 req.responseType = 'blob';
6572 6573 }
6573   -
  6574 +
6574 6575 if (headers)
6575 6576 {
6576 6577 for (var key in headers)
... ... @@ -6578,13 +6579,13 @@ App.prototype.convertFile = function(url, filename, mimeType, extension, success
6578 6579 req.setRequestHeader(key, headers[key]);
6579 6580 }
6580 6581 }
6581   -
  6582 +
6582 6583 req.onload = mxUtils.bind(this, function()
6583 6584 {
6584 6585 if (req.status >= 200 && req.status <= 299)
6585 6586 {
6586 6587 var blob = null;
6587   -
  6588 +
6588 6589 if (gitHubUrl)
6589 6590 {
6590 6591 var file = JSON.parse(req.responseText);
... ... @@ -6594,7 +6595,7 @@ App.prototype.convertFile = function(url, filename, mimeType, extension, success
6594 6595 {
6595 6596 blob = new Blob([req.response], {type: 'application/octet-stream'});
6596 6597 }
6597   -
  6598 +
6598 6599 this.importVisio(blob, mxUtils.bind(this, function(xml)
6599 6600 {
6600 6601 success(new LocalFile(this, xml, name, true));
... ... @@ -6618,7 +6619,7 @@ App.prototype.convertFile = function(url, filename, mimeType, extension, success
6618 6619 if (/\.pdf$/i.test(filename))
6619 6620 {
6620 6621 var temp = Editor.extractGraphModelFromPdf(data);
6621   -
  6622 +
6622 6623 if (temp != null && temp.length > 0)
6623 6624 {
6624 6625 success(new LocalFile(this, temp, name, true));
... ... @@ -6627,7 +6628,7 @@ App.prototype.convertFile = function(url, filename, mimeType, extension, success
6627 6628 else if (/\.png$/i.test(filename))
6628 6629 {
6629 6630 var temp = this.extractGraphModelFromPng(data);
6630   -
  6631 +
6631 6632 if (temp != null)
6632 6633 {
6633 6634 success(new LocalFile(this, temp, name, true));
... ... @@ -6671,7 +6672,7 @@ App.prototype.convertFile = function(url, filename, mimeType, extension, success
6671 6672 var binary = /\.png$/i.test(filename) || /\.jpe?g$/i.test(filename) ||
6672 6673 /\.pdf$/i.test(filename) || (mimeType != null &&
6673 6674 mimeType.substring(0, 6) == 'image/');
6674   -
  6675 +
6675 6676 // NOTE: Cannot force non-binary request via loadUrl so needs separate
6676 6677 // code as decoding twice on content with binary data did not work
6677 6678 if (gitHubUrl)
... ... @@ -6684,16 +6685,16 @@ App.prototype.convertFile = function(url, filename, mimeType, extension, success
6684 6685 {
6685 6686 var file = JSON.parse(req.getText());
6686 6687 var data = file.content;
6687   -
  6688 +
6688 6689 if (file.encoding === 'base64')
6689 6690 {
6690 6691 if (/\.png$/i.test(filename))
6691 6692 {
6692   - data = 'data:image/png;base64,' + data;
  6693 + data = 'data:image/png;base64,' + data;
6693 6694 }
6694 6695 else if (/\.pdf$/i.test(filename))
6695 6696 {
6696   - data = 'data:application/pdf;base64,' + data;
  6697 + data = 'data:application/pdf;base64,' + data;
6697 6698 }
6698 6699 else
6699 6700 {
... ... @@ -6701,7 +6702,7 @@ App.prototype.convertFile = function(url, filename, mimeType, extension, success
6701 6702 data = (window.atob && !mxClient.IS_IE && !mxClient.IS_IE11) ? atob(data) : Base64.decode(data);
6702 6703 }
6703 6704 }
6704   -
  6705 +
6705 6706 handleData(data);
6706 6707 }
6707 6708 }
... ... @@ -6736,7 +6737,7 @@ App.prototype.convertFile = function(url, filename, mimeType, extension, success
6736 6737
6737 6738 /**
6738 6739 * Adds the listener for automatically saving the diagram for local changes.
6739   - */
  6740 + */
6740 6741 App.prototype.updateHeader = function()
6741 6742 {
6742 6743 if (this.menubar != null)
... ... @@ -6749,40 +6750,40 @@ App.prototype.updateHeader = function()
6749 6750 this.appIcon.style.margin = '14px 0px 8px 16px';
6750 6751 this.appIcon.style.opacity = '0.85';
6751 6752 this.appIcon.style.borderRadius = '3px';
6752   -
  6753 +
6753 6754 if (uiTheme != 'dark')
6754 6755 {
6755 6756 this.appIcon.style.backgroundColor = '#f08705';
6756 6757 }
6757   -
  6758 +
6758 6759 mxEvent.disableContextMenu(this.appIcon);
6759   -
  6760 +
6760 6761 mxEvent.addListener(this.appIcon, 'click', mxUtils.bind(this, function(evt)
6761 6762 {
6762 6763 this.appIconClicked(evt);
6763 6764 }));
6764   -
  6765 +
6765 6766 // LATER: Use Alpha image loader in IE6
6766 6767 // NOTE: This uses the diagram bit of the old logo as it looks better in this case
6767 6768 //this.appIcon.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=' + IMAGE_PATH + '/logo-white.png,sizingMethod=\'scale\')';
6768 6769 var logo = (!mxClient.IS_SVG) ? 'url(\'' + IMAGE_PATH + '/logo-white.png\')' :
6769 6770 ((uiTheme == 'dark') ? 'url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMzA2LjE4NSAxMjAuMjk2IgogICB2aWV3Qm94PSIyNCAyNiA2OCA2OCIKICAgeT0iMHB4IgogICB4PSIwcHgiCiAgIHZlcnNpb249IjEuMSI+CiAgIAkgPGc+PGxpbmUKICAgICAgIHkyPSI3Mi4zOTQiCiAgICAgICB4Mj0iNDEuMDYxIgogICAgICAgeTE9IjQzLjM4NCIKICAgICAgIHgxPSI1OC4wNjkiCiAgICAgICBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiCiAgICAgICBzdHJva2Utd2lkdGg9IjMuNTUyOCIKICAgICAgIHN0cm9rZT0iI0ZGRkZGRiIKICAgICAgIGZpbGw9Im5vbmUiIC8+PGxpbmUKICAgICAgIHkyPSI3Mi4zOTQiCiAgICAgICB4Mj0iNzUuMDc2IgogICAgICAgeTE9IjQzLjM4NCIKICAgICAgIHgxPSI1OC4wNjgiCiAgICAgICBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiCiAgICAgICBzdHJva2Utd2lkdGg9IjMuNTAwOCIKICAgICAgIHN0cm9rZT0iI0ZGRkZGRiIKICAgICAgIGZpbGw9Im5vbmUiIC8+PGc+PHBhdGgKICAgICAgICAgZD0iTTUyLjc3Myw3Ny4wODRjMCwxLjk1NC0xLjU5OSwzLjU1My0zLjU1MywzLjU1M0gzNi45OTljLTEuOTU0LDAtMy41NTMtMS41OTktMy41NTMtMy41NTN2LTkuMzc5ICAgIGMwLTEuOTU0LDEuNTk5LTMuNTUzLDMuNTUzLTMuNTUzaDEyLjIyMmMxLjk1NCwwLDMuNTUzLDEuNTk5LDMuNTUzLDMuNTUzVjc3LjA4NHoiCiAgICAgICAgIGZpbGw9IiNGRkZGRkYiIC8+PC9nPjxnCiAgICAgICBpZD0iZzM0MTkiPjxwYXRoCiAgICAgICAgIGQ9Ik02Ny43NjIsNDguMDc0YzAsMS45NTQtMS41OTksMy41NTMtMy41NTMsMy41NTNINTEuOTg4Yy0xLjk1NCwwLTMuNTUzLTEuNTk5LTMuNTUzLTMuNTUzdi05LjM3OSAgICBjMC0xLjk1NCwxLjU5OS0zLjU1MywzLjU1My0zLjU1M0g2NC4yMWMxLjk1NCwwLDMuNTUzLDEuNTk5LDMuNTUzLDMuNTUzVjQ4LjA3NHoiCiAgICAgICAgIGZpbGw9IiNGRkZGRkYiIC8+PC9nPjxnPjxwYXRoCiAgICAgICAgIGQ9Ik04Mi43NTIsNzcuMDg0YzAsMS45NTQtMS41OTksMy41NTMtMy41NTMsMy41NTNINjYuOTc3Yy0xLjk1NCwwLTMuNTUzLTEuNTk5LTMuNTUzLTMuNTUzdi05LjM3OSAgICBjMC0xLjk1NCwxLjU5OS0zLjU1MywzLjU1My0zLjU1M2gxMi4yMjJjMS45NTQsMCwzLjU1MywxLjU5OSwzLjU1MywzLjU1M1Y3Ny4wODR6IgogICAgICAgICBmaWxsPSIjRkZGRkZGIiAvPjwvZz48L2c+PC9zdmc+)' :
6770 6771 'url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJFYmVuZV8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIKCSB2aWV3Qm94PSIwIDAgMjI1IDIyNSIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMjI1IDIyNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MXtmaWxsOiNERjZDMEM7fQoJLnN0MntmaWxsOiNGRkZGRkY7fQo8L3N0eWxlPgo8cGF0aCBjbGFzcz0ic3QxIiBkPSJNMjI1LDIxNS40YzAsNS4zLTQuMyw5LjYtOS41LDkuNmwwLDBINzcuMWwtNDQuOC00NS41TDYwLjIsMTM0bDgyLjctMTAyLjdsODIuMSw4NC41VjIxNS40eiIvPgo8cGF0aCBjbGFzcz0ic3QyIiBkPSJNMTg0LjYsMTI1LjhoLTIzLjdsLTI1LTQyLjdjNS43LTEuMiw5LjgtNi4yLDkuNy0xMlYzOWMwLTYuOC01LjQtMTIuMy0xMi4yLTEyLjNoLTAuMUg5MS42CgljLTYuOCwwLTEyLjMsNS40LTEyLjMsMTIuMlYzOXYzMi4xYzAsNS44LDQsMTAuOCw5LjcsMTJsLTI1LDQyLjdINDAuNGMtNi44LDAtMTIuMyw1LjQtMTIuMywxMi4ydjAuMXYzMi4xCgljMCw2LjgsNS40LDEyLjMsMTIuMiwxMi4zaDAuMWg0MS43YzYuOCwwLDEyLjMtNS40LDEyLjMtMTIuMnYtMC4xdi0zMi4xYzAtNi44LTUuNC0xMi4zLTEyLjItMTIuM2gtMC4xaC00bDI0LjgtNDIuNGgxOS4zCglsMjQuOSw0Mi40SDE0M2MtNi44LDAtMTIuMyw1LjQtMTIuMywxMi4ydjAuMXYzMi4xYzAsNi44LDUuNCwxMi4zLDEyLjIsMTIuM2gwLjFoNDEuN2M2LjgsMCwxMi4zLTUuNCwxMi4zLTEyLjJ2LTAuMXYtMzIuMQoJYzAtNi44LTUuNC0xMi4zLTEyLjItMTIuM0MxODQuNywxMjUuOCwxODQuNywxMjUuOCwxODQuNiwxMjUuOHoiLz4KPC9zdmc+Cg==)');
6771   - this.appIcon.style.backgroundImage = logo;
  6772 + this.appIcon.style.backgroundImage = logo;
6772 6773 this.appIcon.style.backgroundPosition = 'center center';
6773 6774 this.appIcon.style.backgroundSize = '100% 100%';
6774 6775 this.appIcon.style.backgroundRepeat = 'no-repeat';
6775   -
  6776 +
6776 6777 mxUtils.setPrefixedStyle(this.appIcon.style, 'transition', 'all 125ms linear');
6777   -
  6778 +
6778 6779 mxEvent.addListener(this.appIcon, 'mouseover', mxUtils.bind(this, function()
6779 6780 {
6780 6781 var file = this.getCurrentFile();
6781   -
  6782 +
6782 6783 if (file != null)
6783 6784 {
6784 6785 var mode = file.getMode();
6785   -
  6786 +
6786 6787 if (mode == App.MODE_GOOGLE)
6787 6788 {
6788 6789 this.appIcon.style.backgroundImage = 'url(' + IMAGE_PATH + '/google-drive-logo-white.svg)';
... ... @@ -6815,18 +6816,18 @@ App.prototype.updateHeader = function()
6815 6816 }
6816 6817 }
6817 6818 }));
6818   -
  6819 +
6819 6820 mxEvent.addListener(this.appIcon, 'mouseout', mxUtils.bind(this, function()
6820 6821 {
6821 6822 this.appIcon.style.backgroundImage = logo;
6822 6823 this.appIcon.style.backgroundSize = '90% 90%';
6823 6824 }));
6824   -
  6825 +
6825 6826 if (urlParams['embed'] != '1')
6826 6827 {
6827 6828 this.menubarContainer.appendChild(this.appIcon);
6828 6829 }
6829   -
  6830 +
6830 6831 this.fnameWrapper = document.createElement('div');
6831 6832 this.fnameWrapper.style.position = 'absolute';
6832 6833 this.fnameWrapper.style.right = '120px';
... ... @@ -6836,7 +6837,7 @@ App.prototype.updateHeader = function()
6836 6837 this.fnameWrapper.style.display = 'none';
6837 6838 this.fnameWrapper.style.overflow = 'hidden';
6838 6839 this.fnameWrapper.style.textOverflow = 'ellipsis';
6839   -
  6840 +
6840 6841 this.fname = document.createElement('a');
6841 6842 this.fname.setAttribute('title', mxResources.get('rename'));
6842 6843 this.fname.className = 'geItem';
... ... @@ -6844,18 +6845,18 @@ App.prototype.updateHeader = function()
6844 6845 this.fname.style.display = 'inline';
6845 6846 this.fname.style.fontSize = '18px';
6846 6847 this.fname.style.whiteSpace = 'nowrap';
6847   -
  6848 +
6848 6849 // Prevents focus
6849 6850 mxEvent.addListener(this.fname, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown',
6850 6851 mxUtils.bind(this, function(evt)
6851 6852 {
6852 6853 evt.preventDefault();
6853 6854 }));
6854   -
  6855 +
6855 6856 mxEvent.addListener(this.fname, 'click', mxUtils.bind(this, function(evt)
6856 6857 {
6857 6858 var file = this.getCurrentFile();
6858   -
  6859 +
6859 6860 if (file != null && file.isRenamable())
6860 6861 {
6861 6862 if (this.editor.graph.isEditing())
... ... @@ -6865,23 +6866,23 @@ App.prototype.updateHeader = function()
6865 6866
6866 6867 this.actions.get('rename').funct();
6867 6868 }
6868   -
  6869 +
6869 6870 mxEvent.consume(evt);
6870 6871 }));
6871   -
  6872 +
6872 6873 this.fnameWrapper.appendChild(this.fname);
6873   -
  6874 +
6874 6875 if (urlParams['embed'] != '1')
6875 6876 {
6876 6877 this.menubarContainer.appendChild(this.fnameWrapper);
6877   -
  6878 +
6878 6879 this.menubar.container.style.position = 'absolute';
6879 6880 this.menubar.container.style.paddingLeft = '59px';
6880 6881 this.toolbar.container.style.paddingLeft = '16px';
6881 6882 this.menubar.container.style.boxSizing = 'border-box';
6882 6883 this.menubar.container.style.top = '34px';
6883 6884 }
6884   -
  6885 +
6885 6886 /**
6886 6887 * Adds format panel toggle.
6887 6888 */
... ... @@ -6899,24 +6900,24 @@ App.prototype.updateHeader = function()
6899 6900 this.toggleFormatElement.style.backgroundPosition = '50% 50%';
6900 6901 this.toggleFormatElement.style.backgroundRepeat = 'no-repeat';
6901 6902 this.toolbarContainer.appendChild(this.toggleFormatElement);
6902   -
  6903 +
6903 6904 if (uiTheme == 'dark')
6904 6905 {
6905 6906 this.toggleFormatElement.style.filter = 'invert(100%)';
6906 6907 }
6907   -
  6908 +
6908 6909 // Prevents focus
6909 6910 mxEvent.addListener(this.toggleFormatElement, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown',
6910 6911 mxUtils.bind(this, function(evt)
6911 6912 {
6912 6913 evt.preventDefault();
6913 6914 }));
6914   -
  6915 +
6915 6916 mxEvent.addListener(this.toggleFormatElement, 'click', mxUtils.bind(this, function(evt)
6916 6917 {
6917 6918 EditorUi.logEvent({category: 'TOOLBAR-ACTION-',
6918 6919 action: 'formatPanel'});
6919   -
  6920 +
6920 6921 this.actions.get('formatPanel').funct();
6921 6922 mxEvent.consume(evt);
6922 6923 }));
... ... @@ -6932,7 +6933,7 @@ App.prototype.updateHeader = function()
6932 6933 this.toggleFormatElement.style.backgroundImage = 'url(\'' + this.formatHideImage + '\')';
6933 6934 }
6934 6935 });
6935   -
  6936 +
6936 6937 this.addListener('formatWidthChanged', toggleFormatPanel);
6937 6938 toggleFormatPanel();
6938 6939
... ... @@ -6951,35 +6952,35 @@ App.prototype.updateHeader = function()
6951 6952 this.fullscreenElement.style.backgroundRepeat = 'no-repeat';
6952 6953 this.fullscreenElement.style.backgroundImage = 'url(\'' + this.fullscreenImage + '\')';
6953 6954 this.toolbarContainer.appendChild(this.fullscreenElement);
6954   -
  6955 +
6955 6956 // Prevents focus
6956 6957 mxEvent.addListener(this.fullscreenElement, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown',
6957 6958 mxUtils.bind(this, function(evt)
6958 6959 {
6959 6960 evt.preventDefault();
6960 6961 }));
6961   -
  6962 +
6962 6963 // Some style changes in Atlas theme
6963 6964 if (uiTheme == 'atlas')
6964 6965 {
6965 6966 mxUtils.setOpacity(this.toggleFormatElement, 70);
6966 6967 mxUtils.setOpacity(this.fullscreenElement, 70);
6967 6968 }
6968   -
  6969 +
6969 6970 var initialPosition = this.hsplitPosition;
6970 6971
6971 6972 if (uiTheme == 'dark')
6972 6973 {
6973 6974 this.fullscreenElement.style.filter = 'invert(100%)';
6974 6975 }
6975   -
  6976 +
6976 6977 mxEvent.addListener(this.fullscreenElement, 'click', mxUtils.bind(this, function(evt)
6977 6978 {
6978 6979 var visible = this.fullscreenMode;
6979 6980
6980 6981 EditorUi.logEvent({category: 'TOOLBAR-ACTION-',
6981 6982 action: 'fullscreen' , currentstate: visible});
6982   -
  6983 +
6983 6984 if (uiTheme != 'atlas' && urlParams['embed'] != '1')
6984 6985 {
6985 6986 this.toggleCompactMode(visible);
... ... @@ -6989,7 +6990,7 @@ App.prototype.updateHeader = function()
6989 6990 {
6990 6991 initialPosition = this.hsplitPosition;
6991 6992 }
6992   -
  6993 +
6993 6994 this.hsplitPosition = (visible) ? initialPosition : 0;
6994 6995 this.toggleFormatPanel(visible);
6995 6996 this.fullscreenMode = !visible;
... ... @@ -7015,22 +7016,22 @@ App.prototype.updateHeader = function()
7015 7016 this.toggleElement.style.fontSize = '14px';
7016 7017 this.toggleElement.style.textDecoration = 'none';
7017 7018 this.toggleElement.style.backgroundImage = 'url(\'' + this.chevronUpImage + '\')';
7018   -
  7019 +
7019 7020 this.toggleElement.style.backgroundPosition = '50% 50%';
7020 7021 this.toggleElement.style.backgroundRepeat = 'no-repeat';
7021   -
  7022 +
7022 7023 if (uiTheme == 'dark')
7023 7024 {
7024 7025 this.toggleElement.style.filter = 'invert(100%)';
7025 7026 }
7026   -
  7027 +
7027 7028 // Prevents focus
7028 7029 mxEvent.addListener(this.toggleElement, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown',
7029 7030 mxUtils.bind(this, function(evt)
7030 7031 {
7031 7032 evt.preventDefault();
7032 7033 }));
7033   -
  7034 +
7034 7035 // Toggles compact mode
7035 7036 mxEvent.addListener(this.toggleElement, 'click', mxUtils.bind(this, function(evt)
7036 7037 {
... ... @@ -7039,12 +7040,12 @@ App.prototype.updateHeader = function()
7039 7040 this.toggleCompactMode();
7040 7041 mxEvent.consume(evt);
7041 7042 }));
7042   -
  7043 +
7043 7044 if (uiTheme != 'atlas')
7044 7045 {
7045 7046 this.toolbarContainer.appendChild(this.toggleElement);
7046 7047 }
7047   -
  7048 +
7048 7049 // Enable compact mode for small screens except for Firefox where the height is wrong
7049 7050 if (!mxClient.IS_FF && screen.height <= 740 && typeof this.toggleElement.click !== 'undefined')
7050 7051 {
... ... @@ -7065,7 +7066,7 @@ App.prototype.updateHeader = function()
7065 7066 App.prototype.toggleCompactMode = function(visible)
7066 7067 {
7067 7068 visible = (visible != null) ? visible : this.compactMode;
7068   -
  7069 +
7069 7070 if (visible)
7070 7071 {
7071 7072 this.menubar.container.style.position = 'absolute';
... ... @@ -7098,7 +7099,7 @@ App.prototype.toggleCompactMode = function(visible)
7098 7099 this.refresh();
7099 7100 this.toggleElement.style.backgroundImage = 'url(\'' + this.chevronDownImage + '\')';
7100 7101 }
7101   -
  7102 +
7102 7103 this.compactMode = !visible;
7103 7104 };
7104 7105
... ... @@ -7138,7 +7139,7 @@ App.prototype.updateUserElement = function()
7138 7139 this.userElement.style.backgroundImage = 'url(' + IMAGE_PATH + '/expanded.gif)';
7139 7140 this.userElement.style.backgroundPosition = '100% 60%';
7140 7141 this.userElement.style.backgroundRepeat = 'no-repeat';
7141   -
  7142 +
7142 7143 this.menubarContainer.appendChild(this.userElement);
7143 7144
7144 7145 // Prevents focus
... ... @@ -7162,10 +7163,10 @@ App.prototype.updateUserElement = function()
7162 7163 div.style.padding = '0px';
7163 7164 div.style.cursor = 'default';
7164 7165 div.style.minWidth = '300px';
7165   -
  7166 +
7166 7167 this.userPanel = div;
7167 7168 }
7168   -
  7169 +
7169 7170 if (this.userPanel.parentNode != null)
7170 7171 {
7171 7172 this.userPanel.parentNode.removeChild(this.userPanel);
... ... @@ -7174,7 +7175,7 @@ App.prototype.updateUserElement = function()
7174 7175 {
7175 7176 var connected = false;
7176 7177 this.userPanel.innerHTML = '';
7177   -
  7178 +
7178 7179 var img = document.createElement('img');
7179 7180
7180 7181 img.setAttribute('src', Dialog.prototype.closeImage);
... ... @@ -7182,7 +7183,7 @@ App.prototype.updateUserElement = function()
7182 7183 img.className = 'geDialogClose';
7183 7184 img.style.top = '8px';
7184 7185 img.style.right = '8px';
7185   -
  7186 +
7186 7187 mxEvent.addListener(img, 'click', mxUtils.bind(this, function()
7187 7188 {
7188 7189 if (this.userPanel.parentNode != null)
... ... @@ -7190,13 +7191,13 @@ App.prototype.updateUserElement = function()
7190 7191 this.userPanel.parentNode.removeChild(this.userPanel);
7191 7192 }
7192 7193 }));
7193   -
  7194 +
7194 7195 this.userPanel.appendChild(img);
7195   -
  7196 +
7196 7197 if (this.drive != null)
7197 7198 {
7198 7199 var driveUsers = this.drive.getUsersList();
7199   -
  7200 +
7200 7201 if (driveUsers.length > 0)
7201 7202 {
7202 7203 // LATER: Cannot change user while file is open since close will not work with new
... ... @@ -7208,7 +7209,7 @@ App.prototype.updateUserElement = function()
7208 7209 if (file != null && file.constructor == DriveFile)
7209 7210 {
7210 7211 this.spinner.spin(document.body, spinnerMsg);
7211   -
  7212 +
7212 7213 // file.close();
7213 7214 this.fileLoaded(null);
7214 7215
... ... @@ -7224,7 +7225,7 @@ App.prototype.updateUserElement = function()
7224 7225 callback();
7225 7226 }
7226 7227 });
7227   -
  7228 +
7228 7229 var createUserRow = mxUtils.bind(this, function (user)
7229 7230 {
7230 7231 var tr = document.createElement('tr');
... ... @@ -7244,7 +7245,7 @@ App.prototype.updateUserElement = function()
7244 7245 img.style.margin = '4px 8px 0 8px';
7245 7246 td.appendChild(img);
7246 7247 tr.appendChild(td);
7247   -
  7248 +
7248 7249 var td = document.createElement('td');
7249 7250 td.setAttribute('valign', 'middle');
7250 7251 td.style.whiteSpace = 'nowrap';
... ... @@ -7255,17 +7256,17 @@ App.prototype.updateUserElement = function()
7255 7256 mxUtils.write(td, user.displayName +
7256 7257 ((user.isCurrent && driveUsers.length > 1) ?
7257 7258 ' (' + mxResources.get('default') + ')' : ''));
7258   -
  7259 +
7259 7260 if (user.email != null)
7260 7261 {
7261 7262 mxUtils.br(td);
7262   -
  7263 +
7263 7264 var small = document.createElement('small');
7264 7265 small.style.color = 'gray';
7265 7266 mxUtils.write(small, user.email);
7266 7267 td.appendChild(small);
7267 7268 }
7268   -
  7269 +
7269 7270 var div = document.createElement('div');
7270 7271 div.style.marginTop = '4px';
7271 7272
... ... @@ -7286,7 +7287,7 @@ App.prototype.updateUserElement = function()
7286 7287 {
7287 7288 this.stateArg = null;
7288 7289 this.drive.setUser(user);
7289   -
  7290 +
7290 7291 this.drive.authorize(true, mxUtils.bind(this, function()
7291 7292 {
7292 7293 this.setMode(App.MODE_GOOGLE);
... ... @@ -7297,16 +7298,16 @@ App.prototype.updateUserElement = function()
7297 7298 this.handleError(resp);
7298 7299 }), true); //Remember is true since add account imply keeping that account
7299 7300 }), mxResources.get('closingFile') + '...');
7300   -
  7301 +
7301 7302 mxEvent.consume(evt);
7302 7303 }));
7303 7304 }
7304   -
  7305 +
7305 7306 return tr;
7306 7307 });
7307   -
  7308 +
7308 7309 connected = true;
7309   -
  7310 +
7310 7311 var driveUserTable = document.createElement('table');
7311 7312 driveUserTable.style.borderSpacing = '0';
7312 7313 driveUserTable.style.fontSize = '10pt';
... ... @@ -7317,9 +7318,9 @@ App.prototype.updateUserElement = function()
7317 7318 {
7318 7319 driveUserTable.appendChild(createUserRow(driveUsers[i]));
7319 7320 }
7320   -
  7321 +
7321 7322 this.userPanel.appendChild(driveUserTable);
7322   -
  7323 +
7323 7324 var div = document.createElement('div');
7324 7325 div.style.textAlign = 'left';
7325 7326 div.style.padding = '10px';
... ... @@ -7343,18 +7344,18 @@ App.prototype.updateUserElement = function()
7343 7344 btn.className = 'geBtn';
7344 7345 btn.style.float = 'right';
7345 7346 div.appendChild(btn);
7346   -
  7347 +
7347 7348 var btn = mxUtils.button(mxResources.get('addAccount'), mxUtils.bind(this, function()
7348 7349 {
7349 7350 var authWin = this.drive.createAuthWin();
7350 7351 //FIXME This doean't work to set focus back to main window until closing the file is done
7351 7352 authWin.blur();
7352 7353 window.focus();
7353   -
  7354 +
7354 7355 closeFile(mxUtils.bind(this, function()
7355 7356 {
7356 7357 this.stateArg = null;
7357   -
  7358 +
7358 7359 this.drive.authorize(false, mxUtils.bind(this, function()
7359 7360 {
7360 7361 this.setMode(App.MODE_GOOGLE);
... ... @@ -7372,7 +7373,7 @@ App.prototype.updateUserElement = function()
7372 7373 this.userPanel.appendChild(div);
7373 7374 }
7374 7375 }
7375   -
  7376 +
7376 7377 var addUser = mxUtils.bind(this, function(user, logo, logout, label)
7377 7378 {
7378 7379 if (user != null)
... ... @@ -7381,7 +7382,7 @@ App.prototype.updateUserElement = function()
7381 7382 {
7382 7383 this.userPanel.appendChild(document.createElement('hr'));
7383 7384 }
7384   -
  7385 +
7385 7386 connected = true;
7386 7387 var userTable = document.createElement('table');
7387 7388 userTable.style.borderSpacing = '0';
... ... @@ -7448,18 +7449,18 @@ App.prototype.updateUserElement = function()
7448 7449 div.style.textAlign = 'center';
7449 7450 div.style.padding = '10px';
7450 7451 div.style.whiteSpace = 'nowrap';
7451   -
  7452 +
7452 7453 if (logout != null)
7453 7454 {
7454 7455 var btn = mxUtils.button(mxResources.get('signOut'), logout);
7455 7456 btn.className = 'geBtn';
7456 7457 div.appendChild(btn);
7457 7458 }
7458   -
  7459 +
7459 7460 this.userPanel.appendChild(div);
7460 7461 }
7461 7462 });
7462   -
  7463 +
7463 7464 if (this.dropbox != null)
7464 7465 {
7465 7466 addUser(this.dropbox.getUser(), IMAGE_PATH + '/dropbox-logo.svg', mxUtils.bind(this, function()
... ... @@ -7473,7 +7474,7 @@ App.prototype.updateUserElement = function()
7473 7474 this.dropbox.logout();
7474 7475 window.location.hash = '';
7475 7476 });
7476   -
  7477 +
7477 7478 if (!file.isModified())
7478 7479 {
7479 7480 doLogout();
... ... @@ -7504,7 +7505,7 @@ App.prototype.updateUserElement = function()
7504 7505 this.oneDrive.logout();
7505 7506 window.location.hash = '';
7506 7507 });
7507   -
  7508 +
7508 7509 if (!file.isModified())
7509 7510 {
7510 7511 doLogout();
... ... @@ -7535,7 +7536,7 @@ App.prototype.updateUserElement = function()
7535 7536 this.gitHub.logout();
7536 7537 window.location.hash = '';
7537 7538 });
7538   -
  7539 +
7539 7540 if (!file.isModified())
7540 7541 {
7541 7542 doLogout();
... ... @@ -7552,7 +7553,7 @@ App.prototype.updateUserElement = function()
7552 7553 }
7553 7554 }), mxResources.get('github'));
7554 7555 }
7555   -
  7556 +
7556 7557 if (this.gitLab != null)
7557 7558 {
7558 7559 addUser(this.gitLab.getUser(), IMAGE_PATH + '/gitlab-logo.svg', mxUtils.bind(this, function()
... ... @@ -7598,7 +7599,7 @@ App.prototype.updateUserElement = function()
7598 7599 this.trello.logout();
7599 7600 window.location.hash = '';
7600 7601 });
7601   -
  7602 +
7602 7603 if (!file.isModified())
7603 7604 {
7604 7605 doLogout();
... ... @@ -7615,24 +7616,24 @@ App.prototype.updateUserElement = function()
7615 7616 }
7616 7617 }), mxResources.get('trello'));
7617 7618 }
7618   -
  7619 +
7619 7620 if (!connected)
7620 7621 {
7621 7622 var div = document.createElement('div');
7622 7623 div.style.textAlign = 'center';
7623 7624 div.style.padding = '10px';
7624 7625 div.innerHTML = mxResources.get('notConnected');
7625   -
  7626 +
7626 7627 this.userPanel.appendChild(div);
7627 7628 }
7628   -
  7629 +
7629 7630 var div = document.createElement('div');
7630 7631 div.style.textAlign = 'center';
7631 7632 div.style.padding = '10px';
7632 7633 div.style.background = Editor.isDarkMode() ? '' : 'whiteSmoke';
7633 7634 div.style.borderTop = '1px solid #e0e0e0';
7634 7635 div.style.whiteSpace = 'nowrap';
7635   -
  7636 +
7636 7637 if (urlParams['sketch'] == '1')
7637 7638 {
7638 7639 var btn = mxUtils.button(mxResources.get('share'), mxUtils.bind(this, function()
... ... @@ -7642,7 +7643,7 @@ App.prototype.updateUserElement = function()
7642 7643 btn.className = 'geBtn';
7643 7644 div.appendChild(btn);
7644 7645 this.userPanel.appendChild(div);
7645   -
  7646 +
7646 7647 if (this.commentsSupported())
7647 7648 {
7648 7649 btn = mxUtils.button(mxResources.get('comments'), mxUtils.bind(this, function()
... ... @@ -7670,10 +7671,10 @@ App.prototype.updateUserElement = function()
7670 7671
7671 7672 document.body.appendChild(this.userPanel);
7672 7673 }
7673   -
  7674 +
7674 7675 mxEvent.consume(evt);
7675 7676 }));
7676   -
  7677 +
7677 7678 mxEvent.addListener(document.body, 'click', mxUtils.bind(this, function(evt)
7678 7679 {
7679 7680 if (!mxEvent.isConsumed(evt) && this.userPanel != null && this.userPanel.parentNode != null)
... ... @@ -7682,9 +7683,9 @@ App.prototype.updateUserElement = function()
7682 7683 }
7683 7684 }));
7684 7685 }
7685   -
  7686 +
7686 7687 var user = null;
7687   -
  7688 +
7688 7689 if (this.drive != null && this.drive.getUser() != null)
7689 7690 {
7690 7691 user = this.drive.getUser();
... ... @@ -7706,11 +7707,11 @@ App.prototype.updateUserElement = function()
7706 7707 user = this.gitLab.getUser();
7707 7708 }
7708 7709 //TODO Trello no user issue
7709   -
  7710 +
7710 7711 if (user != null)
7711 7712 {
7712 7713 this.userElement.innerHTML = '';
7713   -
  7714 +
7714 7715 if (screen.width > 560)
7715 7716 {
7716 7717 mxUtils.write(this.userElement, user.displayName);
... ... @@ -7728,7 +7729,7 @@ App.prototype.updateUserElement = function()
7728 7729 App.prototype.getCurrentUser = function()
7729 7730 {
7730 7731 var user = null;
7731   -
  7732 +
7732 7733 if (this.drive != null && this.drive.getUser() != null)
7733 7734 {
7734 7735 user = this.drive.getUser();
... ... @@ -7746,17 +7747,17 @@ App.prototype.getCurrentUser = function()
7746 7747 user = this.gitHub.getUser();
7747 7748 }
7748 7749 //TODO Trello no user issue
7749   -
  7750 +
7750 7751 return user;
7751 7752 }
7752 7753 /**
7753 7754 * Override depends on mxSettings which is not defined in the minified viewer.
7754 7755 */
7755   -var editorResetGraph = Editor.prototype.resetGraph;
  7756 +var editorResetGraph = Editor.prototype.resetGraph;
7756 7757 Editor.prototype.resetGraph = function()
7757 7758 {
7758 7759 editorResetGraph.apply(this, arguments);
7759   -
  7760 +
7760 7761 // Overrides default with persisted value
7761 7762 if (this.graph.defaultPageFormat == null)
7762 7763 {
... ...