linkcommand.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  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 link/linkcommand
  7. */
  8. import Command from '@ckeditor/ckeditor5-core/src/command';
  9. import findLinkRange from './findlinkrange';
  10. import toMap from '@ckeditor/ckeditor5-utils/src/tomap';
  11. /**
  12. * The link command. It is used by the {@link module:link/link~Link link feature}.
  13. *
  14. * @extends module:core/command~Command
  15. */
  16. export default class LinkCommand extends Command {
  17. /**
  18. * The value of the `'linkHref'` attribute if the start of the selection is located in a node with this attribute.
  19. *
  20. * @observable
  21. * @readonly
  22. * @member {Object|undefined} #value
  23. */
  24. /**
  25. * @inheritDoc
  26. */
  27. refresh() {
  28. const model = this.editor.model;
  29. const doc = model.document;
  30. this.value = doc.selection.getAttribute( 'linkHref' );
  31. this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, 'linkHref' );
  32. }
  33. /**
  34. * Executes the command.
  35. *
  36. * When the selection is non-collapsed, the `linkHref` attribute will be applied to nodes inside the selection, but only to
  37. * those nodes where the `linkHref` attribute is allowed (disallowed nodes will be omitted).
  38. *
  39. * When the selection is collapsed and is not inside the text with the `linkHref` attribute, the
  40. * new {@link module:engine/model/text~Text Text node} with the `linkHref` attribute will be inserted in place of caret, but
  41. * only if such element is allowed in this place. The `_data` of the inserted text will equal the `href` parameter.
  42. * The selection will be updated to wrap the just inserted text node.
  43. *
  44. * When the selection is collapsed and inside the text with the `linkHref` attribute, the attribute value will be updated.
  45. *
  46. * @fires execute
  47. * @param {String} href Link destination.
  48. */
  49. execute( href ) {
  50. const model = this.editor.model;
  51. const selection = model.document.selection;
  52. model.change( writer => {
  53. // If selection is collapsed then update selected link or insert new one at the place of caret.
  54. if ( selection.isCollapsed ) {
  55. const position = selection.getFirstPosition();
  56. // When selection is inside text with `linkHref` attribute.
  57. if ( selection.hasAttribute( 'linkHref' ) ) {
  58. // Then update `linkHref` value.
  59. const linkRange = findLinkRange( selection.getFirstPosition(), selection.getAttribute( 'linkHref' ), model );
  60. writer.setAttribute( 'linkHref', href, linkRange );
  61. // Create new range wrapping changed link.
  62. writer.setSelection( linkRange );
  63. }
  64. // If not then insert text node with `linkHref` attribute in place of caret.
  65. // However, since selection in collapsed, attribute value will be used as data for text node.
  66. // So, if `href` is empty, do not create text node.
  67. else if ( href !== '' ) {
  68. const attributes = toMap( selection.getAttributes() );
  69. attributes.set( 'linkHref', href );
  70. const node = writer.createText( href, attributes );
  71. writer.insert( node, position );
  72. // Create new range wrapping created node.
  73. writer.setSelection( writer.createRangeOn( node ) );
  74. }
  75. } else {
  76. // If selection has non-collapsed ranges, we change attribute on nodes inside those ranges
  77. // omitting nodes where `linkHref` attribute is disallowed.
  78. const ranges = model.schema.getValidRanges( selection.getRanges(), 'linkHref' );
  79. for ( const range of ranges ) {
  80. writer.setAttribute( 'linkHref', href, range );
  81. }
  82. }
  83. } );
  84. }
  85. }