8
0

tablemocking.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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. /* globals console, window, document */
  6. import { createTableAsciiArt, modelTable, prepareModelTableInput, prettyFormatModelTableInput } from '../_utils/utils';
  7. import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
  8. import { setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  9. import { diffString } from 'json-diff';
  10. import { debounce } from 'lodash-es';
  11. import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
  12. import TableWalker from '../../src/tablewalker';
  13. import { getSelectionAffectedTableCells } from '../../src/utils/selection';
  14. ClassicEditor
  15. .create( document.querySelector( '#editor' ), {
  16. plugins: [ ArticlePluginSet ],
  17. toolbar: [
  18. 'insertTable', 'undo', 'redo'
  19. ],
  20. table: {
  21. contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ]
  22. }
  23. } )
  24. .then( editor => {
  25. window.editor = editor;
  26. const asciiOut = document.getElementById( 'ascii-art' );
  27. const modelData = document.getElementById( 'model-data' );
  28. editor.editing.view.document.on( 'paste', ( evt, data ) => {
  29. document.getElementById( 'clipboard' ).innerText = data.dataTransfer.getData( 'text/html' ).replace( />(?=<)/g, '>\n' );
  30. } );
  31. document.getElementById( 'clear-content' ).addEventListener( 'click', () => {
  32. editor.setData( '' );
  33. } );
  34. document.getElementById( 'set-model-data' ).addEventListener( 'click', () => {
  35. updateInputStatus();
  36. const table = findTable( editor );
  37. const inputModelData = parseModelData( modelData.value );
  38. if ( inputModelData ) {
  39. const element = setModelData._parse( modelTable( inputModelData ), editor.model.schema );
  40. editor.model.change( writer => {
  41. editor.model.insertContent( element, table ? editor.model.createRangeOn( table ) : null );
  42. writer.setSelection( element, 'on' );
  43. } );
  44. editor.editing.view.focus();
  45. }
  46. } );
  47. document.getElementById( 'get-model-data' ).addEventListener( 'click', () => {
  48. updateInputStatus();
  49. const table = findTable( editor, true );
  50. modelData.value = table ? prettyFormatModelTableInput( prepareModelTableInput( editor.model, table ) ) : '';
  51. updateAsciiAndDiff();
  52. } );
  53. document.getElementById( 'renumber-cells' ).addEventListener( 'click', () => {
  54. const table = findTable( editor, true );
  55. if ( !table ) {
  56. return;
  57. }
  58. const useLetters = document.getElementById( 'use-letters' ).checked;
  59. editor.model.change( writer => {
  60. for ( const { row, column, cell } of new TableWalker( table ) ) {
  61. const selection = editor.model.createSelection( cell, 'in' );
  62. editor.model.insertContent( writer.createText( createCellText( row, column, useLetters ) ), selection );
  63. }
  64. } );
  65. updateAsciiAndDiff();
  66. } );
  67. editor.model.document.on( 'change:data', debounce( () => updateAsciiAndDiff(), 100 ) );
  68. updateAsciiAndDiff();
  69. function updateAsciiAndDiff() {
  70. const tables = getAllTables( editor );
  71. if ( !tables.length ) {
  72. asciiOut.innerText = '-- table not found --';
  73. return;
  74. }
  75. const inputModelData = parseModelData( modelData.value );
  76. const currentModelData = prepareModelTableInput( editor.model, tables[ 0 ] );
  77. const diffOutput = inputModelData ? diffString( inputModelData, currentModelData, {
  78. theme: {
  79. ' ': string => string,
  80. '+': string => `<span class="diff-add">${ string }</span>`,
  81. '-': string => `<span class="diff-del">${ string }</span>`
  82. }
  83. } ) : '-- no input --';
  84. const asciiArt = tables
  85. .map( table => createTableAsciiArt( editor.model, table ) )
  86. .join( '\n\n' );
  87. asciiOut.innerHTML = asciiArt + '\n\n' +
  88. 'Diff: input vs post-fixed model (only first table):\n' + ( diffOutput ? diffOutput : '-- no differences --' );
  89. }
  90. function findTable( editor, useAnyTable = false ) {
  91. const selection = editor.model.document.selection;
  92. const tableCells = getSelectionAffectedTableCells( selection );
  93. if ( tableCells.length ) {
  94. return tableCells[ 0 ].findAncestor( 'table' );
  95. }
  96. const element = selection.getSelectedElement();
  97. if ( element && element.is( 'element', 'table' ) ) {
  98. return element;
  99. }
  100. if ( useAnyTable ) {
  101. const range = editor.model.createRangeIn( editor.model.document.getRoot() );
  102. for ( const element of range.getItems() ) {
  103. if ( element.is( 'element', 'table' ) ) {
  104. return element;
  105. }
  106. }
  107. }
  108. return null;
  109. }
  110. function getAllTables( editor ) {
  111. const range = editor.model.createRangeIn( editor.model.document.getRoot() );
  112. const tables = [];
  113. for ( const element of range.getItems() ) {
  114. if ( element.is( 'element', 'table' ) ) {
  115. tables.push( element );
  116. }
  117. }
  118. return tables;
  119. }
  120. function parseModelData( string ) {
  121. if ( !string.trim() ) {
  122. return null;
  123. }
  124. const jsonString = string
  125. .replace( /'/g, '"' )
  126. .replace( /([a-z0-9$_]+)\s*:/gi, '"$1":' );
  127. try {
  128. return JSON.parse( jsonString );
  129. } catch ( error ) {
  130. updateInputStatus( error.message );
  131. }
  132. return null;
  133. }
  134. function updateInputStatus( message = '' ) {
  135. document.getElementById( 'input-status' ).innerText = message;
  136. }
  137. function createCellText( row, column, useLetters ) {
  138. const rowLabel = useLetters ? String.fromCharCode( row + 'a'.charCodeAt( 0 ) ) : row;
  139. const columnLabel = useLetters ? String.fromCharCode( column + 'a'.charCodeAt( 0 ) ) : column;
  140. return `${ rowLabel }${ columnLabel }`;
  141. }
  142. } )
  143. .catch( err => {
  144. console.error( err.stack );
  145. } );