8
0

unlinkcommand.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import Command from '../core/command/command.js';
  6. import Range from '../engine/model/range.js';
  7. /**
  8. * The unlink command. It is used by the {@link Link.Link link feature}.
  9. *
  10. * @memberOf link
  11. * @extends core.command.Command
  12. */
  13. export default class LinkCommand extends Command {
  14. /**
  15. * Executes the command.
  16. *
  17. * When selection is collapsed then whole link will be unlinked. {@link engine.model.TreeWalker TreeWalker} is
  18. * looking for link bounds going forward and backward from the caret position.
  19. *
  20. * If selection is non-collapsed then only selected range will be unlinked.
  21. *
  22. * @protected
  23. */
  24. _doExecute() {
  25. const document = this.editor.document;
  26. const selection = document.selection;
  27. const ranges = selection.getRanges();
  28. let rangesToUpdate = [];
  29. document.enqueueChanges( () => {
  30. // When selection is collapsed we have to find link bounds.
  31. if ( selection.isCollapsed ) {
  32. // Loop through each selection.
  33. for ( let range of ranges ) {
  34. // Get searching area.
  35. const parentNodeRange = Range.createIn( range.start.parent );
  36. // Create walker instances for going forward and backward.
  37. const walker = parentNodeRange.getWalker( { startPosition: range.start } );
  38. const backwardWalker = parentNodeRange.getWalker( { startPosition: range.start, direction: 'backward' } );
  39. // Store current link attribute value.
  40. const attributeValue = selection.getAttribute( 'link' );
  41. // Search link bounds and store found range.
  42. rangesToUpdate.push(
  43. new Range( getLinkBound( backwardWalker, attributeValue ), getLinkBound( walker, attributeValue ) )
  44. );
  45. }
  46. } else {
  47. // When selection is non-collapsed then we are unlinking selected ranges.
  48. rangesToUpdate = ranges;
  49. }
  50. // Keep it as one undo step.
  51. const batch = document.batch();
  52. // Remove attribute from each range.
  53. for ( let range of rangesToUpdate ) {
  54. batch.removeAttribute( range, 'link' );
  55. }
  56. // Remove attribute from selection.
  57. selection.removeAttribute( 'link' );
  58. } );
  59. }
  60. }
  61. // Walk trough the range and search for link bound.
  62. //
  63. // @param {engine.model.TreeWalker} walker TreeWalker instance.
  64. // @param {String} linkValue Value of searching link.
  65. // @returns {engine.model.Position} Position of link bound.
  66. function getLinkBound( walker, linkValue ) {
  67. let lastPosition = walker.position;
  68. let current = walker.next();
  69. while ( !current.done ) {
  70. if ( current.value.item.getAttribute( 'link' ) != linkValue ) {
  71. return lastPosition;
  72. }
  73. lastPosition = walker.position;
  74. current = walker.next();
  75. }
  76. return lastPosition;
  77. }