autoformat.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import BlockAutoformatEngine from './blockautoformatengine.js';
  6. import InlineAutoformatEngine from './inlineautoformatengine.js';
  7. import Feature from '../core/feature.js';
  8. import HeadingEngine from '../heading/headingengine.js';
  9. import ListEngine from '../list/listengine.js';
  10. import BoldEngine from '../basic-styles/boldengine.js';
  11. import ItalicEngine from '../basic-styles/italicengine.js';
  12. /**
  13. * Includes set of predefined Autoformatting actions:
  14. * * Bulleted list,
  15. * * Numbered list,
  16. * * Headings.
  17. *
  18. * @memberOf autoformat
  19. * @extends core.Feature
  20. */
  21. export default class Autoformat extends Feature {
  22. /**
  23. * @inheritDoc
  24. */
  25. static get requires() {
  26. return [ HeadingEngine, ListEngine, BoldEngine, ItalicEngine ];
  27. }
  28. /**
  29. * @inheritDoc
  30. */
  31. init() {
  32. this._addListAutoformats();
  33. this._addHeadingAutoformats();
  34. this._addInlineAutoformats();
  35. }
  36. /**
  37. * Add autoformats related to ListEngine commands.
  38. *
  39. * When typed:
  40. *
  41. * `* ` or `- `
  42. * Paragraph will be changed to a bulleted list.
  43. *
  44. * `1. ` or `1) `
  45. * Paragraph will be changed to a numbered list (1 can be any digit or list of digits).
  46. *
  47. * @private
  48. */
  49. _addListAutoformats() {
  50. new BlockAutoformatEngine( this.editor, /^[\*\-]\s$/, 'bulletedList' );
  51. new BlockAutoformatEngine( this.editor, /^\d+[\.|)]?\s$/, 'numberedList' );
  52. }
  53. /**
  54. * Add autoformats related to HeadingEngine commands.
  55. *
  56. * When typed:
  57. *
  58. * `#` or `##` or `###`
  59. * Paragraph will be changed to a corresponding heading level.
  60. *
  61. * @private
  62. */
  63. _addHeadingAutoformats() {
  64. new BlockAutoformatEngine( this.editor, /^(#{1,3})\s$/, ( context ) => {
  65. const { batch, match } = context;
  66. const headingLevel = match[ 1 ].length;
  67. this.editor.execute( 'heading', {
  68. batch,
  69. formatId: `heading${ headingLevel }`
  70. } );
  71. } );
  72. }
  73. _addInlineAutoformats() {
  74. // Bold autoformat.
  75. new InlineAutoformatEngine(
  76. this.editor,
  77. ( text ) => {
  78. const pattern = /\*\*(.+?)\*\*/g;
  79. let result;
  80. let remove = [];
  81. let format = [];
  82. while ( ( result = pattern.exec( text ) ) !== null ) {
  83. const start = result.index;
  84. const fullMatchLen = result[ 0 ].length;
  85. const delimiterLen = 2; // Length of '**'.
  86. const delStart = [ start, start + delimiterLen ];
  87. const delEnd = [ start + fullMatchLen - delimiterLen, start + fullMatchLen ];
  88. remove.push( delStart );
  89. remove.push( delEnd );
  90. // Calculation of offsets after text deletion is not needed.
  91. format.push( [ start, start + fullMatchLen - delimiterLen ] );
  92. }
  93. return {
  94. remove,
  95. format
  96. };
  97. },
  98. ( editor, range, batch ) => {
  99. this.editor.execute( 'bold', { ranges: [ range ], batch } );
  100. }
  101. );
  102. // Italic autoformat.
  103. new InlineAutoformatEngine(
  104. this.editor,
  105. ( text ) => {
  106. // For a text: 'Brown *fox* jumps over the lazy dog' the expression below will return following values:
  107. //
  108. // [0]: ' *fox* ',
  109. // [1]: ' ',
  110. // [2]: '*fox*',
  111. // [index]: 5
  112. //
  113. // Value at index 1 is a "prefix". It can be empty, if the matched word is at the
  114. // beginning of the line. Length of the prefix is used to calculate `start` index.
  115. const pattern = /([^\*]|^)(\*[^\*].+?[^\*]\*)(?:[^\*]|$)/g;
  116. let result;
  117. let remove = [];
  118. let format = [];
  119. while ( ( result = pattern.exec( text ) ) !== null ) {
  120. // Add "prefix" match length.
  121. const start = result.index + result[ 1 ].length;
  122. const fullMatchLen = result[ 2 ].length;
  123. const delimiterLen = 1; // Length of '*'.
  124. const delStart = [ start, start + delimiterLen ];
  125. const delEnd = [ start + fullMatchLen - delimiterLen, start + fullMatchLen ];
  126. remove.push( delStart );
  127. remove.push( delEnd );
  128. // Calculation of offsets after deletion is not needed.
  129. format.push( [ start, start + fullMatchLen - delimiterLen ] );
  130. }
  131. return {
  132. remove,
  133. format
  134. };
  135. },
  136. ( editor, range, batch ) => {
  137. this.editor.execute( 'italic', { ranges: [ range ], batch } );
  138. }
  139. );
  140. // 3 capture groups: (remove)(format)(remove).
  141. // new InlineAutoformatEngine( this.editor, /(\*\*.+?\*\*)/g, 'bold' );
  142. }
  143. }