8
0

pagebreakediting.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /**
  6. * @module page-break/pagebreakediting
  7. */
  8. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  9. import PageBreakCommand from './pagebreakcommand';
  10. import { toWidget } from '@ckeditor/ckeditor5-widget/src/utils';
  11. import '../theme/pagebreak.css';
  12. /**
  13. * The page break editing feature.
  14. *
  15. * @extends module:core/plugin~Plugin
  16. */
  17. export default class PageBreakEditing extends Plugin {
  18. /**
  19. * @inheritDoc
  20. */
  21. static get pluginName() {
  22. return 'PageBreakEditing';
  23. }
  24. /**
  25. * @inheritDoc
  26. */
  27. init() {
  28. const editor = this.editor;
  29. const schema = editor.model.schema;
  30. const t = editor.t;
  31. const conversion = editor.conversion;
  32. schema.register( 'pageBreak', {
  33. isObject: true,
  34. allowWhere: '$block'
  35. } );
  36. conversion.for( 'dataDowncast' ).elementToElement( {
  37. model: 'pageBreak',
  38. view: ( modelElement, { writer } ) => {
  39. const divElement = writer.createContainerElement( 'div', {
  40. class: 'page-break',
  41. // If user has no `.ck-content` styles, it should always break a page during print.
  42. style: 'page-break-after: always'
  43. } );
  44. // For a rationale of using span inside a div see:
  45. // https://github.com/ckeditor/ckeditor5-page-break/pull/1#discussion_r328934062.
  46. const spanElement = writer.createContainerElement( 'span', {
  47. style: 'display: none'
  48. } );
  49. writer.insert( writer.createPositionAt( divElement, 0 ), spanElement );
  50. return divElement;
  51. }
  52. } );
  53. conversion.for( 'editingDowncast' ).elementToElement( {
  54. model: 'pageBreak',
  55. view: ( modelElement, { writer } ) => {
  56. const label = t( 'Page break' );
  57. const viewWrapper = writer.createContainerElement( 'div' );
  58. const viewLabelElement = writer.createContainerElement( 'span' );
  59. const innerText = writer.createText( t( 'Page break' ) );
  60. writer.addClass( 'page-break', viewWrapper );
  61. writer.setCustomProperty( 'pageBreak', true, viewWrapper );
  62. writer.addClass( 'page-break__label', viewLabelElement );
  63. writer.insert( writer.createPositionAt( viewWrapper, 0 ), viewLabelElement );
  64. writer.insert( writer.createPositionAt( viewLabelElement, 0 ), innerText );
  65. return toPageBreakWidget( viewWrapper, writer, label );
  66. }
  67. } );
  68. conversion.for( 'upcast' )
  69. .elementToElement( {
  70. view: element => {
  71. // For upcast conversion it's enough if we check for element style and verify if it's empty
  72. // or contains only hidden span element.
  73. const hasPageBreakBefore = element.getStyle( 'page-break-before' ) == 'always';
  74. const hasPageBreakAfter = element.getStyle( 'page-break-after' ) == 'always';
  75. if ( !hasPageBreakBefore && !hasPageBreakAfter ) {
  76. return;
  77. }
  78. // The "page break" div accepts only single child or no child at all.
  79. if ( element.childCount == 1 ) {
  80. const viewSpan = element.getChild( 0 );
  81. // The child must be the "span" element that is not displayed and has a space inside.
  82. if ( !viewSpan.is( 'element', 'span' ) || viewSpan.getStyle( 'display' ) != 'none' || viewSpan.childCount != 1 ) {
  83. return;
  84. }
  85. const text = viewSpan.getChild( 0 );
  86. if ( !text.is( '$text' ) || text.data !== ' ' ) {
  87. return;
  88. }
  89. } else if ( element.childCount > 1 ) {
  90. return;
  91. }
  92. return { name: true };
  93. },
  94. model: 'pageBreak',
  95. // This conversion must be checked before <br> conversion because some editors use
  96. // <br style="page-break-before:always"> as a page break marker.
  97. converterPriority: 'high'
  98. } );
  99. editor.commands.add( 'pageBreak', new PageBreakCommand( editor ) );
  100. }
  101. }
  102. // Converts a given {@link module:engine/view/element~Element} to a page break widget:
  103. // * Adds a {@link module:engine/view/element~Element#_setCustomProperty custom property} allowing to
  104. // recognize the page break widget element.
  105. // * Calls the {@link module:widget/utils~toWidget} function with the proper element's label creator.
  106. //
  107. // @param {module:engine/view/element~Element} viewElement
  108. // @param {module:engine/view/downcastwriter~DowncastWriter} writer An instance of the view writer.
  109. // @param {String} label The element's label.
  110. // @returns {module:engine/view/element~Element}
  111. function toPageBreakWidget( viewElement, writer, label ) {
  112. writer.setCustomProperty( 'pageBreak', true, viewElement );
  113. return toWidget( viewElement, writer, { label } );
  114. }