8
0

utils.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module widget/utils
  7. */
  8. import VirtualSelectionStack from './virtualselectionstack';
  9. const widgetSymbol = Symbol( 'isWidget' );
  10. const labelSymbol = Symbol( 'label' );
  11. /**
  12. * CSS class added to each widget element.
  13. *
  14. * @const {String}
  15. */
  16. export const WIDGET_CLASS_NAME = 'ck-widget';
  17. /**
  18. * CSS class added to currently selected widget element.
  19. *
  20. * @const {String}
  21. */
  22. export const WIDGET_SELECTED_CLASS_NAME = 'ck-widget_selected';
  23. /**
  24. * Returns `true` if given {@link module:engine/view/element~Element} is a widget.
  25. *
  26. * @param {module:engine/view/element~Element} element
  27. * @returns {Boolean}
  28. */
  29. export function isWidget( element ) {
  30. return !!element.getCustomProperty( widgetSymbol );
  31. }
  32. /**
  33. * Converts given {@link module:engine/view/element~Element} to widget in following way:
  34. * * sets `contenteditable` attribute to `true`,
  35. * * adds custom `getFillerOffset` method returning `null`,
  36. * * adds `ck-widget` CSS class,
  37. * * adds custom property allowing to recognize widget elements by using {@link ~isWidget},
  38. * * implements `setVirtualSelection` and `removeVirtualSelection` custom properties to handle virtual selection
  39. * on widgets.
  40. *
  41. * @param {module:engine/view/element~Element} element
  42. * @param {Object} [options={}]
  43. * @param {String|Function} [options.label] Element's label provided to {@link ~setLabel} function. It can be passed as
  44. * a plain string or a function returning a string.
  45. * @returns {module:engine/view/element~Element} Returns same element.
  46. */
  47. export function toWidget( element, options = {} ) {
  48. element.setAttribute( 'contenteditable', false );
  49. element.getFillerOffset = getFillerOffset;
  50. element.addClass( WIDGET_CLASS_NAME );
  51. element.setCustomProperty( widgetSymbol, true );
  52. if ( options.label ) {
  53. setLabel( element, options.label );
  54. }
  55. setVirtualSelectionHandling( element, setVirtualSelection, removeVirtualSelection );
  56. return element;
  57. }
  58. /**
  59. * Sets virtual selection handling methods. Uses {@link module:widget/virtualselectionstack~VirtualSelectionStack} to
  60. * properly determine which virtual selection should be used at given time.
  61. *
  62. * @param {module:engine/view/element~Element} element
  63. * @param {Function} add
  64. * @param {Function} remove
  65. */
  66. export function setVirtualSelectionHandling( element, add, remove ) {
  67. const stack = new VirtualSelectionStack();
  68. stack.on( 'change:top', ( evt, data ) => {
  69. if ( data.oldDescriptor ) {
  70. remove( element, data.oldDescriptor );
  71. }
  72. if ( data.newDescriptor ) {
  73. add( element, data.newDescriptor );
  74. }
  75. } );
  76. element.setCustomProperty( 'setVirtualSelection', ( element, descriptor ) => stack.add( descriptor ) );
  77. element.setCustomProperty( 'removeVirtualSelection', ( element, descriptor ) => stack.remove( descriptor ) );
  78. }
  79. /**
  80. * Sets label for given element.
  81. * It can be passed as a plain string or a function returning a string. Function will be called each time label is retrieved by
  82. * {@link ~getLabel}.
  83. *
  84. * @param {module:engine/view/element~Element} element
  85. * @param {String|Function} labelOrCreator
  86. */
  87. export function setLabel( element, labelOrCreator ) {
  88. element.setCustomProperty( labelSymbol, labelOrCreator );
  89. }
  90. /**
  91. * Returns label for provided element.
  92. *
  93. * @param {module:engine/view/element~Element} element
  94. * @return {String}
  95. */
  96. export function getLabel( element ) {
  97. const labelCreator = element.getCustomProperty( labelSymbol );
  98. if ( !labelCreator ) {
  99. return '';
  100. }
  101. return typeof labelCreator == 'function' ? labelCreator() : labelCreator;
  102. }
  103. /**
  104. * Adds functionality to provided {module:engine/view/editableelement~EditableElement} to act as a widget's editable:
  105. * * adds `ck-editable` CSS class,
  106. * * sets `contenteditable` as `true` when {module:engine/view/editableelement~EditableElement#isReadOnly} is `false`
  107. * otherwise set `false`,
  108. * * adds `ck-editable_focused` CSS class when editable is focused and removes it when it's blurred.
  109. *
  110. * @param {module:engine/view/editableelement~EditableElement} editable
  111. * @returns {module:engine/view/editableelement~EditableElement} Returns same element that was provided in `editable` param.
  112. */
  113. export function toWidgetEditable( editable ) {
  114. editable.addClass( 'ck-editable' );
  115. // Set initial contenteditable value.
  116. editable.setAttribute( 'contenteditable', !editable.isReadOnly );
  117. // Bind contenteditable property to element#isReadOnly.
  118. editable.on( 'change:isReadOnly', ( evt, property, is ) => {
  119. editable.setAttribute( 'contenteditable', !is );
  120. } );
  121. editable.on( 'change:isFocused', ( evt, property, is ) => {
  122. if ( is ) {
  123. editable.addClass( 'ck-editable_focused' );
  124. } else {
  125. editable.removeClass( 'ck-editable_focused' );
  126. }
  127. } );
  128. return editable;
  129. }
  130. // Default filler offset function applied to all widget elements.
  131. //
  132. // @returns {null}
  133. function getFillerOffset() {
  134. return null;
  135. }
  136. // Used as default function for setting virtual selection on widgets.
  137. //
  138. // @param {module:engine/view/element~Element} element
  139. // @param {module:engine/conversion/buildmodelconverter~VirtualSelectionDescriptor} descriptor
  140. function setVirtualSelection( element, descriptor ) {
  141. element.addClass( descriptor.class );
  142. }
  143. // Used as default function for removing virtual selection from widgets.
  144. //
  145. // @param {module:engine/view/element~Element} element
  146. // @param {module:engine/conversion/buildmodelconverter~VirtualSelectionDescriptor} descriptor
  147. function removeVirtualSelection( element, descriptor ) {
  148. element.removeClass( descriptor.class );
  149. }