headingcommand.js 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. /**
  2. * @license Copyright (c) 2003-2017, 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 Position from '@ckeditor/ckeditor5-engine/src/model/position';
  10. import first from '@ckeditor/ckeditor5-utils/src/first';
  11. /**
  12. * The heading command. It is used by the {@link module:heading/heading~Heading heading feature} to apply headings.
  13. *
  14. * @extends module:core/command~Command
  15. */
  16. export default class HeadingCommand extends Command {
  17. /**
  18. * Creates an instance of the command.
  19. *
  20. * @param {module:core/editor/editor~Editor} editor Editor instance.
  21. * @param {String} modelElement Name of the element which this command will apply in the model.
  22. */
  23. constructor( editor, modelElement ) {
  24. super( editor );
  25. /**
  26. * Whether the selection starts in a heading of {@link #modelElement this level}.
  27. *
  28. * @observable
  29. * @readonly
  30. * @member {Boolean} #value
  31. */
  32. /**
  33. * Unique identifier of the command, also element's name in the model.
  34. * See {@link module:heading/heading~HeadingOption}.
  35. *
  36. * @readonly
  37. * @member {String}
  38. */
  39. this.modelElement = modelElement;
  40. }
  41. /**
  42. * @inheritDoc
  43. */
  44. refresh() {
  45. const block = first( this.editor.model.document.selection.getSelectedBlocks() );
  46. this.value = !!block && block.is( this.modelElement );
  47. this.isEnabled = !!block && checkCanBecomeHeading( block, this.modelElement, this.editor.model.schema );
  48. }
  49. /**
  50. * Executes the command. Applies the heading to the selected blocks or, if the first selected
  51. * block is a heading already, turns selected headings (of this level only) to paragraphs.
  52. *
  53. * @fires execute
  54. */
  55. execute() {
  56. const model = this.editor.model;
  57. const document = model.document;
  58. model.change( writer => {
  59. const blocks = Array.from( document.selection.getSelectedBlocks() )
  60. .filter( block => {
  61. return checkCanBecomeHeading( block, this.modelElement, model.schema );
  62. } );
  63. for ( const block of blocks ) {
  64. if ( !block.is( this.modelElement ) ) {
  65. writer.rename( block, this.modelElement );
  66. }
  67. }
  68. } );
  69. }
  70. }
  71. // Checks whether the given block can be replaced by a specific heading.
  72. //
  73. // @private
  74. // @param {module:engine/model/element~Element} block A block to be tested.
  75. // @param {module:heading/headingcommand~HeadingCommand#modelElement} heading Command element name in the model.
  76. // @param {module:engine/model/schema~Schema} schema The schema of the document.
  77. // @returns {Boolean}
  78. function checkCanBecomeHeading( block, heading, schema ) {
  79. return schema.check( {
  80. name: heading,
  81. inside: Position.createBefore( block )
  82. } );
  83. }