imageuploadcommand.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import FileRepository from '@ckeditor/ckeditor5-upload/src/filerepository';
  6. import Command from '@ckeditor/ckeditor5-core/src/command';
  7. import { findOptimalInsertionPosition } from '@ckeditor/ckeditor5-widget/src/utils';
  8. /**
  9. * @module image/imageupload/imageuploadcommand
  10. */
  11. /**
  12. * Image upload command.
  13. *
  14. * @extends module:core/command~Command
  15. */
  16. export default class ImageUploadCommand extends Command {
  17. /**
  18. * @inheritDoc
  19. */
  20. refresh() {
  21. const model = this.editor.model;
  22. const selection = model.document.selection;
  23. const schema = model.schema;
  24. this.isEnabled = isImageAllowedInParent( selection, schema ) && checkSelectionWithImage( selection );
  25. }
  26. /**
  27. * Executes the command.
  28. *
  29. * @fires execute
  30. * @param {Object} options Options for the executed command.
  31. * @param {File|Array.<File>} options.files The image file or an array of image files to upload.
  32. */
  33. execute( options ) {
  34. const editor = this.editor;
  35. editor.model.change( writer => {
  36. const filesToUpload = Array.isArray( options.files ) ? options.files : [ options.files ];
  37. for ( const file of filesToUpload ) {
  38. uploadImage( writer, editor, file );
  39. }
  40. } );
  41. }
  42. }
  43. // Handles uploading single file.
  44. //
  45. // @param {module:engine/model/writer~writer} writer
  46. // @param {module:core/editor/editor~Editor} editor
  47. // @param {File} file
  48. function uploadImage( writer, editor, file ) {
  49. const doc = editor.model.document;
  50. const fileRepository = editor.plugins.get( FileRepository );
  51. const loader = fileRepository.createLoader( file );
  52. // Do not throw when upload adapter is not set. FileRepository will log an error anyway.
  53. if ( !loader ) {
  54. return;
  55. }
  56. const imageElement = writer.createElement( 'image', { uploadId: loader.id } );
  57. const insertAtSelection = findOptimalInsertionPosition( doc.selection );
  58. editor.model.insertContent( imageElement, insertAtSelection );
  59. // Inserting an image might've failed due to schema regulations.
  60. if ( imageElement.parent ) {
  61. writer.setSelection( imageElement, 'on' );
  62. }
  63. }
  64. // Checks if image is allowed by schema in optimal insertion parent.
  65. function isImageAllowedInParent( selection, schema ) {
  66. const parent = getInsertImageParent( selection );
  67. return schema.checkChild( parent, 'image' );
  68. }
  69. // Additional check for when the command should be disabled:
  70. // - selection is on image
  71. // - selection is inside image (image caption)
  72. function checkSelectionWithImage( selection ) {
  73. const selectedElement = selection.getSelectedElement();
  74. const isSelectionOnImage = !!selectedElement && selectedElement.is( 'image' );
  75. const isSelectionInImage = !![ ...selection.focus.parent.getAncestors() ].find( ancestor => ancestor.name == 'image' );
  76. return !isSelectionOnImage && !isSelectionInImage;
  77. }
  78. // Returns a node that will be used to insert image with `model.insertContent` to check if image can be placed there.
  79. function getInsertImageParent( selection ) {
  80. const insertAt = findOptimalInsertionPosition( selection );
  81. let parent = insertAt.parent;
  82. if ( !parent.is( '$root' ) ) {
  83. parent = parent.parent;
  84. }
  85. return parent;
  86. }