8
0

headingcommand.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module heading/headingcommand
  7. */
  8. import Command from '@ckeditor/ckeditor5-core/src/command';
  9. import first from '@ckeditor/ckeditor5-utils/src/first';
  10. /**
  11. * The heading command. It is used by the {@link module:heading/heading~Heading heading feature} to apply headings.
  12. *
  13. * @extends module:core/command~Command
  14. */
  15. export default class HeadingCommand extends Command {
  16. /**
  17. * Creates an instance of the command.
  18. *
  19. * @param {module:core/editor/editor~Editor} editor Editor instance.
  20. * @param {Array.<String>} modelElements Names of the element which this command can apply in the model.
  21. */
  22. constructor( editor, modelElements ) {
  23. super( editor );
  24. /**
  25. * If the selection starts in a one of headings of {@link #modelElements} the value gets name of heading model element.
  26. * Is set to false otherwise.
  27. *
  28. * @observable
  29. * @readonly
  30. * @member {Boolean|String} #value
  31. */
  32. /**
  33. * Set of defined model's elements names that this command recognise.
  34. * See {@link module:heading/heading~HeadingOption}.
  35. *
  36. * @readonly
  37. * @member {Array.<String>}
  38. */
  39. this.modelElements = modelElements;
  40. }
  41. /**
  42. * @inheritDoc
  43. */
  44. refresh() {
  45. const block = first( this.editor.model.document.selection.getSelectedBlocks() );
  46. /**
  47. * The name of a heading.
  48. *
  49. * @observable
  50. * @readonly
  51. * @member {Boolean|String} #value
  52. */
  53. this.value = !!block && this.modelElements.includes( block.name ) && block.name;
  54. this.isEnabled = !!block && this.modelElements.some( heading => checkCanBecomeHeading( block, heading, this.editor.model.schema ) );
  55. }
  56. /**
  57. * Executes the command. Applies the heading to the selected blocks or, if the first selected
  58. * block is a heading already, turns selected headings (of this level only) to paragraphs.
  59. *
  60. * @param {Object} options
  61. * @param {String} options.value Name of the element which this command will apply in the model.
  62. * @fires execute
  63. */
  64. execute( options = {} ) {
  65. const model = this.editor.model;
  66. const document = model.document;
  67. const modelElement = options.value;
  68. if ( !this.modelElements.includes( modelElement ) ) {
  69. return;
  70. }
  71. model.change( writer => {
  72. const blocks = Array.from( document.selection.getSelectedBlocks() )
  73. .filter( block => {
  74. return checkCanBecomeHeading( block, modelElement, model.schema );
  75. } );
  76. for ( const block of blocks ) {
  77. if ( !block.is( modelElement ) ) {
  78. writer.rename( block, modelElement );
  79. }
  80. }
  81. } );
  82. }
  83. }
  84. // Checks whether the given block can be replaced by a specific heading.
  85. //
  86. // @private
  87. // @param {module:engine/model/element~Element} block A block to be tested.
  88. // @param {module:heading/headingcommand~HeadingCommand#modelElement} heading Command element name in the model.
  89. // @param {module:engine/model/schema~Schema} schema The schema of the document.
  90. // @returns {Boolean}
  91. function checkCanBecomeHeading( block, heading, schema ) {
  92. return schema.checkChild( block.parent, heading ) && !schema.isObject( block );
  93. }