Dashboard sipadu mbip
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

plugin.js 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or http://ckeditor.com/license
  4. */
  5. 'use strict';
  6. ( function() {
  7. CKEDITOR.plugins.add( 'filetools', {
  8. lang: 'ca,cs,da,de,de-ch,en,eo,es,eu,fr,gl,id,it,ja,km,ko,ku,nb,nl,oc,pl,pt,pt-br,ru,sv,tr,ug,uk,zh,zh-cn', // %REMOVE_LINE_CORE%
  9. beforeInit: function( editor ) {
  10. /**
  11. * An instance of the {@link CKEDITOR.fileTools.uploadRepository upload repository}.
  12. * It allows you to create and get {@link CKEDITOR.fileTools.fileLoader file loaders}.
  13. *
  14. * var loader = editor.uploadRepository.create( file );
  15. * loader.loadAndUpload( 'http://foo/bar' );
  16. *
  17. * @since 4.5
  18. * @readonly
  19. * @property {CKEDITOR.fileTools.uploadRepository} uploadRepository
  20. * @member CKEDITOR.editor
  21. */
  22. editor.uploadRepository = new UploadRepository( editor );
  23. /**
  24. * Event fired when the {@link CKEDITOR.fileTools.fileLoader file loader} should send XHR. If the event is not
  25. * {@link CKEDITOR.eventInfo#stop stopped} or {@link CKEDITOR.eventInfo#cancel canceled}, the default request
  26. * will be sent. Refer to the [Uploading Dropped or Pasted Files](#!/guide/dev_file_upload) article for more information.
  27. *
  28. * @since 4.5
  29. * @event fileUploadRequest
  30. * @member CKEDITOR.editor
  31. * @param data
  32. * @param {CKEDITOR.fileTools.fileLoader} data.fileLoader A file loader instance.
  33. * @param {Object} data.requestData An object containing all data to be sent to the server.
  34. */
  35. editor.on( 'fileUploadRequest', function( evt ) {
  36. var fileLoader = evt.data.fileLoader;
  37. fileLoader.xhr.open( 'POST', fileLoader.uploadUrl, true );
  38. // Adding file to event's data by default - allows overwriting it by user's event listeners. (#13518)
  39. evt.data.requestData.upload = { file: fileLoader.file, name: fileLoader.fileName };
  40. }, null, null, 5 );
  41. editor.on( 'fileUploadRequest', function( evt ) {
  42. var fileLoader = evt.data.fileLoader,
  43. $formData = new FormData(),
  44. requestData = evt.data.requestData;
  45. for ( var name in requestData ) {
  46. var value = requestData[ name ];
  47. // Treating files in special way
  48. if ( typeof value === 'object' && value.file ) {
  49. $formData.append( name, value.file, value.name );
  50. }
  51. else {
  52. $formData.append( name, value );
  53. }
  54. }
  55. // Append token preventing CSRF attacks.
  56. $formData.append( 'ckCsrfToken', CKEDITOR.tools.getCsrfToken() );
  57. fileLoader.xhr.send( $formData );
  58. }, null, null, 999 );
  59. /**
  60. * Event fired when the {CKEDITOR.fileTools.fileLoader file upload} response is received and needs to be parsed.
  61. * If the event is not {@link CKEDITOR.eventInfo#stop stopped} or {@link CKEDITOR.eventInfo#cancel canceled},
  62. * the default response handler will be used. Refer to the
  63. * [Uploading Dropped or Pasted Files](#!/guide/dev_file_upload) article for more information.
  64. *
  65. * @since 4.5
  66. * @event fileUploadResponse
  67. * @member CKEDITOR.editor
  68. * @param data All data will be passed to {@link CKEDITOR.fileTools.fileLoader#responseData}.
  69. * @param {CKEDITOR.fileTools.fileLoader} data.fileLoader A file loader instance.
  70. * @param {String} data.message The message from the server. Needs to be set in the listener — see the example above.
  71. * @param {String} data.fileName The file name on server. Needs to be set in the listener — see the example above.
  72. * @param {String} data.url The URL to the uploaded file. Needs to be set in the listener — see the example above.
  73. */
  74. editor.on( 'fileUploadResponse', function( evt ) {
  75. var fileLoader = evt.data.fileLoader,
  76. xhr = fileLoader.xhr,
  77. data = evt.data;
  78. try {
  79. var response = JSON.parse( xhr.responseText );
  80. // Error message does not need to mean that upload finished unsuccessfully.
  81. // It could mean that ex. file name was changes during upload due to naming collision.
  82. if ( response.error && response.error.message ) {
  83. data.message = response.error.message;
  84. }
  85. // But !uploaded means error.
  86. if ( !response.uploaded ) {
  87. evt.cancel();
  88. } else {
  89. for ( var i in response ) {
  90. data[ i ] = response[ i ];
  91. }
  92. }
  93. } catch ( err ) {
  94. // Response parsing error.
  95. data.message = fileLoader.lang.filetools.responseError;
  96. CKEDITOR.warn( 'filetools-response-error', { responseText: xhr.responseText } );
  97. evt.cancel();
  98. }
  99. }, null, null, 999 );
  100. }
  101. } );
  102. /**
  103. * File loader repository. It allows you to create and get {@link CKEDITOR.fileTools.fileLoader file loaders}.
  104. *
  105. * An instance of the repository is available as the {@link CKEDITOR.editor#uploadRepository}.
  106. *
  107. * var loader = editor.uploadRepository.create( file );
  108. * loader.loadAndUpload( 'http://foo/bar' );
  109. *
  110. * To find more information about handling files see the {@link CKEDITOR.fileTools.fileLoader} class.
  111. *
  112. * @since 4.5
  113. * @class CKEDITOR.fileTools.uploadRepository
  114. * @mixins CKEDITOR.event
  115. * @constructor Creates an instance of the repository.
  116. * @param {CKEDITOR.editor} editor Editor instance. Used only to get the language data.
  117. */
  118. function UploadRepository( editor ) {
  119. this.editor = editor;
  120. this.loaders = [];
  121. }
  122. UploadRepository.prototype = {
  123. /**
  124. * Creates a {@link CKEDITOR.fileTools.fileLoader file loader} instance with a unique ID.
  125. * The instance can be later retrieved from the repository using the {@link #loaders} array.
  126. *
  127. * Fires the {@link CKEDITOR.fileTools.uploadRepository#instanceCreated instanceCreated} event.
  128. *
  129. * @param {Blob/String} fileOrData See {@link CKEDITOR.fileTools.fileLoader}.
  130. * @param {String} fileName See {@link CKEDITOR.fileTools.fileLoader}.
  131. * @returns {CKEDITOR.fileTools.fileLoader} The created file loader instance.
  132. */
  133. create: function( fileOrData, fileName ) {
  134. var id = this.loaders.length,
  135. loader = new FileLoader( this.editor, fileOrData, fileName );
  136. loader.id = id;
  137. this.loaders[ id ] = loader;
  138. this.fire( 'instanceCreated', loader );
  139. return loader;
  140. },
  141. /**
  142. * Returns `true` if all loaders finished their jobs.
  143. *
  144. * @returns {Boolean} `true` if all loaders finished their job, `false` otherwise.
  145. */
  146. isFinished: function() {
  147. for ( var id = 0; id < this.loaders.length; ++id ) {
  148. if ( !this.loaders[ id ].isFinished() ) {
  149. return false;
  150. }
  151. }
  152. return true;
  153. }
  154. /**
  155. * Array of loaders created by the {@link #create} method. Loaders' {@link CKEDITOR.fileTools.fileLoader#id IDs}
  156. * are indexes.
  157. *
  158. * @readonly
  159. * @property {CKEDITOR.fileTools.fileLoader[]} loaders
  160. */
  161. /**
  162. * Event fired when the {@link CKEDITOR.fileTools.fileLoader file loader} is created.
  163. *
  164. * @event instanceCreated
  165. * @param {CKEDITOR.fileTools.fileLoader} data Created file loader.
  166. */
  167. };
  168. /**
  169. * The `FileLoader` class is a wrapper which handles two file operations: loading the content of the file stored on
  170. * the user's device into the memory and uploading the file to the server.
  171. *
  172. * There are two possible ways to crate a `FileLoader` instance: with a [Blob](https://developer.mozilla.org/en/docs/Web/API/Blob)
  173. * (e.g. acquired from the {@link CKEDITOR.plugins.clipboard.dataTransfer#getFile} method) or with data as a Base64 string.
  174. * Note that if the constructor gets the data as a Base64 string, there is no need to load the data, the data is already loaded.
  175. *
  176. * The `FileLoader` is created for a single load and upload process so if you abort the process,
  177. * you need to create a new `FileLoader`.
  178. *
  179. * All process parameters are stored in public properties.
  180. *
  181. * `FileLoader` implements events so you can listen to them to react to changes. There are two types of events:
  182. * events to notify the listeners about changes and an event that lets the listeners synchronize with current {@link #status}.
  183. *
  184. * The first group of events contains {@link #event-loading}, {@link #event-loaded}, {@link #event-uploading},
  185. * {@link #event-uploaded}, {@link #event-error} and {@link #event-abort}. These events are called only once,
  186. * when the {@link #status} changes.
  187. *
  188. * The second type is the {@link #event-update} event. It is fired every time the {@link #status} changes, the progress changes
  189. * or the {@link #method-update} method is called. Is is created to synchronize the visual representation of the loader with
  190. * its status. For example if the dialog window shows the upload progress, it should be refreshed on
  191. * the {@link #event-update} listener. Then when the user closes and reopens this dialog, the {@link #method-update} method should
  192. * be called to refresh the progress.
  193. *
  194. * Default request and response formats will work with CKFinder 2.4.3 and above. If you need a custom request
  195. * or response handling you need to overwrite the default behavior using the {@link CKEDITOR.editor#fileUploadRequest} and
  196. * {@link CKEDITOR.editor#fileUploadResponse} events. For more information see their documentation.
  197. *
  198. * To create a `FileLoader` instance, use the {@link CKEDITOR.fileTools.uploadRepository} class.
  199. *
  200. * Here is a simple `FileLoader` usage example:
  201. *
  202. * editor.on( 'paste', function( evt ) {
  203. * for ( var i = 0; i < evt.data.dataTransfer.getFilesCount(); i++ ) {
  204. * var file = evt.data.dataTransfer.getFile( i );
  205. *
  206. * if ( CKEDITOR.fileTools.isTypeSupported( file, /image\/png/ ) ) {
  207. * var loader = editor.uploadRepository.create( file );
  208. *
  209. * loader.on( 'update', function() {
  210. * document.getElementById( 'uploadProgress' ).innerHTML = loader.status;
  211. * } );
  212. *
  213. * loader.on( 'error', function() {
  214. * alert( 'Error!' );
  215. * } );
  216. *
  217. * loader.loadAndUpload( 'http://upload.url/' );
  218. *
  219. * evt.data.dataValue += 'loading...'
  220. * }
  221. * }
  222. * } );
  223. *
  224. * Note that `FileLoader` uses the native file API which is supported **since Internet Explorer 10**.
  225. *
  226. * @since 4.5
  227. * @class CKEDITOR.fileTools.fileLoader
  228. * @mixins CKEDITOR.event
  229. * @constructor Creates an instance of the class and sets initial values for all properties.
  230. * @param {CKEDITOR.editor} editor The editor instance. Used only to get language data.
  231. * @param {Blob/String} fileOrData A [blob object](https://developer.mozilla.org/en/docs/Web/API/Blob) or a data
  232. * string encoded with Base64.
  233. * @param {String} [fileName] The file name. If not set and the second parameter is a file, then its name will be used.
  234. * If not set and the second parameter is a Base64 data string, then the file name will be created based on
  235. * the {@link CKEDITOR.config#fileTools_defaultFileName} option.
  236. */
  237. function FileLoader( editor, fileOrData, fileName ) {
  238. var mimeParts,
  239. defaultFileName = editor.config.fileTools_defaultFileName;
  240. this.editor = editor;
  241. this.lang = editor.lang;
  242. if ( typeof fileOrData === 'string' ) {
  243. // Data is already loaded from disc.
  244. this.data = fileOrData;
  245. this.file = dataToFile( this.data );
  246. this.total = this.file.size;
  247. this.loaded = this.total;
  248. } else {
  249. this.data = null;
  250. this.file = fileOrData;
  251. this.total = this.file.size;
  252. this.loaded = 0;
  253. }
  254. if ( fileName ) {
  255. this.fileName = fileName;
  256. } else if ( this.file.name ) {
  257. this.fileName = this.file.name;
  258. } else {
  259. mimeParts = this.file.type.split( '/' );
  260. if ( defaultFileName ) {
  261. mimeParts[ 0 ] = defaultFileName;
  262. }
  263. this.fileName = mimeParts.join( '.' );
  264. }
  265. this.uploaded = 0;
  266. this.uploadTotal = null;
  267. this.responseData = null;
  268. this.status = 'created';
  269. this.abort = function() {
  270. this.changeStatus( 'abort' );
  271. };
  272. }
  273. /**
  274. * The loader status. Possible values:
  275. *
  276. * * `created` &ndash; The loader was created, but neither load nor upload started.
  277. * * `loading` &ndash; The file is being loaded from the user's storage.
  278. * * `loaded` &ndash; The file was loaded, the process is finished.
  279. * * `uploading` &ndash; The file is being uploaded to the server.
  280. * * `uploaded` &ndash; The file was uploaded, the process is finished.
  281. * * `error` &ndash; The process stops because of an error, more details are available in the {@link #message} property.
  282. * * `abort` &ndash; The process was stopped by the user.
  283. *
  284. * @property {String} status
  285. */
  286. /**
  287. * String data encoded with Base64. If the `FileLoader` is created with a Base64 string, the `data` is that string.
  288. * If a file was passed to the constructor, the data is `null` until loading is completed.
  289. *
  290. * @readonly
  291. * @property {String} data
  292. */
  293. /**
  294. * File object which represents the handled file. This property is set for both constructor options (file or data).
  295. *
  296. * @readonly
  297. * @property {Blob} file
  298. */
  299. /**
  300. * The name of the file. If there is no file name, it is created by using the
  301. * {@link CKEDITOR.config#fileTools_defaultFileName} option.
  302. *
  303. * @readonly
  304. * @property {String} fileName
  305. */
  306. /**
  307. * The number of loaded bytes. If the `FileLoader` was created with a data string,
  308. * the loaded value equals the {@link #total} value.
  309. *
  310. * @readonly
  311. * @property {Number} loaded
  312. */
  313. /**
  314. * The number of uploaded bytes.
  315. *
  316. * @readonly
  317. * @property {Number} uploaded
  318. */
  319. /**
  320. * The total file size in bytes.
  321. *
  322. * @readonly
  323. * @property {Number} total
  324. */
  325. /**
  326. * All data received in the response from the server. If the server returns additional data, it will be available
  327. * in this property.
  328. *
  329. * It contains all data set in the {@link CKEDITOR.editor#fileUploadResponse} event listener.
  330. *
  331. * @readonly
  332. * @property {Object} responseData
  333. */
  334. /**
  335. * The total size of upload data in bytes.
  336. * If the `xhr.upload` object is present, this value will indicate the total size of the request payload, not only the file
  337. * size itself. If the `xhr.upload` object is not available and the real upload size cannot be obtained, this value will
  338. * be equal to {@link #total}. It has a `null` value until the upload size is known.
  339. *
  340. * loader.on( 'update', function() {
  341. * // Wait till uploadTotal is present.
  342. * if ( loader.uploadTotal ) {
  343. * console.log( 'uploadTotal: ' + loader.uploadTotal );
  344. * }
  345. * });
  346. *
  347. * @readonly
  348. * @property {Number} uploadTotal
  349. */
  350. /**
  351. * The error message or additional information received from the server.
  352. *
  353. * @readonly
  354. * @property {String} message
  355. */
  356. /**
  357. * The URL to the file when it is uploaded or received from the server.
  358. *
  359. * @readonly
  360. * @property {String} url
  361. */
  362. /**
  363. * The target of the upload.
  364. *
  365. * @readonly
  366. * @property {String} uploadUrl
  367. */
  368. /**
  369. *
  370. * Native `FileReader` reference used to load the file.
  371. *
  372. * @readonly
  373. * @property {FileReader} reader
  374. */
  375. /**
  376. * Native `XMLHttpRequest` reference used to upload the file.
  377. *
  378. * @readonly
  379. * @property {XMLHttpRequest} xhr
  380. */
  381. /**
  382. * If `FileLoader` was created using {@link CKEDITOR.fileTools.uploadRepository},
  383. * it gets an identifier which is stored in this property.
  384. *
  385. * @readonly
  386. * @property {Number} id
  387. */
  388. /**
  389. * Aborts the process.
  390. *
  391. * This method has a different behavior depending on the current {@link #status}.
  392. *
  393. * * If the {@link #status} is `loading` or `uploading`, current operation will be aborted.
  394. * * If the {@link #status} is `created`, `loading` or `uploading`, the {@link #status} will be changed to `abort`
  395. * and the {@link #event-abort} event will be called.
  396. * * If the {@link #status} is `loaded`, `uploaded`, `error` or `abort`, this method will do nothing.
  397. *
  398. * @method abort
  399. */
  400. FileLoader.prototype = {
  401. /**
  402. * Loads a file from the storage on the user's device to the `data` attribute and uploads it to the server.
  403. *
  404. * The order of {@link #status statuses} for a successful load and upload is:
  405. *
  406. * * `created`,
  407. * * `loading`,
  408. * * `uploading`,
  409. * * `uploaded`.
  410. *
  411. * @param {String} url The upload URL.
  412. * @param {Object} [additionalRequestParameters] Additional parameters that would be passed to
  413. * the {@link CKEDITOR.editor#fileUploadRequest} event.
  414. */
  415. loadAndUpload: function( url, additionalRequestParameters ) {
  416. var loader = this;
  417. this.once( 'loaded', function( evt ) {
  418. // Cancel both 'loaded' and 'update' events,
  419. // because 'loaded' is terminated state.
  420. evt.cancel();
  421. loader.once( 'update', function( evt ) {
  422. evt.cancel();
  423. }, null, null, 0 );
  424. // Start uploading.
  425. loader.upload( url, additionalRequestParameters );
  426. }, null, null, 0 );
  427. this.load();
  428. },
  429. /**
  430. * Loads a file from the storage on the user's device to the `data` attribute.
  431. *
  432. * The order of the {@link #status statuses} for a successful load is:
  433. *
  434. * * `created`,
  435. * * `loading`,
  436. * * `loaded`.
  437. */
  438. load: function() {
  439. var loader = this;
  440. this.reader = new FileReader();
  441. var reader = this.reader;
  442. loader.changeStatus( 'loading' );
  443. this.abort = function() {
  444. loader.reader.abort();
  445. };
  446. reader.onabort = function() {
  447. loader.changeStatus( 'abort' );
  448. };
  449. reader.onerror = function() {
  450. loader.message = loader.lang.filetools.loadError;
  451. loader.changeStatus( 'error' );
  452. };
  453. reader.onprogress = function( evt ) {
  454. loader.loaded = evt.loaded;
  455. loader.update();
  456. };
  457. reader.onload = function() {
  458. loader.loaded = loader.total;
  459. loader.data = reader.result;
  460. loader.changeStatus( 'loaded' );
  461. };
  462. reader.readAsDataURL( this.file );
  463. },
  464. /**
  465. * Uploads a file to the server.
  466. *
  467. * The order of the {@link #status statuses} for a successful upload is:
  468. *
  469. * * `created`,
  470. * * `uploading`,
  471. * * `uploaded`.
  472. *
  473. * @param {String} url The upload URL.
  474. * @param {Object} [additionalRequestParameters] Additional data that would be passed to
  475. * the {@link CKEDITOR.editor#fileUploadRequest} event.
  476. */
  477. upload: function( url, additionalRequestParameters ) {
  478. var requestData = additionalRequestParameters || {};
  479. if ( !url ) {
  480. this.message = this.lang.filetools.noUrlError;
  481. this.changeStatus( 'error' );
  482. } else {
  483. this.uploadUrl = url;
  484. this.xhr = new XMLHttpRequest();
  485. this.attachRequestListeners();
  486. if ( this.editor.fire( 'fileUploadRequest', { fileLoader: this, requestData: requestData } ) ) {
  487. this.changeStatus( 'uploading' );
  488. }
  489. }
  490. },
  491. /**
  492. * Attaches listeners to the XML HTTP request object.
  493. *
  494. * @private
  495. * @param {XMLHttpRequest} xhr XML HTTP request object.
  496. */
  497. attachRequestListeners: function() {
  498. var loader = this,
  499. xhr = this.xhr;
  500. loader.abort = function() {
  501. xhr.abort();
  502. onAbort();
  503. };
  504. xhr.onerror = onError;
  505. xhr.onabort = onAbort;
  506. // #13533 - When xhr.upload is present attach onprogress, onerror and onabort functions to get actual upload
  507. // information.
  508. if ( xhr.upload ) {
  509. xhr.upload.onprogress = function( evt ) {
  510. if ( evt.lengthComputable ) {
  511. // Set uploadTotal with correct data.
  512. if ( !loader.uploadTotal ) {
  513. loader.uploadTotal = evt.total;
  514. }
  515. loader.uploaded = evt.loaded;
  516. loader.update();
  517. }
  518. };
  519. xhr.upload.onerror = onError;
  520. xhr.upload.onabort = onAbort;
  521. } else {
  522. // #13533 - If xhr.upload is not supported - fire update event anyway and set uploadTotal to file size.
  523. loader.uploadTotal = loader.total;
  524. loader.update();
  525. }
  526. xhr.onload = function() {
  527. // #13433 - Call update at the end of the upload. When xhr.upload object is not supported there will be
  528. // no update events fired during the whole process.
  529. loader.update();
  530. // #13433 - Check if loader was not aborted during last update.
  531. if ( loader.status == 'abort' ) {
  532. return;
  533. }
  534. loader.uploaded = loader.uploadTotal;
  535. if ( xhr.status < 200 || xhr.status > 299 ) {
  536. loader.message = loader.lang.filetools[ 'httpError' + xhr.status ];
  537. if ( !loader.message ) {
  538. loader.message = loader.lang.filetools.httpError.replace( '%1', xhr.status );
  539. }
  540. loader.changeStatus( 'error' );
  541. } else {
  542. var data = {
  543. fileLoader: loader
  544. },
  545. // Values to copy from event to FileLoader.
  546. valuesToCopy = [ 'message', 'fileName', 'url' ],
  547. success = loader.editor.fire( 'fileUploadResponse', data );
  548. for ( var i = 0; i < valuesToCopy.length; i++ ) {
  549. var key = valuesToCopy[ i ];
  550. if ( typeof data[ key ] === 'string' ) {
  551. loader[ key ] = data[ key ];
  552. }
  553. }
  554. // The whole response is also hold for use by uploadwidgets (#13519).
  555. loader.responseData = data;
  556. // But without reference to the loader itself.
  557. delete loader.responseData.fileLoader;
  558. if ( success === false ) {
  559. loader.changeStatus( 'error' );
  560. } else {
  561. loader.changeStatus( 'uploaded' );
  562. }
  563. }
  564. };
  565. function onError() {
  566. // Prevent changing status twice, when HHR.error and XHR.upload.onerror could be called together.
  567. if ( loader.status == 'error' ) {
  568. return;
  569. }
  570. loader.message = loader.lang.filetools.networkError;
  571. loader.changeStatus( 'error' );
  572. }
  573. function onAbort() {
  574. // Prevent changing status twice, when HHR.onabort and XHR.upload.onabort could be called together.
  575. if ( loader.status == 'abort' ) {
  576. return;
  577. }
  578. loader.changeStatus( 'abort' );
  579. }
  580. },
  581. /**
  582. * Changes {@link #status} to the new status, updates the {@link #method-abort} method if needed and fires two events:
  583. * new status and {@link #event-update}.
  584. *
  585. * @private
  586. * @param {String} newStatus New status to be set.
  587. */
  588. changeStatus: function( newStatus ) {
  589. this.status = newStatus;
  590. if ( newStatus == 'error' || newStatus == 'abort' ||
  591. newStatus == 'loaded' || newStatus == 'uploaded' ) {
  592. this.abort = function() {};
  593. }
  594. this.fire( newStatus );
  595. this.update();
  596. },
  597. /**
  598. * Updates the state of the `FileLoader` listeners. This method should be called if the state of the visual representation
  599. * of the upload process is out of synchronization and needs to be refreshed (e.g. because of an undo operation or
  600. * because the dialog window with the upload is closed and reopened). Fires the {@link #event-update} event.
  601. */
  602. update: function() {
  603. this.fire( 'update' );
  604. },
  605. /**
  606. * Returns `true` if the loading and uploading finished (successfully or not), so the {@link #status} is
  607. * `loaded`, `uploaded`, `error` or `abort`.
  608. *
  609. * @returns {Boolean} `true` if the loading and uploading finished.
  610. */
  611. isFinished: function() {
  612. return !!this.status.match( /^(?:loaded|uploaded|error|abort)$/ );
  613. }
  614. /**
  615. * Event fired when the {@link #status} changes to `loading`. It will be fired once for the `FileLoader`.
  616. *
  617. * @event loading
  618. */
  619. /**
  620. * Event fired when the {@link #status} changes to `loaded`. It will be fired once for the `FileLoader`.
  621. *
  622. * @event loaded
  623. */
  624. /**
  625. * Event fired when the {@link #status} changes to `uploading`. It will be fired once for the `FileLoader`.
  626. *
  627. * @event uploading
  628. */
  629. /**
  630. * Event fired when the {@link #status} changes to `uploaded`. It will be fired once for the `FileLoader`.
  631. *
  632. * @event uploaded
  633. */
  634. /**
  635. * Event fired when the {@link #status} changes to `error`. It will be fired once for the `FileLoader`.
  636. *
  637. * @event error
  638. */
  639. /**
  640. * Event fired when the {@link #status} changes to `abort`. It will be fired once for the `FileLoader`.
  641. *
  642. * @event abort
  643. */
  644. /**
  645. * Event fired every time the `FileLoader` {@link #status} or progress changes or the {@link #method-update} method is called.
  646. * This event was designed to allow showing the visualization of the progress and refresh that visualization
  647. * every time the status changes. Note that multiple `update` events may be fired with the same status.
  648. *
  649. * @event update
  650. */
  651. };
  652. CKEDITOR.event.implementOn( UploadRepository.prototype );
  653. CKEDITOR.event.implementOn( FileLoader.prototype );
  654. var base64HeaderRegExp = /^data:(\S*?);base64,/;
  655. // Transforms Base64 string data into file and creates name for that file based on the mime type.
  656. //
  657. // @private
  658. // @param {String} data Base64 string data.
  659. // @returns {Blob} File.
  660. function dataToFile( data ) {
  661. var contentType = data.match( base64HeaderRegExp )[ 1 ],
  662. base64Data = data.replace( base64HeaderRegExp, '' ),
  663. byteCharacters = atob( base64Data ),
  664. byteArrays = [],
  665. sliceSize = 512,
  666. offset, slice, byteNumbers, i, byteArray;
  667. for ( offset = 0; offset < byteCharacters.length; offset += sliceSize ) {
  668. slice = byteCharacters.slice( offset, offset + sliceSize );
  669. byteNumbers = new Array( slice.length );
  670. for ( i = 0; i < slice.length; i++ ) {
  671. byteNumbers[ i ] = slice.charCodeAt( i );
  672. }
  673. byteArray = new Uint8Array( byteNumbers );
  674. byteArrays.push( byteArray );
  675. }
  676. return new Blob( byteArrays, { type: contentType } );
  677. }
  678. //
  679. // PUBLIC API -------------------------------------------------------------
  680. //
  681. // Two plugins extend this object.
  682. if ( !CKEDITOR.fileTools ) {
  683. /**
  684. * Helpers to load and upload a file.
  685. *
  686. * @since 4.5
  687. * @singleton
  688. * @class CKEDITOR.fileTools
  689. */
  690. CKEDITOR.fileTools = {};
  691. }
  692. CKEDITOR.tools.extend( CKEDITOR.fileTools, {
  693. uploadRepository: UploadRepository,
  694. fileLoader: FileLoader,
  695. /**
  696. * Gets the upload URL from the {@link CKEDITOR.config configuration}. Because of backward compatibility
  697. * the URL can be set using multiple configuration options.
  698. *
  699. * If the `type` is defined, then four configuration options will be checked in the following order
  700. * (examples for `type='image'`):
  701. *
  702. * * `[type]UploadUrl`, e.g. {@link CKEDITOR.config#imageUploadUrl},
  703. * * {@link CKEDITOR.config#uploadUrl},
  704. * * `filebrowser[uppercased type]uploadUrl`, e.g. {@link CKEDITOR.config#filebrowserImageUploadUrl},
  705. * * {@link CKEDITOR.config#filebrowserUploadUrl}.
  706. *
  707. * If the `type` is not defined, two configuration options will be checked:
  708. *
  709. * * {@link CKEDITOR.config#uploadUrl},
  710. * * {@link CKEDITOR.config#filebrowserUploadUrl}.
  711. *
  712. * `filebrowser[type]uploadUrl` and `filebrowserUploadUrl` are checked for backward compatibility with the
  713. * `filebrowser` plugin.
  714. *
  715. * For both `filebrowser[type]uploadUrl` and `filebrowserUploadUrl` `&responseType=json` is added to the end of the URL.
  716. *
  717. * @param {Object} config The configuration file.
  718. * @param {String} [type] Upload file type.
  719. * @returns {String/null} Upload URL or `null` if none of the configuration options were defined.
  720. */
  721. getUploadUrl: function( config, type ) {
  722. var capitalize = CKEDITOR.tools.capitalize;
  723. if ( type && config[ type + 'UploadUrl' ] ) {
  724. return config[ type + 'UploadUrl' ];
  725. } else if ( config.uploadUrl ) {
  726. return config.uploadUrl;
  727. } else if ( type && config[ 'filebrowser' + capitalize( type, 1 ) + 'UploadUrl' ] ) {
  728. return config[ 'filebrowser' + capitalize( type, 1 ) + 'UploadUrl' ] + '&responseType=json';
  729. } else if ( config.filebrowserUploadUrl ) {
  730. return config.filebrowserUploadUrl + '&responseType=json';
  731. }
  732. return null;
  733. },
  734. /**
  735. * Checks if the MIME type of the given file is supported.
  736. *
  737. * CKEDITOR.fileTools.isTypeSupported( { type: 'image/png' }, /image\/(png|jpeg)/ ); // true
  738. * CKEDITOR.fileTools.isTypeSupported( { type: 'image/png' }, /image\/(gif|jpeg)/ ); // false
  739. *
  740. * @param {Blob} file The file to check.
  741. * @param {RegExp} supportedTypes A regular expression to check the MIME type of the file.
  742. * @returns {Boolean} `true` if the file type is supported.
  743. */
  744. isTypeSupported: function( file, supportedTypes ) {
  745. return !!file.type.match( supportedTypes );
  746. }
  747. } );
  748. } )();
  749. /**
  750. * The URL where files should be uploaded.
  751. *
  752. * An empty string means that the option is disabled.
  753. *
  754. * @since 4.5
  755. * @cfg {String} [uploadUrl='']
  756. * @member CKEDITOR.config
  757. */
  758. /**
  759. * Default file name (without extension) that will be used for files created from a Base64 data string
  760. * (for example for files pasted into the editor).
  761. * This name will be combined with the MIME type to create the full file name with the extension.
  762. *
  763. * If `fileTools_defaultFileName` is set to `default-name` and data's MIME type is `image/png`,
  764. * the resulting file name will be `default-name.png`.
  765. *
  766. * If `fileTools_defaultFileName` is not set, the file name will be created using only its MIME type.
  767. * For example for `image/png` the file name will be `image.png`.
  768. *
  769. * @since 4.5.3
  770. * @cfg {String} [fileTools_defaultFileName='']
  771. * @member CKEDITOR.config
  772. */