headingengine.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module heading/headingengine
  7. */
  8. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  9. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  10. import {
  11. modelElementToViewContainerElement,
  12. viewToModelElement
  13. } from '@ckeditor/ckeditor5-engine/src/conversion/definition-based-converters';
  14. import HeadingCommand from './headingcommand';
  15. const defaultModelElement = 'paragraph';
  16. /**
  17. * The headings engine feature. It handles switching between block formats – headings and paragraph.
  18. * This class represents the engine part of the heading feature. See also {@link module:heading/heading~Heading}.
  19. *
  20. * @extends module:core/plugin~Plugin
  21. */
  22. export default class HeadingEngine extends Plugin {
  23. /**
  24. * @inheritDoc
  25. */
  26. constructor( editor ) {
  27. super( editor );
  28. editor.config.define( 'heading', {
  29. options: [
  30. {
  31. model: 'paragraph',
  32. title: 'Paragraph',
  33. class: 'ck-heading_paragraph'
  34. },
  35. {
  36. model: 'heading1',
  37. title: 'Heading 1',
  38. class: 'ck-heading_heading1',
  39. view: {
  40. name: 'h2'
  41. }
  42. },
  43. {
  44. model: 'heading2',
  45. title: 'Heading 2',
  46. view: {
  47. name: 'h3'
  48. },
  49. class: 'ck-heading_heading2'
  50. },
  51. {
  52. model: 'heading3',
  53. title: 'Heading 3',
  54. class: 'ck-heading_heading3',
  55. view: {
  56. name: 'h4'
  57. }
  58. }
  59. ]
  60. } );
  61. }
  62. /**
  63. * @inheritDoc
  64. */
  65. static get requires() {
  66. return [ Paragraph ];
  67. }
  68. /**
  69. * @inheritDoc
  70. */
  71. init() {
  72. const editor = this.editor;
  73. const data = editor.data;
  74. const editing = editor.editing;
  75. const options = editor.config.get( 'heading.options' );
  76. for ( const option of options ) {
  77. // Skip paragraph - it is defined in required Paragraph feature.
  78. if ( option.model !== defaultModelElement ) {
  79. // Schema.
  80. editor.model.schema.registerItem( option.model, '$block' );
  81. // Build converter from model to view for data and editing pipelines.
  82. modelElementToViewContainerElement( option, [ data.modelToView, editing.modelToView ] );
  83. // Build converter from view to model for data pipeline.
  84. viewToModelElement( option, [ data.viewToModel ] );
  85. // Register the heading command for this option.
  86. editor.commands.add( option.model, new HeadingCommand( editor, option.model ) );
  87. }
  88. }
  89. }
  90. /**
  91. * @inheritDoc
  92. */
  93. afterInit() {
  94. // If the enter command is added to the editor, alter its behavior.
  95. // Enter at the end of a heading element should create a paragraph.
  96. const editor = this.editor;
  97. const enterCommand = editor.commands.get( 'enter' );
  98. const options = editor.config.get( 'heading.options' );
  99. if ( enterCommand ) {
  100. // @TODO This should be handled by a post-fixer.
  101. this.listenTo( enterCommand, 'afterExecute', ( evt, data ) => {
  102. const positionParent = editor.model.document.selection.getFirstPosition().parent;
  103. const isHeading = options.some( option => positionParent.is( option.model ) );
  104. if ( isHeading && !positionParent.is( defaultModelElement ) && positionParent.childCount === 0 ) {
  105. data.writer.rename( positionParent, defaultModelElement );
  106. }
  107. } );
  108. }
  109. }
  110. }