restrictededitingnavigationcommand.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /**
  2. * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /**
  6. * @module restricted-editing/restrictededitingnavigationcommand
  7. */
  8. import Command from '@ckeditor/ckeditor5-core/src/command';
  9. /**
  10. * The command that allows navigation across the exceptions in the edited document.
  11. *
  12. * @extends module:core/command~Command
  13. */
  14. export default class RestrictedEditingNavigationCommand extends Command {
  15. /**
  16. * Creates an instance of the command.
  17. *
  18. * @param {module:core/editor/editor~Editor} editor Editor instance.
  19. * @param {String} direction Direction the command works. Can be either `'forward'` or `'backward'`.
  20. */
  21. constructor( editor, direction ) {
  22. super( editor );
  23. /**
  24. * A direction of the command. Can be `'forward'` or `'backward'`.
  25. *
  26. * @readonly
  27. * @private
  28. * @member {String}
  29. */
  30. this._direction = direction;
  31. }
  32. /**
  33. * @inheritDoc
  34. */
  35. refresh() {
  36. this.isEnabled = this._checkEnabled();
  37. }
  38. /**
  39. * Executes the command.
  40. *
  41. * @fires execute
  42. */
  43. execute() {
  44. const position = getNearestExceptionPosition( this.editor.model, this._direction );
  45. this.editor.model.change( writer => {
  46. writer.setSelection( position );
  47. } );
  48. }
  49. /**
  50. * Checks whether the command can be enabled in the current context.
  51. *
  52. * @private
  53. * @returns {Boolean} Whether the command should be enabled.
  54. */
  55. _checkEnabled() {
  56. return !!getNearestExceptionPosition( this.editor.model, this._direction );
  57. }
  58. }
  59. // Returns the start position of the exception marker closest to the last position of the
  60. // model selection.
  61. //
  62. // @param {module:engine/model/model~Model} model
  63. // @param {String} direction Either "forward" or "backward".
  64. // @returns {module:engine/model/position~Position|null}
  65. function getNearestExceptionPosition( model, direction ) {
  66. const selection = model.document.selection;
  67. const selectionPosition = selection.getFirstPosition();
  68. const markerStartPositions = [];
  69. // Get all exception marker positions that start after/before the selection position.
  70. for ( const marker of model.markers.getMarkersGroup( 'restricted-editing-exception' ) ) {
  71. const markerRange = marker.getRange();
  72. const markerRangeStart = markerRange.start;
  73. const isMarkerRangeTouching = selectionPosition.isTouching( markerRange.start ) || selectionPosition.isTouching( markerRange.end );
  74. // <paragraph>foo <marker≥b[]ar</marker> baz</paragraph>
  75. // <paragraph>foo <marker≥b[ar</marker> ba]z</paragraph>
  76. // <paragraph>foo <marker≥bar</marker>[] baz</paragraph>
  77. // <paragraph>foo []<marker≥bar</marker> baz</paragraph>
  78. if ( markerRange.containsPosition( selectionPosition ) || isMarkerRangeTouching ) {
  79. continue;
  80. }
  81. if ( direction === 'forward' && markerRangeStart.isAfter( selectionPosition ) ) {
  82. markerStartPositions.push( markerRangeStart );
  83. } else if ( direction === 'backward' && markerRangeStart.isBefore( selectionPosition ) ) {
  84. markerStartPositions.push( markerRangeStart );
  85. }
  86. }
  87. if ( !markerStartPositions.length ) {
  88. return null;
  89. }
  90. // Get the marker closest to the selection position among many. To know that, we need to sort
  91. // them first.
  92. return markerStartPositions.sort( ( posA, posB ) => {
  93. if ( direction === 'forward' ) {
  94. return posA.isAfter( posB ) ? 1 : -1;
  95. } else {
  96. return posA.isBefore( posB ) ? 1 : -1;
  97. }
  98. } ).shift();
  99. }