8
0

imageuploadprogress.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module image/imageupload/imageuploadprogress
  7. */
  8. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  9. import FileRepository from '@ckeditor/ckeditor5-upload/src/filerepository';
  10. import uploadingPlaceholder from '../../theme/icons/image_placeholder.svg';
  11. import UIElement from '@ckeditor/ckeditor5-engine/src/view/uielement';
  12. import '../../theme/imageuploadprogress.css';
  13. /**
  14. * Image upload progress plugin.
  15. * Shows placeholder when image is read from disk and progress bar while image is uploading.
  16. *
  17. * @extends module:core/plugin~Plugin
  18. */
  19. export default class ImageUploadProgress extends Plugin {
  20. /**
  21. * @inheritDoc
  22. */
  23. constructor( editor ) {
  24. super( editor );
  25. /**
  26. * Image's placeholder that is displayed before real image data can be accessed.
  27. *
  28. * @protected
  29. * @member {String} #placeholder
  30. */
  31. this.placeholder = 'data:image/svg+xml;utf8,' + encodeURIComponent( uploadingPlaceholder );
  32. }
  33. /**
  34. * @inheritDoc
  35. */
  36. init() {
  37. const editor = this.editor;
  38. // Upload status change - update image's view according to that status.
  39. editor.editing.downcastDispatcher.on( 'attribute:uploadStatus:image', ( ...args ) => this.uploadStatusChange( ...args ) );
  40. }
  41. /**
  42. * This ethod is called each time image's `uploadStatus` attribute is changed.
  43. *
  44. * @param {module:utils/eventinfo~EventInfo} evt Object containing information about the fired event.
  45. * @param {Object} data Additional information about the change.
  46. * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable Values to consume.
  47. */
  48. uploadStatusChange( evt, data, consumable ) {
  49. const editor = this.editor;
  50. const modelImage = data.item;
  51. const uploadId = modelImage.getAttribute( 'uploadId' );
  52. if ( !consumable.consume( data.item, evt.name ) || !uploadId ) {
  53. return;
  54. }
  55. const fileRepository = editor.plugins.get( FileRepository );
  56. const placeholder = this.placeholder;
  57. const status = data.attributeNewValue;
  58. const viewFigure = editor.editing.mapper.toViewElement( modelImage );
  59. // Show placeholder with infinite progress bar on the top while image is read from disk.
  60. if ( status == 'reading' ) {
  61. viewFigure.addClass( 'ck-appear', 'ck-infinite-progress', 'ck-image-upload-placeholder' );
  62. const viewImg = viewFigure.getChild( 0 );
  63. viewImg.setAttribute( 'src', placeholder );
  64. return;
  65. }
  66. // Show progress bar on the top of the image when image is uploading.
  67. if ( status == 'uploading' ) {
  68. const loader = fileRepository.loaders.get( uploadId );
  69. if ( loader ) {
  70. const progressBar = createProgressBar();
  71. viewFigure.removeClass( 'ck-infinite-progress', 'ck-image-upload-placeholder' );
  72. viewFigure.appendChildren( progressBar );
  73. // Update progress bar width when uploadedPercent is changed.
  74. loader.on( 'change:uploadedPercent', ( evt, name, value ) => {
  75. progressBar.setStyle( 'width', value + '%' );
  76. editor.editing.view.render();
  77. } );
  78. }
  79. return;
  80. }
  81. // Hide progress bar and clean up classes.
  82. const progressBar = getProgressBar( viewFigure );
  83. if ( progressBar ) {
  84. progressBar.remove();
  85. } else {
  86. viewFigure.removeClass( 'ck-infinite-progress' );
  87. }
  88. viewFigure.removeClass( 'ck-appear', 'ck-image-upload-placeholder' );
  89. }
  90. }
  91. // Symbol added to progress bar UIElement to distinguish it from other elements.
  92. const progressBarSymbol = Symbol( 'progress-bar' );
  93. // Create progress bar element using {@link module:engine/view/uielement~UIElement}.
  94. //
  95. // @private
  96. // @returns {module:engine/view/uielement~UIElement}
  97. function createProgressBar() {
  98. const progressBar = new UIElement( 'div', { class: 'ck-progress-bar' } );
  99. progressBar.setCustomProperty( progressBarSymbol, true );
  100. return progressBar;
  101. }
  102. // Returns progress bar {@link module:engine/view/uielement~UIElement} from image figure element. Returns `undefined` if
  103. // progress bar element is not found.
  104. //
  105. // @private
  106. // @param {module:engine/view/element~Element} imageFigure
  107. // @returns {module:engine/view/uielement~UIElement|undefined}
  108. function getProgressBar( imageFigure ) {
  109. for ( const child of imageFigure.getChildren() ) {
  110. if ( child.getCustomProperty( progressBarSymbol ) ) {
  111. return child;
  112. }
  113. }
  114. }