imageuploadengine.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module upload/imageuploadengine
  7. */
  8. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  9. import FileRepository from './filerepository';
  10. import ImageUploadCommand from './imageuploadcommand';
  11. import Notification from '@ckeditor/ckeditor5-ui/src/notification/notification';
  12. import { isImageType } from './utils';
  13. /**
  14. * Image upload engine plugin.
  15. *
  16. * @extends module:core/plugin~Plugin
  17. */
  18. export default class ImageUploadEngine extends Plugin {
  19. /**
  20. * @inheritDoc
  21. */
  22. static get requires() {
  23. return [ FileRepository, Notification ];
  24. }
  25. /**
  26. * @inheritDoc
  27. */
  28. init() {
  29. const editor = this.editor;
  30. const doc = editor.document;
  31. const schema = doc.schema;
  32. // Setup schema to allow uploadId for images.
  33. schema.allow( { name: 'image', attributes: [ 'uploadId' ], inside: '$root' } );
  34. schema.allow( { name: 'image', attributes: [ 'uploadStatus' ], inside: '$root' } );
  35. schema.requireAttributes( 'image', [ 'uploadId' ] );
  36. // Register imageUpload command.
  37. editor.commands.set( 'imageUpload', new ImageUploadCommand( editor ) );
  38. // Execute imageUpload command when image is dropped or pasted.
  39. editor.editing.view.on( 'clipboardInput', ( evt, data ) => {
  40. for ( const file of data.dataTransfer.files ) {
  41. if ( isImageType( file ) ) {
  42. editor.execute( 'imageUpload', { file } );
  43. evt.stop();
  44. }
  45. }
  46. } );
  47. // Listen on document changes and start upload process when image with `uploadId` attribute is present.
  48. doc.on( 'change', ( evt, type, data, batch ) => {
  49. if ( type === 'insert' ) {
  50. for ( const value of data.range ) {
  51. if ( value.type === 'elementStart' && value.item.name === 'image' ) {
  52. const imageElement = value.item;
  53. const uploadId = imageElement.getAttribute( 'uploadId' );
  54. const fileRepository = editor.plugins.get( FileRepository );
  55. if ( uploadId ) {
  56. const loader = fileRepository.loaders.get( uploadId );
  57. if ( loader && loader.status == 'idle' ) {
  58. this.load( loader, batch, imageElement );
  59. }
  60. }
  61. }
  62. }
  63. }
  64. } );
  65. }
  66. /**
  67. * Performs image loading. Image is read from the disk and temporary data is displayed, after uploading process
  68. * is complete we replace temporary data with target image from the server.
  69. *
  70. * @protected
  71. * @param {module:upload/filerepository~FileLoader} loader
  72. * @param {module:engine/model/batch~Batch} batch
  73. * @param {module:engine/model/element~Element} imageElement
  74. */
  75. load( loader, batch, imageElement ) {
  76. const editor = this.editor;
  77. const doc = editor.document;
  78. const fileRepository = editor.plugins.get( FileRepository );
  79. const notification = editor.plugins.get( Notification );
  80. doc.enqueueChanges( () => {
  81. batch.setAttribute( imageElement, 'uploadStatus', 'reading' );
  82. } );
  83. loader.read()
  84. .then( data => {
  85. const viewFigure = editor.editing.mapper.toViewElement( imageElement );
  86. const viewImg = viewFigure.getChild( 0 );
  87. const promise = loader.upload();
  88. viewImg.setAttribute( 'src', data );
  89. editor.editing.view.render();
  90. doc.enqueueChanges( () => {
  91. batch.setAttribute( imageElement, 'uploadStatus', 'uploading' );
  92. } );
  93. return promise;
  94. } )
  95. .then( data => {
  96. doc.enqueueChanges( () => {
  97. batch.setAttribute( imageElement, 'uploadStatus', 'complete' );
  98. batch.setAttribute( imageElement, 'src', data.original );
  99. } );
  100. clean();
  101. } )
  102. .catch( msg => {
  103. // Might be 'aborted'.
  104. if ( loader.status == 'error' ) {
  105. notification.showWarning( msg, { namespace: 'upload' } );
  106. }
  107. clean();
  108. } );
  109. function clean() {
  110. doc.enqueueChanges( () => {
  111. batch.removeAttribute( imageElement, 'uploadId' );
  112. batch.removeAttribute( imageElement, 'uploadStatus' );
  113. } );
  114. fileRepository.destroyLoader( loader );
  115. }
  116. }
  117. }