1/** 2 * Copyright (c) 2006-2017, JGraph Ltd 3 * Copyright (c) 2006-2017, Gaudenz Alder 4 */ 5/** 6 * Constructs a new point for the optional x and y coordinates. If no 7 * coordinates are given, then the default values for <x> and <y> are used. 8 * @constructor 9 * @class Implements a basic 2D point. Known subclassers = {@link mxRectangle}. 10 * @param {number} x X-coordinate of the point. 11 * @param {number} y Y-coordinate of the point. 12 */ 13StorageFile = function(ui, data, title) 14{ 15 DrawioFile.call(this, ui, data); 16 17 this.title = title; 18}; 19 20//Extends mxEventSource 21mxUtils.extend(StorageFile, DrawioFile); 22 23/** 24 * Sets the delay for autosave in milliseconds. Default is 2000. 25 */ 26StorageFile.prototype.autosaveDelay = 2000; 27 28/** 29 * Sets the delay for autosave in milliseconds. Default is 20000. 30 */ 31StorageFile.prototype.maxAutosaveDelay = 20000; 32 33/** 34 * A differentiator of the stored object type (file or lib) 35 */ 36StorageFile.prototype.type = 'F'; 37 38/** 39 * Translates this point by the given vector. 40 * 41 * @param {number} dx X-coordinate of the translation. 42 * @param {number} dy Y-coordinate of the translation. 43 */ 44StorageFile.prototype.getMode = function() 45{ 46 return App.MODE_BROWSER; 47}; 48 49/** 50 * Overridden to enable the autosave option in the document properties dialog. 51 */ 52StorageFile.prototype.isAutosaveOptional = function() 53{ 54 return true; 55}; 56 57/** 58 * Translates this point by the given vector. 59 * 60 * @param {number} dx X-coordinate of the translation. 61 * @param {number} dy Y-coordinate of the translation. 62 */ 63StorageFile.prototype.getHash = function() 64{ 65 return 'L' + encodeURIComponent(this.getTitle()); 66}; 67 68/** 69 * Translates this point by the given vector. 70 * 71 * @param {number} dx X-coordinate of the translation. 72 * @param {number} dy Y-coordinate of the translation. 73 */ 74StorageFile.prototype.getTitle = function() 75{ 76 return this.title; 77}; 78 79/** 80 * Translates this point by the given vector. 81 * 82 * @param {number} dx X-coordinate of the translation. 83 * @param {number} dy Y-coordinate of the translation. 84 */ 85StorageFile.prototype.isRenamable = function() 86{ 87 return true; 88}; 89 90/** 91 * Translates this point by the given vector. 92 * 93 * @param {number} dx X-coordinate of the translation. 94 * @param {number} dy Y-coordinate of the translation. 95 */ 96StorageFile.prototype.save = function(revision, success, error) 97{ 98 this.saveAs(this.getTitle(), success, error); 99}; 100 101/** 102 * Translates this point by the given vector. 103 * 104 * @param {number} dx X-coordinate of the translation. 105 * @param {number} dy Y-coordinate of the translation. 106 */ 107StorageFile.prototype.saveAs = function(title, success, error) 108{ 109 DrawioFile.prototype.save.apply(this, arguments); 110 this.saveFile(title, false, success, error); 111}; 112 113/** 114 * Translates this point by the given vector. 115 * 116 * @param {number} dx X-coordinate of the translation. 117 * @param {number} dy Y-coordinate of the translation. 118 */ 119StorageFile.insertFile = function(ui, title, data, success, error) 120{ 121 var createStorageFile = mxUtils.bind(this, function(exists) 122 { 123 var fn = function() 124 { 125 var file = new StorageFile(ui, data, title); 126 127 // Inserts data into local storage 128 file.saveFile(title, false, function() 129 { 130 success(file); 131 }, error); 132 }; 133 134 if (exists) 135 { 136 ui.confirm(mxResources.get('replaceIt', [title]), fn, error); 137 } 138 else 139 { 140 fn(); 141 } 142 }); 143 144 StorageFile.getFileContent(ui, title, function(data) 145 { 146 createStorageFile(data != null); 147 }, function() 148 { 149 createStorageFile(false); 150 }); 151}; 152 153/** 154 * Translates this point by the given vector. 155 * 156 * @param {number} dx X-coordinate of the translation. 157 * @param {number} dy Y-coordinate of the translation. 158 */ 159StorageFile.getFileContent = function(ui, title, success, error) 160{ 161 ui.getDatabaseItem(title, function(obj) 162 { 163 success(obj != null? obj.data : null); 164 }, 165 mxUtils.bind(this, function() 166 { 167 if (ui.database == null) //fallback to localstorage 168 { 169 ui.getLocalData(title, success); 170 } 171 else if (error != null) 172 { 173 error(); 174 } 175 }), 'files'); 176}; 177 178/** 179 * Translates this point by the given vector. 180 * 181 * @param {number} dx X-coordinate of the translation. 182 * @param {number} dy Y-coordinate of the translation. 183 */ 184StorageFile.getFileInfo = function(ui, title, success, error) 185{ 186 ui.getDatabaseItem(title, function(obj) 187 { 188 success(obj); 189 }, 190 mxUtils.bind(this, function() 191 { 192 if (ui.database == null) //fallback to localstorage 193 { 194 ui.getLocalData(title, function(data) 195 { 196 success(data != null? {title: title} : null); 197 }); 198 } 199 else if (error != null) 200 { 201 error(); 202 } 203 }), 'filesInfo'); 204}; 205 206/** 207 * Translates this point by the given vector. 208 * 209 * @param {number} dx X-coordinate of the translation. 210 * @param {number} dy Y-coordinate of the translation. 211 */ 212StorageFile.prototype.saveFile = function(title, revision, success, error) 213{ 214 if (!this.isEditable()) 215 { 216 if (success != null) 217 { 218 success(); 219 } 220 } 221 else 222 { 223 var fn = mxUtils.bind(this, function() 224 { 225 if (this.isRenamable()) 226 { 227 this.title = title; 228 } 229 230 try 231 { 232 var saveDone = mxUtils.bind(this, function() 233 { 234 this.setModified(this.getShadowModified()); 235 this.contentChanged(); 236 237 if (success != null) 238 { 239 success(); 240 } 241 }); 242 243 this.setShadowModified(false); 244 var data = this.getData(); 245 246 this.ui.setDatabaseItem(null, [{ 247 title: this.title, 248 size: data.length, 249 lastModified: Date.now(), 250 type: this.type 251 }, { 252 title: this.title, 253 data: data 254 }], saveDone, mxUtils.bind(this, function() 255 { 256 if (this.ui.database == null) //fallback to localstorage 257 { 258 this.ui.setLocalData(this.title, data, saveDone); 259 } 260 else if (error != null) 261 { 262 error(); 263 } 264 }), ['filesInfo', 'files']); 265 } 266 catch (e) 267 { 268 if (error != null) 269 { 270 error(e); 271 } 272 } 273 }); 274 275 // Checks for trailing dots 276 if (this.isRenamable() && title.charAt(0) == '.' && error != null) 277 { 278 error({message: mxResources.get('invalidName')}); 279 } 280 else 281 { 282 StorageFile.getFileInfo(this.ui, title, mxUtils.bind(this, function(data) 283 { 284 if (!this.isRenamable() || this.getTitle() == title || data == null) 285 { 286 fn(); 287 } 288 else 289 { 290 this.ui.confirm(mxResources.get('replaceIt', [title]), fn, error); 291 } 292 }), error); 293 } 294 } 295}; 296 297/** 298 * Translates this point by the given vector. 299 * 300 * @param {number} dx X-coordinate of the translation. 301 * @param {number} dy Y-coordinate of the translation. 302 */ 303StorageFile.prototype.rename = function(title, success, error) 304{ 305 var oldTitle = this.getTitle(); 306 307 if (oldTitle != title) 308 { 309 StorageFile.getFileInfo(this.ui, title, mxUtils.bind(this, function(data) 310 { 311 var fn = mxUtils.bind(this, function() 312 { 313 this.title = title; 314 315 // Updates the data if the extension has changed 316 if (!this.hasSameExtension(oldTitle, title)) 317 { 318 this.setData(this.ui.getFileData()); 319 } 320 321 this.saveFile(title, false, mxUtils.bind(this, function() 322 { 323 this.ui.removeLocalData(oldTitle, success); 324 }), error); 325 }); 326 327 if (data != null) 328 { 329 this.ui.confirm(mxResources.get('replaceIt', [title]), fn, error); 330 } 331 else 332 { 333 fn(); 334 } 335 }), error); 336 } 337 else 338 { 339 success(); 340 } 341}; 342 343/** 344 * Returns the location as a new object. 345 * @type mx.Point 346 */ 347StorageFile.prototype.open = function() 348{ 349 DrawioFile.prototype.open.apply(this, arguments); 350 351 // Immediately creates the storage entry 352 this.saveFile(this.getTitle()); 353}; 354 355/** 356 * Adds the listener for automatically saving the diagram for local changes. 357 */ 358StorageFile.prototype.getLatestVersion = function(success, error) 359{ 360 StorageFile.getFileContent(this.ui, this.title, mxUtils.bind(this, function(data) 361 { 362 success(new StorageFile(this.ui, data, this.title)); 363 }), error); 364}; 365 366/** 367 * Stops any pending autosaves and removes all listeners. 368 */ 369StorageFile.prototype.destroy = function() 370{ 371 DrawioFile.prototype.destroy.apply(this, arguments); 372 373 if (this.storageListener != null) 374 { 375 mxEvent.removeListener(window, 'storage', this.storageListener); 376 this.storageListener = null; 377 } 378}; 379 380/** 381 * Translates this point by the given vector. 382 * 383 * @param {number} dx X-coordinate of the translation. 384 * @param {number} dy Y-coordinate of the translation. 385 */ 386StorageFile.listLocalStorageFiles = function(type) 387{ 388 var filesInfo = []; 389 390 for (var i = 0; i < localStorage.length; i++) 391 { 392 var key = localStorage.key(i); 393 var value = localStorage.getItem(key); 394 395 if (key.length > 0 && key.charAt(0) != '.' && value.length > 0) 396 { 397 var isFile = (type == null || type == 'F') && (value.substring(0, 8) === '<mxfile ' || 398 value.substring(0, 5) === '<?xml' || value.substring(0, 12) === '<!--[if IE]>'); 399 var isLib = (type == null || type == 'L') && (value.substring(0, 11) === '<mxlibrary>'); 400 401 if (isFile || isLib) 402 { 403 filesInfo.push({ 404 title: key, 405 type: isFile? 'F' : 'L', 406 size: value.length, 407 lastModified: Date.now() 408 }); 409 } 410 } 411 } 412 413 return filesInfo; 414}; 415 416/** 417 * Translates this point by the given vector. 418 * 419 * @param {number} dx X-coordinate of the translation. 420 * @param {number} dy Y-coordinate of the translation. 421 */ 422StorageFile.migrate = function(db) 423{ 424 var lsFilesInfo = StorageFile.listLocalStorageFiles(); 425 lsFilesInfo.push({title: '.scratchpad', type: 'L'}); //Adding scratchpad also since it is a library (storage file) 426 var tx = db.transaction(['files', 'filesInfo'], 'readwrite'); 427 var files = tx.objectStore('files'); 428 var filesInfo = tx.objectStore('filesInfo'); 429 430 for (var i = 0; i < lsFilesInfo.length; i++) 431 { 432 var lsFileInfo = lsFilesInfo[i]; 433 var data = localStorage.getItem(lsFileInfo.title); 434 files.add({ 435 title: lsFileInfo.title, 436 data: data 437 }); 438 filesInfo.add(lsFileInfo); 439 } 440}; 441 442/** 443 * Translates this point by the given vector. 444 * 445 * @param {number} dx X-coordinate of the translation. 446 * @param {number} dy Y-coordinate of the translation. 447 */ 448StorageFile.listFiles = function(ui, type, success, error) 449{ 450 ui.getDatabaseItems(function(filesInfo) 451 { 452 var files = []; 453 454 if (filesInfo != null) 455 { 456 for (var i = 0; i < filesInfo.length; i++) 457 { 458 if (filesInfo[i].title.charAt(0) != '.' && (type == null || filesInfo[i].type == type)) 459 { 460 files.push(filesInfo[i]); 461 } 462 } 463 } 464 465 success(files); 466 }, function() 467 { 468 if (ui.database == null) //fallback to localstorage 469 { 470 success(StorageFile.listLocalStorageFiles(type)); 471 } 472 else if (error != null) 473 { 474 error(); 475 } 476 }, 'filesInfo'); 477}; 478 479/** 480 * Translates this point by the given vector. 481 * 482 * @param {number} dx X-coordinate of the translation. 483 * @param {number} dy Y-coordinate of the translation. 484 */ 485StorageFile.deleteFile = function(ui, title, success, error) 486{ 487 ui.removeDatabaseItem([title, title], success, function() 488 { 489 if (ui.database == null) //fallback to localstorage 490 { 491 localStorage.removeItem(title) 492 success(); 493 } 494 else if (error != null) 495 { 496 error(); 497 } 498 }, ['files', 'filesInfo']); 499};