modifyselection.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. import Position from '../position.js';
  7. import TreeWalker from '../treewalker.js';
  8. import Range from '../range.js';
  9. /**
  10. * Modifies the selection. Currently the supported modifications are:
  11. *
  12. * * Extending. The selection focus is moved in the specified `options.direction` with a step specified in `options.unit`
  13. * (defaults to `'CHARACTER'`, other values are not not yet supported).
  14. * Note: if you extend a forward selection in a backward direction you will in fact shrink it.
  15. *
  16. * @method engine.treeModel.composer.modifySelection
  17. * @param {engine.treeModel.Selection} The selection to modify.
  18. * @param {Object} [options]
  19. * @param {'FORWARD'|'BACKWARD'} [options.direction='FORWARD'] The direction in which the selection should be modified.
  20. */
  21. export default function modifySelection( selection, options = {} ) {
  22. const isForward = options.direction != 'BACKWARD';
  23. const focus = selection.focus;
  24. const walker = new TreeWalker( {
  25. boundaries: getSearchRange( focus, isForward ),
  26. singleCharacters: true,
  27. direction: isForward ? 'FORWARD' : 'BACKWARD'
  28. } );
  29. let next = walker.next();
  30. // 1. Nothing to do here.
  31. if ( next.done ) {
  32. return;
  33. }
  34. let value = next.value;
  35. // 2. Consume next character.
  36. if ( value.type == 'CHARACTER' ) {
  37. selection.setFocus( value.nextPosition );
  38. return;
  39. }
  40. // 3. We're entering an element, so let's consume it fully.
  41. if ( value.type == ( isForward ? 'ELEMENT_START' : 'ELEMENT_END' ) ) {
  42. selection.setFocus( value.item, isForward ? 'AFTER' : 'BEFORE' );
  43. return;
  44. }
  45. // 4. We're leaving an element. That's more tricky.
  46. next = walker.next();
  47. // 4.1. Nothing left, so let's stay where we were.
  48. if ( next.done ) {
  49. return;
  50. }
  51. // Replace TreeWalker step wrapper by clean step value.
  52. value = next.value;
  53. // 4.2. Character found after element end. Not really a valid case in our data model, but let's
  54. // do something sensible and put the selection focus before that character.
  55. if ( value.type == 'CHARACTER' ) {
  56. selection.setFocus( value.previousPosition );
  57. }
  58. // 4.3. OK, we're entering a new element. So let's place there the focus.
  59. else {
  60. selection.setFocus( value.item, isForward ? 0 : 'END' );
  61. }
  62. }
  63. function getSearchRange( start, isForward ) {
  64. const root = start.root;
  65. const searchEnd = Position.createAt( root, isForward ? 'END' : 0 );
  66. if ( isForward ) {
  67. return new Range( start, searchEnd );
  68. } else {
  69. return new Range( searchEnd, start );
  70. }
  71. }