8
0

tableclipboard-paste.js 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256
  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 */
  6. import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
  7. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  8. import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
  9. import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  10. import TableEditing from '../src/tableediting';
  11. import { assertSelectedCells, modelTable, viewTable } from './_utils/utils';
  12. import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
  13. import TableClipboard from '../src/tableclipboard';
  14. describe( 'table clipboard', () => {
  15. let editor, model, modelRoot, tableSelection, viewDocument, element;
  16. describe( 'Clipboard integration - paste (selection scenarios)', () => {
  17. beforeEach( async () => {
  18. element = document.createElement( 'div' );
  19. document.body.appendChild( element );
  20. editor = await ClassicTestEditor.create( element, {
  21. plugins: [ TableEditing, TableClipboard, Paragraph, Clipboard ]
  22. } );
  23. model = editor.model;
  24. modelRoot = model.document.getRoot();
  25. viewDocument = editor.editing.view.document;
  26. tableSelection = editor.plugins.get( 'TableSelection' );
  27. setModelData( model, modelTable( [
  28. [ '00[]', '01', '02', '03' ],
  29. [ '10', '11', '12', '13' ],
  30. [ '20', '21', '22', '23' ],
  31. [ '30', '31', '32', '33' ]
  32. ] ) );
  33. } );
  34. afterEach( async () => {
  35. await editor.destroy();
  36. element.remove();
  37. } );
  38. it( 'should be disabled in a readonly mode', () => {
  39. editor.isReadOnly = true;
  40. tableSelection.setCellSelection(
  41. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  42. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  43. );
  44. const data = pasteTable( [
  45. [ 'aa', 'ab' ],
  46. [ 'ba', 'bb' ]
  47. ] );
  48. editor.isReadOnly = false;
  49. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  50. [ '00', '01', '02', '03' ],
  51. [ '10', '11', '12', '13' ],
  52. [ '20', '21', '22', '23' ],
  53. [ '30', '31', '32', '33' ]
  54. ] ) );
  55. sinon.assert.calledOnce( data.preventDefault );
  56. } );
  57. it( 'should allow normal paste if no table cells are selected', () => {
  58. const data = {
  59. dataTransfer: createDataTransfer(),
  60. preventDefault: sinon.spy(),
  61. stopPropagation: sinon.spy()
  62. };
  63. data.dataTransfer.setData( 'text/html', '<p>foo</p>' );
  64. viewDocument.fire( 'paste', data );
  65. editor.isReadOnly = false;
  66. assertEqualMarkup( getModelData( model ), modelTable( [
  67. [ '00foo[]', '01', '02', '03' ],
  68. [ '10', '11', '12', '13' ],
  69. [ '20', '21', '22', '23' ],
  70. [ '30', '31', '32', '33' ]
  71. ] ) );
  72. } );
  73. it( 'should block non-rectangular selection', () => {
  74. setModelData( model, modelTable( [
  75. [ { contents: '00', colspan: 3 } ],
  76. [ '10', '11', '12' ],
  77. [ '20', '21', '22' ]
  78. ] ) );
  79. tableSelection.setCellSelection(
  80. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  81. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  82. );
  83. pasteTable( [
  84. [ 'aa', 'ab' ],
  85. [ 'ba', 'bb' ]
  86. ] );
  87. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  88. [ { contents: '00', colspan: 3 } ],
  89. [ '10', '11', '12' ],
  90. [ '20', '21', '22' ]
  91. ] ) );
  92. } );
  93. describe( 'single cell selected', () => {
  94. it( 'blocks this case', () => {
  95. setModelData( model, modelTable( [
  96. [ '00', '01', '02' ],
  97. [ '10', '11', '12' ],
  98. [ '20', '21', '22' ]
  99. ] ) );
  100. tableSelection.setCellSelection(
  101. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  102. modelRoot.getNodeByPath( [ 0, 0, 0 ] )
  103. );
  104. pasteTable( [
  105. [ 'aa', 'ab' ],
  106. [ 'ba', 'bb' ]
  107. ] );
  108. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  109. [ '00', '01', '02' ],
  110. [ '10', '11', '12' ],
  111. [ '20', '21', '22' ]
  112. ] ) );
  113. } );
  114. } );
  115. describe( 'pasted table is equal to the selected area', () => {
  116. describe( 'no spans', () => {
  117. it( 'handles simple table paste to a simple table fragment - at the beginning of a table', () => {
  118. tableSelection.setCellSelection(
  119. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  120. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  121. );
  122. pasteTable( [
  123. [ 'aa', 'ab' ],
  124. [ 'ba', 'bb' ]
  125. ] );
  126. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  127. [ 'aa', 'ab', '02', '03' ],
  128. [ 'ba', 'bb', '12', '13' ],
  129. [ '20', '21', '22', '23' ],
  130. [ '30', '31', '32', '33' ]
  131. ] ) );
  132. assertSelectedCells( model, [
  133. [ 1, 1, 0, 0 ],
  134. [ 1, 1, 0, 0 ],
  135. [ 0, 0, 0, 0 ],
  136. [ 0, 0, 0, 0 ]
  137. ] );
  138. } );
  139. it( 'handles simple table paste to a simple table fragment - at the end of a table', () => {
  140. tableSelection.setCellSelection(
  141. modelRoot.getNodeByPath( [ 0, 2, 2 ] ),
  142. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  143. );
  144. pasteTable( [
  145. [ 'aa', 'ab' ],
  146. [ 'ba', 'bb' ]
  147. ] );
  148. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  149. [ '00', '01', '02', '03' ],
  150. [ '10', '11', '12', '13' ],
  151. [ '20', '21', 'aa', 'ab' ],
  152. [ '30', '31', 'ba', 'bb' ]
  153. ] ) );
  154. assertSelectedCells( model, [
  155. [ 0, 0, 0, 0 ],
  156. [ 0, 0, 0, 0 ],
  157. [ 0, 0, 1, 1 ],
  158. [ 0, 0, 1, 1 ]
  159. ] );
  160. } );
  161. it( 'handles simple table paste to a simple table fragment - in the middle of a table', () => {
  162. tableSelection.setCellSelection(
  163. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  164. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  165. );
  166. pasteTable( [
  167. [ 'aa', 'ab' ],
  168. [ 'ba', 'bb' ]
  169. ] );
  170. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  171. [ '00', '01', '02', '03' ],
  172. [ '10', 'aa', 'ab', '13' ],
  173. [ '20', 'ba', 'bb', '23' ],
  174. [ '30', '31', '32', '33' ]
  175. ] ) );
  176. assertSelectedCells( model, [
  177. [ 0, 0, 0, 0 ],
  178. [ 0, 1, 1, 0 ],
  179. [ 0, 1, 1, 0 ],
  180. [ 0, 0, 0, 0 ]
  181. ] );
  182. } );
  183. it( 'handles simple row paste to a simple row fragment - in the middle of a table', () => {
  184. tableSelection.setCellSelection(
  185. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  186. modelRoot.getNodeByPath( [ 0, 1, 2 ] )
  187. );
  188. pasteTable( [
  189. [ 'aa', 'ab' ]
  190. ] );
  191. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  192. [ '00', '01', '02', '03' ],
  193. [ '10', 'aa', 'ab', '13' ],
  194. [ '20', '21', '22', '23' ],
  195. [ '30', '31', '32', '33' ]
  196. ] ) );
  197. assertSelectedCells( model, [
  198. [ 0, 0, 0, 0 ],
  199. [ 0, 1, 1, 0 ],
  200. [ 0, 0, 0, 0 ],
  201. [ 0, 0, 0, 0 ]
  202. ] );
  203. } );
  204. it( 'handles simple column paste to a simple column fragment - in the middle of a table', () => {
  205. tableSelection.setCellSelection(
  206. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  207. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  208. );
  209. pasteTable( [
  210. [ 'aa' ],
  211. [ 'ba' ]
  212. ] );
  213. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  214. [ '00', '01', '02', '03' ],
  215. [ '10', 'aa', '12', '13' ],
  216. [ '20', 'ba', '22', '23' ],
  217. [ '30', '31', '32', '33' ]
  218. ] ) );
  219. assertSelectedCells( model, [
  220. [ 0, 0, 0, 0 ],
  221. [ 0, 1, 0, 0 ],
  222. [ 0, 1, 0, 0 ],
  223. [ 0, 0, 0, 0 ]
  224. ] );
  225. } );
  226. it( 'handles simple table paste to a simple table fragment - whole table selected', () => {
  227. tableSelection.setCellSelection(
  228. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  229. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  230. );
  231. pasteTable( [
  232. [ 'aa', 'ab', 'ac', 'ad' ],
  233. [ 'ba', 'bb', 'bc', 'bd' ],
  234. [ 'ca', 'cb', 'cc', 'cd' ],
  235. [ 'da', 'db', 'dc', 'dd' ]
  236. ] );
  237. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  238. [ 'aa', 'ab', 'ac', 'ad' ],
  239. [ 'ba', 'bb', 'bc', 'bd' ],
  240. [ 'ca', 'cb', 'cc', 'cd' ],
  241. [ 'da', 'db', 'dc', 'dd' ]
  242. ] ) );
  243. assertSelectedCells( model, [
  244. [ 1, 1, 1, 1 ],
  245. [ 1, 1, 1, 1 ],
  246. [ 1, 1, 1, 1 ],
  247. [ 1, 1, 1, 1 ]
  248. ] );
  249. } );
  250. } );
  251. describe( 'pasted table has spans', () => {
  252. it( 'handles pasting table that has cell with colspan', () => {
  253. tableSelection.setCellSelection(
  254. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  255. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  256. );
  257. pasteTable( [
  258. [ { colspan: 2, contents: 'aa' } ],
  259. [ 'ba', 'bb' ]
  260. ] );
  261. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  262. [ '00', '01', '02', '03' ],
  263. [ '10', { colspan: 2, contents: 'aa' }, '13' ],
  264. [ '20', 'ba', 'bb', '23' ],
  265. [ '30', '31', '32', '33' ]
  266. ] ) );
  267. /* eslint-disable no-multi-spaces */
  268. assertSelectedCells( model, [
  269. [ 0, 0, 0, 0 ],
  270. [ 0, 1, 0 ],
  271. [ 0, 1, 1, 0 ],
  272. [ 0, 0, 0, 0 ]
  273. ] );
  274. /* eslint-enable no-multi-spaces */
  275. } );
  276. it( 'handles pasting table that has many cells with various colspan', () => {
  277. tableSelection.setCellSelection(
  278. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  279. modelRoot.getNodeByPath( [ 0, 3, 2 ] )
  280. );
  281. pasteTable( [
  282. [ 'aa', { colspan: 2, contents: 'ab' } ],
  283. [ { colspan: 3, contents: 'ba' } ],
  284. [ 'ca', 'cb', 'cc' ],
  285. [ { colspan: 2, contents: 'da' }, 'dc' ]
  286. ] );
  287. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  288. [ 'aa', { colspan: 2, contents: 'ab' }, '03' ],
  289. [ { colspan: 3, contents: 'ba' }, '13' ],
  290. [ 'ca', 'cb', 'cc', '23' ],
  291. [ { colspan: 2, contents: 'da' }, 'dc', '33' ]
  292. ] ) );
  293. /* eslint-disable no-multi-spaces */
  294. assertSelectedCells( model, [
  295. [ 1, 1, 0 ],
  296. [ 1, 0 ],
  297. [ 1, 1, 1, 0 ],
  298. [ 1, 1, 0 ]
  299. ] );
  300. /* eslint-enable no-multi-spaces */
  301. } );
  302. it( 'handles pasting table that has cell with rowspan', () => {
  303. tableSelection.setCellSelection(
  304. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  305. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  306. );
  307. pasteTable( [
  308. [ { rowspan: 2, contents: 'aa' }, 'ab' ],
  309. [ 'bb' ]
  310. ] );
  311. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  312. [ '00', '01', '02', '03' ],
  313. [ '10', { rowspan: 2, contents: 'aa' }, 'ab', '13' ],
  314. [ '20', 'bb', '23' ],
  315. [ '30', '31', '32', '33' ]
  316. ] ) );
  317. /* eslint-disable no-multi-spaces */
  318. assertSelectedCells( model, [
  319. [ 0, 0, 0, 0 ],
  320. [ 0, 1, 1, 0 ],
  321. [ 0, 1, 0 ],
  322. [ 0, 0, 0, 0 ]
  323. ] );
  324. /* eslint-enable no-multi-spaces */
  325. } );
  326. it( 'handles pasting table that has many cells with various rowspan', () => {
  327. tableSelection.setCellSelection(
  328. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  329. modelRoot.getNodeByPath( [ 0, 2, 3 ] )
  330. );
  331. pasteTable( [
  332. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  333. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  334. [ 'cc', 'cd' ]
  335. ] );
  336. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  337. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  338. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  339. [ 'cc', 'cd' ],
  340. [ '30', '31', '32', '33' ]
  341. ] ) );
  342. /* eslint-disable no-multi-spaces */
  343. assertSelectedCells( model, [
  344. [ 1, 1, 1, 1 ],
  345. [ 1, 1 ],
  346. [ 1, 1 ],
  347. [ 0, 0, 0 ]
  348. ] );
  349. /* eslint-enable no-multi-spaces */
  350. } );
  351. it( 'handles pasting multi-spanned table', () => {
  352. setModelData( model, modelTable( [
  353. [ '00', '01', '02', '03', '04', '05' ],
  354. [ '10', '11', '12', '13', '14', '15' ],
  355. [ '20', '21', '22', '23', '24', '25' ],
  356. [ '30', '31', '32', '33', '34', '35' ],
  357. [ '40', '41', '42', '43', '44', '45' ]
  358. ] ) );
  359. tableSelection.setCellSelection(
  360. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  361. modelRoot.getNodeByPath( [ 0, 3, 4 ] )
  362. );
  363. // +----+----+----+----+----+
  364. // | aa | ac | ad | ae |
  365. // +----+----+----+----+ +
  366. // | ba | bb | |
  367. // +----+ +----+
  368. // | ca | | ce |
  369. // + +----+----+----+----+
  370. // | | db | dc | dd |
  371. // +----+----+----+----+----+
  372. pasteTable( [
  373. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 } ],
  374. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 } ],
  375. [ { contents: 'ca', rowspan: 2 }, 'ce' ],
  376. [ 'db', 'dc', { contents: 'dd', colspan: 2 } ]
  377. ] );
  378. // +----+----+----+----+----+----+
  379. // | aa | ac | ad | ae | 05 |
  380. // +----+----+----+----+ +----+
  381. // | ba | bb | | 15 |
  382. // +----+ +----+----+
  383. // | ca | | ce | 25 |
  384. // + +----+----+----+----+----+
  385. // | | db | dc | dd | 35 |
  386. // +----+----+----+----+----+----+
  387. // | 40 | 41 | 42 | 43 | 44 | 45 |
  388. // +----+----+----+----+----+----+
  389. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  390. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 }, '05' ],
  391. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 }, '15' ],
  392. [ { contents: 'ca', rowspan: 2 }, 'ce', '25' ],
  393. [ 'db', 'dc', { contents: 'dd', colspan: 2 }, '35' ],
  394. [ '40', '41', '42', '43', '44', '45' ]
  395. ] ) );
  396. /* eslint-disable no-multi-spaces */
  397. assertSelectedCells( model, [
  398. [ 1, 1, 1, 1, 0 ],
  399. [ 1, 1, 0 ],
  400. [ 1, 1, 0 ],
  401. [ 1, 1, 1, 0 ],
  402. [ 0, 0, 0, 0, 0, 0 ]
  403. ] );
  404. /* eslint-enable no-multi-spaces */
  405. } );
  406. } );
  407. describe( 'content table has spans', () => {
  408. it( 'handles pasting simple table over a table with colspans (no colspan exceeds selection)', () => {
  409. setModelData( model, modelTable( [
  410. [ '00[]', '01', '02', '03' ],
  411. [ { colspan: 3, contents: '10' }, '13' ],
  412. [ { colspan: 2, contents: '20' }, '22', '23' ],
  413. [ '30', '31', { colspan: 2, contents: '31' } ]
  414. ] ) );
  415. tableSelection.setCellSelection(
  416. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  417. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  418. );
  419. pasteTable( [
  420. [ 'aa', 'ab', 'ac' ],
  421. [ 'ba', 'bb', 'bc' ],
  422. [ 'ca', 'cb', 'cc' ]
  423. ] );
  424. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  425. [ 'aa', 'ab', 'ac', '03' ],
  426. [ 'ba', 'bb', 'bc', '13' ],
  427. [ 'ca', 'cb', 'cc', '23' ],
  428. [ '30', '31', { colspan: 2, contents: '31' } ]
  429. ] ) );
  430. /* eslint-disable no-multi-spaces */
  431. assertSelectedCells( model, [
  432. [ 1, 1, 1, 0 ],
  433. [ 1, 1, 1, 0 ],
  434. [ 1, 1, 1, 0 ],
  435. [ 0, 0, 0 ]
  436. ] );
  437. /* eslint-enable no-multi-spaces */
  438. } );
  439. it( 'handles pasting simple table over a table with rowspans (no rowspan exceeds selection)', () => {
  440. setModelData( model, modelTable( [
  441. [ '00', { rowspan: 3, contents: '01' }, { rowspan: 2, contents: '02' }, '03' ],
  442. [ { rowspan: 2, contents: '10' }, '13' ],
  443. [ '22', '23' ],
  444. [ '30', '31', '32', '33' ]
  445. ] ) );
  446. tableSelection.setCellSelection(
  447. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  448. modelRoot.getNodeByPath( [ 0, 2, 0 ] )
  449. );
  450. pasteTable( [
  451. [ 'aa', 'ab', 'ac' ],
  452. [ 'ba', 'bb', 'bc' ],
  453. [ 'ca', 'cb', 'cc' ]
  454. ] );
  455. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  456. [ 'aa', 'ab', 'ac', '03' ],
  457. [ 'ba', 'bb', 'bc', '13' ],
  458. [ 'ca', 'cb', 'cc', '23' ],
  459. [ '30', '31', '32', '33' ]
  460. ] ) );
  461. assertSelectedCells( model, [
  462. [ 1, 1, 1, 0 ],
  463. [ 1, 1, 1, 0 ],
  464. [ 1, 1, 1, 0 ],
  465. [ 0, 0, 0, 0 ]
  466. ] );
  467. } );
  468. it( 'handles pasting simple table over table with multi-spans (no span exceeds selection)', () => {
  469. // +----+----+----+----+----+----+
  470. // | 00 | 02 | 03 | 05 |
  471. // + + + +----+
  472. // | | | | 15 |
  473. // +----+----+----+ +----+
  474. // | 20 | 21 | | 25 |
  475. // + +----+----+----+----+----+
  476. // | | 31 | 32 | 34 | 35 |
  477. // +----+----+----+----+----+----+
  478. // | 40 | 41 | 42 | 43 | 44 | 45 |
  479. // +----+----+----+----+----+----+
  480. setModelData( model, modelTable( [
  481. [
  482. { contents: '00', colspan: 2, rowspan: 2 },
  483. { contents: '02', rowspan: 2 },
  484. { contents: '03', colspan: 2, rowspan: 3 },
  485. '05'
  486. ],
  487. [ '15' ],
  488. [ { contents: '20', rowspan: 2 }, { contents: '21', colspan: 2 }, '25' ],
  489. [ '31', { contents: '32', colspan: 2 }, '34', '35' ],
  490. [ '40', '41', '42', '43', '44', '45' ]
  491. ] ) );
  492. tableSelection.setCellSelection(
  493. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  494. modelRoot.getNodeByPath( [ 0, 3, 2 ] )
  495. );
  496. pasteTable( [
  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. ] );
  502. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  503. [ 'aa', 'ab', 'ac', 'ad', 'ae', '05' ],
  504. [ 'ba', 'bb', 'bc', 'bd', 'be', '15' ],
  505. [ 'ca', 'cb', 'cc', 'cd', 'ce', '25' ],
  506. [ 'da', 'db', 'dc', 'dd', 'de', '35' ],
  507. [ '40', '41', '42', '43', '44', '45' ]
  508. ] ) );
  509. assertSelectedCells( model, [
  510. [ 1, 1, 1, 1, 1, 0 ],
  511. [ 1, 1, 1, 1, 1, 0 ],
  512. [ 1, 1, 1, 1, 1, 0 ],
  513. [ 1, 1, 1, 1, 1, 0 ],
  514. [ 0, 0, 0, 0, 0, 0 ]
  515. ] );
  516. } );
  517. // TODO: Skipped case - should allow pasting but no tools to compare areas (like in MergeCellsCommand).
  518. it.skip( 'handles pasting table that has cell with colspan (last row in selection is spanned)', () => {
  519. // +----+----+----+----+
  520. // | 00 | 01 | 02 | 03 |
  521. // +----+----+----+----+
  522. // | 10 | 11 | 13 |
  523. // + + +----+
  524. // | | | 23 |
  525. // +----+----+----+----+
  526. // | 30 | 31 | 32 | 33 |
  527. // +----+----+----+----+
  528. setModelData( model, modelTable( [
  529. [ '00', '01', '02', '03' ],
  530. [ { contents: '10', rowspan: 2 }, { contents: '11', colspan: 2, rowspan: 2 }, '13' ],
  531. [ '23' ],
  532. [ '30', '31', '32', '33' ]
  533. ] ) );
  534. tableSelection.setCellSelection(
  535. modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
  536. modelRoot.getNodeByPath( [ 0, 1, 0 ] )
  537. );
  538. pasteTable( [
  539. [ 'aa', 'ab', 'ac' ],
  540. [ 'ba', 'bb', 'bc' ],
  541. [ 'ca', 'cb', 'cc' ]
  542. ] );
  543. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  544. [ 'aa', 'ab', 'ac', '03' ],
  545. [ 'ba', 'bb', 'bc', '13' ],
  546. [ 'ca', 'cb', 'cc', '23' ],
  547. [ '30', '31', '32', '33' ]
  548. ] ) );
  549. assertSelectedCells( model, [
  550. [ 1, 1, 1, 0 ],
  551. [ 1, 1, 1, 0 ],
  552. [ 1, 1, 1, 0 ],
  553. [ 0, 0, 0, 0 ]
  554. ] );
  555. } );
  556. } );
  557. describe( 'content and paste tables have spans', () => {
  558. it( 'handles pasting colspanned table over table with colspans (no colspan exceeds selection)', () => {
  559. setModelData( model, modelTable( [
  560. [ '00[]', '01', '02', '03' ],
  561. [ { colspan: 3, contents: '10' }, '13' ],
  562. [ { colspan: 2, contents: '20' }, '22', '23' ],
  563. [ '30', '31', { colspan: 2, contents: '31' } ]
  564. ] ) );
  565. tableSelection.setCellSelection(
  566. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  567. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  568. );
  569. pasteTable( [
  570. [ 'aa', { colspan: 2, contents: 'ab' } ],
  571. [ { colspan: 3, contents: 'ba' } ],
  572. [ 'ca', 'cb', 'cc' ]
  573. ] );
  574. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  575. [ 'aa', { colspan: 2, contents: 'ab' }, '03' ],
  576. [ { colspan: 3, contents: 'ba' }, '13' ],
  577. [ 'ca', 'cb', 'cc', '23' ],
  578. [ '30', '31', { colspan: 2, contents: '31' } ]
  579. ] ) );
  580. /* eslint-disable no-multi-spaces */
  581. assertSelectedCells( model, [
  582. [ 1, 1, 0 ],
  583. [ 1, 0 ],
  584. [ 1, 1, 1, 0 ],
  585. [ 0, 0, 0 ]
  586. ] );
  587. /* eslint-enable no-multi-spaces */
  588. } );
  589. it( 'handles pasting rowspanned table over table with rowspans (no rowspan exceeds selection)', () => {
  590. setModelData( model, modelTable( [
  591. [ { rowspan: 3, contents: '00' }, { rowspan: 2, contents: '01' }, '02', '03' ],
  592. [ { rowspan: 2, contents: '12' }, '13' ],
  593. [ '21', '23' ],
  594. [ '30', '31', '32', '33' ]
  595. ] ) );
  596. tableSelection.setCellSelection(
  597. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  598. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  599. );
  600. pasteTable( [
  601. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  602. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  603. [ 'cc', 'cd' ]
  604. ] );
  605. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  606. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  607. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  608. [ 'cc', 'cd' ],
  609. [ '30', '31', '32', '33' ]
  610. ] ) );
  611. /* eslint-disable no-multi-spaces */
  612. assertSelectedCells( model, [
  613. [ 1, 1, 1, 1 ],
  614. [ 1, 1 ],
  615. [ 1, 1 ],
  616. [ 0, 0, 0, 0 ]
  617. ] );
  618. /* eslint-enable no-multi-spaces */
  619. } );
  620. it( 'handles pasting multi-spanned table over table with multi-spans (no span exceeds selection)', () => {
  621. // +----+----+----+----+----+----+
  622. // | 00 | 02 | 03 | 05 |
  623. // + + + +----+
  624. // | | | | 15 |
  625. // +----+----+----+ +----+
  626. // | 20 | 21 | | 25 |
  627. // + +----+----+----+----+----+
  628. // | | 31 | 32 | 34 | 35 |
  629. // +----+----+----+----+----+----+
  630. // | 40 | 41 | 42 | 43 | 44 | 45 |
  631. // +----+----+----+----+----+----+
  632. setModelData( model, modelTable( [
  633. [
  634. { contents: '00', colspan: 2, rowspan: 2 },
  635. { contents: '02', rowspan: 2 },
  636. { contents: '03', colspan: 2, rowspan: 3 },
  637. '05'
  638. ],
  639. [ '15' ],
  640. [ { contents: '20', rowspan: 2 }, { contents: '21', colspan: 2 }, '25' ],
  641. [ '31', { contents: '32', colspan: 2 }, '34', '35' ],
  642. [ '40', '41', '42', '43', '44', '45' ]
  643. ] ) );
  644. tableSelection.setCellSelection(
  645. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  646. modelRoot.getNodeByPath( [ 0, 3, 2 ] )
  647. );
  648. // +----+----+----+----+----+
  649. // | aa | ac | ad | ae |
  650. // +----+----+----+----+ +
  651. // | ba | bb | |
  652. // +----+ +----+
  653. // | ca | | ce |
  654. // + +----+----+----+----+
  655. // | | db | dc | dd |
  656. // +----+----+----+----+----+
  657. pasteTable( [
  658. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 } ],
  659. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 } ],
  660. [ { contents: 'ca', rowspan: 2 }, 'ce' ],
  661. [ 'db', 'dc', { contents: 'dd', colspan: 2 } ]
  662. ] );
  663. // +----+----+----+----+----+----+
  664. // | aa | ac | ad | ae | 05 |
  665. // +----+----+----+----+ +----+
  666. // | ba | bb | | 15 |
  667. // +----+ +----+----+
  668. // | ca | | ce | 25 |
  669. // + +----+----+----+----+----+
  670. // | | db | dc | dd | 35 |
  671. // +----+----+----+----+----+----+
  672. // | 40 | 41 | 42 | 43 | 44 | 45 |
  673. // +----+----+----+----+----+----+
  674. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  675. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 }, '05' ],
  676. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 }, '15' ],
  677. [ { contents: 'ca', rowspan: 2 }, 'ce', '25' ],
  678. [ 'db', 'dc', { contents: 'dd', colspan: 2 }, '35' ],
  679. [ '40', '41', '42', '43', '44', '45' ]
  680. ] ) );
  681. /* eslint-disable no-multi-spaces */
  682. assertSelectedCells( model, [
  683. [ 1, 1, 1, 1, 0 ],
  684. [ 1, 1, 0 ],
  685. [ 1, 1, 0 ],
  686. [ 1, 1, 1, 0 ],
  687. [ 0, 0, 0, 0, 0, 0 ]
  688. ] );
  689. /* eslint-enable no-multi-spaces */
  690. } );
  691. // TODO: Skipped case - should allow pasting but no tools to compare areas (like in MergeCellsCommand).
  692. it.skip( 'handles pasting table that has cell with colspan (last row in selection is spanned)', () => {
  693. // +----+----+----+----+
  694. // | 00 | 01 | 02 | 03 |
  695. // +----+----+----+----+
  696. // | 10 | 11 | 13 |
  697. // + + +----+
  698. // | | | 23 |
  699. // +----+----+----+----+
  700. // | 30 | 31 | 32 | 33 |
  701. // +----+----+----+----+
  702. setModelData( model, modelTable( [
  703. [ '00', '01', '02', '03' ],
  704. [ { contents: '10', rowspan: 2 }, { contents: '11', colspan: 2, rowspan: 2 }, '13' ],
  705. [ '23' ],
  706. [ '30', '31', '32', '33' ]
  707. ] ) );
  708. tableSelection.setCellSelection(
  709. modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
  710. modelRoot.getNodeByPath( [ 0, 1, 0 ] )
  711. );
  712. // +----+----+----+
  713. // | aa | ab | ac |
  714. // +----+----+----+
  715. // | ba | bc |
  716. // + +----+
  717. // | | cc |
  718. // +----+----+----+
  719. pasteTable( [
  720. [ 'aa', 'ab', 'ac' ],
  721. [ { contents: 'ba', colspan: 2, rowspan: 2 }, 'bc' ],
  722. [ 'cc' ]
  723. ] );
  724. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  725. [ '00', '01', '02', '03' ],
  726. [ '10', { colspan: 2, contents: 'aa' }, '13' ],
  727. [ '20', 'ba', 'bb', '23' ],
  728. [ '30', '31', '32', '33' ]
  729. ] ) );
  730. /* eslint-disable no-multi-spaces */
  731. assertSelectedCells( model, [
  732. [ 0, 0, 0, 0 ],
  733. [ 0, 1, 0 ],
  734. [ 0, 1, 1, 0 ],
  735. [ 0, 0, 0, 0 ]
  736. ] );
  737. /* eslint-enable no-multi-spaces */
  738. } );
  739. } );
  740. } );
  741. describe( 'pasted table is bigger than the selected area', () => {
  742. describe( 'no spans', () => {
  743. it( 'handles simple table paste to a simple table fragment - at the beginning of a table', () => {
  744. tableSelection.setCellSelection(
  745. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  746. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  747. );
  748. pasteTable( [
  749. [ 'aa', 'ab', 'ac' ],
  750. [ 'ba', 'bb', 'bc' ],
  751. [ 'ca', 'cb', 'cc' ]
  752. ] );
  753. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  754. [ 'aa', 'ab', '02', '03' ],
  755. [ 'ba', 'bb', '12', '13' ],
  756. [ '20', '21', '22', '23' ],
  757. [ '30', '31', '32', '33' ]
  758. ] ) );
  759. assertSelectedCells( model, [
  760. [ 1, 1, 0, 0 ],
  761. [ 1, 1, 0, 0 ],
  762. [ 0, 0, 0, 0 ],
  763. [ 0, 0, 0, 0 ]
  764. ] );
  765. } );
  766. it( 'handles simple table paste to a simple table fragment - at the end of a table', () => {
  767. tableSelection.setCellSelection(
  768. modelRoot.getNodeByPath( [ 0, 2, 2 ] ),
  769. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  770. );
  771. pasteTable( [
  772. [ 'aa', 'ab', 'ac' ],
  773. [ 'ba', 'bb', 'bc' ],
  774. [ 'ca', 'cb', 'cc' ]
  775. ] );
  776. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  777. [ '00', '01', '02', '03' ],
  778. [ '10', '11', '12', '13' ],
  779. [ '20', '21', 'aa', 'ab' ],
  780. [ '30', '31', 'ba', 'bb' ]
  781. ] ) );
  782. assertSelectedCells( model, [
  783. [ 0, 0, 0, 0 ],
  784. [ 0, 0, 0, 0 ],
  785. [ 0, 0, 1, 1 ],
  786. [ 0, 0, 1, 1 ]
  787. ] );
  788. } );
  789. it( 'handles simple table paste to a simple table fragment - in the middle of a table', () => {
  790. tableSelection.setCellSelection(
  791. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  792. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  793. );
  794. pasteTable( [
  795. [ 'aa', 'ab', 'ac' ],
  796. [ 'ba', 'bb', 'bc' ],
  797. [ 'ca', 'cb', 'cc' ]
  798. ] );
  799. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  800. [ '00', '01', '02', '03' ],
  801. [ '10', 'aa', 'ab', '13' ],
  802. [ '20', 'ba', 'bb', '23' ],
  803. [ '30', '31', '32', '33' ]
  804. ] ) );
  805. assertSelectedCells( model, [
  806. [ 0, 0, 0, 0 ],
  807. [ 0, 1, 1, 0 ],
  808. [ 0, 1, 1, 0 ],
  809. [ 0, 0, 0, 0 ]
  810. ] );
  811. } );
  812. it( 'handles paste to a simple row fragment - in the middle of a table', () => {
  813. tableSelection.setCellSelection(
  814. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  815. modelRoot.getNodeByPath( [ 0, 1, 2 ] )
  816. );
  817. pasteTable( [
  818. [ 'aa', 'ab', 'ac' ],
  819. [ 'ba', 'bb', 'bc' ],
  820. [ 'ca', 'cb', 'cc' ]
  821. ] );
  822. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  823. [ '00', '01', '02', '03' ],
  824. [ '10', 'aa', 'ab', '13' ],
  825. [ '20', '21', '22', '23' ],
  826. [ '30', '31', '32', '33' ]
  827. ] ) );
  828. assertSelectedCells( model, [
  829. [ 0, 0, 0, 0 ],
  830. [ 0, 1, 1, 0 ],
  831. [ 0, 0, 0, 0 ],
  832. [ 0, 0, 0, 0 ]
  833. ] );
  834. } );
  835. it( 'handles paste to a simple column fragment - in the middle of a table', () => {
  836. tableSelection.setCellSelection(
  837. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  838. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  839. );
  840. pasteTable( [
  841. [ 'aa', 'ab', 'ac' ],
  842. [ 'ba', 'bb', 'bc' ],
  843. [ 'ca', 'cb', 'cc' ]
  844. ] );
  845. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  846. [ '00', '01', '02', '03' ],
  847. [ '10', 'aa', '12', '13' ],
  848. [ '20', 'ba', '22', '23' ],
  849. [ '30', '31', '32', '33' ]
  850. ] ) );
  851. assertSelectedCells( model, [
  852. [ 0, 0, 0, 0 ],
  853. [ 0, 1, 0, 0 ],
  854. [ 0, 1, 0, 0 ],
  855. [ 0, 0, 0, 0 ]
  856. ] );
  857. } );
  858. it( 'handles simple table paste to a simple table fragment - whole table selected', () => {
  859. tableSelection.setCellSelection(
  860. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  861. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  862. );
  863. pasteTable( [
  864. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  865. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  866. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  867. [ 'da', 'db', 'dc', 'dd', 'de' ],
  868. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  869. ] );
  870. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  871. [ 'aa', 'ab', 'ac', 'ad' ],
  872. [ 'ba', 'bb', 'bc', 'bd' ],
  873. [ 'ca', 'cb', 'cc', 'cd' ],
  874. [ 'da', 'db', 'dc', 'dd' ]
  875. ] ) );
  876. assertSelectedCells( model, [
  877. [ 1, 1, 1, 1 ],
  878. [ 1, 1, 1, 1 ],
  879. [ 1, 1, 1, 1 ],
  880. [ 1, 1, 1, 1 ]
  881. ] );
  882. } );
  883. } );
  884. describe( 'pasted table has spans', () => {
  885. it( 'handles pasting table that has cell with colspan', () => {
  886. tableSelection.setCellSelection(
  887. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  888. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  889. );
  890. pasteTable( [
  891. [ { colspan: 3, contents: 'aa' } ],
  892. [ 'ba', 'bb', 'bc' ]
  893. ] );
  894. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  895. [ '00', '01', '02', '03' ],
  896. [ '10', { colspan: 2, contents: 'aa' }, '13' ],
  897. [ '20', 'ba', 'bb', '23' ],
  898. [ '30', '31', '32', '33' ]
  899. ] ) );
  900. /* eslint-disable no-multi-spaces */
  901. assertSelectedCells( model, [
  902. [ 0, 0, 0, 0 ],
  903. [ 0, 1, 0 ],
  904. [ 0, 1, 1, 0 ],
  905. [ 0, 0, 0, 0 ]
  906. ] );
  907. /* eslint-enable no-multi-spaces */
  908. } );
  909. it( 'handles pasting table that has many cells with various colspan', () => {
  910. tableSelection.setCellSelection(
  911. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  912. modelRoot.getNodeByPath( [ 0, 3, 2 ] )
  913. );
  914. pasteTable( [
  915. [ 'aa', { colspan: 3, contents: 'ab' } ],
  916. [ { colspan: 4, contents: 'ba' } ],
  917. [ 'ca', 'cb', 'cc', 'cd' ],
  918. [ { colspan: 2, contents: 'da' }, 'dc', 'dd' ]
  919. ] );
  920. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  921. [ 'aa', { colspan: 2, contents: 'ab' }, '03' ],
  922. [ { colspan: 3, contents: 'ba' }, '13' ],
  923. [ 'ca', 'cb', 'cc', '23' ],
  924. [ { colspan: 2, contents: 'da' }, 'dc', '33' ]
  925. ] ) );
  926. /* eslint-disable no-multi-spaces */
  927. assertSelectedCells( model, [
  928. [ 1, 1, 0 ],
  929. [ 1, 0 ],
  930. [ 1, 1, 1, 0 ],
  931. [ 1, 1, 0 ]
  932. ] );
  933. /* eslint-enable no-multi-spaces */
  934. } );
  935. it( 'handles pasting table that has cell with rowspan', () => {
  936. tableSelection.setCellSelection(
  937. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  938. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  939. );
  940. pasteTable( [
  941. [ { rowspan: 3, contents: 'aa' }, 'ab' ],
  942. [ 'bb' ],
  943. [ 'cb' ]
  944. ] );
  945. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  946. [ '00', '01', '02', '03' ],
  947. [ '10', { rowspan: 2, contents: 'aa' }, 'ab', '13' ],
  948. [ '20', 'bb', '23' ],
  949. [ '30', '31', '32', '33' ]
  950. ] ) );
  951. /* eslint-disable no-multi-spaces */
  952. assertSelectedCells( model, [
  953. [ 0, 0, 0, 0 ],
  954. [ 0, 1, 1, 0 ],
  955. [ 0, 1, 0 ],
  956. [ 0, 0, 0, 0 ]
  957. ] );
  958. /* eslint-enable no-multi-spaces */
  959. } );
  960. it( 'handles pasting table that has many cells with various rowspan', () => {
  961. tableSelection.setCellSelection(
  962. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  963. modelRoot.getNodeByPath( [ 0, 2, 3 ] )
  964. );
  965. pasteTable( [
  966. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad', 'ae' ],
  967. [ { rowspan: 3, contents: 'ba' }, 'bd', 'be' ],
  968. [ 'cc', 'cd', 'ce' ],
  969. [ 'da', 'db', 'dc', 'dd' ]
  970. ] );
  971. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  972. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  973. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  974. [ 'cc', 'cd' ],
  975. [ '30', '31', '32', '33' ]
  976. ] ) );
  977. /* eslint-disable no-multi-spaces */
  978. assertSelectedCells( model, [
  979. [ 1, 1, 1, 1 ],
  980. [ 1, 1 ],
  981. [ 1, 1 ],
  982. [ 0, 0, 0 ]
  983. ] );
  984. /* eslint-enable no-multi-spaces */
  985. } );
  986. it( 'handles pasting multi-spanned table', () => {
  987. setModelData( model, modelTable( [
  988. [ '00', '01', '02', '03', '04', '05' ],
  989. [ '10', '11', '12', '13', '14', '15' ],
  990. [ '20', '21', '22', '23', '24', '25' ],
  991. [ '30', '31', '32', '33', '34', '35' ],
  992. [ '40', '41', '42', '43', '44', '45' ]
  993. ] ) );
  994. tableSelection.setCellSelection(
  995. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  996. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  997. );
  998. // +----+----+----+----+----+
  999. // | aa | ac | ad | ae |
  1000. // +----+----+----+----+ +
  1001. // | ba | bb | |
  1002. // +----+ +----+
  1003. // | ca | | ce |
  1004. // + +----+----+----+----+
  1005. // | | db | dc | dd |
  1006. // +----+----+----+----+----+
  1007. pasteTable( [
  1008. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 } ],
  1009. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 } ],
  1010. [ { contents: 'ca', rowspan: 2 }, 'ce' ],
  1011. [ 'db', 'dc', { contents: 'dd', colspan: 2 } ]
  1012. ] );
  1013. // +----+----+----+----+----+----+
  1014. // | 00 | 01 | 02 | 03 | 04 | 05 |
  1015. // +----+----+----+----+----+----+
  1016. // | 10 | aa | ac | 14 | 15 |
  1017. // +----+----+----+----+----+----+
  1018. // | 20 | ba | bb | 24 | 25 |
  1019. // +----+----+ +----+----+
  1020. // | 30 | ca | | 34 | 35 |
  1021. // +----+----+----+----+----+----+
  1022. // | 40 | 41 | 42 | 43 | 44 | 45 |
  1023. // +----+----+----+----+----+----+
  1024. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1025. [ '00', '01', '02', '03', '04', '05' ],
  1026. [ '10', { contents: 'aa', colspan: 2 }, 'ac', '14', '15' ],
  1027. [ '20', 'ba', { contents: 'bb', colspan: 2, rowspan: 2 }, '24', '25' ],
  1028. [ '30', 'ca', '34', '35' ],
  1029. [ '40', '41', '42', '43', '44', '45' ]
  1030. ] ) );
  1031. /* eslint-disable no-multi-spaces */
  1032. assertSelectedCells( model, [
  1033. [ 0, 0, 0, 0, 0, 0 ],
  1034. [ 0, 1, 1, 0 ],
  1035. [ 0, 1, 1, 0 ],
  1036. [ 0, 1, 0 ],
  1037. [ 0, 0, 0, 0, 0, 0 ]
  1038. ] );
  1039. /* eslint-enable no-multi-spaces */
  1040. } );
  1041. } );
  1042. } );
  1043. describe( 'pasted table is smaller than the selected area', () => {
  1044. it( 'blocks this case', () => {
  1045. tableSelection.setCellSelection(
  1046. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1047. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  1048. );
  1049. pasteTable( [
  1050. [ 'aa', 'ab' ],
  1051. [ 'ba', 'bb' ]
  1052. ] );
  1053. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1054. [ '00', '01', '02', '03' ],
  1055. [ '10', '11', '12', '13' ],
  1056. [ '20', '21', '22', '23' ],
  1057. [ '30', '31', '32', '33' ]
  1058. ] ) );
  1059. assertSelectedCells( model, [
  1060. [ 1, 1, 1, 1 ],
  1061. [ 1, 1, 1, 1 ],
  1062. [ 1, 1, 1, 1 ],
  1063. [ 1, 1, 1, 1 ]
  1064. ] );
  1065. } );
  1066. } );
  1067. } );
  1068. function pasteTable( tableData ) {
  1069. const data = {
  1070. dataTransfer: createDataTransfer(),
  1071. preventDefault: sinon.spy(),
  1072. stopPropagation: sinon.spy()
  1073. };
  1074. data.dataTransfer.setData( 'text/html', viewTable( tableData ) );
  1075. viewDocument.fire( 'paste', data );
  1076. return data;
  1077. }
  1078. function createDataTransfer() {
  1079. const store = new Map();
  1080. return {
  1081. setData( type, data ) {
  1082. store.set( type, data );
  1083. },
  1084. getData( type ) {
  1085. return store.get( type );
  1086. }
  1087. };
  1088. }
  1089. } );