1/** 2 * Copyright (c) 2006-2017, JGraph Ltd 3 * Copyright (c) 2006-2017, Gaudenz Alder 4 */ 5//Add a closure to hide the class private variables without changing the code a lot 6(function() 7{ 8 9var _token = null; 10 11window.DropboxClient = function(editorUi) 12{ 13 DrawioClient.call(this, editorUi, 'dbauth'); 14 15 this.client = new Dropbox({clientId: this.clientId}); 16}; 17 18// Extends DrawioClient 19mxUtils.extend(DropboxClient, DrawioClient); 20 21/** 22 * FIXME: How to find name of app folder for current user. The Apps part of the 23 * name is internationalized so this hardcoded check does not work everywhere. 24 */ 25DropboxClient.prototype.appPath = '/drawio/'; 26 27/** 28 * Executes the first step for connecting to Google Drive. 29 */ 30DropboxClient.prototype.extension = '.drawio'; 31 32/** 33 * Executes the first step for connecting to Google Drive. 34 */ 35DropboxClient.prototype.writingFile = false; 36 37/** 38 * Executes the first step for connecting to Google Drive. 39 */ 40DropboxClient.prototype.maxRetries = 4; 41 42DropboxClient.prototype.clientId = window.DRAWIO_DROPBOX_ID; 43 44DropboxClient.prototype.redirectUri = window.location.protocol + '//' + window.location.host + '/dropbox'; 45 46/** 47 * Authorizes the client, gets the userId and calls <open>. 48 */ 49DropboxClient.prototype.logout = function() 50{ 51 //Send to server to clear refresh token cookie 52 this.ui.editor.loadUrl(this.redirectUri + '?doLogout=1&state=' + encodeURIComponent('cId=' + this.clientId + '&domain=' + window.location.hostname)); 53 this.clearPersistentToken(); 54 this.setUser(null); 55 _token = null; 56 57 this.client.authTokenRevoke().then(mxUtils.bind(this, function() 58 { 59 this.client.setAccessToken(null); 60 })); 61}; 62 63/** 64 * Checks if the client is authorized and calls the next step. 65 */ 66DropboxClient.prototype.updateUser = function(success, error, failOnAuth) 67{ 68 var acceptResponse = true; 69 70 var timeoutThread = window.setTimeout(mxUtils.bind(this, function() 71 { 72 acceptResponse = false; 73 error({code: App.ERROR_TIMEOUT}); 74 }), this.ui.timeout); 75 76 var promise = this.client.usersGetCurrentAccount(); 77 promise.then(mxUtils.bind(this, function(response) 78 { 79 window.clearTimeout(timeoutThread); 80 81 if (acceptResponse) 82 { 83 this.setUser(new DrawioUser(response.account_id, 84 response.email, response.name.display_name)); 85 success(); 86 } 87 })); 88 // Workaround for IE8/9 support with catch function 89 promise['catch'](mxUtils.bind(this, function(err) 90 { 91 window.clearTimeout(timeoutThread); 92 93 if (acceptResponse) 94 { 95 if (err != null && err.status === 401 && !failOnAuth) 96 { 97 this.setUser(null); 98 this.client.setAccessToken(null); 99 _token = null; 100 101 this.authenticate(mxUtils.bind(this, function() 102 { 103 this.updateUser(success, error, true); 104 }), error); 105 } 106 else 107 { 108 error({message: mxResources.get('accessDenied')}); 109 } 110 } 111 })); 112}; 113 114/** 115 * Authorizes the client, gets the userId and calls <open>. 116 */ 117DropboxClient.prototype.authenticate = function(success, error) 118{ 119 var req = new mxXmlRequest(this.redirectUri + '?getState=1', null, 'GET'); 120 121 req.send(mxUtils.bind(this, function(req) 122 { 123 if (req.getStatus() >= 200 && req.getStatus() <= 299) 124 { 125 this.authenticateStep2(req.getText(), success, error); 126 } 127 else if (error != null) 128 { 129 error(req); 130 } 131 }), error); 132}; 133 134DropboxClient.prototype.authenticateStep2 = function(state, success, error) 135{ 136 if (window.onDropboxCallback == null) 137 { 138 var auth = mxUtils.bind(this, function() 139 { 140 var acceptAuthResponse = true; 141 142 var authRemembered = this.getPersistentToken(true); 143 144 if (authRemembered != null) 145 { 146 var req = new mxXmlRequest(this.redirectUri + '?state=' + encodeURIComponent('cId=' + this.clientId + '&domain=' + window.location.hostname + '&token=' + state), null, 'GET'); //To identify which app/domain is used 147 148 req.send(mxUtils.bind(this, function(req) 149 { 150 if (req.getStatus() >= 200 && req.getStatus() <= 299) 151 { 152 _token = JSON.parse(req.getText()).access_token; 153 this.client.setAccessToken(_token); 154 this.setUser(null); 155 success(); 156 } 157 else 158 { 159 this.clearPersistentToken(); 160 this.setUser(null); 161 _token = null; 162 this.client.setAccessToken(null); 163 164 if (req.getStatus() == 401) // (Unauthorized) [e.g, invalid refresh token] 165 { 166 auth(); 167 } 168 else 169 { 170 error({message: mxResources.get('accessDenied'), retry: auth}); 171 } 172 } 173 }), error); 174 } 175 else 176 { 177 this.ui.showAuthDialog(this, true, mxUtils.bind(this, function(remember, authSuccess) 178 { 179 var win = window.open('https://www.dropbox.com/oauth2/authorize?client_id=' + 180 this.clientId + (remember? '&token_access_type=offline' : '') + 181 '&redirect_uri=' + encodeURIComponent(this.redirectUri) + 182 '&response_type=code&state=' + encodeURIComponent('cId=' + this.clientId + //To identify which app/domain is used 183 '&domain=' + window.location.hostname + '&token=' + state), 'dbauth'); 184 185 if (win != null) 186 { 187 window.onDropboxCallback = mxUtils.bind(this, function(newAuthInfo, authWindow) 188 { 189 if (acceptAuthResponse) 190 { 191 window.onDropboxCallback = null; 192 acceptAuthResponse = false; 193 194 try 195 { 196 if (newAuthInfo == null) 197 { 198 error({message: mxResources.get('accessDenied'), retry: auth}); 199 } 200 else 201 { 202 if (authSuccess != null) 203 { 204 authSuccess(); 205 } 206 207 _token = newAuthInfo.access_token; 208 this.client.setAccessToken(_token); 209 this.setUser(null); 210 211 if (remember) 212 { 213 this.setPersistentToken('remembered'); 214 } 215 216 success(); 217 } 218 } 219 catch (e) 220 { 221 error(e); 222 } 223 finally 224 { 225 if (authWindow != null) 226 { 227 authWindow.close(); 228 } 229 } 230 } 231 else if (authWindow != null) 232 { 233 authWindow.close(); 234 } 235 }); 236 } 237 else 238 { 239 error({message: mxResources.get('serviceUnavailableOrBlocked'), retry: auth}); 240 } 241 }), mxUtils.bind(this, function() 242 { 243 if (acceptAuthResponse) 244 { 245 window.onDropboxCallback = null; 246 acceptAuthResponse = false; 247 error({message: mxResources.get('accessDenied'), retry: auth}); 248 } 249 })); 250 } 251 }); 252 253 auth(); 254 } 255 else 256 { 257 error({code: App.ERROR_BUSY}); 258 } 259}; 260 261/** 262 * Authorizes the client, gets the userId and calls <open>. 263 */ 264DropboxClient.prototype.executePromise = function(promiseFn, success, error) 265{ 266 var doExecute = mxUtils.bind(this, function(failOnAuth) 267 { 268 var acceptResponse = true; 269 270 var timeoutThread = window.setTimeout(mxUtils.bind(this, function() 271 { 272 acceptResponse = false; 273 error({code: App.ERROR_TIMEOUT, retry: fn}); 274 }), this.ui.timeout); 275 276 //Dropbox client start executing the promise once created so auth fails, so we send a function instead to delay promise creation 277 var promise = promiseFn(); 278 279 promise.then(mxUtils.bind(this, function(response) 280 { 281 window.clearTimeout(timeoutThread); 282 283 if (acceptResponse && success != null) 284 { 285 success(response); 286 } 287 })); 288 // Workaround for IE8/9 support with catch function 289 promise['catch'](mxUtils.bind(this, function(err) 290 { 291 window.clearTimeout(timeoutThread); 292 293 if (acceptResponse) 294 { 295 if (err != null && (err.status == 500 || err.status == 400 || 296 err.status == 401)) 297 { 298 this.setUser(null); 299 this.client.setAccessToken(null); 300 _token = null; 301 302 if (!failOnAuth) 303 { 304 this.authenticate(function() 305 { 306 doExecute(true); 307 }, error); 308 } 309 else 310 { 311 error({message: mxResources.get('accessDenied'), retry: mxUtils.bind(this, function() 312 { 313 this.authenticate(function() 314 { 315 fn(true); 316 }, error); 317 })}); 318 } 319 } 320 else 321 { 322 error({message: mxResources.get('error') + ' ' + err.status}); 323 } 324 } 325 })); 326 }); 327 328 var fn = mxUtils.bind(this, function(failOnAuth) 329 { 330 if (this.user == null) 331 { 332 this.updateUser(function() 333 { 334 fn(true); 335 }, error, failOnAuth); 336 } 337 else 338 { 339 doExecute(failOnAuth); 340 } 341 }); 342 343 if (_token == null) 344 { 345 this.authenticate(function() 346 { 347 fn(true); 348 }, error); 349 } 350 else 351 { 352 fn(false); 353 } 354}; 355 356/** 357 * Checks if the client is authorized and calls the next step. 358 */ 359DropboxClient.prototype.getLibrary = function(path, success, error) 360{ 361 this.getFile(path, success, error, true); 362}; 363 364/** 365 * DenyConvert is ignored in this client, just added for API compatibility. 366 */ 367DropboxClient.prototype.getFile = function(path, success, error, asLibrary) 368{ 369 asLibrary = (asLibrary != null) ? asLibrary : false; 370 var binary = /\.png$/i.test(path); 371 372 if (/^https:\/\//i.test(path) || /\.v(dx|sdx?)$/i.test(path) || /\.gliffy$/i.test(path) || 373 /\.pdf$/i.test(path) || (!this.ui.useCanvasForExport && binary)) 374 { 375 var fn = mxUtils.bind(this, function() 376 { 377 var tokens = path.split('/'); 378 var name = (tokens.length > 0) ? tokens[tokens.length - 1] : path; 379 380 this.ui.convertFile(path, name, null, this.extension, success, error); 381 }); 382 383 if (_token != null) 384 { 385 fn(); 386 } 387 else 388 { 389 this.authenticate(fn, error); 390 } 391 } 392 else 393 { 394 var arg = {path: '/' + path}; 395 396 if (urlParams['rev'] != null) 397 { 398 arg.rev = urlParams['rev']; 399 } 400 401 this.readFile(arg, mxUtils.bind(this, function(data, response) 402 { 403 var index = (binary) ? data.lastIndexOf(',') : -1; 404 var file = null; 405 406 if (index > 0) 407 { 408 var xml = this.ui.extractGraphModelFromPng(data); 409 410 if (xml != null && xml.length > 0) 411 { 412 data = xml; 413 } 414 else 415 { 416 // Imports as PNG image 417 file = new LocalFile(this, data, path, true); 418 } 419 } 420 421 success((file != null) ? file : 422 ((asLibrary) ? new DropboxLibrary(this.ui, data, response) : 423 new DropboxFile(this.ui, data, response))); 424 }), error, binary); 425 } 426}; 427 428/** 429 * Translates this point by the given vector. 430 * 431 * @param {number} dx X-coordinate of the translation. 432 * @param {number} dy Y-coordinate of the translation. 433 */ 434DropboxClient.prototype.readFile = function(arg, success, error, binary) 435{ 436 var doExecute = mxUtils.bind(this, function(failOnAuth) 437 { 438 var acceptResponse = true; 439 440 var timeoutThread = window.setTimeout(mxUtils.bind(this, function() 441 { 442 acceptResponse = false; 443 error({code: App.ERROR_TIMEOUT}); 444 }), this.ui.timeout); 445 446 // Workaround for Uncaught DOMException in filesDownload is to 447 // get the metadata to handle the file not found case 448 var checkPromise = this.client.filesGetMetadata({path: '/' + arg.path.substring(1), include_deleted: false}); 449 450 checkPromise.then(mxUtils.bind(this, function(response) 451 { 452 // ignore 453 })); 454 455 // Workaround for IE8/9 support with catch function 456 checkPromise['catch'](function(err) 457 { 458 window.clearTimeout(timeoutThread); 459 460 if (acceptResponse && err != null && err.status == 409) 461 { 462 acceptResponse = false; 463 error({message: mxResources.get('fileNotFound')}); 464 } 465 }); 466 467 // Download file in parallel 468 // LATER: Report Uncaught DOMException with path/not_found in filesDownload 469 var promise = this.client.filesDownload(arg); 470 471 promise.then(mxUtils.bind(this, function(response) 472 { 473 window.clearTimeout(timeoutThread); 474 475 if (acceptResponse) 476 { 477 acceptResponse = false; 478 479 try 480 { 481 var reader = new FileReader(); 482 483 reader.onload = mxUtils.bind(this, function(event) 484 { 485 success(reader.result, response); 486 }); 487 488 if (binary) 489 { 490 reader.readAsDataURL(response.fileBlob); 491 } 492 else 493 { 494 reader.readAsText(response.fileBlob); 495 } 496 } 497 catch (e) 498 { 499 error(e); 500 } 501 } 502 })); 503 // Workaround for IE8/9 support with catch function 504 promise['catch'](mxUtils.bind(this, function(err) 505 { 506 window.clearTimeout(timeoutThread); 507 508 if (acceptResponse) 509 { 510 acceptResponse = false; 511 512 if (err != null && (err.status == 500 || err.status == 400 || 513 err.status == 401)) 514 { 515 this.client.setAccessToken(null); 516 this.setUser(null); 517 _token = null; 518 519 if (!failOnAuth) 520 { 521 this.authenticate(function() 522 { 523 doExecute(true); 524 }, error); 525 } 526 else 527 { 528 error({message: mxResources.get('accessDenied'), retry: mxUtils.bind(this, function() 529 { 530 this.authenticate(function() 531 { 532 fn(true); 533 }, error); 534 })}); 535 } 536 } 537 else 538 { 539 error({message: mxResources.get('error') + ' ' + err.status}); 540 } 541 } 542 })); 543 }); 544 545 var fn = mxUtils.bind(this, function(failOnAuth) 546 { 547 if (this.user == null) 548 { 549 this.updateUser(function() 550 { 551 fn(true); 552 }, error, failOnAuth); 553 } 554 else 555 { 556 doExecute(failOnAuth); 557 } 558 }); 559 560 if (_token == null) 561 { 562 this.authenticate(function() 563 { 564 fn(true); 565 }, error); 566 } 567 else 568 { 569 fn(false); 570 } 571}; 572 573/** 574 * Translates this point by the given vector. 575 * 576 * @param {number} dx X-coordinate of the translation. 577 * @param {number} dy Y-coordinate of the translation. 578 */ 579DropboxClient.prototype.checkExists = function(filename, fn, noConfirm) 580{ 581 var promiseFn = mxUtils.bind(this, function() 582 { 583 return this.client.filesGetMetadata({path: '/' + filename.toLowerCase(), include_deleted: false}); 584 }); 585 586 this.executePromise(promiseFn, mxUtils.bind(this, function(response) 587 { 588 if (noConfirm) 589 { 590 fn(false, true, response); 591 } 592 else 593 { 594 this.ui.confirm(mxResources.get('replaceIt', [filename]), function() 595 { 596 fn(true, true, response); 597 }, function() 598 { 599 fn(false, true, response); 600 }); 601 } 602 }), function(err) 603 { 604 fn(true, false); 605 }); 606}; 607 608/** 609 * Translates this point by the given vector. 610 * 611 * @param {number} dx X-coordinate of the translation. 612 * @param {number} dy Y-coordinate of the translation. 613 */ 614DropboxClient.prototype.renameFile = function(file, filename, success, error) 615{ 616 if (/[\\\/:\?\*"\|]/.test(filename)) 617 { 618 error({message: mxResources.get('dropboxCharsNotAllowed')}); 619 } 620 else 621 { 622 // Appends working directory of source file 623 if (file != null && filename != null) 624 { 625 var path = file.stat.path_display.substring(1); 626 var idx = path.lastIndexOf('/'); 627 628 if (idx > 0) 629 { 630 filename = path.substring(0, idx + 1) + filename; 631 } 632 } 633 634 if (file != null && filename != null && file.stat.path_lower.substring(1) !== filename.toLowerCase()) 635 { 636 // Checks if file exists 637 this.checkExists(filename, mxUtils.bind(this, function(checked, exists, response) 638 { 639 if (checked) 640 { 641 var thenHandler = mxUtils.bind(this, function(deleteResponse) 642 { 643 var move = mxUtils.bind(this, function() 644 { 645 return this.client.filesMove({from_path: file.stat.path_display, to_path: '/' + 646 filename, autorename: false}); 647 }); 648 649 this.executePromise(move, success, error); 650 }); 651 652 // API fails on same name with different upper-/lowercase 653 if (!exists || response.path_lower.substring(1) === filename.toLowerCase()) 654 { 655 thenHandler(); 656 } 657 else 658 { 659 // Deletes file first to avoid conflict in filesMove (non-atomic) 660 var promiseFn = mxUtils.bind(this, function() 661 { 662 return this.client.filesDelete({path: '/' + filename.toLowerCase()}); 663 }); 664 665 this.executePromise(promiseFn, thenHandler, error); 666 } 667 } 668 else 669 { 670 error(); 671 } 672 })); 673 } 674 else 675 { 676 // Same name with different upper-/lowercase not supported by Dropbox API 677 error({message: mxResources.get('invalidName')}); 678 } 679 } 680}; 681 682/** 683 * Translates this point by the given vector. 684 * 685 * @param {number} dx X-coordinate of the translation. 686 * @param {number} dy Y-coordinate of the translation. 687 */ 688DropboxClient.prototype.insertLibrary = function(filename, data, success, error) 689{ 690 this.insertFile(filename, data, success, error, true); 691}; 692 693/** 694 * Translates this point by the given vector. 695 * 696 * @param {number} dx X-coordinate of the translation. 697 * @param {number} dy Y-coordinate of the translation. 698 */ 699DropboxClient.prototype.insertFile = function(filename, data, success, error, asLibrary) 700{ 701 asLibrary = (asLibrary != null) ? asLibrary : false; 702 703 this.checkExists(filename, mxUtils.bind(this, function(checked) 704 { 705 if (checked) 706 { 707 this.saveFile(filename, data, mxUtils.bind(this, function(stat) 708 { 709 if (asLibrary) 710 { 711 success(new DropboxLibrary(this.ui, data, stat)); 712 } 713 else 714 { 715 success(new DropboxFile(this.ui, data, stat)); 716 } 717 }), error); 718 } 719 else 720 { 721 error(); 722 } 723 })); 724}; 725 726/** 727 * Translates this point by the given vector. 728 * 729 * @param {number} dx X-coordinate of the translation. 730 * @param {number} dy Y-coordinate of the translation. 731 */ 732DropboxClient.prototype.saveFile = function(filename, data, success, error, folder) 733{ 734 if (/[\\\/:\?\*"\|]/.test(filename)) 735 { 736 error({message: mxResources.get('dropboxCharsNotAllowed')}); 737 } 738 else if (data.length >= 150000000 /*150MB*/) 739 { 740 error({message: mxResources.get('drawingTooLarge') + ' (' + 741 this.ui.formatFileSize(data.length) + ' / 150 MB)'}); 742 } 743 else 744 { 745 folder = (folder != null) ? folder : ''; 746 747 // Mute switch is ignored 748 var promiseFn = mxUtils.bind(this, function() 749 { 750 return this.client.filesUpload({path: '/' + folder + filename, 751 mode: {'.tag': 'overwrite'}, mute: true, 752 contents: new Blob([data], {type: 'text/plain'})}); 753 }); 754 755 this.executePromise(promiseFn, success, error); 756 } 757}; 758 759/** 760 * Translates this point by the given vector. 761 * 762 * @param {number} dx X-coordinate of the translation. 763 * @param {number} dy Y-coordinate of the translation. 764 */ 765DropboxClient.prototype.pickLibrary = function(fn) 766{ 767 // Authentication will be carried out on open to make sure the 768 // autosave does not show an auth dialog. Showing it here will 769 // block the second dialog (the file picker) so it's too early. 770 Dropbox.choose( 771 { 772 linkType : 'direct', 773 cancel: mxUtils.bind(this, function() 774 { 775 // do nothing 776 }), 777 success : mxUtils.bind(this, function(files) 778 { 779 if (this.ui.spinner.spin(document.body, mxResources.get('loading'))) 780 { 781 var error = mxUtils.bind(this, function(e) 782 { 783 this.ui.spinner.stop(); 784 this.ui.handleError(e); 785 }); 786 787 var tmp = files[0].link.indexOf(this.appPath); 788 789 if (tmp > 0) 790 { 791 // Checks if file is in app folder by loading file from there and comparing the ID 792 var rel = decodeURIComponent(files[0].link.substring(tmp + this.appPath.length - 1)); 793 794 this.readFile({path: rel}, mxUtils.bind(this, function(data, stat) 795 { 796 if (stat != null && stat.id == files[0].id) 797 { 798 // No need to load file a second time 799 try 800 { 801 this.ui.spinner.stop(); 802 fn(rel.substring(1), new DropboxLibrary(this.ui, data, stat)); 803 } 804 catch (e) 805 { 806 this.ui.handleError(e); 807 } 808 } 809 else 810 { 811 this.createLibrary(files[0], fn, error); 812 } 813 }), error); 814 } 815 else 816 { 817 this.createLibrary(files[0], fn, error); 818 } 819 } 820 }) 821 }); 822}; 823 824/** 825 * Translates this point by the given vector. 826 * 827 * @param {number} dx X-coordinate of the translation. 828 * @param {number} dy Y-coordinate of the translation. 829 */ 830DropboxClient.prototype.createLibrary = function(file, success, error) 831{ 832 this.ui.confirm(mxResources.get('note') + ': ' + mxResources.get('fileWillBeSavedInAppFolder', 833 [file.name]), mxUtils.bind(this, function() 834 { 835 this.ui.editor.loadUrl(file.link, mxUtils.bind(this, function(data) 836 { 837 this.insertFile(file.name, data, mxUtils.bind(this, function(newFile) 838 { 839 try 840 { 841 this.ui.spinner.stop(); 842 success(newFile.getHash().substring(1), newFile); 843 } 844 catch (e) 845 { 846 error(e); 847 } 848 }), error, true); 849 }), error); 850 }), mxUtils.bind(this, function() 851 { 852 this.ui.spinner.stop(); 853 })); 854}; 855 856/** 857 * Translates this point by the given vector. 858 * 859 * @param {number} dx X-coordinate of the translation. 860 * @param {number} dy Y-coordinate of the translation. 861 */ 862DropboxClient.prototype.pickFile = function(fn, readOnly) 863{ 864 if (Dropbox.choose != null) 865 { 866 fn = (fn != null) ? fn : mxUtils.bind(this, function(path, file) 867 { 868 this.ui.loadFile((path != null) ? 'D' + encodeURIComponent(path) : file.getHash(), null, file); 869 }); 870 871 // Authentication will be carried out on open to make sure the 872 // autosave does not show an auth dialog. Showing it here will 873 // block the second dialog (the file picker) so it's too early. 874 Dropbox.choose( 875 { 876 linkType : 'direct', 877 cancel: mxUtils.bind(this, function() 878 { 879 // do nothing 880 }), 881 success : mxUtils.bind(this, function(files) 882 { 883 if (this.ui.spinner.spin(document.body, mxResources.get('loading'))) 884 { 885 // File used for read-only 886 if (readOnly) 887 { 888 this.ui.spinner.stop(); 889 fn(files[0].link); 890 } 891 else 892 { 893 var error = mxUtils.bind(this, function(e) 894 { 895 this.ui.spinner.stop(); 896 this.ui.handleError(e); 897 }); 898 899 var success = mxUtils.bind(this, function(path, file) 900 { 901 this.ui.spinner.stop(); 902 fn(path, file); 903 }); 904 905 var binary = /\.png$/i.test(files[0].name); 906 907 if (/\.vsdx$/i.test(files[0].name) || /\.gliffy$/i.test(files[0].name) || 908 (!this.ui.useCanvasForExport && binary)) 909 { 910 success(files[0].link); 911 } 912 else 913 { 914 var tmp = files[0].link.indexOf(this.appPath); 915 916 if (tmp > 0) 917 { 918 // Checks if file is in app folder by loading file from there and comparing the ID 919 var rel = decodeURIComponent(files[0].link.substring(tmp + this.appPath.length - 1)); 920 921 this.readFile({path: rel}, mxUtils.bind(this, function(data, stat) 922 { 923 if (stat != null && stat.id == files[0].id) 924 { 925 var index = (binary) ? data.lastIndexOf(',') : -1; 926 this.ui.spinner.stop(); 927 var file = null; 928 929 if (index > 0) 930 { 931 var xml = this.ui.extractGraphModelFromPng(data); 932 933 if (xml != null && xml.length > 0) 934 { 935 data = xml; 936 } 937 else 938 { 939 // Imports as PNG image 940 file = new LocalFile(this, data, rel, true); 941 } 942 } 943 944 // No need to load file a second time 945 fn(rel.substring(1), (file != null) ? file : new DropboxFile(this.ui, data, stat)); 946 } 947 else 948 { 949 this.createFile(files[0], success, error); 950 } 951 }), error, binary); 952 } 953 else 954 { 955 this.createFile(files[0], success, error); 956 } 957 } 958 } 959 } 960 }) 961 }); 962 } 963 else 964 { 965 this.ui.handleError({message: mxResources.get('serviceUnavailableOrBlocked')}); 966 } 967}; 968 969/** 970 * Translates this point by the given vector. 971 * 972 * @param {number} dx X-coordinate of the translation. 973 * @param {number} dy Y-coordinate of the translation. 974 */ 975DropboxClient.prototype.createFile = function(file, success, error) 976{ 977 var binary = /(\.png)$/i.test(file.name); 978 979 this.ui.editor.loadUrl(file.link, mxUtils.bind(this, function(data) 980 { 981 if (data != null && data.length > 0) 982 { 983 this.ui.confirm(mxResources.get('note') + ': ' + mxResources.get('fileWillBeSavedInAppFolder', [file.name]), mxUtils.bind(this, function() 984 { 985 var index = (binary) ? data.lastIndexOf(',') : -1; 986 987 if (index > 0) 988 { 989 var xml = this.ui.extractGraphModelFromPng(data.substring(index + 1)); 990 991 if (xml != null && xml.length > 0) 992 { 993 data = xml; 994 } 995 } 996 997 this.insertFile(file.name, data, mxUtils.bind(this, function(newFile) 998 { 999 success(file.name, newFile); 1000 }), error); 1001 }), mxUtils.bind(this, function() 1002 { 1003 this.ui.spinner.stop(); 1004 })); 1005 } 1006 else 1007 { 1008 this.ui.spinner.stop(); 1009 error({message: mxResources.get('errorLoadingFile')}); 1010 } 1011 }), error, binary); 1012}; 1013 1014})();