DropboxClient.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856
  1. /**
  2. * Copyright (c) 2006-2017, JGraph Ltd
  3. * Copyright (c) 2006-2017, Gaudenz Alder
  4. */
  5. DropboxClient = function(editorUi)
  6. {
  7. DrawioClient.call(this, editorUi, 'dbauth');
  8. this.client = new Dropbox({clientId: App.DROPBOX_APPKEY});
  9. this.client.setAccessToken(this.token);
  10. };
  11. // Extends DrawioClient
  12. mxUtils.extend(DropboxClient, DrawioClient);
  13. /**
  14. * FIXME: How to find name of app folder for current user. The Apps part of the
  15. * name is internationalized so this hardcoded check does not work everywhere.
  16. */
  17. DropboxClient.prototype.appPath = '/drawio/';
  18. /**
  19. * Executes the first step for connecting to Google Drive.
  20. */
  21. DropboxClient.prototype.extension = '.html';
  22. /**
  23. * Executes the first step for connecting to Google Drive.
  24. */
  25. DropboxClient.prototype.writingFile = false;
  26. /**
  27. * Executes the first step for connecting to Google Drive.
  28. */
  29. DropboxClient.prototype.maxRetries = 4;
  30. /**
  31. * Authorizes the client, gets the userId and calls <open>.
  32. */
  33. DropboxClient.prototype.logout = function()
  34. {
  35. this.clearPersistentToken();
  36. this.setUser(null);
  37. this.token = null;
  38. this.client.authTokenRevoke().then(mxUtils.bind(this, function()
  39. {
  40. this.client.setAccessToken(null);
  41. }));
  42. };
  43. /**
  44. * Checks if the client is authorized and calls the next step.
  45. */
  46. DropboxClient.prototype.updateUser = function(success, error, failOnAuth)
  47. {
  48. var acceptResponse = true;
  49. var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
  50. {
  51. acceptResponse = false;
  52. error({code: App.ERROR_TIMEOUT});
  53. }), this.ui.timeout);
  54. var promise = this.client.usersGetCurrentAccount();
  55. promise.then(mxUtils.bind(this, function(response)
  56. {
  57. window.clearTimeout(timeoutThread);
  58. if (acceptResponse)
  59. {
  60. this.setUser(new DrawioUser(response.account_id,
  61. response.email, response.name.display_name));
  62. success();
  63. }
  64. }));
  65. // Workaround for IE8/9 support with catch function
  66. promise['catch'](mxUtils.bind(this, function(err)
  67. {
  68. window.clearTimeout(timeoutThread);
  69. if (acceptResponse)
  70. {
  71. if (err != null && err.status === 401 && !failOnAuth)
  72. {
  73. this.setUser(null);
  74. this.client.setAccessToken(null);
  75. this.authenticate(mxUtils.bind(this, function()
  76. {
  77. this.updateUser(success, error, true);
  78. }), error);
  79. }
  80. else
  81. {
  82. error({message: mxResources.get('accessDenied')});
  83. }
  84. }
  85. }));
  86. };
  87. /**
  88. * Authorizes the client, gets the userId and calls <open>.
  89. */
  90. DropboxClient.prototype.authenticate = function(success, error)
  91. {
  92. if (window.onDropboxCallback == null)
  93. {
  94. var auth = mxUtils.bind(this, function()
  95. {
  96. var acceptAuthResponse = true;
  97. this.ui.showAuthDialog(this, true, mxUtils.bind(this, function(remember, authSuccess)
  98. {
  99. var win = window.open(this.client.getAuthenticationUrl('https://' +
  100. window.location.host + '/dropbox.html'), 'dbauth');
  101. if (win != null)
  102. {
  103. window.onDropboxCallback = mxUtils.bind(this, function(token, authWindow)
  104. {
  105. if (acceptAuthResponse)
  106. {
  107. window.onDropboxCallback = null;
  108. acceptAuthResponse = false;
  109. try
  110. {
  111. if (token == null)
  112. {
  113. error({message: mxResources.get('accessDenied'), retry: auth});
  114. }
  115. else
  116. {
  117. if (authSuccess != null)
  118. {
  119. authSuccess();
  120. }
  121. this.client.setAccessToken(token);
  122. this.setUser(null);
  123. if (remember)
  124. {
  125. this.setPersistentToken(token);
  126. }
  127. success();
  128. }
  129. }
  130. catch (e)
  131. {
  132. error(e);
  133. }
  134. finally
  135. {
  136. if (authWindow != null)
  137. {
  138. authWindow.close();
  139. }
  140. }
  141. }
  142. else if (authWindow != null)
  143. {
  144. authWindow.close();
  145. }
  146. });
  147. }
  148. else
  149. {
  150. error({message: mxResources.get('serviceUnavailableOrBlocked'), retry: auth});
  151. }
  152. }), mxUtils.bind(this, function()
  153. {
  154. if (acceptAuthResponse)
  155. {
  156. window.onDropboxCallback = null;
  157. acceptAuthResponse = false;
  158. error({message: mxResources.get('accessDenied'), retry: auth});
  159. }
  160. }));
  161. });
  162. auth();
  163. }
  164. else
  165. {
  166. error({code: App.ERROR_BUSY});
  167. }
  168. };
  169. /**
  170. * Authorizes the client, gets the userId and calls <open>.
  171. */
  172. DropboxClient.prototype.executePromise = function(promise, success, error)
  173. {
  174. var doExecute = mxUtils.bind(this, function(failOnAuth)
  175. {
  176. var acceptResponse = true;
  177. var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
  178. {
  179. acceptResponse = false;
  180. error({code: App.ERROR_TIMEOUT, retry: fn});
  181. }), this.ui.timeout);
  182. promise.then(mxUtils.bind(this, function(response)
  183. {
  184. window.clearTimeout(timeoutThread);
  185. if (acceptResponse && success != null)
  186. {
  187. success(response);
  188. }
  189. }));
  190. // Workaround for IE8/9 support with catch function
  191. promise['catch'](mxUtils.bind(this, function(err)
  192. {
  193. window.clearTimeout(timeoutThread);
  194. if (acceptResponse)
  195. {
  196. if (err != null && (err.status == 500 || err.status == 400 ||
  197. err.status == 401))
  198. {
  199. this.setUser(null);
  200. this.client.setAccessToken(null);
  201. if (!failOnAuth)
  202. {
  203. this.authenticate(function()
  204. {
  205. doExecute(true);
  206. }, error);
  207. }
  208. else
  209. {
  210. error({message: mxResources.get('accessDenied'), retry: mxUtils.bind(this, function()
  211. {
  212. this.authenticate(function()
  213. {
  214. fn(true);
  215. }, error);
  216. })});
  217. }
  218. }
  219. else
  220. {
  221. error({message: mxResources.get('error') + ' ' + err.status});
  222. }
  223. }
  224. }));
  225. });
  226. var fn = mxUtils.bind(this, function(failOnAuth)
  227. {
  228. if (this.user == null)
  229. {
  230. this.updateUser(function()
  231. {
  232. fn(true);
  233. }, error, failOnAuth);
  234. }
  235. else
  236. {
  237. doExecute(failOnAuth);
  238. }
  239. });
  240. if (this.client.getAccessToken() === null)
  241. {
  242. this.authenticate(function()
  243. {
  244. fn(true);
  245. }, error);
  246. }
  247. else
  248. {
  249. fn(false);
  250. }
  251. };
  252. /**
  253. * Checks if the client is authorized and calls the next step.
  254. */
  255. DropboxClient.prototype.getLibrary = function(path, success, error)
  256. {
  257. this.getFile(path, success, error, true);
  258. };
  259. /**
  260. * DenyConvert is ignored in this client, just added for API compatibility.
  261. */
  262. DropboxClient.prototype.getFile = function(path, success, error, asLibrary)
  263. {
  264. asLibrary = (asLibrary != null) ? asLibrary : false;
  265. if (/^https:\/\//i.test(path) || /\.vsdx$/i.test(path) || /\.gliffy$/i.test(path) || /\.png$/i.test(path))
  266. {
  267. // Should never be null
  268. if (this.token != null)
  269. {
  270. var tokens = path.split('/');
  271. var name = (tokens.length > 0) ? tokens[tokens.length - 1] : path;
  272. this.ui.convertFile(path, name, null, this.extension, success, error);
  273. }
  274. else
  275. {
  276. error({message: mxResources.get('accessDenied')});
  277. }
  278. }
  279. else
  280. {
  281. var arg = {path: '/' + path};
  282. if (urlParams['rev'] != null)
  283. {
  284. arg.rev = urlParams['rev'];
  285. }
  286. this.readFile(arg, mxUtils.bind(this, function(data, response)
  287. {
  288. success((asLibrary) ? new DropboxLibrary(this.ui, data, response) :
  289. new DropboxFile(this.ui, data, response));
  290. }), error);
  291. }
  292. };
  293. /**
  294. * Translates this point by the given vector.
  295. *
  296. * @param {number} dx X-coordinate of the translation.
  297. * @param {number} dy Y-coordinate of the translation.
  298. */
  299. DropboxClient.prototype.readFile = function(arg, success, error)
  300. {
  301. var doExecute = mxUtils.bind(this, function(failOnAuth)
  302. {
  303. var acceptResponse = true;
  304. var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
  305. {
  306. acceptResponse = false;
  307. error({code: App.ERROR_TIMEOUT});
  308. }), this.ui.timeout);
  309. // Workaround for Uncaught DOMException in filesDownload is to
  310. // get the metadata to handle the file not found case
  311. var checkPromise = this.client.filesGetMetadata({path: '/' + arg.path.substring(1), include_deleted: false});
  312. checkPromise.then(mxUtils.bind(this, function(response)
  313. {
  314. // ignore
  315. }));
  316. // Workaround for IE8/9 support with catch function
  317. checkPromise['catch'](function(err)
  318. {
  319. window.clearTimeout(timeoutThread);
  320. if (acceptResponse && err != null && err.status == 409)
  321. {
  322. acceptResponse = false;
  323. error({message: mxResources.get('fileNotFound')});
  324. }
  325. });
  326. // Download file in parallel
  327. // LATER: Report Uncaught DOMException with path/not_found in filesDownload
  328. var promise = this.client.filesDownload(arg);
  329. promise.then(mxUtils.bind(this, function(response)
  330. {
  331. window.clearTimeout(timeoutThread);
  332. if (acceptResponse)
  333. {
  334. acceptResponse = false;
  335. try
  336. {
  337. var reader = new FileReader();
  338. reader.onload = mxUtils.bind(this, function(event)
  339. {
  340. success(reader.result, response);
  341. });
  342. reader.readAsText(response.fileBlob);
  343. }
  344. catch (e)
  345. {
  346. error(e);
  347. }
  348. }
  349. }));
  350. // Workaround for IE8/9 support with catch function
  351. promise['catch'](mxUtils.bind(this, function(err)
  352. {
  353. window.clearTimeout(timeoutThread);
  354. if (acceptResponse)
  355. {
  356. acceptResponse = false;
  357. if (err != null && (err.status == 500 || err.status == 400 ||
  358. err.status == 401))
  359. {
  360. this.client.setAccessToken(null);
  361. this.setUser(null);
  362. if (!failOnAuth)
  363. {
  364. this.authenticate(function()
  365. {
  366. doExecute(true);
  367. }, error);
  368. }
  369. else
  370. {
  371. error({message: mxResources.get('accessDenied'), retry: mxUtils.bind(this, function()
  372. {
  373. this.authenticate(function()
  374. {
  375. fn(true);
  376. }, error);
  377. })});
  378. }
  379. }
  380. else
  381. {
  382. error({message: mxResources.get('error') + ' ' + err.status});
  383. }
  384. }
  385. }));
  386. });
  387. var fn = mxUtils.bind(this, function(failOnAuth)
  388. {
  389. if (this.user == null)
  390. {
  391. this.updateUser(function()
  392. {
  393. fn(true);
  394. }, error, failOnAuth);
  395. }
  396. else
  397. {
  398. doExecute(failOnAuth);
  399. }
  400. });
  401. if (this.client.getAccessToken() === null)
  402. {
  403. this.authenticate(function()
  404. {
  405. fn(true);
  406. }, error);
  407. }
  408. else
  409. {
  410. fn(false);
  411. }
  412. };
  413. /**
  414. * Translates this point by the given vector.
  415. *
  416. * @param {number} dx X-coordinate of the translation.
  417. * @param {number} dy Y-coordinate of the translation.
  418. */
  419. DropboxClient.prototype.checkExists = function(filename, fn, noConfirm)
  420. {
  421. var promise = this.client.filesGetMetadata({path: '/' + filename.toLowerCase(), include_deleted: false});
  422. this.executePromise(promise, mxUtils.bind(this, function(response)
  423. {
  424. if (noConfirm)
  425. {
  426. fn(false, true, response);
  427. }
  428. else
  429. {
  430. this.ui.confirm(mxResources.get('replaceIt', [filename]), function()
  431. {
  432. fn(true, true, response);
  433. }, function()
  434. {
  435. fn(false, true, response);
  436. });
  437. }
  438. }), function(err)
  439. {
  440. fn(true, false);
  441. });
  442. };
  443. /**
  444. * Translates this point by the given vector.
  445. *
  446. * @param {number} dx X-coordinate of the translation.
  447. * @param {number} dy Y-coordinate of the translation.
  448. */
  449. DropboxClient.prototype.renameFile = function(file, filename, success, error)
  450. {
  451. if (/[\\\/:\?\*"\|]/.test(filename))
  452. {
  453. error({message: mxResources.get('dropboxCharsNotAllowed')});
  454. }
  455. else
  456. {
  457. // Appends working directory of source file
  458. if (file != null && filename != null)
  459. {
  460. var path = file.stat.path_display.substring(1);
  461. var idx = path.lastIndexOf('/');
  462. if (idx > 0)
  463. {
  464. filename = path.substring(0, idx + 1) + filename;
  465. }
  466. }
  467. if (file != null && filename != null && file.stat.path_lower.substring(1) !== filename.toLowerCase())
  468. {
  469. // Checks if file exists
  470. this.checkExists(filename, mxUtils.bind(this, function(checked, exists, response)
  471. {
  472. if (checked)
  473. {
  474. var thenHandler = mxUtils.bind(this, function(deleteResponse)
  475. {
  476. var move = this.client.filesMove({from_path: file.stat.path_display, to_path: '/' +
  477. filename, autorename: false});
  478. this.executePromise(move, success, error);
  479. });
  480. // API fails on same name with different upper-/lowercase
  481. if (!exists || response.path_lower.substring(1) === filename.toLowerCase())
  482. {
  483. thenHandler();
  484. }
  485. else
  486. {
  487. // Deletes file first to avoid conflict in filesMove (non-atomic)
  488. var promise = this.client.filesDelete({path: '/' + filename.toLowerCase()});
  489. this.executePromise(promise, thenHandler, error);
  490. }
  491. }
  492. else
  493. {
  494. error();
  495. }
  496. }));
  497. }
  498. else
  499. {
  500. // Same name with different upper-/lowercase not supported by Dropbox API
  501. error({message: mxResources.get('invalidName')});
  502. }
  503. }
  504. };
  505. /**
  506. * Translates this point by the given vector.
  507. *
  508. * @param {number} dx X-coordinate of the translation.
  509. * @param {number} dy Y-coordinate of the translation.
  510. */
  511. DropboxClient.prototype.insertLibrary = function(filename, data, success, error)
  512. {
  513. this.insertFile(filename, data, success, error, true);
  514. };
  515. /**
  516. * Translates this point by the given vector.
  517. *
  518. * @param {number} dx X-coordinate of the translation.
  519. * @param {number} dy Y-coordinate of the translation.
  520. */
  521. DropboxClient.prototype.insertFile = function(filename, data, success, error, asLibrary)
  522. {
  523. asLibrary = (asLibrary != null) ? asLibrary : false;
  524. this.checkExists(filename, mxUtils.bind(this, function(checked)
  525. {
  526. if (checked)
  527. {
  528. this.saveFile(filename, data, mxUtils.bind(this, function(stat)
  529. {
  530. if (asLibrary)
  531. {
  532. success(new DropboxLibrary(this.ui, data, stat));
  533. }
  534. else
  535. {
  536. success(new DropboxFile(this.ui, data, stat));
  537. }
  538. }), error);
  539. }
  540. else
  541. {
  542. error();
  543. }
  544. }));
  545. };
  546. /**
  547. * Translates this point by the given vector.
  548. *
  549. * @param {number} dx X-coordinate of the translation.
  550. * @param {number} dy Y-coordinate of the translation.
  551. */
  552. DropboxClient.prototype.saveFile = function(filename, data, success, error)
  553. {
  554. if (/[\\\/:\?\*"\|]/.test(filename))
  555. {
  556. error({message: mxResources.get('dropboxCharsNotAllowed')});
  557. }
  558. else if (data.length >= 150000000 /*150MB*/)
  559. {
  560. error({message: mxResources.get('drawingTooLarge') + ' (' +
  561. this.ui.formatFileSize(data.length) + ' / 150 MB)'});
  562. }
  563. else
  564. {
  565. // Mute switch is ignored
  566. var promise = this.client.filesUpload({path: '/' + filename, mode: {'.tag': 'overwrite'},
  567. contents: new Blob([data], {type: 'text/plain'}), mute: true});
  568. this.executePromise(promise, success, error);
  569. }
  570. };
  571. /**
  572. * Translates this point by the given vector.
  573. *
  574. * @param {number} dx X-coordinate of the translation.
  575. * @param {number} dy Y-coordinate of the translation.
  576. */
  577. DropboxClient.prototype.pickLibrary = function(fn)
  578. {
  579. // Authentication will be carried out on open to make sure the
  580. // autosave does not show an auth dialog. Showing it here will
  581. // block the second dialog (the file picker) so it's too early.
  582. Dropbox.choose(
  583. {
  584. linkType : 'direct',
  585. cancel: mxUtils.bind(this, function()
  586. {
  587. // do nothing
  588. }),
  589. success : mxUtils.bind(this, function(files)
  590. {
  591. if (this.ui.spinner.spin(document.body, mxResources.get('loading')))
  592. {
  593. var error = mxUtils.bind(this, function(e)
  594. {
  595. this.ui.spinner.stop();
  596. this.ui.handleError(e);
  597. });
  598. var tmp = files[0].link.indexOf(this.appPath);
  599. if (tmp > 0)
  600. {
  601. // Checks if file is in app folder by loading file from there and comparing relative path and size
  602. // KNOWN: This check fails if a file is inside a drawio directory with same relative path and size
  603. var rel = decodeURIComponent(files[0].link.substring(tmp + this.appPath.length - 1));
  604. this.readFile({path: rel}, mxUtils.bind(this, function(data, stat)
  605. {
  606. if (stat != null && parseInt(files[0].bytes) === parseInt(stat.size) && rel === stat.path_display)
  607. {
  608. // No need to load file a second time
  609. try
  610. {
  611. this.ui.spinner.stop();
  612. fn(rel.substring(1), new DropboxLibrary(this.ui, data, stat));
  613. }
  614. catch (e)
  615. {
  616. this.ui.handleError(e);
  617. }
  618. }
  619. else
  620. {
  621. this.createLibrary(files[0], fn, error);
  622. }
  623. }), error);
  624. }
  625. else
  626. {
  627. this.createLibrary(files[0], fn, error);
  628. }
  629. }
  630. })
  631. });
  632. };
  633. /**
  634. * Translates this point by the given vector.
  635. *
  636. * @param {number} dx X-coordinate of the translation.
  637. * @param {number} dy Y-coordinate of the translation.
  638. */
  639. DropboxClient.prototype.createLibrary = function(file, success, error)
  640. {
  641. this.ui.confirm(mxResources.get('note') + ': ' + mxResources.get('fileWillBeSavedInAppFolder', [file.name]), mxUtils.bind(this, function()
  642. {
  643. this.ui.loadUrl(file.link, mxUtils.bind(this, function(data)
  644. {
  645. this.insertFile(file.name, data, mxUtils.bind(this, function(newFile)
  646. {
  647. try
  648. {
  649. this.ui.spinner.stop();
  650. success(newFile.getHash().substring(1), newFile);
  651. }
  652. catch (e)
  653. {
  654. error(e);
  655. }
  656. }), error, true);
  657. }), error);
  658. }), mxUtils.bind(this, function()
  659. {
  660. this.ui.spinner.stop();
  661. }));
  662. };
  663. /**
  664. * Translates this point by the given vector.
  665. *
  666. * @param {number} dx X-coordinate of the translation.
  667. * @param {number} dy Y-coordinate of the translation.
  668. */
  669. DropboxClient.prototype.pickFile = function(fn, readOnly)
  670. {
  671. if (Dropbox.choose != null)
  672. {
  673. fn = (fn != null) ? fn : mxUtils.bind(this, function(path, file)
  674. {
  675. this.ui.loadFile((path != null) ? 'D' + encodeURIComponent(path) : file.getHash(), null, file);
  676. });
  677. // Authentication will be carried out on open to make sure the
  678. // autosave does not show an auth dialog. Showing it here will
  679. // block the second dialog (the file picker) so it's too early.
  680. Dropbox.choose(
  681. {
  682. linkType : 'direct',
  683. cancel: mxUtils.bind(this, function()
  684. {
  685. // do nothing
  686. }),
  687. success : mxUtils.bind(this, function(files)
  688. {
  689. if (this.ui.spinner.spin(document.body, mxResources.get('loading')))
  690. {
  691. // File used for read-only
  692. if (readOnly)
  693. {
  694. this.ui.spinner.stop();
  695. fn(files[0].link);
  696. }
  697. else
  698. {
  699. var error = mxUtils.bind(this, function(e)
  700. {
  701. this.ui.spinner.stop();
  702. this.ui.handleError(e);
  703. });
  704. var success = mxUtils.bind(this, function(path, file)
  705. {
  706. this.ui.spinner.stop();
  707. fn(path, file);
  708. });
  709. if ((/\.vsdx$/i.test(files[0].name) || /\.gliffy$/i.test(files[0].name) ||
  710. /\.png$/i.test(files[0].name)))
  711. {
  712. success(files[0].link);
  713. }
  714. else
  715. {
  716. var tmp = files[0].link.indexOf(this.appPath);
  717. if (tmp > 0)
  718. {
  719. // Checks if file is in app folder by loading file from there and comparing relative path and size
  720. // KNOWN: This check fails if a file is inside a drawio directory with same relative path and size
  721. var rel = decodeURIComponent(files[0].link.substring(tmp + this.appPath.length - 1));
  722. this.readFile({path: rel}, mxUtils.bind(this, function(data, stat)
  723. {
  724. if (stat != null && parseInt(files[0].bytes) === parseInt(stat.size) && rel === stat.path_display)
  725. {
  726. this.ui.spinner.stop();
  727. // No need to load file a second time
  728. fn(rel.substring(1), new DropboxFile(this.ui, data, stat));
  729. }
  730. else
  731. {
  732. this.createFile(files[0], success, error);
  733. }
  734. }), error);
  735. }
  736. else
  737. {
  738. this.createFile(files[0], success, error);
  739. }
  740. }
  741. }
  742. }
  743. })
  744. });
  745. }
  746. else
  747. {
  748. this.ui.handleError({message: mxResources.get('serviceUnavailableOrBlocked')});
  749. }
  750. };
  751. /**
  752. * Translates this point by the given vector.
  753. *
  754. * @param {number} dx X-coordinate of the translation.
  755. * @param {number} dy Y-coordinate of the translation.
  756. */
  757. DropboxClient.prototype.createFile = function(file, success, error)
  758. {
  759. this.ui.loadUrl(file.link, mxUtils.bind(this, function(data)
  760. {
  761. if (data != null && data.length > 0)
  762. {
  763. this.ui.confirm(mxResources.get('note') + ': ' + mxResources.get('fileWillBeSavedInAppFolder', [file.name]), mxUtils.bind(this, function()
  764. {
  765. this.insertFile(file.name, data, mxUtils.bind(this, function(newFile)
  766. {
  767. success(file.name, newFile);
  768. }), error);
  769. }), mxUtils.bind(this, function()
  770. {
  771. this.ui.spinner.stop();
  772. }));
  773. }
  774. else
  775. {
  776. this.ui.spinner.stop();
  777. error({message: mxResources.get('errorLoadingFile')});
  778. }
  779. }), error, /(\.png)$/i.test(file.name));
  780. };