utils.js 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * Utilities used in modules from {@link module:list/list list} package.
  7. *
  8. * @module list/utils
  9. */
  10. import Position from '@ckeditor/ckeditor5-engine/src/model/position';
  11. /**
  12. * For given {@link module:engine/model/position~Position position}, returns the closest ancestor of that position which is a
  13. * `listItem` element.
  14. *
  15. * @param {module:engine/model/position~Position} position Position which ancestor should be check looking for `listItem` element.
  16. * @returns {module:engine/model/element~Element|null} Element with `listItem` name that is a closest ancestor of given `position`, or
  17. * `null` if neither of `position` ancestors is a `listItem`.
  18. */
  19. export function getClosestListItem( position ) {
  20. return Array.from( position.getAncestors() ).find( ( parent ) => parent.name == 'listItem' ) || null;
  21. }
  22. /**
  23. * For given {@link module:engine/model/selection~Selection selection} and {@link module:engine/model/schema~Schema schema},
  24. * returns an array with all elements that are in the selection and are extending `$block` schema item.
  25. *
  26. * @param {module:engine/model/selection~Selection} selection Selection from which blocks will be taken.
  27. * @param {module:engine/model/schema~Schema} schema Schema which will be used to check if a model element extends `$block`.
  28. * @returns {Array.<module:engine/model/element~Element>} All blocks from the selection.
  29. */
  30. export function getSelectedBlocks( selection, schema ) {
  31. let position = getPositionBeforeBlock( selection.getFirstPosition(), schema );
  32. const endPosition = selection.getLastPosition();
  33. const blocks = [];
  34. // Traverse model from the first position before a block to the end position of selection.
  35. // Store all elements that were after the correct positions.
  36. while ( position !== null && position.isBefore( endPosition ) ) {
  37. blocks.push( position.nodeAfter );
  38. position.offset++;
  39. position = getPositionBeforeBlock( position, schema );
  40. }
  41. return blocks;
  42. }
  43. /**
  44. * For given {@link module:engine/model/position~Position position}, finds a model element extending `$block` schema item which is
  45. * closest element to that position. First node after the position is checked and then the position's ancestors. `null`
  46. * is returned if such element has not been found or found element is a root element.
  47. *
  48. * @param position
  49. * @param schema
  50. * @returns {*}
  51. */
  52. export function getPositionBeforeBlock( position, schema ) {
  53. // Start from the element right after the position. Maybe it is already a `$block` element.
  54. let element = position.nodeAfter;
  55. // If the position is not before an element, check the parent.
  56. if ( !element ) {
  57. element = position.parent;
  58. }
  59. // If proper element is still not found, check the ancestors.
  60. while ( element !== null && !schema.itemExtends( element.name || '$text', '$block' ) ) {
  61. element = element.parent;
  62. }
  63. // If proper element has been found, return position before it, otherwise return null;
  64. return element !== null && element.parent !== null ? Position.createBefore( element ) : null;
  65. }