Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

plugin.js 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  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( 'uploadwidget', {
  8. lang: 'ca,cs,da,de,de-ch,el,en,eo,es,eu,fr,gl,hu,id,it,ja,km,ko,ku,nb,nl,no,oc,pl,pt,pt-br,ru,sv,tr,ug,uk,zh,zh-cn', // %REMOVE_LINE_CORE%
  9. requires: 'widget,clipboard,filetools,notificationaggregator',
  10. init: function( editor ) {
  11. // Images which should be changed into upload widget needs to be marked with `data-widget` on paste,
  12. // because otherwise wrong widget may handle upload placeholder element (e.g. image2 plugin would handle image).
  13. // `data-widget` attribute is allowed only in the elements which has also `data-cke-upload-id` attribute.
  14. editor.filter.allow( '*[!data-widget,!data-cke-upload-id]' );
  15. }
  16. } );
  17. /**
  18. * This function creates an upload widget — a placeholder to show the progress of an upload. The upload widget
  19. * is based on its {@link CKEDITOR.fileTools.uploadWidgetDefinition definition}. The `addUploadWidget` method also
  20. * creates a `paste` event, if the {@link CKEDITOR.fileTools.uploadWidgetDefinition#fileToElement fileToElement} method
  21. * is defined. This event helps in handling pasted files, as it will automatically check if the files were pasted and
  22. * mark them to be uploaded.
  23. *
  24. * The upload widget helps to handle content that is uploaded asynchronously inside the editor. It solves issues such as:
  25. * editing during upload, undo manager integration, getting data, removing or copying uploaded element.
  26. *
  27. * To create an upload widget you need to define two transformation methods:
  28. *
  29. * * The {@link CKEDITOR.fileTools.uploadWidgetDefinition#fileToElement fileToElement} method which will be called on
  30. * `paste` and transform a file into a placeholder.
  31. * * The {@link CKEDITOR.fileTools.uploadWidgetDefinition#onUploaded onUploaded} method with
  32. * the {@link CKEDITOR.fileTools.uploadWidgetDefinition#replaceWith replaceWith} method which will be called to replace
  33. * the upload placeholder with the final HTML when the upload is done.
  34. * If you want to show more information about the progress you can also define
  35. * the {@link CKEDITOR.fileTools.uploadWidgetDefinition#onLoading onLoading} and
  36. * {@link CKEDITOR.fileTools.uploadWidgetDefinition#onUploading onUploading} methods.
  37. *
  38. * The simplest uploading widget which uploads a file and creates a link to it may look like this:
  39. *
  40. * CKEDITOR.fileTools.addUploadWidget( editor, 'uploadfile', {
  41. * uploadUrl: CKEDITOR.fileTools.getUploadUrl( editor.config ),
  42. *
  43. * fileToElement: function( file ) {
  44. * var a = new CKEDITOR.dom.element( 'a' );
  45. * a.setText( file.name );
  46. * a.setAttribute( 'href', '#' );
  47. * return a;
  48. * },
  49. *
  50. * onUploaded: function( upload ) {
  51. * this.replaceWith( '<a href="' + upload.url + '" target="_blank">' + upload.fileName + '</a>' );
  52. * }
  53. * } );
  54. *
  55. * The upload widget uses {@link CKEDITOR.fileTools.fileLoader} as a helper to upload the file. A
  56. * {@link CKEDITOR.fileTools.fileLoader} instance is created when the file is pasted and a proper method is
  57. * called &mdash; by default it is the {@link CKEDITOR.fileTools.fileLoader#loadAndUpload} method. If you want
  58. * to only use the `load` or `upload`, you can use the {@link CKEDITOR.fileTools.uploadWidgetDefinition#loadMethod loadMethod}
  59. * property.
  60. *
  61. * Note that if you want to handle a big file, e.g. a video, you may need to use `upload` instead of
  62. * `loadAndUpload` because the file may be too big to load it to memory at once.
  63. *
  64. * If you do not upload the file, you need to define {@link CKEDITOR.fileTools.uploadWidgetDefinition#onLoaded onLoaded}
  65. * instead of {@link CKEDITOR.fileTools.uploadWidgetDefinition#onUploaded onUploaded}.
  66. * For example, if you want to read the content of the file:
  67. *
  68. * CKEDITOR.fileTools.addUploadWidget( editor, 'fileReader', {
  69. * loadMethod: 'load',
  70. * supportedTypes: /text\/(plain|html)/,
  71. *
  72. * fileToElement: function( file ) {
  73. * var el = new CKEDITOR.dom.element( 'span' );
  74. * el.setText( '...' );
  75. * return el;
  76. * },
  77. *
  78. * onLoaded: function( loader ) {
  79. * this.replaceWith( atob( loader.data.split( ',' )[ 1 ] ) );
  80. * }
  81. * } );
  82. *
  83. * If you need to pass additional data to the request, you can do this using the
  84. * {@link CKEDITOR.fileTools.uploadWidgetDefinition#additionalRequestParameters additionalRequestParameters} property.
  85. * That data is then passed to the upload method defined by {@link CKEDITOR.fileTools.uploadWidgetDefinition#loadMethod},
  86. * and to the {@link CKEDITOR.editor#fileUploadRequest} event (as part of the `requestData` property).
  87. * The syntax of that parameter is compatible with the {@link CKEDITOR.editor#fileUploadRequest} `requestData` property.
  88. *
  89. * CKEDITOR.fileTools.addUploadWidget( editor, 'uploadFile', {
  90. * additionalRequestParameters: {
  91. * foo: 'bar'
  92. * },
  93. *
  94. * fileToElement: function( file ) {
  95. * var el = new CKEDITOR.dom.element( 'span' );
  96. * el.setText( '...' );
  97. * return el;
  98. * },
  99. *
  100. * onUploaded: function( upload ) {
  101. * this.replaceWith( '<a href="' + upload.url + '" target="_blank">' + upload.fileName + '</a>' );
  102. * }
  103. * } );
  104. *
  105. * If you need custom `paste` handling, you need to mark the pasted element to be changed into an upload widget
  106. * using {@link CKEDITOR.fileTools#markElement markElement}. For example, instead of the `fileToElement` helper from the
  107. * example above, a `paste` listener can be created manually:
  108. *
  109. * editor.on( 'paste', function( evt ) {
  110. * var file, i, el, loader;
  111. *
  112. * for ( i = 0; i < evt.data.dataTransfer.getFilesCount(); i++ ) {
  113. * file = evt.data.dataTransfer.getFile( i );
  114. *
  115. * if ( CKEDITOR.fileTools.isTypeSupported( file, /text\/(plain|html)/ ) ) {
  116. * el = new CKEDITOR.dom.element( 'span' ),
  117. * loader = editor.uploadRepository.create( file );
  118. *
  119. * el.setText( '...' );
  120. *
  121. * loader.load();
  122. *
  123. * CKEDITOR.fileTools.markElement( el, 'filereader', loader.id );
  124. *
  125. * evt.data.dataValue += el.getOuterHtml();
  126. * }
  127. * }
  128. * } );
  129. *
  130. * Note that you can bind notifications to the upload widget on paste using
  131. * the {@link CKEDITOR.fileTools#bindNotifications} method, so notifications will automatically
  132. * show the progress of the upload. Because this method shows notifications about upload, do not use it if you only
  133. * {@link CKEDITOR.fileTools.fileLoader#load load} (and not upload) a file.
  134. *
  135. * editor.on( 'paste', function( evt ) {
  136. * var file, i, el, loader;
  137. *
  138. * for ( i = 0; i < evt.data.dataTransfer.getFilesCount(); i++ ) {
  139. * file = evt.data.dataTransfer.getFile( i );
  140. *
  141. * if ( CKEDITOR.fileTools.isTypeSupported( file, /text\/pdf/ ) ) {
  142. * el = new CKEDITOR.dom.element( 'span' ),
  143. * loader = editor.uploadRepository.create( file );
  144. *
  145. * el.setText( '...' );
  146. *
  147. * loader.upload();
  148. *
  149. * CKEDITOR.fileTools.markElement( el, 'pdfuploader', loader.id );
  150. *
  151. * CKEDITOR.fileTools.bindNotifications( editor, loader );
  152. *
  153. * evt.data.dataValue += el.getOuterHtml();
  154. * }
  155. * }
  156. * } );
  157. *
  158. * @member CKEDITOR.fileTools
  159. * @param {CKEDITOR.editor} editor The editor instance.
  160. * @param {String} name The name of the upload widget.
  161. * @param {CKEDITOR.fileTools.uploadWidgetDefinition} def Upload widget definition.
  162. */
  163. function addUploadWidget( editor, name, def ) {
  164. var fileTools = CKEDITOR.fileTools,
  165. uploads = editor.uploadRepository,
  166. // Plugins which support all file type has lower priority than plugins which support specific types.
  167. priority = def.supportedTypes ? 10 : 20;
  168. if ( def.fileToElement ) {
  169. editor.on( 'paste', function( evt ) {
  170. var data = evt.data,
  171. dataTransfer = data.dataTransfer,
  172. filesCount = dataTransfer.getFilesCount(),
  173. loadMethod = def.loadMethod || 'loadAndUpload',
  174. file, i;
  175. if ( data.dataValue || !filesCount ) {
  176. return;
  177. }
  178. for ( i = 0; i < filesCount; i++ ) {
  179. file = dataTransfer.getFile( i );
  180. // No def.supportedTypes means all types are supported.
  181. if ( !def.supportedTypes || fileTools.isTypeSupported( file, def.supportedTypes ) ) {
  182. var el = def.fileToElement( file ),
  183. loader = uploads.create( file );
  184. if ( el ) {
  185. loader[ loadMethod ]( def.uploadUrl, def.additionalRequestParameters );
  186. CKEDITOR.fileTools.markElement( el, name, loader.id );
  187. if ( loadMethod == 'loadAndUpload' || loadMethod == 'upload' ) {
  188. CKEDITOR.fileTools.bindNotifications( editor, loader );
  189. }
  190. data.dataValue += el.getOuterHtml();
  191. }
  192. }
  193. }
  194. }, null, null, priority );
  195. }
  196. /**
  197. * This is an abstract class that describes a definition of an upload widget.
  198. * It is a type of {@link CKEDITOR.fileTools#addUploadWidget} method's second argument.
  199. *
  200. * Note that because the upload widget is a type of a widget, this definition extends
  201. * {@link CKEDITOR.plugins.widget.definition}.
  202. * It adds several new properties and callbacks and implements the {@link CKEDITOR.plugins.widget.definition#downcast}
  203. * and {@link CKEDITOR.plugins.widget.definition#init} callbacks. These two properties
  204. * should not be overwritten.
  205. *
  206. * Also, the upload widget definition defines a few properties ({@link #fileToElement}, {@link #supportedTypes},
  207. * {@link #loadMethod loadMethod}, {@link #uploadUrl} and {@link #additionalRequestParameters}) used in the
  208. * {@link CKEDITOR.editor#paste} listener which is registered by {@link CKEDITOR.fileTools#addUploadWidget}
  209. * if the upload widget definition contains the {@link #fileToElement} callback.
  210. *
  211. * @abstract
  212. * @class CKEDITOR.fileTools.uploadWidgetDefinition
  213. * @mixins CKEDITOR.plugins.widget.definition
  214. */
  215. CKEDITOR.tools.extend( def, {
  216. /**
  217. * Upload widget definition overwrites the {@link CKEDITOR.plugins.widget.definition#downcast} property.
  218. * This should not be changed.
  219. *
  220. * @property {String/Function}
  221. */
  222. downcast: function() {
  223. return new CKEDITOR.htmlParser.text( '' );
  224. },
  225. /**
  226. * Upload widget definition overwrites the {@link CKEDITOR.plugins.widget.definition#init} property.
  227. * If you want to add some code in the `init` callback remember to call the base function.
  228. *
  229. * @property {Function}
  230. */
  231. init: function() {
  232. var widget = this,
  233. id = this.wrapper.findOne( '[data-cke-upload-id]' ).data( 'cke-upload-id' ),
  234. loader = uploads.loaders[ id ],
  235. capitalize = CKEDITOR.tools.capitalize,
  236. oldStyle, newStyle;
  237. loader.on( 'update', function( evt ) {
  238. // Abort if widget was removed.
  239. if ( !widget.wrapper || !widget.wrapper.getParent() ) {
  240. if ( !editor.editable().find( '[data-cke-upload-id="' + id + '"]' ).count() ) {
  241. loader.abort();
  242. }
  243. evt.removeListener();
  244. return;
  245. }
  246. editor.fire( 'lockSnapshot' );
  247. // Call users method, eg. if the status is `uploaded` then
  248. // `onUploaded` method will be called, if exists.
  249. var methodName = 'on' + capitalize( loader.status );
  250. if ( typeof widget[ methodName ] === 'function' ) {
  251. if ( widget[ methodName ]( loader ) === false ) {
  252. editor.fire( 'unlockSnapshot' );
  253. return;
  254. }
  255. }
  256. // Set style to the wrapper if it still exists.
  257. newStyle = 'cke_upload_' + loader.status;
  258. if ( widget.wrapper && newStyle != oldStyle ) {
  259. oldStyle && widget.wrapper.removeClass( oldStyle );
  260. widget.wrapper.addClass( newStyle );
  261. oldStyle = newStyle;
  262. }
  263. // Remove widget on error or abort.
  264. if ( loader.status == 'error' || loader.status == 'abort' ) {
  265. editor.widgets.del( widget );
  266. }
  267. editor.fire( 'unlockSnapshot' );
  268. } );
  269. loader.update();
  270. },
  271. /**
  272. * Replaces the upload widget with the final HTML. This method should be called when the upload is done,
  273. * usually in the {@link #onUploaded} callback.
  274. *
  275. * @property {Function}
  276. * @param {String} data HTML to replace the upload widget.
  277. * @param {String} [mode='html'] See {@link CKEDITOR.editor#method-insertHtml}'s modes.
  278. */
  279. replaceWith: function( data, mode ) {
  280. if ( data.trim() === '' ) {
  281. editor.widgets.del( this );
  282. return;
  283. }
  284. var wasSelected = ( this == editor.widgets.focused ),
  285. editable = editor.editable(),
  286. range = editor.createRange(),
  287. bookmark, bookmarks;
  288. if ( !wasSelected ) {
  289. bookmarks = editor.getSelection().createBookmarks();
  290. }
  291. range.setStartBefore( this.wrapper );
  292. range.setEndAfter( this.wrapper );
  293. if ( wasSelected ) {
  294. bookmark = range.createBookmark();
  295. }
  296. editable.insertHtmlIntoRange( data, range, mode );
  297. editor.widgets.checkWidgets( { initOnlyNew: true } );
  298. // Ensure that old widgets instance will be removed.
  299. // If replaceWith is called in init, because of paste then checkWidgets will not remove it.
  300. editor.widgets.destroy( this, true );
  301. if ( wasSelected ) {
  302. range.moveToBookmark( bookmark );
  303. range.select();
  304. } else {
  305. editor.getSelection().selectBookmarks( bookmarks );
  306. }
  307. }
  308. /**
  309. * If this property is defined, paste listener is created to transform the pasted file into an HTML element.
  310. * It creates an HTML element which will be then transformed into an upload widget.
  311. * It is only called for {@link #supportedTypes supported files}.
  312. * If multiple files were pasted, this function will be called for each file of a supported type.
  313. *
  314. * @property {Function} fileToElement
  315. * @param {Blob} file A pasted file to load or upload.
  316. * @returns {CKEDITOR.dom.element} An element which will be transformed into the upload widget.
  317. */
  318. /**
  319. * Regular expression to check if the file type is supported by this widget.
  320. * If not defined, all files will be handled.
  321. *
  322. * @property {String} [supportedTypes]
  323. */
  324. /**
  325. * The URL to which the file will be uploaded. It should be taken from the configuration using
  326. * {@link CKEDITOR.fileTools#getUploadUrl}.
  327. *
  328. * @property {String} [uploadUrl]
  329. */
  330. /**
  331. * An object containing additional data that should be passed to the function defined by {@link #loadMethod}.
  332. *
  333. * @property {Object} [additionalRequestParameters]
  334. */
  335. /**
  336. * The type of loading operation that should be executed as a result of pasting a file. Possible options are:
  337. *
  338. * * `'loadAndUpload'` &ndash; Default behavior. The {@link CKEDITOR.fileTools.fileLoader#loadAndUpload} method will be
  339. * executed, the file will be loaded first and uploaded immediately after loading is done.
  340. * * `'load'` &ndash; The {@link CKEDITOR.fileTools.fileLoader#load} method will be executed. This loading type should
  341. * be used if you only want to load file data without uploading it.
  342. * * `'upload'` &ndash; The {@link CKEDITOR.fileTools.fileLoader#upload} method will be executed, the file will be uploaded
  343. * without loading it to memory. This loading type should be used if you want to upload a big file,
  344. * otherwise you can get an "out of memory" error.
  345. *
  346. * @property {String} [loadMethod=loadAndUpload]
  347. */
  348. /**
  349. * A function called when the {@link CKEDITOR.fileTools.fileLoader#status status} of the upload changes to `loading`.
  350. *
  351. * @property {Function} [onLoading]
  352. * @param {CKEDITOR.fileTools.fileLoader} loader Loader instance.
  353. * @returns {Boolean}
  354. */
  355. /**
  356. * A function called when the {@link CKEDITOR.fileTools.fileLoader#status status} of the upload changes to `loaded`.
  357. *
  358. * @property {Function} [onLoaded]
  359. * @param {CKEDITOR.fileTools.fileLoader} loader Loader instance.
  360. * @returns {Boolean}
  361. */
  362. /**
  363. * A function called when the {@link CKEDITOR.fileTools.fileLoader#status status} of the upload changes to `uploading`.
  364. *
  365. * @property {Function} [onUploading]
  366. * @param {CKEDITOR.fileTools.fileLoader} loader Loader instance.
  367. * @returns {Boolean}
  368. */
  369. /**
  370. * A function called when the {@link CKEDITOR.fileTools.fileLoader#status status} of the upload changes to `uploaded`.
  371. * At that point the upload is done and the upload widget should be replaced with the final HTML using
  372. * the {@link #replaceWith} method.
  373. *
  374. * @property {Function} [onUploaded]
  375. * @param {CKEDITOR.fileTools.fileLoader} loader Loader instance.
  376. * @returns {Boolean}
  377. */
  378. /**
  379. * A function called when the {@link CKEDITOR.fileTools.fileLoader#status status} of the upload changes to `error`.
  380. * The default behavior is to remove the widget and it can be canceled if this function returns `false`.
  381. *
  382. * @property {Function} [onError]
  383. * @param {CKEDITOR.fileTools.fileLoader} loader Loader instance.
  384. * @returns {Boolean} If `false`, the default behavior (remove widget) will be canceled.
  385. */
  386. /**
  387. * A function called when the {@link CKEDITOR.fileTools.fileLoader#status status} of the upload changes to `abort`.
  388. * The default behavior is to remove the widget and it can be canceled if this function returns `false`.
  389. *
  390. * @property {Function} [onAbort]
  391. * @param {CKEDITOR.fileTools.fileLoader} loader Loader instance.
  392. * @returns {Boolean} If `false`, the default behavior (remove widget) will be canceled.
  393. */
  394. } );
  395. editor.widgets.add( name, def );
  396. }
  397. /**
  398. * Marks an element which should be transformed into an upload widget.
  399. *
  400. * @see CKEDITOR.fileTools.addUploadWidget
  401. *
  402. * @member CKEDITOR.fileTools
  403. * @param {CKEDITOR.dom.element} element Element to be marked.
  404. * @param {String} widgetName The name of the upload widget.
  405. * @param {Number} loaderId The ID of a related {@link CKEDITOR.fileTools.fileLoader}.
  406. */
  407. function markElement( element, widgetName, loaderId ) {
  408. element.setAttributes( {
  409. 'data-cke-upload-id': loaderId,
  410. 'data-widget': widgetName
  411. } );
  412. }
  413. /**
  414. * Binds a notification to the {@link CKEDITOR.fileTools.fileLoader file loader} so the upload widget will use
  415. * the notification to show the status and progress.
  416. * This function uses {@link CKEDITOR.plugins.notificationAggregator}, so even if multiple files are uploading
  417. * only one notification is shown. Warnings are an exception, because they are shown in separate notifications.
  418. * This notification shows only the progress of the upload, so this method should not be used if
  419. * the {@link CKEDITOR.fileTools.fileLoader#load loader.load} method was called. It works with
  420. * {@link CKEDITOR.fileTools.fileLoader#upload upload} and {@link CKEDITOR.fileTools.fileLoader#loadAndUpload loadAndUpload}.
  421. *
  422. * @member CKEDITOR.fileTools
  423. * @param {CKEDITOR.editor} editor The editor instance.
  424. * @param {CKEDITOR.fileTools.fileLoader} loader The file loader instance.
  425. */
  426. function bindNotifications( editor, loader ) {
  427. var aggregator,
  428. task = null;
  429. loader.on( 'update', function() {
  430. // Value of uploadTotal is known after upload start. Task will be created when uploadTotal is present.
  431. if ( !task && loader.uploadTotal ) {
  432. createAggregator();
  433. task = aggregator.createTask( { weight: loader.uploadTotal } );
  434. }
  435. if ( task && loader.status == 'uploading' ) {
  436. task.update( loader.uploaded );
  437. }
  438. } );
  439. loader.on( 'uploaded', function() {
  440. task && task.done();
  441. } );
  442. loader.on( 'error', function() {
  443. task && task.cancel();
  444. editor.showNotification( loader.message, 'warning' );
  445. } );
  446. loader.on( 'abort', function() {
  447. task && task.cancel();
  448. editor.showNotification( editor.lang.uploadwidget.abort, 'info' );
  449. } );
  450. function createAggregator() {
  451. aggregator = editor._.uploadWidgetNotificaionAggregator;
  452. // Create one notification aggregator for all types of upload widgets for the editor.
  453. if ( !aggregator || aggregator.isFinished() ) {
  454. aggregator = editor._.uploadWidgetNotificaionAggregator = new CKEDITOR.plugins.notificationAggregator(
  455. editor, editor.lang.uploadwidget.uploadMany, editor.lang.uploadwidget.uploadOne );
  456. aggregator.once( 'finished', function() {
  457. var tasks = aggregator.getTaskCount();
  458. if ( tasks === 0 ) {
  459. aggregator.notification.hide();
  460. } else {
  461. aggregator.notification.update( {
  462. message: tasks == 1 ?
  463. editor.lang.uploadwidget.doneOne :
  464. editor.lang.uploadwidget.doneMany.replace( '%1', tasks ),
  465. type: 'success',
  466. important: 1
  467. } );
  468. }
  469. } );
  470. }
  471. }
  472. }
  473. // Two plugins extend this object.
  474. if ( !CKEDITOR.fileTools ) {
  475. CKEDITOR.fileTools = {};
  476. }
  477. CKEDITOR.tools.extend( CKEDITOR.fileTools, {
  478. addUploadWidget: addUploadWidget,
  479. markElement: markElement,
  480. bindNotifications: bindNotifications
  481. } );
  482. } )();