utils.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module image/image/utils
  7. */
  8. import { findOptimalInsertionPosition, isWidget, toWidget } from '@ckeditor/ckeditor5-widget/src/utils';
  9. const imageSymbol = Symbol( 'isImage' );
  10. /**
  11. * Converts a given {@link module:engine/view/element~Element} to an image widget:
  12. * * Adds a {@link module:engine/view/element~Element#_setCustomProperty custom property} allowing to recognize the image widget element.
  13. * * Calls the {@link module:widget/utils~toWidget} function with the proper element's label creator.
  14. *
  15. * @param {module:engine/view/element~Element} viewElement
  16. * @param {module:engine/view/downcastwriter~DowncastWriter} writer An instance of the view writer.
  17. * @param {String} label The element's label. It will be concatenated with the image `alt` attribute if one is present.
  18. * @returns {module:engine/view/element~Element}
  19. */
  20. export function toImageWidget( viewElement, writer, label ) {
  21. writer.setCustomProperty( imageSymbol, true, viewElement );
  22. return toWidget( viewElement, writer, { label: labelCreator } );
  23. function labelCreator() {
  24. const imgElement = viewElement.getChild( 0 );
  25. const altText = imgElement.getAttribute( 'alt' );
  26. return altText ? `${ altText } ${ label }` : label;
  27. }
  28. }
  29. /**
  30. * Checks if a given view element is an image widget.
  31. *
  32. * @param {module:engine/view/element~Element} viewElement
  33. * @returns {Boolean}
  34. */
  35. export function isImageWidget( viewElement ) {
  36. return !!viewElement.getCustomProperty( imageSymbol ) && isWidget( viewElement );
  37. }
  38. /**
  39. * Checks if an image widget is the only selected element.
  40. *
  41. * @param {module:engine/view/selection~Selection|module:engine/view/documentselection~DocumentSelection} selection
  42. * @returns {Boolean}
  43. */
  44. export function isImageWidgetSelected( selection ) {
  45. const viewElement = selection.getSelectedElement();
  46. return !!( viewElement && isImageWidget( viewElement ) );
  47. }
  48. /**
  49. * Checks if the provided model element is an `image`.
  50. *
  51. * @param {module:engine/model/element~Element} modelElement
  52. * @returns {Boolean}
  53. */
  54. export function isImage( modelElement ) {
  55. return !!modelElement && modelElement.is( 'image' );
  56. }
  57. /**
  58. * Handles inserting single file. This method unifies image insertion using {@link module:widget/utils~findOptimalInsertionPosition} method.
  59. *
  60. * model.change( writer => {
  61. * insertImage( writer, model, { src: 'path/to/image.jpg' } );
  62. * } );
  63. *
  64. * @param {module:engine/model/writer~Writer} writer
  65. * @param {module:engine/model/model~Model} model
  66. * @param {Object} [attributes={}] Attributes of inserted image
  67. */
  68. export function insertImage( writer, model, attributes = {} ) {
  69. const imageElement = writer.createElement( 'image', attributes );
  70. const insertAtSelection = findOptimalInsertionPosition( model.document.selection, model );
  71. model.insertContent( imageElement, insertAtSelection );
  72. // Inserting an image might've failed due to schema regulations.
  73. if ( imageElement.parent ) {
  74. writer.setSelection( imageElement, 'on' );
  75. }
  76. }
  77. /**
  78. * Checks if image can be inserted at current model selection.
  79. *
  80. * @param {module:engine/model/model~Model} model
  81. * @returns {Boolean}
  82. */
  83. export function isImageAllowed( model ) {
  84. const schema = model.schema;
  85. const selection = model.document.selection;
  86. return isImageAllowedInParent( selection, schema, model ) &&
  87. !checkSelectionOnObject( selection, schema ) &&
  88. isInOtherImage( selection );
  89. }
  90. // Checks if image is allowed by schema in optimal insertion parent.
  91. //
  92. // @returns {Boolean}
  93. function isImageAllowedInParent( selection, schema, model ) {
  94. const parent = getInsertImageParent( selection, model );
  95. return schema.checkChild( parent, 'image' );
  96. }
  97. // Check if selection is on object.
  98. //
  99. // @returns {Boolean}
  100. function checkSelectionOnObject( selection, schema ) {
  101. const selectedElement = selection.getSelectedElement();
  102. return selectedElement && schema.isObject( selectedElement );
  103. }
  104. // Checks if selection is placed in other image (ie. in caption).
  105. function isInOtherImage( selection ) {
  106. return [ ...selection.focus.getAncestors() ].every( ancestor => !ancestor.is( 'image' ) );
  107. }
  108. // Returns a node that will be used to insert image with `model.insertContent` to check if image can be placed there.
  109. function getInsertImageParent( selection, model ) {
  110. const insertAt = findOptimalInsertionPosition( selection, model );
  111. const parent = insertAt.parent;
  112. if ( parent.isEmpty && !parent.is( '$root' ) ) {
  113. return parent.parent;
  114. }
  115. return parent;
  116. }