8
0

converters.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module image/converters
  7. */
  8. import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element';
  9. import ModelPosition from '@ckeditor/ckeditor5-engine/src/model/position';
  10. import modelWriter from '@ckeditor/ckeditor5-engine/src/model/writer';
  11. import { isImageWidget } from './utils';
  12. /**
  13. * Returns function that converts image view representation:
  14. *
  15. * <figure class="image"><img src="..." alt="..."></img></figure>
  16. *
  17. * to model representation:
  18. *
  19. * <image src="..." alt="..."></image>
  20. *
  21. * @returns {Function}
  22. */
  23. export function viewToModelImage() {
  24. return ( evt, data, consumable, conversionApi ) => {
  25. const viewFigureElement = data.input;
  26. // *** Step 1: Validate conversion.
  27. // Check if figure element can be consumed.
  28. if ( !consumable.test( viewFigureElement, { name: true, class: 'image' } ) ) {
  29. return;
  30. }
  31. // Check if image element can be converted in current context.
  32. if ( !conversionApi.schema.check( { name: 'image', inside: data.context, attributes: 'src' } ) ) {
  33. return;
  34. }
  35. // Check if img element is placed inside figure element and can be consumed with `src` attribute.
  36. const viewImg = viewFigureElement.getChild( 0 );
  37. if ( !viewImg || viewImg.name != 'img' || !consumable.test( viewImg, { name: true, attribute: 'src' } ) ) {
  38. return;
  39. }
  40. // *** Step2: Convert to model.
  41. consumable.consume( viewFigureElement, { name: true, class: 'image' } );
  42. consumable.consume( viewImg, { name: true, attribute: 'src' } );
  43. // Create model element.
  44. const modelImage = new ModelElement( 'image', {
  45. src: viewImg.getAttribute( 'src' )
  46. } );
  47. // Convert `alt` attribute if present.
  48. if ( consumable.consume( viewImg, { attribute: [ 'alt' ] } ) ) {
  49. modelImage.setAttribute( 'alt', viewImg.getAttribute( 'alt' ) );
  50. }
  51. // Convert children of converted view element and append them to `modelImage`.
  52. data.context.push( modelImage );
  53. const modelChildren = conversionApi.convertChildren( viewFigureElement, consumable, data );
  54. const insertPosition = ModelPosition.createAt( modelImage, 'end' );
  55. modelWriter.insert( insertPosition, modelChildren );
  56. data.context.pop();
  57. data.output = modelImage;
  58. };
  59. }
  60. /**
  61. * Returns model to view selection converter. This converter is applied after default selection conversion is made.
  62. * It creates fake view selection when {@link module:engine/view/selection~Selection#getSelectedElement} returns instance
  63. * of image widget.
  64. *
  65. * @param {Function} t {@link module:utils/locale~Locale#t Locale#t function} used to translate default fake selection's label.
  66. * @returns {Function}
  67. */
  68. export function modelToViewSelection( t ) {
  69. return ( evt, data, consumable, conversionApi ) => {
  70. const viewSelection = conversionApi.viewSelection;
  71. const selectedElement = viewSelection.getSelectedElement();
  72. if ( !selectedElement || !isImageWidget( selectedElement ) ) {
  73. return;
  74. }
  75. let fakeSelectionLabel = t( 'image widget' );
  76. const imgElement = selectedElement.getChild( 0 );
  77. const altText = imgElement.getAttribute( 'alt' );
  78. if ( altText ) {
  79. fakeSelectionLabel = `${ altText } ${ fakeSelectionLabel }`;
  80. }
  81. viewSelection.setFake( true, { label: fakeSelectionLabel } );
  82. };
  83. }
  84. /**
  85. * Creates image attribute converter for provided model conversion dispatchers.
  86. *
  87. * @param {Array.<module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher>} dispatchers
  88. * @param {String} attributeName
  89. */
  90. export function createImageAttributeConverter( dispatchers, attributeName ) {
  91. for ( let dispatcher of dispatchers ) {
  92. dispatcher.on( `addAttribute:${ attributeName }:image`, modelToViewAttributeConverter );
  93. dispatcher.on( `changeAttribute:${ attributeName }:image`, modelToViewAttributeConverter );
  94. dispatcher.on( `removeAttribute:${ attributeName }:image`, modelToViewAttributeConverter );
  95. }
  96. }
  97. // Model to view image converter converting given attribute, and adding it to `img` element nested inside `figure` element.
  98. //
  99. // @private
  100. function modelToViewAttributeConverter( evt, data, consumable, conversionApi ) {
  101. const parts = evt.name.split( ':' );
  102. const consumableType = parts[ 0 ] + ':' + parts[ 1 ];
  103. if ( !consumable.consume( data.item, consumableType ) ) {
  104. return;
  105. }
  106. const figure = conversionApi.mapper.toViewElement( data.item );
  107. const img = figure.getChild( 0 );
  108. if ( parts[ 0 ] == 'removeAttribute' ) {
  109. img.removeAttribute( data.attributeKey );
  110. } else {
  111. img.setAttribute( data.attributeKey, data.attributeNewValue );
  112. }
  113. }