tableclipboard-paste.js 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146
  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 document console */
  6. import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
  7. import BlockQuoteEditing from '@ckeditor/ckeditor5-block-quote/src/blockquoteediting';
  8. import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
  9. import HorizontalLineEditing from '@ckeditor/ckeditor5-horizontal-line/src/horizontallineediting';
  10. import ImageCaptionEditing from '@ckeditor/ckeditor5-image/src/imagecaption/imagecaptionediting';
  11. import ImageEditing from '@ckeditor/ckeditor5-image/src/image/imageediting';
  12. import ListEditing from '@ckeditor/ckeditor5-list/src/listediting';
  13. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  14. import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
  15. import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  16. import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
  17. import { assertSelectedCells, modelTable, viewTable } from './_utils/utils';
  18. import TableEditing from '../src/tableediting';
  19. import TableCellPropertiesEditing from '../src/tablecellproperties/tablecellpropertiesediting';
  20. import TableWalker from '../src/tablewalker';
  21. import TableClipboard from '../src/tableclipboard';
  22. describe( 'table clipboard', () => {
  23. let editor, model, modelRoot, tableSelection, viewDocument, element;
  24. testUtils.createSinonSandbox();
  25. beforeEach( () => {
  26. element = document.createElement( 'div' );
  27. document.body.appendChild( element );
  28. } );
  29. afterEach( async () => {
  30. await editor.destroy();
  31. element.remove();
  32. } );
  33. describe( 'Clipboard integration - paste (selection scenarios)', () => {
  34. beforeEach( async () => {
  35. await createEditor();
  36. setModelData( model, modelTable( [
  37. [ '00[]', '01', '02', '03' ],
  38. [ '10', '11', '12', '13' ],
  39. [ '20', '21', '22', '23' ],
  40. [ '30', '31', '32', '33' ]
  41. ] ) );
  42. } );
  43. it( 'should be disabled in a readonly mode', () => {
  44. editor.isReadOnly = true;
  45. tableSelection.setCellSelection(
  46. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  47. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  48. );
  49. const data = pasteTable( [
  50. [ 'aa', 'ab' ],
  51. [ 'ba', 'bb' ]
  52. ] );
  53. editor.isReadOnly = false;
  54. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  55. [ '00', '01', '02', '03' ],
  56. [ '10', '11', '12', '13' ],
  57. [ '20', '21', '22', '23' ],
  58. [ '30', '31', '32', '33' ]
  59. ] ) );
  60. sinon.assert.calledOnce( data.preventDefault );
  61. } );
  62. it( 'should allow normal paste if no table cells are selected', () => {
  63. const data = {
  64. dataTransfer: createDataTransfer(),
  65. preventDefault: sinon.spy(),
  66. stopPropagation: sinon.spy()
  67. };
  68. data.dataTransfer.setData( 'text/html', '<p>foo</p>' );
  69. viewDocument.fire( 'paste', data );
  70. editor.isReadOnly = false;
  71. assertEqualMarkup( getModelData( model ), modelTable( [
  72. [ '00foo[]', '01', '02', '03' ],
  73. [ '10', '11', '12', '13' ],
  74. [ '20', '21', '22', '23' ],
  75. [ '30', '31', '32', '33' ]
  76. ] ) );
  77. } );
  78. it( 'should not alter model.insertContent if selectable is different from document selection', () => {
  79. model.change( writer => {
  80. writer.setSelection( modelRoot.getNodeByPath( [ 0, 0, 0 ] ), 0 );
  81. const selectedTableCells = model.createSelection( [
  82. model.createRangeOn( modelRoot.getNodeByPath( [ 0, 0, 0 ] ) ),
  83. model.createRangeOn( modelRoot.getNodeByPath( [ 0, 0, 1 ] ) ),
  84. model.createRangeOn( modelRoot.getNodeByPath( [ 0, 1, 0 ] ) ),
  85. model.createRangeOn( modelRoot.getNodeByPath( [ 0, 1, 1 ] ) )
  86. ] );
  87. const tableToInsert = editor.plugins.get( 'TableUtils' ).createTable( writer, 2, 2 );
  88. for ( const { cell } of new TableWalker( tableToInsert ) ) {
  89. writer.insertText( 'foo', cell.getChild( 0 ), 0 );
  90. }
  91. model.insertContent( tableToInsert, selectedTableCells );
  92. } );
  93. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  94. [ '', '', '02', '03' ],
  95. [ '', '', '12', '13' ],
  96. [ '20', '21', '22', '23' ],
  97. [ '30', '31', '32', '33' ]
  98. ] ) );
  99. } );
  100. it( 'should not alter model.insertContent if no table cells are selected', () => {
  101. model.change( writer => {
  102. writer.insertElement( 'paragraph', modelRoot.getChild( 0 ), 'before' );
  103. writer.setSelection( modelRoot.getChild( 0 ), 'before' );
  104. } );
  105. const data = {
  106. dataTransfer: createDataTransfer(),
  107. preventDefault: sinon.spy(),
  108. stopPropagation: sinon.spy()
  109. };
  110. data.dataTransfer.setData( 'text/html', '<p>foo</p>' );
  111. viewDocument.fire( 'paste', data );
  112. editor.isReadOnly = false;
  113. assertEqualMarkup( getModelData( model ), '<paragraph>foo[]</paragraph>' + modelTable( [
  114. [ '00', '01', '02', '03' ],
  115. [ '10', '11', '12', '13' ],
  116. [ '20', '21', '22', '23' ],
  117. [ '30', '31', '32', '33' ]
  118. ] ) );
  119. } );
  120. it( 'should not alter model.insertContent if no table pasted', () => {
  121. tableSelection.setCellSelection(
  122. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  123. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  124. );
  125. const data = {
  126. dataTransfer: createDataTransfer(),
  127. preventDefault: sinon.spy(),
  128. stopPropagation: sinon.spy()
  129. };
  130. data.dataTransfer.setData( 'text/html', '<p>foo</p>' );
  131. viewDocument.fire( 'paste', data );
  132. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  133. [ 'foo', '', '02', '03' ],
  134. [ '', '', '12', '13' ],
  135. [ '20', '21', '22', '23' ],
  136. [ '30', '31', '32', '33' ]
  137. ] ) );
  138. } );
  139. it( 'should not alter model.insertContent if mixed content is pasted (table + paragraph)', () => {
  140. tableSelection.setCellSelection(
  141. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  142. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  143. );
  144. const table = viewTable( [
  145. [ 'aa', 'ab' ],
  146. [ 'ba', 'bb' ] ] );
  147. const data = {
  148. dataTransfer: createDataTransfer(),
  149. preventDefault: sinon.spy(),
  150. stopPropagation: sinon.spy()
  151. };
  152. data.dataTransfer.setData( 'text/html', `${ table }<p>foo</p>` );
  153. viewDocument.fire( 'paste', data );
  154. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  155. [ 'foo', '', '02', '03' ],
  156. [ '', '', '12', '13' ],
  157. [ '20', '21', '22', '23' ],
  158. [ '30', '31', '32', '33' ]
  159. ] ) );
  160. } );
  161. it( 'should not alter model.insertContent if mixed content is pasted (paragraph + table)', () => {
  162. tableSelection.setCellSelection(
  163. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  164. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  165. );
  166. const table = viewTable( [
  167. [ 'aa', 'ab' ],
  168. [ 'ba', 'bb' ] ] );
  169. const data = {
  170. dataTransfer: createDataTransfer(),
  171. preventDefault: sinon.spy(),
  172. stopPropagation: sinon.spy()
  173. };
  174. data.dataTransfer.setData( 'text/html', `<p>foo</p>${ table }` );
  175. viewDocument.fire( 'paste', data );
  176. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  177. [ 'foo', '', '02', '03' ],
  178. [ '', '', '12', '13' ],
  179. [ '20', '21', '22', '23' ],
  180. [ '30', '31', '32', '33' ]
  181. ] ) );
  182. } );
  183. it( 'should not alter model.insertContent if mixed content is pasted (table + table)', () => {
  184. tableSelection.setCellSelection(
  185. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  186. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  187. );
  188. const table = viewTable( [
  189. [ 'aa', 'ab' ],
  190. [ 'ba', 'bb' ] ] );
  191. const data = {
  192. dataTransfer: createDataTransfer(),
  193. preventDefault: sinon.spy(),
  194. stopPropagation: sinon.spy()
  195. };
  196. data.dataTransfer.setData( 'text/html', `${ table }${ table }` );
  197. viewDocument.fire( 'paste', data );
  198. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  199. [ '', '', '02', '03' ],
  200. [ '', '', '12', '13' ],
  201. [ '20', '21', '22', '23' ],
  202. [ '30', '31', '32', '33' ]
  203. ] ) );
  204. } );
  205. it( 'should alter model.insertContent if selectable is a document selection', () => {
  206. tableSelection.setCellSelection(
  207. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  208. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  209. );
  210. model.change( writer => {
  211. const tableToInsert = editor.plugins.get( 'TableUtils' ).createTable( writer, 2, 2 );
  212. for ( const { cell } of new TableWalker( tableToInsert ) ) {
  213. writer.insertText( 'foo', cell.getChild( 0 ), 0 );
  214. }
  215. model.insertContent( tableToInsert, editor.model.document.selection );
  216. } );
  217. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  218. [ 'foo', 'foo', '02', '03' ],
  219. [ 'foo', 'foo', '12', '13' ],
  220. [ '20', '21', '22', '23' ],
  221. [ '30', '31', '32', '33' ]
  222. ] ) );
  223. } );
  224. it( 'should block non-rectangular selection', () => {
  225. setModelData( model, modelTable( [
  226. [ { contents: '00', colspan: 3 } ],
  227. [ '10', '11', '12' ],
  228. [ '20', '21', '22' ]
  229. ] ) );
  230. tableSelection.setCellSelection(
  231. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  232. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  233. );
  234. // Catches the temporary console log in the CK_DEBUG mode.
  235. sinon.stub( console, 'log' );
  236. pasteTable( [
  237. [ 'aa', 'ab' ],
  238. [ 'ba', 'bb' ]
  239. ] );
  240. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  241. [ { contents: '00', colspan: 3 } ],
  242. [ '10', '11', '12' ],
  243. [ '20', '21', '22' ]
  244. ] ) );
  245. } );
  246. describe( 'single cell selected', () => {
  247. beforeEach( () => {
  248. setModelData( model, modelTable( [
  249. [ '00[]', '01', '02' ],
  250. [ '10', '11', '12' ],
  251. [ '20', '21', '22' ]
  252. ] ) );
  253. } );
  254. describe( 'with the selection on the middle cell', () => {
  255. beforeEach( () => {
  256. tableSelection.setCellSelection(
  257. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  258. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  259. );
  260. } );
  261. it( 'should replace the table cells (with single undo step)', () => {
  262. const batches = setupBatchWatch();
  263. pasteTable( [
  264. [ 'aa', 'ab' ],
  265. [ 'ba', 'bb' ]
  266. ] );
  267. expect( batches.size ).to.equal( 1 );
  268. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  269. [ '00', '01', '02' ],
  270. [ '10', 'aa', 'ab' ],
  271. [ '20', 'ba', 'bb' ]
  272. ] ) );
  273. } );
  274. it( 'should expand the table width and replace the table cells (with single undo step)', () => {
  275. const batches = setupBatchWatch();
  276. pasteTable( [
  277. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  278. [ 'ba', 'bb', 'bc', 'bd', 'be' ]
  279. ] );
  280. expect( batches.size ).to.equal( 1 );
  281. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  282. [ '00', '01', '02', '', '', '' ],
  283. [ '10', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  284. [ '20', 'ba', 'bb', 'bc', 'bd', 'be' ]
  285. ] ) );
  286. } );
  287. it( 'should expand the table height and replace the table cells (with single undo step)', () => {
  288. const batches = setupBatchWatch();
  289. pasteTable( [
  290. [ 'aa', 'ab' ],
  291. [ 'ba', 'bb' ],
  292. [ 'ca', 'cb' ],
  293. [ 'da', 'db' ],
  294. [ 'ea', 'eb' ]
  295. ] );
  296. expect( batches.size ).to.equal( 1 );
  297. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  298. [ '00', '01', '02' ],
  299. [ '10', 'aa', 'ab' ],
  300. [ '20', 'ba', 'bb' ],
  301. [ '', 'ca', 'cb' ],
  302. [ '', 'da', 'db' ],
  303. [ '', 'ea', 'eb' ]
  304. ] ) );
  305. } );
  306. it( 'should expand the table width, height and replace the table cells (with single undo step)', () => {
  307. const batches = setupBatchWatch();
  308. pasteTable( [
  309. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  310. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  311. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  312. [ 'da', 'db', 'dc', 'dd', 'de' ],
  313. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  314. ] );
  315. expect( batches.size ).to.equal( 1 );
  316. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  317. [ '00', '01', '02', '', '', '' ],
  318. [ '10', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  319. [ '20', 'ba', 'bb', 'bc', 'bd', 'be' ],
  320. [ '', 'ca', 'cb', 'cc', 'cd', 'ce' ],
  321. [ '', 'da', 'db', 'dc', 'dd', 'de' ],
  322. [ '', 'ea', 'eb', 'ec', 'ed', 'ee' ]
  323. ] ) );
  324. } );
  325. } );
  326. describe( 'with the selection on the first cell', () => {
  327. beforeEach( () => {
  328. tableSelection.setCellSelection(
  329. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  330. modelRoot.getNodeByPath( [ 0, 0, 0 ] )
  331. );
  332. } );
  333. it( 'should replace the table cells', () => {
  334. pasteTable( [
  335. [ 'aa', 'ab' ],
  336. [ 'ba', 'bb' ]
  337. ] );
  338. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  339. [ 'aa', 'ab', '02' ],
  340. [ 'ba', 'bb', '12' ],
  341. [ '20', '21', '22' ]
  342. ] ) );
  343. } );
  344. it( 'should expand the table and replace the table cells', () => {
  345. pasteTable( [
  346. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  347. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  348. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  349. [ 'da', 'db', 'dc', 'dd', 'de' ],
  350. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  351. ] );
  352. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  353. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  354. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  355. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  356. [ 'da', 'db', 'dc', 'dd', 'de' ],
  357. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  358. ] ) );
  359. } );
  360. } );
  361. describe( 'with the selection on the middle cell of the first row', () => {
  362. beforeEach( () => {
  363. tableSelection.setCellSelection(
  364. modelRoot.getNodeByPath( [ 0, 0, 1 ] ),
  365. modelRoot.getNodeByPath( [ 0, 0, 1 ] )
  366. );
  367. } );
  368. it( 'should replace the table cells', () => {
  369. pasteTable( [
  370. [ 'aa', 'ab' ],
  371. [ 'ba', 'bb' ]
  372. ] );
  373. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  374. [ '00', 'aa', 'ab' ],
  375. [ '10', 'ba', 'bb' ],
  376. [ '20', '21', '22' ]
  377. ] ) );
  378. } );
  379. it( 'should expand the table and replace the table cells', () => {
  380. pasteTable( [
  381. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  382. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  383. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  384. [ 'da', 'db', 'dc', 'dd', 'de' ],
  385. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  386. ] );
  387. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  388. [ '00', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  389. [ '10', 'ba', 'bb', 'bc', 'bd', 'be' ],
  390. [ '20', 'ca', 'cb', 'cc', 'cd', 'ce' ],
  391. [ '', 'da', 'db', 'dc', 'dd', 'de' ],
  392. [ '', 'ea', 'eb', 'ec', 'ed', 'ee' ]
  393. ] ) );
  394. } );
  395. } );
  396. describe( 'with the selection on the last cell of the first row', () => {
  397. beforeEach( () => {
  398. tableSelection.setCellSelection(
  399. modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
  400. modelRoot.getNodeByPath( [ 0, 0, 2 ] )
  401. );
  402. } );
  403. it( 'should replace the table cells', () => {
  404. pasteTable( [
  405. [ 'aa', 'ab' ],
  406. [ 'ba', 'bb' ]
  407. ] );
  408. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  409. [ '00', '01', 'aa', 'ab' ],
  410. [ '10', '11', 'ba', 'bb' ],
  411. [ '20', '21', '22', '' ]
  412. ] ) );
  413. } );
  414. it( 'should expand the table and replace the table cells', () => {
  415. pasteTable( [
  416. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  417. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  418. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  419. [ 'da', 'db', 'dc', 'dd', 'de' ],
  420. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  421. ] );
  422. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  423. [ '00', '01', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  424. [ '10', '11', 'ba', 'bb', 'bc', 'bd', 'be' ],
  425. [ '20', '21', 'ca', 'cb', 'cc', 'cd', 'ce' ],
  426. [ '', '', 'da', 'db', 'dc', 'dd', 'de' ],
  427. [ '', '', 'ea', 'eb', 'ec', 'ed', 'ee' ]
  428. ] ) );
  429. } );
  430. } );
  431. describe( 'with the selection is on the middle cell of the first column', () => {
  432. beforeEach( () => {
  433. tableSelection.setCellSelection(
  434. modelRoot.getNodeByPath( [ 0, 1, 0 ] ),
  435. modelRoot.getNodeByPath( [ 0, 1, 0 ] )
  436. );
  437. } );
  438. it( 'should replace the table cells', () => {
  439. pasteTable( [
  440. [ 'aa', 'ab' ],
  441. [ 'ba', 'bb' ]
  442. ] );
  443. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  444. [ '00', '01', '02' ],
  445. [ 'aa', 'ab', '12' ],
  446. [ 'ba', 'bb', '22' ]
  447. ] ) );
  448. } );
  449. it( 'should expand the table and replace the table cells', () => {
  450. pasteTable( [
  451. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  452. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  453. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  454. [ 'da', 'db', 'dc', 'dd', 'de' ],
  455. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  456. ] );
  457. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  458. [ '00', '01', '02', '', '' ],
  459. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  460. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  461. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  462. [ 'da', 'db', 'dc', 'dd', 'de' ],
  463. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  464. ] ) );
  465. } );
  466. } );
  467. describe( 'with the selection is on the last cell of the first column', () => {
  468. beforeEach( () => {
  469. tableSelection.setCellSelection(
  470. modelRoot.getNodeByPath( [ 0, 2, 0 ] ),
  471. modelRoot.getNodeByPath( [ 0, 2, 0 ] )
  472. );
  473. } );
  474. it( 'should replace the table cells', () => {
  475. pasteTable( [
  476. [ 'aa', 'ab' ],
  477. [ 'ba', 'bb' ]
  478. ] );
  479. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  480. [ '00', '01', '02' ],
  481. [ '10', '11', '12' ],
  482. [ 'aa', 'ab', '22' ],
  483. [ 'ba', 'bb', '' ]
  484. ] ) );
  485. } );
  486. it( 'should expand the table and replace the table cells', () => {
  487. pasteTable( [
  488. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  489. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  490. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  491. [ 'da', 'db', 'dc', 'dd', 'de' ],
  492. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  493. ] );
  494. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  495. [ '00', '01', '02', '', '' ],
  496. [ '10', '11', '12', '', '' ],
  497. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  498. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  499. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  500. [ 'da', 'db', 'dc', 'dd', 'de' ],
  501. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  502. ] ) );
  503. } );
  504. } );
  505. describe( 'with the selection is on the last cell of the last column', () => {
  506. beforeEach( () => {
  507. tableSelection.setCellSelection(
  508. modelRoot.getNodeByPath( [ 0, 2, 2 ] ),
  509. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  510. );
  511. } );
  512. it( 'should replace the table cells', () => {
  513. pasteTable( [
  514. [ 'aa', 'ab' ],
  515. [ 'ba', 'bb' ]
  516. ] );
  517. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  518. [ '00', '01', '02', '' ],
  519. [ '10', '11', '12', '' ],
  520. [ '20', '21', 'aa', 'ab' ],
  521. [ '', '', 'ba', 'bb' ]
  522. ] ) );
  523. } );
  524. it( 'should expand the table and replace the table cells', () => {
  525. pasteTable( [
  526. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  527. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  528. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  529. [ 'da', 'db', 'dc', 'dd', 'de' ],
  530. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  531. ] );
  532. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  533. [ '00', '01', '02', '', '', '', '' ],
  534. [ '10', '11', '12', '', '', '', '' ],
  535. [ '20', '21', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  536. [ '', '', 'ba', 'bb', 'bc', 'bd', 'be' ],
  537. [ '', '', 'ca', 'cb', 'cc', 'cd', 'ce' ],
  538. [ '', '', 'da', 'db', 'dc', 'dd', 'de' ],
  539. [ '', '', 'ea', 'eb', 'ec', 'ed', 'ee' ]
  540. ] ) );
  541. } );
  542. } );
  543. describe( 'the selection inside the table cell', () => {
  544. it( 'should replace the table cells when the collapsed selection is in the first cell', () => {
  545. pasteTable( [
  546. [ 'aa', 'ab' ],
  547. [ 'ba', 'bb' ]
  548. ] );
  549. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  550. [ 'aa', 'ab', '02' ],
  551. [ 'ba', 'bb', '12' ],
  552. [ '20', '21', '22' ]
  553. ] ) );
  554. } );
  555. it( 'should replace the table cells when the expanded selection is in the first cell', () => {
  556. model.change( writer => {
  557. writer.setSelection( modelRoot.getNodeByPath( [ 0, 0, 0 ] ), 'in' );
  558. } );
  559. pasteTable( [
  560. [ 'aa', 'ab' ],
  561. [ 'ba', 'bb' ]
  562. ] );
  563. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  564. [ 'aa', 'ab', '02' ],
  565. [ 'ba', 'bb', '12' ],
  566. [ '20', '21', '22' ]
  567. ] ) );
  568. } );
  569. it( 'should replace the table cells when selection is on the image inside the table cell', async () => {
  570. await editor.destroy();
  571. await createEditor( [ ImageEditing, ImageCaptionEditing ] );
  572. setModelData( model, modelTable( [
  573. [ '00', '01', '02' ],
  574. [ '10', '11', '12' ],
  575. [ '20', '21', '[<image src="/assets/sample.jpg"><caption></caption></image>]' ]
  576. ] ) );
  577. pasteTable( [
  578. [ 'aa', 'ab' ],
  579. [ 'ba', 'bb' ]
  580. ] );
  581. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  582. [ '00', '01', '02', '' ],
  583. [ '10', '11', '12', '' ],
  584. [ '20', '21', 'aa', 'ab' ],
  585. [ '', '', 'ba', 'bb' ]
  586. ] ) );
  587. } );
  588. it( 'should replace the table cells when selection is in the image caption inside the table cell', async () => {
  589. await editor.destroy();
  590. await createEditor( [ ImageEditing, ImageCaptionEditing ] );
  591. setModelData( model, modelTable( [
  592. [ '00', '01', '02' ],
  593. [ '10', '11', '12' ],
  594. [ '20', '21', '<image src="/assets/sample.jpg"><caption>fo[]o</caption></image>' ]
  595. ] ) );
  596. pasteTable( [
  597. [ 'aa', 'ab' ],
  598. [ 'ba', 'bb' ]
  599. ] );
  600. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  601. [ '00', '01', '02', '' ],
  602. [ '10', '11', '12', '' ],
  603. [ '20', '21', 'aa', 'ab' ],
  604. [ '', '', 'ba', 'bb' ]
  605. ] ) );
  606. } );
  607. } );
  608. } );
  609. describe( 'pasted table is equal to the selected area', () => {
  610. describe( 'no spans', () => {
  611. it( 'handles simple table paste to a simple table fragment - at the beginning of a table', () => {
  612. tableSelection.setCellSelection(
  613. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  614. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  615. );
  616. pasteTable( [
  617. [ 'aa', 'ab' ],
  618. [ 'ba', 'bb' ]
  619. ] );
  620. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  621. [ 'aa', 'ab', '02', '03' ],
  622. [ 'ba', 'bb', '12', '13' ],
  623. [ '20', '21', '22', '23' ],
  624. [ '30', '31', '32', '33' ]
  625. ] ) );
  626. assertSelectedCells( model, [
  627. [ 1, 1, 0, 0 ],
  628. [ 1, 1, 0, 0 ],
  629. [ 0, 0, 0, 0 ],
  630. [ 0, 0, 0, 0 ]
  631. ] );
  632. } );
  633. it( 'handles simple table paste to a simple table fragment - at the end of a table', () => {
  634. tableSelection.setCellSelection(
  635. modelRoot.getNodeByPath( [ 0, 2, 2 ] ),
  636. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  637. );
  638. pasteTable( [
  639. [ 'aa', 'ab' ],
  640. [ 'ba', 'bb' ]
  641. ] );
  642. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  643. [ '00', '01', '02', '03' ],
  644. [ '10', '11', '12', '13' ],
  645. [ '20', '21', 'aa', 'ab' ],
  646. [ '30', '31', 'ba', 'bb' ]
  647. ] ) );
  648. assertSelectedCells( model, [
  649. [ 0, 0, 0, 0 ],
  650. [ 0, 0, 0, 0 ],
  651. [ 0, 0, 1, 1 ],
  652. [ 0, 0, 1, 1 ]
  653. ] );
  654. } );
  655. it( 'handles simple table paste to a simple table fragment - in the middle of a table', () => {
  656. tableSelection.setCellSelection(
  657. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  658. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  659. );
  660. pasteTable( [
  661. [ 'aa', 'ab' ],
  662. [ 'ba', 'bb' ]
  663. ] );
  664. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  665. [ '00', '01', '02', '03' ],
  666. [ '10', 'aa', 'ab', '13' ],
  667. [ '20', 'ba', 'bb', '23' ],
  668. [ '30', '31', '32', '33' ]
  669. ] ) );
  670. assertSelectedCells( model, [
  671. [ 0, 0, 0, 0 ],
  672. [ 0, 1, 1, 0 ],
  673. [ 0, 1, 1, 0 ],
  674. [ 0, 0, 0, 0 ]
  675. ] );
  676. } );
  677. it( 'handles simple row paste to a simple row fragment - in the middle of a table', () => {
  678. tableSelection.setCellSelection(
  679. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  680. modelRoot.getNodeByPath( [ 0, 1, 2 ] )
  681. );
  682. pasteTable( [
  683. [ 'aa', 'ab' ]
  684. ] );
  685. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  686. [ '00', '01', '02', '03' ],
  687. [ '10', 'aa', 'ab', '13' ],
  688. [ '20', '21', '22', '23' ],
  689. [ '30', '31', '32', '33' ]
  690. ] ) );
  691. assertSelectedCells( model, [
  692. [ 0, 0, 0, 0 ],
  693. [ 0, 1, 1, 0 ],
  694. [ 0, 0, 0, 0 ],
  695. [ 0, 0, 0, 0 ]
  696. ] );
  697. } );
  698. it( 'handles simple column paste to a simple column fragment - in the middle of a table', () => {
  699. tableSelection.setCellSelection(
  700. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  701. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  702. );
  703. pasteTable( [
  704. [ 'aa' ],
  705. [ 'ba' ]
  706. ] );
  707. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  708. [ '00', '01', '02', '03' ],
  709. [ '10', 'aa', '12', '13' ],
  710. [ '20', 'ba', '22', '23' ],
  711. [ '30', '31', '32', '33' ]
  712. ] ) );
  713. assertSelectedCells( model, [
  714. [ 0, 0, 0, 0 ],
  715. [ 0, 1, 0, 0 ],
  716. [ 0, 1, 0, 0 ],
  717. [ 0, 0, 0, 0 ]
  718. ] );
  719. } );
  720. it( 'handles simple table paste to a simple table fragment - whole table selected', () => {
  721. tableSelection.setCellSelection(
  722. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  723. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  724. );
  725. pasteTable( [
  726. [ 'aa', 'ab', 'ac', 'ad' ],
  727. [ 'ba', 'bb', 'bc', 'bd' ],
  728. [ 'ca', 'cb', 'cc', 'cd' ],
  729. [ 'da', 'db', 'dc', 'dd' ]
  730. ] );
  731. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  732. [ 'aa', 'ab', 'ac', 'ad' ],
  733. [ 'ba', 'bb', 'bc', 'bd' ],
  734. [ 'ca', 'cb', 'cc', 'cd' ],
  735. [ 'da', 'db', 'dc', 'dd' ]
  736. ] ) );
  737. assertSelectedCells( model, [
  738. [ 1, 1, 1, 1 ],
  739. [ 1, 1, 1, 1 ],
  740. [ 1, 1, 1, 1 ],
  741. [ 1, 1, 1, 1 ]
  742. ] );
  743. } );
  744. } );
  745. describe( 'pasted table has spans', () => {
  746. it( 'handles pasting table that has cell with colspan', () => {
  747. tableSelection.setCellSelection(
  748. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  749. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  750. );
  751. pasteTable( [
  752. [ { colspan: 2, contents: 'aa' } ],
  753. [ 'ba', 'bb' ]
  754. ] );
  755. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  756. [ '00', '01', '02', '03' ],
  757. [ '10', { colspan: 2, contents: 'aa' }, '13' ],
  758. [ '20', 'ba', 'bb', '23' ],
  759. [ '30', '31', '32', '33' ]
  760. ] ) );
  761. /* eslint-disable no-multi-spaces */
  762. assertSelectedCells( model, [
  763. [ 0, 0, 0, 0 ],
  764. [ 0, 1, 0 ],
  765. [ 0, 1, 1, 0 ],
  766. [ 0, 0, 0, 0 ]
  767. ] );
  768. /* eslint-enable no-multi-spaces */
  769. } );
  770. it( 'handles pasting table that has many cells with various colspan', () => {
  771. tableSelection.setCellSelection(
  772. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  773. modelRoot.getNodeByPath( [ 0, 3, 2 ] )
  774. );
  775. pasteTable( [
  776. [ 'aa', { colspan: 2, contents: 'ab' } ],
  777. [ { colspan: 3, contents: 'ba' } ],
  778. [ 'ca', 'cb', 'cc' ],
  779. [ { colspan: 2, contents: 'da' }, 'dc' ]
  780. ] );
  781. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  782. [ 'aa', { colspan: 2, contents: 'ab' }, '03' ],
  783. [ { colspan: 3, contents: 'ba' }, '13' ],
  784. [ 'ca', 'cb', 'cc', '23' ],
  785. [ { colspan: 2, contents: 'da' }, 'dc', '33' ]
  786. ] ) );
  787. /* eslint-disable no-multi-spaces */
  788. assertSelectedCells( model, [
  789. [ 1, 1, 0 ],
  790. [ 1, 0 ],
  791. [ 1, 1, 1, 0 ],
  792. [ 1, 1, 0 ]
  793. ] );
  794. /* eslint-enable no-multi-spaces */
  795. } );
  796. it( 'handles pasting table that has cell with rowspan', () => {
  797. tableSelection.setCellSelection(
  798. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  799. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  800. );
  801. pasteTable( [
  802. [ { rowspan: 2, contents: 'aa' }, 'ab' ],
  803. [ 'bb' ]
  804. ] );
  805. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  806. [ '00', '01', '02', '03' ],
  807. [ '10', { rowspan: 2, contents: 'aa' }, 'ab', '13' ],
  808. [ '20', 'bb', '23' ],
  809. [ '30', '31', '32', '33' ]
  810. ] ) );
  811. /* eslint-disable no-multi-spaces */
  812. assertSelectedCells( model, [
  813. [ 0, 0, 0, 0 ],
  814. [ 0, 1, 1, 0 ],
  815. [ 0, 1, 0 ],
  816. [ 0, 0, 0, 0 ]
  817. ] );
  818. /* eslint-enable no-multi-spaces */
  819. } );
  820. it( 'handles pasting table that has many cells with various rowspan', () => {
  821. tableSelection.setCellSelection(
  822. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  823. modelRoot.getNodeByPath( [ 0, 2, 3 ] )
  824. );
  825. pasteTable( [
  826. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  827. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  828. [ 'cc', 'cd' ]
  829. ] );
  830. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  831. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  832. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  833. [ 'cc', 'cd' ],
  834. [ '30', '31', '32', '33' ]
  835. ] ) );
  836. /* eslint-disable no-multi-spaces */
  837. assertSelectedCells( model, [
  838. [ 1, 1, 1, 1 ],
  839. [ 1, 1 ],
  840. [ 1, 1 ],
  841. [ 0, 0, 0, 0 ]
  842. ] );
  843. /* eslint-enable no-multi-spaces */
  844. } );
  845. it( 'handles pasting multi-spanned table', () => {
  846. setModelData( model, modelTable( [
  847. [ '00', '01', '02', '03', '04', '05' ],
  848. [ '10', '11', '12', '13', '14', '15' ],
  849. [ '20', '21', '22', '23', '24', '25' ],
  850. [ '30', '31', '32', '33', '34', '35' ],
  851. [ '40', '41', '42', '43', '44', '45' ]
  852. ] ) );
  853. tableSelection.setCellSelection(
  854. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  855. modelRoot.getNodeByPath( [ 0, 3, 4 ] )
  856. );
  857. // +----+----+----+----+----+
  858. // | aa | ac | ad | ae |
  859. // +----+----+----+----+ +
  860. // | ba | bb | |
  861. // +----+ +----+
  862. // | ca | | ce |
  863. // + +----+----+----+----+
  864. // | | db | dc | dd |
  865. // +----+----+----+----+----+
  866. pasteTable( [
  867. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 } ],
  868. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 } ],
  869. [ { contents: 'ca', rowspan: 2 }, 'ce' ],
  870. [ 'db', 'dc', { contents: 'dd', colspan: 2 } ]
  871. ] );
  872. // +----+----+----+----+----+----+
  873. // | aa | ac | ad | ae | 05 |
  874. // +----+----+----+----+ +----+
  875. // | ba | bb | | 15 |
  876. // +----+ +----+----+
  877. // | ca | | ce | 25 |
  878. // + +----+----+----+----+----+
  879. // | | db | dc | dd | 35 |
  880. // +----+----+----+----+----+----+
  881. // | 40 | 41 | 42 | 43 | 44 | 45 |
  882. // +----+----+----+----+----+----+
  883. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  884. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 }, '05' ],
  885. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 }, '15' ],
  886. [ { contents: 'ca', rowspan: 2 }, 'ce', '25' ],
  887. [ 'db', 'dc', { contents: 'dd', colspan: 2 }, '35' ],
  888. [ '40', '41', '42', '43', '44', '45' ]
  889. ] ) );
  890. /* eslint-disable no-multi-spaces */
  891. assertSelectedCells( model, [
  892. [ 1, 1, 1, 1, 0 ],
  893. [ 1, 1, 0 ],
  894. [ 1, 1, 0 ],
  895. [ 1, 1, 1, 0 ],
  896. [ 0, 0, 0, 0, 0, 0 ]
  897. ] );
  898. /* eslint-enable no-multi-spaces */
  899. } );
  900. } );
  901. describe( 'content table has spans', () => {
  902. it( 'handles pasting simple table over a table with colspans (no colspan exceeds selection)', () => {
  903. setModelData( model, modelTable( [
  904. [ '00[]', '01', '02', '03' ],
  905. [ { colspan: 3, contents: '10' }, '13' ],
  906. [ { colspan: 2, contents: '20' }, '22', '23' ],
  907. [ '30', '31', { colspan: 2, contents: '31' } ]
  908. ] ) );
  909. tableSelection.setCellSelection(
  910. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  911. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  912. );
  913. pasteTable( [
  914. [ 'aa', 'ab', 'ac' ],
  915. [ 'ba', 'bb', 'bc' ],
  916. [ 'ca', 'cb', 'cc' ]
  917. ] );
  918. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  919. [ 'aa', 'ab', 'ac', '03' ],
  920. [ 'ba', 'bb', 'bc', '13' ],
  921. [ 'ca', 'cb', 'cc', '23' ],
  922. [ '30', '31', { colspan: 2, contents: '31' } ]
  923. ] ) );
  924. /* eslint-disable no-multi-spaces */
  925. assertSelectedCells( model, [
  926. [ 1, 1, 1, 0 ],
  927. [ 1, 1, 1, 0 ],
  928. [ 1, 1, 1, 0 ],
  929. [ 0, 0, 0 ]
  930. ] );
  931. /* eslint-enable no-multi-spaces */
  932. } );
  933. it( 'handles pasting simple table over a table with rowspans (no rowspan exceeds selection)', () => {
  934. setModelData( model, modelTable( [
  935. [ '00', { rowspan: 3, contents: '01' }, { rowspan: 2, contents: '02' }, '03' ],
  936. [ { rowspan: 2, contents: '10' }, '13' ],
  937. [ '22', '23' ],
  938. [ '30', '31', '32', '33' ]
  939. ] ) );
  940. tableSelection.setCellSelection(
  941. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  942. modelRoot.getNodeByPath( [ 0, 2, 0 ] )
  943. );
  944. pasteTable( [
  945. [ 'aa', 'ab', 'ac' ],
  946. [ 'ba', 'bb', 'bc' ],
  947. [ 'ca', 'cb', 'cc' ]
  948. ] );
  949. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  950. [ 'aa', 'ab', 'ac', '03' ],
  951. [ 'ba', 'bb', 'bc', '13' ],
  952. [ 'ca', 'cb', 'cc', '23' ],
  953. [ '30', '31', '32', '33' ]
  954. ] ) );
  955. assertSelectedCells( model, [
  956. [ 1, 1, 1, 0 ],
  957. [ 1, 1, 1, 0 ],
  958. [ 1, 1, 1, 0 ],
  959. [ 0, 0, 0, 0 ]
  960. ] );
  961. } );
  962. it( 'handles pasting simple table over table with multi-spans (no span exceeds selection)', () => {
  963. // +----+----+----+----+----+----+
  964. // | 00 | 02 | 03 | 05 |
  965. // + + + +----+
  966. // | | | | 15 |
  967. // +----+----+----+ +----+
  968. // | 20 | 21 | | 25 |
  969. // + +----+----+----+----+----+
  970. // | | 31 | 32 | 34 | 35 |
  971. // +----+----+----+----+----+----+
  972. // | 40 | 41 | 42 | 43 | 44 | 45 |
  973. // +----+----+----+----+----+----+
  974. setModelData( model, modelTable( [
  975. [
  976. { contents: '00', colspan: 2, rowspan: 2 },
  977. { contents: '02', rowspan: 2 },
  978. { contents: '03', colspan: 2, rowspan: 3 },
  979. '05'
  980. ],
  981. [ '15' ],
  982. [ { contents: '20', rowspan: 2 }, { contents: '21', colspan: 2 }, '25' ],
  983. [ '31', { contents: '32', colspan: 2 }, '34', '35' ],
  984. [ '40', '41', '42', '43', '44', '45' ]
  985. ] ) );
  986. tableSelection.setCellSelection(
  987. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  988. modelRoot.getNodeByPath( [ 0, 3, 2 ] ) // Cell 34.
  989. );
  990. pasteTable( [
  991. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  992. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  993. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  994. [ 'da', 'db', 'dc', 'dd', 'de' ]
  995. ] );
  996. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  997. [ 'aa', 'ab', 'ac', 'ad', 'ae', '05' ],
  998. [ 'ba', 'bb', 'bc', 'bd', 'be', '15' ],
  999. [ 'ca', 'cb', 'cc', 'cd', 'ce', '25' ],
  1000. [ 'da', 'db', 'dc', 'dd', 'de', '35' ],
  1001. [ '40', '41', '42', '43', '44', '45' ]
  1002. ] ) );
  1003. assertSelectedCells( model, [
  1004. [ 1, 1, 1, 1, 1, 0 ],
  1005. [ 1, 1, 1, 1, 1, 0 ],
  1006. [ 1, 1, 1, 1, 1, 0 ],
  1007. [ 1, 1, 1, 1, 1, 0 ],
  1008. [ 0, 0, 0, 0, 0, 0 ]
  1009. ] );
  1010. } );
  1011. it( 'handles pasting simple table over a table with rowspan (rowspan before selection)', () => {
  1012. // +----+----+----+----+----+
  1013. // | 00 | 01 | 02 | 03 | 04 |
  1014. // +----+----+----+----+----+
  1015. // | 10 | 11 | 12 | 13 | 14 |
  1016. // +----+ +----+----+----+
  1017. // | 20 | | 22 | 23 | 24 |
  1018. // +----+ +----+----+----+
  1019. // | 30 | | 32 | 33 | 34 |
  1020. // +----+----+----+----+----+
  1021. // | 40 | 41 | 42 | 43 | 44 |
  1022. // +----+----+----+----+----+
  1023. setModelData( model, modelTable( [
  1024. [ '00', '01', '02', '03', '04' ],
  1025. [ '10', { contents: '11', rowspan: 3 }, '12', '13', '14' ],
  1026. [ '20', '22', '23', '24' ],
  1027. [ '30', '32', '33', '34' ],
  1028. [ '40', '41', '42', '43', '44' ]
  1029. ] ) );
  1030. tableSelection.setCellSelection(
  1031. modelRoot.getNodeByPath( [ 0, 1, 2 ] ),
  1032. modelRoot.getNodeByPath( [ 0, 3, 2 ] ) // Cell 33.
  1033. );
  1034. pasteTable( [
  1035. [ 'aa', 'ab' ],
  1036. [ 'ba', 'bb' ],
  1037. [ 'ca', 'cb' ]
  1038. ] );
  1039. // +----+----+----+----+----+
  1040. // | 00 | 01 | 02 | 03 | 04 |
  1041. // +----+----+----+----+----+
  1042. // | 10 | 11 | aa | ab | 14 |
  1043. // +----+ +----+----+----+
  1044. // | 20 | | ba | bb | 24 |
  1045. // +----+ +----+----+----+
  1046. // | 30 | | ca | cb | 34 |
  1047. // +----+----+----+----+----+
  1048. // | 40 | 41 | 42 | 43 | 44 |
  1049. // +----+----+----+----+----+
  1050. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1051. [ '00', '01', '02', '03', '04' ],
  1052. [ '10', { contents: '11', rowspan: 3 }, 'aa', 'ab', '14' ],
  1053. [ '20', 'ba', 'bb', '24' ],
  1054. [ '30', 'ca', 'cb', '34' ],
  1055. [ '40', '41', '42', '43', '44' ]
  1056. ] ) );
  1057. /* eslint-disable no-multi-spaces */
  1058. assertSelectedCells( model, [
  1059. [ 0, 0, 0, 0, 0 ],
  1060. [ 0, 0, 1, 1, 0 ],
  1061. [ 0, 1, 1, 0 ],
  1062. [ 0, 1, 1, 0 ],
  1063. [ 0, 0, 0, 0, 0 ]
  1064. ] );
  1065. /* eslint-enable no-multi-spaces */
  1066. } );
  1067. it( 'handles pasting simple table over a table with rowspans (rowspan before selection)', () => {
  1068. // +----+----+----+----+----+----+
  1069. // | 00 | 01 | 02 | 03 | 04 | 05 |
  1070. // +----+----+----+----+----+----+
  1071. // | 10 | 12 | 13 | 14 | 15 |
  1072. // +----+----+----+----+----+----+
  1073. // | 20 | 23 | 24 | 25 |
  1074. // +----+----+----+----+----+----+
  1075. // | 30 | 31 | 32 | 33 | 34 | 35 |
  1076. // +----+----+----+----+----+----+
  1077. setModelData( model, modelTable( [
  1078. [ '00', '01', '02', '03', '04', '05' ],
  1079. [ { contents: '10', colspan: 2 }, '12', '13', '14', '15' ],
  1080. [ { contents: '20', colspan: 3 }, '23', '24', '25' ],
  1081. [ '30', '31', '32', '33', '34', '35' ]
  1082. ] ) );
  1083. tableSelection.setCellSelection(
  1084. modelRoot.getNodeByPath( [ 0, 1, 2 ] ), // Cell 13.
  1085. modelRoot.getNodeByPath( [ 0, 2, 2 ] ) // Cell 24.
  1086. );
  1087. pasteTable( [
  1088. [ 'aa', 'ab' ],
  1089. [ 'ba', 'bb' ]
  1090. ] );
  1091. // +----+----+----+----+----+----+
  1092. // | 00 | 01 | 02 | 03 | 04 | 05 |
  1093. // +----+----+----+----+----+----+
  1094. // | 10 | 12 | aa | ab | 15 |
  1095. // +----+----+----+----+----+----+
  1096. // | 20 | ba | bb | 25 |
  1097. // +----+----+----+----+----+----+
  1098. // | 30 | 31 | 32 | 33 | 34 | 35 |
  1099. // +----+----+----+----+----+----+
  1100. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1101. [ '00', '01', '02', '03', '04', '05' ],
  1102. [ { contents: '10', colspan: 2 }, '12', 'aa', 'ab', '15' ],
  1103. [ { contents: '20', colspan: 3 }, 'ba', 'bb', '25' ],
  1104. [ '30', '31', '32', '33', '34', '35' ]
  1105. ] ) );
  1106. /* eslint-disable no-multi-spaces */
  1107. assertSelectedCells( model, [
  1108. [ 0, 0, 0, 0, 0, 0 ],
  1109. [ 0, 0, 1, 1, 0 ],
  1110. [ 0, 1, 1, 0 ],
  1111. [ 0, 0, 0, 0, 0, 0 ]
  1112. ] );
  1113. /* eslint-enable no-multi-spaces */
  1114. } );
  1115. // TODO: Skipped case - should allow pasting but no tools to compare areas (like in MergeCellsCommand).
  1116. it.skip( 'handles pasting table that has cell with colspan (last row in selection is spanned)', () => {
  1117. // +----+----+----+----+
  1118. // | 00 | 01 | 02 | 03 |
  1119. // +----+----+----+----+
  1120. // | 10 | 11 | 13 |
  1121. // + + +----+
  1122. // | | | 23 |
  1123. // +----+----+----+----+
  1124. // | 30 | 31 | 32 | 33 |
  1125. // +----+----+----+----+
  1126. setModelData( model, modelTable( [
  1127. [ '00', '01', '02', '03' ],
  1128. [ { contents: '10', rowspan: 2 }, { contents: '11', colspan: 2, rowspan: 2 }, '13' ],
  1129. [ '23' ],
  1130. [ '30', '31', '32', '33' ]
  1131. ] ) );
  1132. tableSelection.setCellSelection(
  1133. modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
  1134. modelRoot.getNodeByPath( [ 0, 1, 0 ] )
  1135. );
  1136. pasteTable( [
  1137. [ 'aa', 'ab', 'ac' ],
  1138. [ 'ba', 'bb', 'bc' ],
  1139. [ 'ca', 'cb', 'cc' ]
  1140. ] );
  1141. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1142. [ 'aa', 'ab', 'ac', '03' ],
  1143. [ 'ba', 'bb', 'bc', '13' ],
  1144. [ 'ca', 'cb', 'cc', '23' ],
  1145. [ '30', '31', '32', '33' ]
  1146. ] ) );
  1147. assertSelectedCells( model, [
  1148. [ 1, 1, 1, 0 ],
  1149. [ 1, 1, 1, 0 ],
  1150. [ 1, 1, 1, 0 ],
  1151. [ 0, 0, 0, 0 ]
  1152. ] );
  1153. } );
  1154. } );
  1155. describe( 'content and paste tables have spans', () => {
  1156. it( 'handles pasting colspanned table over table with colspans (no colspan exceeds selection)', () => {
  1157. setModelData( model, modelTable( [
  1158. [ '00[]', '01', '02', '03' ],
  1159. [ { colspan: 3, contents: '10' }, '13' ],
  1160. [ { colspan: 2, contents: '20' }, '22', '23' ],
  1161. [ '30', '31', { colspan: 2, contents: '31' } ]
  1162. ] ) );
  1163. tableSelection.setCellSelection(
  1164. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1165. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  1166. );
  1167. pasteTable( [
  1168. [ 'aa', { colspan: 2, contents: 'ab' } ],
  1169. [ { colspan: 3, contents: 'ba' } ],
  1170. [ 'ca', 'cb', 'cc' ]
  1171. ] );
  1172. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1173. [ 'aa', { colspan: 2, contents: 'ab' }, '03' ],
  1174. [ { colspan: 3, contents: 'ba' }, '13' ],
  1175. [ 'ca', 'cb', 'cc', '23' ],
  1176. [ '30', '31', { colspan: 2, contents: '31' } ]
  1177. ] ) );
  1178. /* eslint-disable no-multi-spaces */
  1179. assertSelectedCells( model, [
  1180. [ 1, 1, 0 ],
  1181. [ 1, 0 ],
  1182. [ 1, 1, 1, 0 ],
  1183. [ 0, 0, 0 ]
  1184. ] );
  1185. /* eslint-enable no-multi-spaces */
  1186. } );
  1187. it( 'handles pasting rowspanned table over table with rowspans (no rowspan exceeds selection)', () => {
  1188. setModelData( model, modelTable( [
  1189. [ { rowspan: 3, contents: '00' }, { rowspan: 2, contents: '01' }, '02', '03' ],
  1190. [ { rowspan: 2, contents: '12' }, '13' ],
  1191. [ '21', '23' ],
  1192. [ '30', '31', '32', '33' ]
  1193. ] ) );
  1194. tableSelection.setCellSelection(
  1195. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1196. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  1197. );
  1198. pasteTable( [
  1199. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  1200. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  1201. [ 'cc', 'cd' ]
  1202. ] );
  1203. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1204. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  1205. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  1206. [ 'cc', 'cd' ],
  1207. [ '30', '31', '32', '33' ]
  1208. ] ) );
  1209. /* eslint-disable no-multi-spaces */
  1210. assertSelectedCells( model, [
  1211. [ 1, 1, 1, 1 ],
  1212. [ 1, 1 ],
  1213. [ 1, 1 ],
  1214. [ 0, 0, 0, 0 ]
  1215. ] );
  1216. /* eslint-enable no-multi-spaces */
  1217. } );
  1218. it( 'handles pasting multi-spanned table over table with multi-spans (no span exceeds selection)', () => {
  1219. // +----+----+----+----+----+----+
  1220. // | 00 | 02 | 03 | 05 |
  1221. // + + + +----+
  1222. // | | | | 15 |
  1223. // +----+----+----+ +----+
  1224. // | 20 | 21 | | 25 |
  1225. // + +----+----+----+----+----+
  1226. // | | 31 | 32 | 34 | 35 |
  1227. // +----+----+----+----+----+----+
  1228. // | 40 | 41 | 42 | 43 | 44 | 45 |
  1229. // +----+----+----+----+----+----+
  1230. setModelData( model, modelTable( [
  1231. [
  1232. { contents: '00', colspan: 2, rowspan: 2 },
  1233. { contents: '02', rowspan: 2 },
  1234. { contents: '03', colspan: 2, rowspan: 3 },
  1235. '05'
  1236. ],
  1237. [ '15' ],
  1238. [ { contents: '20', rowspan: 2 }, { contents: '21', colspan: 2 }, '25' ],
  1239. [ '31', { contents: '32', colspan: 2 }, '34', '35' ],
  1240. [ '40', '41', '42', '43', '44', '45' ]
  1241. ] ) );
  1242. tableSelection.setCellSelection(
  1243. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1244. modelRoot.getNodeByPath( [ 0, 3, 2 ] )
  1245. );
  1246. // +----+----+----+----+----+
  1247. // | aa | ac | ad | ae |
  1248. // +----+----+----+----+ +
  1249. // | ba | bb | |
  1250. // +----+ +----+
  1251. // | ca | | ce |
  1252. // + +----+----+----+----+
  1253. // | | db | dc | dd |
  1254. // +----+----+----+----+----+
  1255. pasteTable( [
  1256. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 } ],
  1257. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 } ],
  1258. [ { contents: 'ca', rowspan: 2 }, 'ce' ],
  1259. [ 'db', 'dc', { contents: 'dd', colspan: 2 } ]
  1260. ] );
  1261. // +----+----+----+----+----+----+
  1262. // | aa | ac | ad | ae | 05 |
  1263. // +----+----+----+----+ +----+
  1264. // | ba | bb | | 15 |
  1265. // +----+ +----+----+
  1266. // | ca | | ce | 25 |
  1267. // + +----+----+----+----+----+
  1268. // | | db | dc | dd | 35 |
  1269. // +----+----+----+----+----+----+
  1270. // | 40 | 41 | 42 | 43 | 44 | 45 |
  1271. // +----+----+----+----+----+----+
  1272. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1273. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 }, '05' ],
  1274. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 }, '15' ],
  1275. [ { contents: 'ca', rowspan: 2 }, 'ce', '25' ],
  1276. [ 'db', 'dc', { contents: 'dd', colspan: 2 }, '35' ],
  1277. [ '40', '41', '42', '43', '44', '45' ]
  1278. ] ) );
  1279. /* eslint-disable no-multi-spaces */
  1280. assertSelectedCells( model, [
  1281. [ 1, 1, 1, 1, 0 ],
  1282. [ 1, 1, 0 ],
  1283. [ 1, 1, 0 ],
  1284. [ 1, 1, 1, 0 ],
  1285. [ 0, 0, 0, 0, 0, 0 ]
  1286. ] );
  1287. /* eslint-enable no-multi-spaces */
  1288. } );
  1289. // TODO: Skipped case - should allow pasting but no tools to compare areas (like in MergeCellsCommand).
  1290. it.skip( 'handles pasting table that has cell with colspan (last row in selection is spanned)', () => {
  1291. // +----+----+----+----+
  1292. // | 00 | 01 | 02 | 03 |
  1293. // +----+----+----+----+
  1294. // | 10 | 11 | 13 |
  1295. // + + +----+
  1296. // | | | 23 |
  1297. // +----+----+----+----+
  1298. // | 30 | 31 | 32 | 33 |
  1299. // +----+----+----+----+
  1300. setModelData( model, modelTable( [
  1301. [ '00', '01', '02', '03' ],
  1302. [ { contents: '10', rowspan: 2 }, { contents: '11', colspan: 2, rowspan: 2 }, '13' ],
  1303. [ '23' ],
  1304. [ '30', '31', '32', '33' ]
  1305. ] ) );
  1306. tableSelection.setCellSelection(
  1307. modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
  1308. modelRoot.getNodeByPath( [ 0, 1, 0 ] )
  1309. );
  1310. // +----+----+----+
  1311. // | aa | ab | ac |
  1312. // +----+----+----+
  1313. // | ba | bc |
  1314. // + +----+
  1315. // | | cc |
  1316. // +----+----+----+
  1317. pasteTable( [
  1318. [ 'aa', 'ab', 'ac' ],
  1319. [ { contents: 'ba', colspan: 2, rowspan: 2 }, 'bc' ],
  1320. [ 'cc' ]
  1321. ] );
  1322. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1323. [ '00', '01', '02', '03' ],
  1324. [ '10', { colspan: 2, contents: 'aa' }, '13' ],
  1325. [ '20', 'ba', 'bb', '23' ],
  1326. [ '30', '31', '32', '33' ]
  1327. ] ) );
  1328. /* eslint-disable no-multi-spaces */
  1329. assertSelectedCells( model, [
  1330. [ 0, 0, 0, 0 ],
  1331. [ 0, 1, 0 ],
  1332. [ 0, 1, 1, 0 ],
  1333. [ 0, 0, 0, 0 ]
  1334. ] );
  1335. /* eslint-enable no-multi-spaces */
  1336. } );
  1337. } );
  1338. } );
  1339. describe( 'pasted table is bigger than the selected area', () => {
  1340. describe( 'no spans', () => {
  1341. it( 'handles simple table paste to a simple table fragment - at the beginning of a table', () => {
  1342. tableSelection.setCellSelection(
  1343. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1344. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  1345. );
  1346. pasteTable( [
  1347. [ 'aa', 'ab', 'ac' ],
  1348. [ 'ba', 'bb', 'bc' ],
  1349. [ 'ca', 'cb', 'cc' ]
  1350. ] );
  1351. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1352. [ 'aa', 'ab', '02', '03' ],
  1353. [ 'ba', 'bb', '12', '13' ],
  1354. [ '20', '21', '22', '23' ],
  1355. [ '30', '31', '32', '33' ]
  1356. ] ) );
  1357. assertSelectedCells( model, [
  1358. [ 1, 1, 0, 0 ],
  1359. [ 1, 1, 0, 0 ],
  1360. [ 0, 0, 0, 0 ],
  1361. [ 0, 0, 0, 0 ]
  1362. ] );
  1363. } );
  1364. it( 'handles simple table paste to a simple table fragment - at the end of a table', () => {
  1365. tableSelection.setCellSelection(
  1366. modelRoot.getNodeByPath( [ 0, 2, 2 ] ),
  1367. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  1368. );
  1369. pasteTable( [
  1370. [ 'aa', 'ab', 'ac' ],
  1371. [ 'ba', 'bb', 'bc' ],
  1372. [ 'ca', 'cb', 'cc' ]
  1373. ] );
  1374. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1375. [ '00', '01', '02', '03' ],
  1376. [ '10', '11', '12', '13' ],
  1377. [ '20', '21', 'aa', 'ab' ],
  1378. [ '30', '31', 'ba', 'bb' ]
  1379. ] ) );
  1380. assertSelectedCells( model, [
  1381. [ 0, 0, 0, 0 ],
  1382. [ 0, 0, 0, 0 ],
  1383. [ 0, 0, 1, 1 ],
  1384. [ 0, 0, 1, 1 ]
  1385. ] );
  1386. } );
  1387. it( 'handles simple table paste to a simple table fragment - in the middle of a table', () => {
  1388. tableSelection.setCellSelection(
  1389. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  1390. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  1391. );
  1392. pasteTable( [
  1393. [ 'aa', 'ab', 'ac' ],
  1394. [ 'ba', 'bb', 'bc' ],
  1395. [ 'ca', 'cb', 'cc' ]
  1396. ] );
  1397. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1398. [ '00', '01', '02', '03' ],
  1399. [ '10', 'aa', 'ab', '13' ],
  1400. [ '20', 'ba', 'bb', '23' ],
  1401. [ '30', '31', '32', '33' ]
  1402. ] ) );
  1403. assertSelectedCells( model, [
  1404. [ 0, 0, 0, 0 ],
  1405. [ 0, 1, 1, 0 ],
  1406. [ 0, 1, 1, 0 ],
  1407. [ 0, 0, 0, 0 ]
  1408. ] );
  1409. } );
  1410. it( 'handles paste to a simple row fragment - in the middle of a table', () => {
  1411. tableSelection.setCellSelection(
  1412. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  1413. modelRoot.getNodeByPath( [ 0, 1, 2 ] )
  1414. );
  1415. pasteTable( [
  1416. [ 'aa', 'ab', 'ac' ],
  1417. [ 'ba', 'bb', 'bc' ],
  1418. [ 'ca', 'cb', 'cc' ]
  1419. ] );
  1420. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1421. [ '00', '01', '02', '03' ],
  1422. [ '10', 'aa', 'ab', '13' ],
  1423. [ '20', '21', '22', '23' ],
  1424. [ '30', '31', '32', '33' ]
  1425. ] ) );
  1426. assertSelectedCells( model, [
  1427. [ 0, 0, 0, 0 ],
  1428. [ 0, 1, 1, 0 ],
  1429. [ 0, 0, 0, 0 ],
  1430. [ 0, 0, 0, 0 ]
  1431. ] );
  1432. } );
  1433. it( 'handles paste to a simple column fragment - in the middle of a table', () => {
  1434. tableSelection.setCellSelection(
  1435. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  1436. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  1437. );
  1438. pasteTable( [
  1439. [ 'aa', 'ab', 'ac' ],
  1440. [ 'ba', 'bb', 'bc' ],
  1441. [ 'ca', 'cb', 'cc' ]
  1442. ] );
  1443. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1444. [ '00', '01', '02', '03' ],
  1445. [ '10', 'aa', '12', '13' ],
  1446. [ '20', 'ba', '22', '23' ],
  1447. [ '30', '31', '32', '33' ]
  1448. ] ) );
  1449. assertSelectedCells( model, [
  1450. [ 0, 0, 0, 0 ],
  1451. [ 0, 1, 0, 0 ],
  1452. [ 0, 1, 0, 0 ],
  1453. [ 0, 0, 0, 0 ]
  1454. ] );
  1455. } );
  1456. it( 'handles simple table paste to a simple table fragment - whole table selected', () => {
  1457. tableSelection.setCellSelection(
  1458. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1459. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  1460. );
  1461. pasteTable( [
  1462. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  1463. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  1464. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  1465. [ 'da', 'db', 'dc', 'dd', 'de' ],
  1466. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  1467. ] );
  1468. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1469. [ 'aa', 'ab', 'ac', 'ad' ],
  1470. [ 'ba', 'bb', 'bc', 'bd' ],
  1471. [ 'ca', 'cb', 'cc', 'cd' ],
  1472. [ 'da', 'db', 'dc', 'dd' ]
  1473. ] ) );
  1474. assertSelectedCells( model, [
  1475. [ 1, 1, 1, 1 ],
  1476. [ 1, 1, 1, 1 ],
  1477. [ 1, 1, 1, 1 ],
  1478. [ 1, 1, 1, 1 ]
  1479. ] );
  1480. } );
  1481. } );
  1482. describe( 'pasted table has spans', () => {
  1483. it( 'handles pasting table that has cell with colspan', () => {
  1484. tableSelection.setCellSelection(
  1485. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  1486. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  1487. );
  1488. pasteTable( [
  1489. [ { colspan: 3, contents: 'aa' } ],
  1490. [ 'ba', 'bb', 'bc' ]
  1491. ] );
  1492. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1493. [ '00', '01', '02', '03' ],
  1494. [ '10', { colspan: 2, contents: 'aa' }, '13' ],
  1495. [ '20', 'ba', 'bb', '23' ],
  1496. [ '30', '31', '32', '33' ]
  1497. ] ) );
  1498. /* eslint-disable no-multi-spaces */
  1499. assertSelectedCells( model, [
  1500. [ 0, 0, 0, 0 ],
  1501. [ 0, 1, 0 ],
  1502. [ 0, 1, 1, 0 ],
  1503. [ 0, 0, 0, 0 ]
  1504. ] );
  1505. /* eslint-enable no-multi-spaces */
  1506. } );
  1507. it( 'handles pasting table that has many cells with various colspan', () => {
  1508. tableSelection.setCellSelection(
  1509. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1510. modelRoot.getNodeByPath( [ 0, 3, 2 ] )
  1511. );
  1512. pasteTable( [
  1513. [ 'aa', { colspan: 3, contents: 'ab' } ],
  1514. [ { colspan: 4, contents: 'ba' } ],
  1515. [ 'ca', 'cb', 'cc', 'cd' ],
  1516. [ { colspan: 2, contents: 'da' }, 'dc', 'dd' ]
  1517. ] );
  1518. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1519. [ 'aa', { colspan: 2, contents: 'ab' }, '03' ],
  1520. [ { colspan: 3, contents: 'ba' }, '13' ],
  1521. [ 'ca', 'cb', 'cc', '23' ],
  1522. [ { colspan: 2, contents: 'da' }, 'dc', '33' ]
  1523. ] ) );
  1524. /* eslint-disable no-multi-spaces */
  1525. assertSelectedCells( model, [
  1526. [ 1, 1, 0 ],
  1527. [ 1, 0 ],
  1528. [ 1, 1, 1, 0 ],
  1529. [ 1, 1, 0 ]
  1530. ] );
  1531. /* eslint-enable no-multi-spaces */
  1532. } );
  1533. it( 'handles pasting table that has cell with rowspan', () => {
  1534. tableSelection.setCellSelection(
  1535. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  1536. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  1537. );
  1538. pasteTable( [
  1539. [ { rowspan: 3, contents: 'aa' }, 'ab' ],
  1540. [ 'bb' ],
  1541. [ 'cb' ]
  1542. ] );
  1543. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1544. [ '00', '01', '02', '03' ],
  1545. [ '10', { rowspan: 2, contents: 'aa' }, 'ab', '13' ],
  1546. [ '20', 'bb', '23' ],
  1547. [ '30', '31', '32', '33' ]
  1548. ] ) );
  1549. /* eslint-disable no-multi-spaces */
  1550. assertSelectedCells( model, [
  1551. [ 0, 0, 0, 0 ],
  1552. [ 0, 1, 1, 0 ],
  1553. [ 0, 1, 0 ],
  1554. [ 0, 0, 0, 0 ]
  1555. ] );
  1556. /* eslint-enable no-multi-spaces */
  1557. } );
  1558. it( 'handles pasting table that has many cells with various rowspan', () => {
  1559. tableSelection.setCellSelection(
  1560. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1561. modelRoot.getNodeByPath( [ 0, 2, 3 ] )
  1562. );
  1563. pasteTable( [
  1564. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad', 'ae' ],
  1565. [ { rowspan: 3, contents: 'ba' }, 'bd', 'be' ],
  1566. [ 'cc', 'cd', 'ce' ],
  1567. [ 'da', 'db', 'dc', 'dd' ]
  1568. ] );
  1569. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1570. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  1571. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  1572. [ 'cc', 'cd' ],
  1573. [ '30', '31', '32', '33' ]
  1574. ] ) );
  1575. /* eslint-disable no-multi-spaces */
  1576. assertSelectedCells( model, [
  1577. [ 1, 1, 1, 1 ],
  1578. [ 1, 1 ],
  1579. [ 1, 1 ],
  1580. [ 0, 0, 0, 0 ]
  1581. ] );
  1582. /* eslint-enable no-multi-spaces */
  1583. } );
  1584. it( 'handles pasting multi-spanned table', () => {
  1585. setModelData( model, modelTable( [
  1586. [ '00', '01', '02', '03', '04', '05' ],
  1587. [ '10', '11', '12', '13', '14', '15' ],
  1588. [ '20', '21', '22', '23', '24', '25' ],
  1589. [ '30', '31', '32', '33', '34', '35' ],
  1590. [ '40', '41', '42', '43', '44', '45' ]
  1591. ] ) );
  1592. tableSelection.setCellSelection(
  1593. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  1594. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  1595. );
  1596. // +----+----+----+----+----+
  1597. // | aa | ac | ad | ae |
  1598. // +----+----+----+----+ +
  1599. // | ba | bb | |
  1600. // +----+ +----+
  1601. // | ca | | ce |
  1602. // + +----+----+----+----+
  1603. // | | db | dc | dd |
  1604. // +----+----+----+----+----+
  1605. pasteTable( [
  1606. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 } ],
  1607. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 } ],
  1608. [ { contents: 'ca', rowspan: 2 }, 'ce' ],
  1609. [ 'db', 'dc', { contents: 'dd', colspan: 2 } ]
  1610. ] );
  1611. // +----+----+----+----+----+----+
  1612. // | 00 | 01 | 02 | 03 | 04 | 05 |
  1613. // +----+----+----+----+----+----+
  1614. // | 10 | aa | ac | 14 | 15 |
  1615. // +----+----+----+----+----+----+
  1616. // | 20 | ba | bb | 24 | 25 |
  1617. // +----+----+ +----+----+
  1618. // | 30 | ca | | 34 | 35 |
  1619. // +----+----+----+----+----+----+
  1620. // | 40 | 41 | 42 | 43 | 44 | 45 |
  1621. // +----+----+----+----+----+----+
  1622. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1623. [ '00', '01', '02', '03', '04', '05' ],
  1624. [ '10', { contents: 'aa', colspan: 2 }, 'ac', '14', '15' ],
  1625. [ '20', 'ba', { contents: 'bb', colspan: 2, rowspan: 2 }, '24', '25' ],
  1626. [ '30', 'ca', '34', '35' ],
  1627. [ '40', '41', '42', '43', '44', '45' ]
  1628. ] ) );
  1629. /* eslint-disable no-multi-spaces */
  1630. assertSelectedCells( model, [
  1631. [ 0, 0, 0, 0, 0, 0 ],
  1632. [ 0, 1, 1, 0, 0 ],
  1633. [ 0, 1, 1, 0, 0 ],
  1634. [ 0, 1, 0, 0 ],
  1635. [ 0, 0, 0, 0, 0, 0 ]
  1636. ] );
  1637. /* eslint-enable no-multi-spaces */
  1638. } );
  1639. } );
  1640. } );
  1641. describe( 'pasted table is smaller than the selected area', () => {
  1642. it( 'blocks this case', () => {
  1643. tableSelection.setCellSelection(
  1644. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1645. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  1646. );
  1647. // Catches the temporary console log in the CK_DEBUG mode.
  1648. sinon.stub( console, 'log' );
  1649. pasteTable( [
  1650. [ 'aa', 'ab' ],
  1651. [ 'ba', 'bb' ]
  1652. ] );
  1653. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1654. [ '00', '01', '02', '03' ],
  1655. [ '10', '11', '12', '13' ],
  1656. [ '20', '21', '22', '23' ],
  1657. [ '30', '31', '32', '33' ]
  1658. ] ) );
  1659. assertSelectedCells( model, [
  1660. [ 1, 1, 1, 1 ],
  1661. [ 1, 1, 1, 1 ],
  1662. [ 1, 1, 1, 1 ],
  1663. [ 1, 1, 1, 1 ]
  1664. ] );
  1665. } );
  1666. } );
  1667. } );
  1668. describe( 'Clipboard integration - paste (content scenarios)', () => {
  1669. it( 'handles multiple paragraphs in table cell', async () => {
  1670. await createEditor();
  1671. setModelData( model, modelTable( [
  1672. [ '00', '01', '02' ],
  1673. [ '01', '11', '12' ],
  1674. [ '02', '21', '22' ]
  1675. ] ) );
  1676. tableSelection.setCellSelection(
  1677. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1678. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  1679. );
  1680. pasteTable( [
  1681. [ '<p>a</p><p>a</p><p>a</p>', 'ab' ],
  1682. [ 'ba', 'bb' ]
  1683. ] );
  1684. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1685. [ '<paragraph>a</paragraph><paragraph>a</paragraph><paragraph>a</paragraph>', 'ab', '02' ],
  1686. [ 'ba', 'bb', '12' ],
  1687. [ '02', '21', '22' ]
  1688. ] ) );
  1689. } );
  1690. it( 'handles image in table cell', async () => {
  1691. await createEditor( [ ImageEditing, ImageCaptionEditing ] );
  1692. setModelData( model, modelTable( [
  1693. [ '00', '01', '02' ],
  1694. [ '01', '11', '12' ],
  1695. [ '02', '21', '22' ]
  1696. ] ) );
  1697. tableSelection.setCellSelection(
  1698. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1699. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  1700. );
  1701. pasteTable( [
  1702. [ '<img src="/assets/sample.jpg">', 'ab' ],
  1703. [ 'ba', 'bb' ]
  1704. ] );
  1705. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1706. [ '<image src="/assets/sample.jpg"><caption></caption></image>', 'ab', '02' ],
  1707. [ 'ba', 'bb', '12' ],
  1708. [ '02', '21', '22' ]
  1709. ] ) );
  1710. } );
  1711. it( 'handles mixed nested content in table cell', async () => {
  1712. await createEditor( [ ImageEditing, ImageCaptionEditing, BlockQuoteEditing, HorizontalLineEditing, ListEditing ] );
  1713. setModelData( model, modelTable( [
  1714. [ '00', '01', '02' ],
  1715. [ '01', '11', '12' ],
  1716. [ '02', '21', '22' ]
  1717. ] ) );
  1718. tableSelection.setCellSelection(
  1719. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1720. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  1721. );
  1722. const img = '<img src="/assets/sample.jpg">';
  1723. const list = '<ul><li>foo</li><li>bar</li></ul>';
  1724. const blockquote = `<blockquote><p>baz</p>${ list }</blockquote>`;
  1725. pasteTable( [
  1726. [ `${ img }${ list }${ blockquote }`, 'ab' ],
  1727. [ 'ba', 'bb' ]
  1728. ] );
  1729. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1730. [
  1731. '<image src="/assets/sample.jpg"><caption></caption></image>' +
  1732. '<listItem listIndent="0" listType="bulleted">foo</listItem>' +
  1733. '<listItem listIndent="0" listType="bulleted">bar</listItem>' +
  1734. '<blockQuote>' +
  1735. '<paragraph>baz</paragraph>' +
  1736. '<listItem listIndent="0" listType="bulleted">foo</listItem>' +
  1737. '<listItem listIndent="0" listType="bulleted">bar</listItem>' +
  1738. '</blockQuote>',
  1739. 'ab',
  1740. '02' ],
  1741. [ 'ba', 'bb', '12' ],
  1742. [ '02', '21', '22' ]
  1743. ] ) );
  1744. } );
  1745. it( 'handles table cell properties', async () => {
  1746. await createEditor( [ TableCellPropertiesEditing ] );
  1747. setModelData( model, modelTable( [
  1748. [ '00', '01', '02' ],
  1749. [ '01', '11', '12' ],
  1750. [ '02', '21', '22' ]
  1751. ] ) );
  1752. tableSelection.setCellSelection(
  1753. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1754. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  1755. );
  1756. pasteTable( [
  1757. [ { contents: 'aa', style: 'border:1px solid #f00;background:#ba7;width:1337px' }, 'ab' ],
  1758. [ 'ba', 'bb' ]
  1759. ] );
  1760. const tableCell = model.document.getRoot().getNodeByPath( [ 0, 0, 0 ] );
  1761. expect( tableCell.getAttribute( 'borderColor' ) ).to.deep.equal( {
  1762. top: '#f00',
  1763. right: '#f00',
  1764. bottom: '#f00',
  1765. left: '#f00'
  1766. } );
  1767. expect( tableCell.getAttribute( 'borderStyle' ) ).to.deep.equal( {
  1768. top: 'solid',
  1769. right: 'solid',
  1770. bottom: 'solid',
  1771. left: 'solid'
  1772. } );
  1773. expect( tableCell.getAttribute( 'borderWidth' ) ).to.deep.equal( {
  1774. top: '1px',
  1775. right: '1px',
  1776. bottom: '1px',
  1777. left: '1px'
  1778. } );
  1779. expect( tableCell.getAttribute( 'backgroundColor' ) ).to.equal( '#ba7' );
  1780. expect( tableCell.getAttribute( 'width' ) ).to.equal( '1337px' );
  1781. } );
  1782. it( 'discards table properties', async () => {
  1783. await createEditor( [ TableCellPropertiesEditing ] );
  1784. setModelData( model, modelTable( [
  1785. [ '00', '01', '02' ],
  1786. [ '01', '11', '12' ],
  1787. [ '02', '21', '22' ]
  1788. ] ) );
  1789. tableSelection.setCellSelection(
  1790. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1791. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  1792. );
  1793. const tableStyle = 'border:1px solid #f00;background:#ba7;width:1337px';
  1794. const pastedTable = `<table style="${ tableStyle }"><tr><td>aa</td><td>ab</td></tr><tr><td>ba</td><td>bb</td></tr></table>`;
  1795. const data = {
  1796. dataTransfer: createDataTransfer(),
  1797. preventDefault: sinon.spy(),
  1798. stopPropagation: sinon.spy()
  1799. };
  1800. data.dataTransfer.setData( 'text/html', pastedTable );
  1801. viewDocument.fire( 'paste', data );
  1802. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1803. [ 'aa', 'ab', '02' ],
  1804. [ 'ba', 'bb', '12' ],
  1805. [ '02', '21', '22' ]
  1806. ] ) );
  1807. } );
  1808. } );
  1809. async function createEditor( extraPlugins = [] ) {
  1810. editor = await ClassicTestEditor.create( element, {
  1811. plugins: [ TableEditing, TableClipboard, Paragraph, Clipboard, ...extraPlugins ]
  1812. } );
  1813. model = editor.model;
  1814. modelRoot = model.document.getRoot();
  1815. viewDocument = editor.editing.view.document;
  1816. tableSelection = editor.plugins.get( 'TableSelection' );
  1817. }
  1818. function pasteTable( tableData ) {
  1819. const data = {
  1820. dataTransfer: createDataTransfer(),
  1821. preventDefault: sinon.spy(),
  1822. stopPropagation: sinon.spy()
  1823. };
  1824. data.dataTransfer.setData( 'text/html', viewTable( tableData ) );
  1825. viewDocument.fire( 'paste', data );
  1826. return data;
  1827. }
  1828. function createDataTransfer() {
  1829. const store = new Map();
  1830. return {
  1831. setData( type, data ) {
  1832. store.set( type, data );
  1833. },
  1834. getData( type ) {
  1835. return store.get( type );
  1836. }
  1837. };
  1838. }
  1839. function setupBatchWatch() {
  1840. const createdBatches = new Set();
  1841. model.on( 'applyOperation', ( evt, [ operation ] ) => {
  1842. if ( operation.isDocumentOperation ) {
  1843. createdBatches.add( operation.batch );
  1844. }
  1845. } );
  1846. return createdBatches;
  1847. }
  1848. } );