tableclipboard-paste.js 107 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532
  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 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 selection is outside table', () => {
  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. ] );
  148. const data = {
  149. dataTransfer: createDataTransfer(),
  150. preventDefault: sinon.spy(),
  151. stopPropagation: sinon.spy()
  152. };
  153. data.dataTransfer.setData( 'text/html', `${ table }<p>foo</p>` );
  154. viewDocument.fire( 'paste', data );
  155. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  156. [ 'foo', '', '02', '03' ],
  157. [ '', '', '12', '13' ],
  158. [ '20', '21', '22', '23' ],
  159. [ '30', '31', '32', '33' ]
  160. ] ) );
  161. } );
  162. it( 'should not alter model.insertContent if mixed content is pasted (paragraph + table)', () => {
  163. tableSelection.setCellSelection(
  164. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  165. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  166. );
  167. const table = viewTable( [
  168. [ 'aa', 'ab' ],
  169. [ 'ba', 'bb' ]
  170. ] );
  171. const data = {
  172. dataTransfer: createDataTransfer(),
  173. preventDefault: sinon.spy(),
  174. stopPropagation: sinon.spy()
  175. };
  176. data.dataTransfer.setData( 'text/html', `<p>foo</p>${ table }` );
  177. viewDocument.fire( 'paste', data );
  178. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  179. [ 'foo', '', '02', '03' ],
  180. [ '', '', '12', '13' ],
  181. [ '20', '21', '22', '23' ],
  182. [ '30', '31', '32', '33' ]
  183. ] ) );
  184. } );
  185. it( 'should not alter model.insertContent if mixed content is pasted (table + table)', () => {
  186. tableSelection.setCellSelection(
  187. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  188. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  189. );
  190. const table = viewTable( [
  191. [ 'aa', 'ab' ],
  192. [ 'ba', 'bb' ]
  193. ] );
  194. const data = {
  195. dataTransfer: createDataTransfer(),
  196. preventDefault: sinon.spy(),
  197. stopPropagation: sinon.spy()
  198. };
  199. data.dataTransfer.setData( 'text/html', `${ table }${ table }` );
  200. viewDocument.fire( 'paste', data );
  201. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  202. [ '', '', '02', '03' ],
  203. [ '', '', '12', '13' ],
  204. [ '20', '21', '22', '23' ],
  205. [ '30', '31', '32', '33' ]
  206. ] ) );
  207. } );
  208. it( 'should alter model.insertContent if mixed content is pasted (table + empty paragraph)', () => {
  209. tableSelection.setCellSelection(
  210. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  211. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  212. );
  213. const table = viewTable( [
  214. [ 'aa', 'ab' ],
  215. [ 'ba', 'bb' ]
  216. ] );
  217. const data = {
  218. dataTransfer: createDataTransfer(),
  219. preventDefault: sinon.spy(),
  220. stopPropagation: sinon.spy()
  221. };
  222. data.dataTransfer.setData( 'text/html', `${ table }<p>&nbsp;</p>` );
  223. viewDocument.fire( 'paste', data );
  224. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  225. [ 'aa', 'ab', '02', '03' ],
  226. [ 'ba', 'bb', '12', '13' ],
  227. [ '20', '21', '22', '23' ],
  228. [ '30', '31', '32', '33' ]
  229. ] ) );
  230. } );
  231. it( 'should alter model.insertContent if mixed content is pasted (table + br)', () => {
  232. tableSelection.setCellSelection(
  233. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  234. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  235. );
  236. const table = viewTable( [
  237. [ 'aa', 'ab' ],
  238. [ 'ba', 'bb' ]
  239. ] );
  240. const data = {
  241. dataTransfer: createDataTransfer(),
  242. preventDefault: sinon.spy(),
  243. stopPropagation: sinon.spy()
  244. };
  245. data.dataTransfer.setData( 'text/html', `${ table }<br>` );
  246. viewDocument.fire( 'paste', data );
  247. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  248. [ 'aa', 'ab', '02', '03' ],
  249. [ 'ba', 'bb', '12', '13' ],
  250. [ '20', '21', '22', '23' ],
  251. [ '30', '31', '32', '33' ]
  252. ] ) );
  253. } );
  254. it( 'should alter model.insertContent if mixed content is pasted (empty paragraph + table)', () => {
  255. tableSelection.setCellSelection(
  256. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  257. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  258. );
  259. const table = viewTable( [
  260. [ 'aa', 'ab' ],
  261. [ 'ba', 'bb' ]
  262. ] );
  263. const data = {
  264. dataTransfer: createDataTransfer(),
  265. preventDefault: sinon.spy(),
  266. stopPropagation: sinon.spy()
  267. };
  268. data.dataTransfer.setData( 'text/html', `<p>&nbsp;</p>${ table }` );
  269. viewDocument.fire( 'paste', data );
  270. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  271. [ 'aa', 'ab', '02', '03' ],
  272. [ 'ba', 'bb', '12', '13' ],
  273. [ '20', '21', '22', '23' ],
  274. [ '30', '31', '32', '33' ]
  275. ] ) );
  276. } );
  277. it( 'should alter model.insertContent if mixed content is pasted (br + table)', () => {
  278. tableSelection.setCellSelection(
  279. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  280. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  281. );
  282. const table = viewTable( [
  283. [ 'aa', 'ab' ],
  284. [ 'ba', 'bb' ]
  285. ] );
  286. const data = {
  287. dataTransfer: createDataTransfer(),
  288. preventDefault: sinon.spy(),
  289. stopPropagation: sinon.spy()
  290. };
  291. data.dataTransfer.setData( 'text/html', `<br>${ table }` );
  292. viewDocument.fire( 'paste', data );
  293. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  294. [ 'aa', 'ab', '02', '03' ],
  295. [ 'ba', 'bb', '12', '13' ],
  296. [ '20', '21', '22', '23' ],
  297. [ '30', '31', '32', '33' ]
  298. ] ) );
  299. } );
  300. it( 'should not alter model.insertContent if element other than a table is passed directly', () => {
  301. tableSelection.setCellSelection(
  302. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  303. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  304. );
  305. model.change( writer => {
  306. const element = writer.createElement( 'paragraph' );
  307. writer.insertText( 'foo', element, 0 );
  308. model.insertContent( element );
  309. } );
  310. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  311. [ 'foo', '', '02', '03' ],
  312. [ '', '', '12', '13' ],
  313. [ '20', '21', '22', '23' ],
  314. [ '30', '31', '32', '33' ]
  315. ] ) );
  316. } );
  317. it( 'should alter model.insertContent if selectable is a document selection', () => {
  318. tableSelection.setCellSelection(
  319. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  320. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  321. );
  322. model.change( writer => {
  323. const tableToInsert = editor.plugins.get( 'TableUtils' ).createTable( writer, 2, 2 );
  324. for ( const { cell } of new TableWalker( tableToInsert ) ) {
  325. writer.insertText( 'foo', cell.getChild( 0 ), 0 );
  326. }
  327. model.insertContent( tableToInsert, editor.model.document.selection );
  328. } );
  329. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  330. [ 'foo', 'foo', '02', '03' ],
  331. [ 'foo', 'foo', '12', '13' ],
  332. [ '20', '21', '22', '23' ],
  333. [ '30', '31', '32', '33' ]
  334. ] ) );
  335. } );
  336. describe( 'single cell selected', () => {
  337. beforeEach( () => {
  338. setModelData( model, modelTable( [
  339. [ '00[]', '01', '02' ],
  340. [ '10', '11', '12' ],
  341. [ '20', '21', '22' ]
  342. ] ) );
  343. } );
  344. describe( 'with the selection on the middle cell', () => {
  345. beforeEach( () => {
  346. tableSelection.setCellSelection(
  347. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  348. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  349. );
  350. } );
  351. it( 'should replace the table cells (with single undo step)', () => {
  352. const batches = setupBatchWatch();
  353. pasteTable( [
  354. [ 'aa', 'ab' ],
  355. [ 'ba', 'bb' ]
  356. ] );
  357. expect( batches.size ).to.equal( 1 );
  358. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  359. [ '00', '01', '02' ],
  360. [ '10', 'aa', 'ab' ],
  361. [ '20', 'ba', 'bb' ]
  362. ] ) );
  363. } );
  364. it( 'should expand the table width and replace the table cells (with single undo step)', () => {
  365. const batches = setupBatchWatch();
  366. pasteTable( [
  367. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  368. [ 'ba', 'bb', 'bc', 'bd', 'be' ]
  369. ] );
  370. expect( batches.size ).to.equal( 1 );
  371. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  372. [ '00', '01', '02', '', '', '' ],
  373. [ '10', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  374. [ '20', 'ba', 'bb', 'bc', 'bd', 'be' ]
  375. ] ) );
  376. } );
  377. it( 'should expand the table height and replace the table cells (with single undo step)', () => {
  378. const batches = setupBatchWatch();
  379. pasteTable( [
  380. [ 'aa', 'ab' ],
  381. [ 'ba', 'bb' ],
  382. [ 'ca', 'cb' ],
  383. [ 'da', 'db' ],
  384. [ 'ea', 'eb' ]
  385. ] );
  386. expect( batches.size ).to.equal( 1 );
  387. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  388. [ '00', '01', '02' ],
  389. [ '10', 'aa', 'ab' ],
  390. [ '20', 'ba', 'bb' ],
  391. [ '', 'ca', 'cb' ],
  392. [ '', 'da', 'db' ],
  393. [ '', 'ea', 'eb' ]
  394. ] ) );
  395. } );
  396. it( 'should expand the table width, height and replace the table cells (with single undo step)', () => {
  397. const batches = setupBatchWatch();
  398. pasteTable( [
  399. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  400. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  401. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  402. [ 'da', 'db', 'dc', 'dd', 'de' ],
  403. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  404. ] );
  405. expect( batches.size ).to.equal( 1 );
  406. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  407. [ '00', '01', '02', '', '', '' ],
  408. [ '10', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  409. [ '20', 'ba', 'bb', 'bc', 'bd', 'be' ],
  410. [ '', 'ca', 'cb', 'cc', 'cd', 'ce' ],
  411. [ '', 'da', 'db', 'dc', 'dd', 'de' ],
  412. [ '', 'ea', 'eb', 'ec', 'ed', 'ee' ]
  413. ] ) );
  414. } );
  415. } );
  416. describe( 'with the selection on the first cell', () => {
  417. beforeEach( () => {
  418. tableSelection.setCellSelection(
  419. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  420. modelRoot.getNodeByPath( [ 0, 0, 0 ] )
  421. );
  422. } );
  423. it( 'should replace the table cells', () => {
  424. pasteTable( [
  425. [ 'aa', 'ab' ],
  426. [ 'ba', 'bb' ]
  427. ] );
  428. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  429. [ 'aa', 'ab', '02' ],
  430. [ 'ba', 'bb', '12' ],
  431. [ '20', '21', '22' ]
  432. ] ) );
  433. } );
  434. it( 'should expand the table and replace the table cells', () => {
  435. pasteTable( [
  436. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  437. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  438. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  439. [ 'da', 'db', 'dc', 'dd', 'de' ],
  440. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  441. ] );
  442. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  443. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  444. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  445. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  446. [ 'da', 'db', 'dc', 'dd', 'de' ],
  447. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  448. ] ) );
  449. } );
  450. } );
  451. describe( 'with the selection on the middle cell of the first row', () => {
  452. beforeEach( () => {
  453. tableSelection.setCellSelection(
  454. modelRoot.getNodeByPath( [ 0, 0, 1 ] ),
  455. modelRoot.getNodeByPath( [ 0, 0, 1 ] )
  456. );
  457. } );
  458. it( 'should replace the table cells', () => {
  459. pasteTable( [
  460. [ 'aa', 'ab' ],
  461. [ 'ba', 'bb' ]
  462. ] );
  463. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  464. [ '00', 'aa', 'ab' ],
  465. [ '10', 'ba', 'bb' ],
  466. [ '20', '21', '22' ]
  467. ] ) );
  468. } );
  469. it( 'should expand the table and replace the table cells', () => {
  470. pasteTable( [
  471. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  472. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  473. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  474. [ 'da', 'db', 'dc', 'dd', 'de' ],
  475. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  476. ] );
  477. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  478. [ '00', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  479. [ '10', 'ba', 'bb', 'bc', 'bd', 'be' ],
  480. [ '20', 'ca', 'cb', 'cc', 'cd', 'ce' ],
  481. [ '', 'da', 'db', 'dc', 'dd', 'de' ],
  482. [ '', 'ea', 'eb', 'ec', 'ed', 'ee' ]
  483. ] ) );
  484. } );
  485. } );
  486. describe( 'with the selection on the last cell of the first row', () => {
  487. beforeEach( () => {
  488. tableSelection.setCellSelection(
  489. modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
  490. modelRoot.getNodeByPath( [ 0, 0, 2 ] )
  491. );
  492. } );
  493. it( 'should replace the table cells', () => {
  494. pasteTable( [
  495. [ 'aa', 'ab' ],
  496. [ 'ba', 'bb' ]
  497. ] );
  498. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  499. [ '00', '01', 'aa', 'ab' ],
  500. [ '10', '11', 'ba', 'bb' ],
  501. [ '20', '21', '22', '' ]
  502. ] ) );
  503. } );
  504. it( 'should expand the table and replace the table cells', () => {
  505. pasteTable( [
  506. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  507. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  508. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  509. [ 'da', 'db', 'dc', 'dd', 'de' ],
  510. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  511. ] );
  512. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  513. [ '00', '01', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  514. [ '10', '11', 'ba', 'bb', 'bc', 'bd', 'be' ],
  515. [ '20', '21', 'ca', 'cb', 'cc', 'cd', 'ce' ],
  516. [ '', '', 'da', 'db', 'dc', 'dd', 'de' ],
  517. [ '', '', 'ea', 'eb', 'ec', 'ed', 'ee' ]
  518. ] ) );
  519. } );
  520. } );
  521. describe( 'with the selection is on the middle cell of the first column', () => {
  522. beforeEach( () => {
  523. tableSelection.setCellSelection(
  524. modelRoot.getNodeByPath( [ 0, 1, 0 ] ),
  525. modelRoot.getNodeByPath( [ 0, 1, 0 ] )
  526. );
  527. } );
  528. it( 'should replace the table cells', () => {
  529. pasteTable( [
  530. [ 'aa', 'ab' ],
  531. [ 'ba', 'bb' ]
  532. ] );
  533. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  534. [ '00', '01', '02' ],
  535. [ 'aa', 'ab', '12' ],
  536. [ 'ba', 'bb', '22' ]
  537. ] ) );
  538. } );
  539. it( 'should expand the table and replace the table cells', () => {
  540. pasteTable( [
  541. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  542. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  543. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  544. [ 'da', 'db', 'dc', 'dd', 'de' ],
  545. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  546. ] );
  547. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  548. [ '00', '01', '02', '', '' ],
  549. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  550. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  551. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  552. [ 'da', 'db', 'dc', 'dd', 'de' ],
  553. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  554. ] ) );
  555. } );
  556. } );
  557. describe( 'with the selection is on the last cell of the first column', () => {
  558. beforeEach( () => {
  559. tableSelection.setCellSelection(
  560. modelRoot.getNodeByPath( [ 0, 2, 0 ] ),
  561. modelRoot.getNodeByPath( [ 0, 2, 0 ] )
  562. );
  563. } );
  564. it( 'should replace the table cells', () => {
  565. pasteTable( [
  566. [ 'aa', 'ab' ],
  567. [ 'ba', 'bb' ]
  568. ] );
  569. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  570. [ '00', '01', '02' ],
  571. [ '10', '11', '12' ],
  572. [ 'aa', 'ab', '22' ],
  573. [ 'ba', 'bb', '' ]
  574. ] ) );
  575. } );
  576. it( 'should expand the table and replace the table cells', () => {
  577. pasteTable( [
  578. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  579. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  580. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  581. [ 'da', 'db', 'dc', 'dd', 'de' ],
  582. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  583. ] );
  584. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  585. [ '00', '01', '02', '', '' ],
  586. [ '10', '11', '12', '', '' ],
  587. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  588. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  589. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  590. [ 'da', 'db', 'dc', 'dd', 'de' ],
  591. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  592. ] ) );
  593. } );
  594. } );
  595. describe( 'with the selection is on the last cell of the last column', () => {
  596. beforeEach( () => {
  597. tableSelection.setCellSelection(
  598. modelRoot.getNodeByPath( [ 0, 2, 2 ] ),
  599. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  600. );
  601. } );
  602. it( 'should replace the table cells', () => {
  603. pasteTable( [
  604. [ 'aa', 'ab' ],
  605. [ 'ba', 'bb' ]
  606. ] );
  607. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  608. [ '00', '01', '02', '' ],
  609. [ '10', '11', '12', '' ],
  610. [ '20', '21', 'aa', 'ab' ],
  611. [ '', '', 'ba', 'bb' ]
  612. ] ) );
  613. } );
  614. it( 'should expand the table and replace the table cells', () => {
  615. pasteTable( [
  616. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  617. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  618. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  619. [ 'da', 'db', 'dc', 'dd', 'de' ],
  620. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  621. ] );
  622. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  623. [ '00', '01', '02', '', '', '', '' ],
  624. [ '10', '11', '12', '', '', '', '' ],
  625. [ '20', '21', 'aa', 'ab', 'ac', 'ad', 'ae' ],
  626. [ '', '', 'ba', 'bb', 'bc', 'bd', 'be' ],
  627. [ '', '', 'ca', 'cb', 'cc', 'cd', 'ce' ],
  628. [ '', '', 'da', 'db', 'dc', 'dd', 'de' ],
  629. [ '', '', 'ea', 'eb', 'ec', 'ed', 'ee' ]
  630. ] ) );
  631. } );
  632. } );
  633. describe( 'the selection inside the table cell', () => {
  634. it( 'should replace the table cells when the collapsed selection is in the first cell', () => {
  635. pasteTable( [
  636. [ 'aa', 'ab' ],
  637. [ 'ba', 'bb' ]
  638. ] );
  639. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  640. [ 'aa', 'ab', '02' ],
  641. [ 'ba', 'bb', '12' ],
  642. [ '20', '21', '22' ]
  643. ] ) );
  644. } );
  645. it( 'should replace the table cells when the expanded selection is in the first cell', () => {
  646. model.change( writer => {
  647. writer.setSelection( modelRoot.getNodeByPath( [ 0, 0, 0 ] ), 'in' );
  648. } );
  649. pasteTable( [
  650. [ 'aa', 'ab' ],
  651. [ 'ba', 'bb' ]
  652. ] );
  653. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  654. [ 'aa', 'ab', '02' ],
  655. [ 'ba', 'bb', '12' ],
  656. [ '20', '21', '22' ]
  657. ] ) );
  658. } );
  659. it( 'should replace the table cells when selection is on the image inside the table cell', async () => {
  660. await editor.destroy();
  661. await createEditor( [ ImageEditing, ImageCaptionEditing ] );
  662. setModelData( model, modelTable( [
  663. [ '00', '01', '02' ],
  664. [ '10', '11', '12' ],
  665. [ '20', '21', '[<image src="/assets/sample.jpg"><caption></caption></image>]' ]
  666. ] ) );
  667. pasteTable( [
  668. [ 'aa', 'ab' ],
  669. [ 'ba', 'bb' ]
  670. ] );
  671. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  672. [ '00', '01', '02', '' ],
  673. [ '10', '11', '12', '' ],
  674. [ '20', '21', 'aa', 'ab' ],
  675. [ '', '', 'ba', 'bb' ]
  676. ] ) );
  677. } );
  678. it( 'should replace the table cells when selection is in the image caption inside the table cell', async () => {
  679. await editor.destroy();
  680. await createEditor( [ ImageEditing, ImageCaptionEditing ] );
  681. setModelData( model, modelTable( [
  682. [ '00', '01', '02' ],
  683. [ '10', '11', '12' ],
  684. [ '20', '21', '<image src="/assets/sample.jpg"><caption>fo[]o</caption></image>' ]
  685. ] ) );
  686. pasteTable( [
  687. [ 'aa', 'ab' ],
  688. [ 'ba', 'bb' ]
  689. ] );
  690. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  691. [ '00', '01', '02', '' ],
  692. [ '10', '11', '12', '' ],
  693. [ '20', '21', 'aa', 'ab' ],
  694. [ '', '', 'ba', 'bb' ]
  695. ] ) );
  696. } );
  697. } );
  698. describe( 'with spanned cells', () => {
  699. it( 'should replace the table structure', () => {
  700. // +----+----+----+----+----+----+
  701. // | 00 | 01 | 02 | 05 |
  702. // +----+----+----+----+----+----+
  703. // | 10 | 11 | 13 | 14 | 15 |
  704. // +----+ +----+----+----+
  705. // | 20 | | 23 | 25 |
  706. // +----+ +----+----+ +
  707. // | 30 | | 33 | |
  708. // +----+----+----+----+----+----+
  709. // | 40 | 42 | 43 | 44 |
  710. // +----+----+----+----+----+----+
  711. setModelData( model, modelTable( [
  712. [ '00', '01', { contents: '02', colspan: 3 }, '05' ],
  713. [ '10', { contents: '11', colspan: 2, rowspan: 3 }, '13', '14', '15' ],
  714. [ '20', { contents: '23', colspan: 2 }, { contents: '25', rowspan: 2 } ],
  715. [ '30', { contents: '33', colspan: 2 } ],
  716. [ { contents: '40', colspan: 2 }, '42', '43', { contents: '44', colspan: 2 } ]
  717. ] ) );
  718. tableSelection.setCellSelection(
  719. modelRoot.getNodeByPath( [ 0, 2, 1 ] ), // Cell 23.
  720. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  721. );
  722. // +----+----+----+----+----+
  723. // | aa | ac | ad | ae |
  724. // + + +----+----+
  725. // | | | bd | be |
  726. // +----+----+----+----+----+
  727. // | ca | cc | cd | ce |
  728. // +----+----+----+----+----+
  729. // | da | dd | de |
  730. // +----+----+----+----+ +
  731. // | ea | eb | ec | ed | |
  732. // +----+----+----+----+----+
  733. pasteTable( [
  734. [ { contents: 'aa', colspan: 2, rowspan: 2 }, { contents: 'ac', rowspan: 2 }, 'ad', 'ae' ],
  735. [ 'bd', 'be' ],
  736. [ { contents: 'ca', colspan: 2 }, 'cc', 'cd', 'ce' ],
  737. [ { contents: 'da', colspan: 3 }, 'dd', { contents: 'de', rowspan: 2 } ],
  738. [ 'ea', 'eb', 'ec', 'ed' ]
  739. ] );
  740. // +----+----+----+----+----+----+----+----+
  741. // | 00 | 01 | 02 | 05 | | |
  742. // +----+----+----+----+----+----+----+----+
  743. // | 10 | 11 | 13 | 14 | 15 | | |
  744. // +----+ +----+----+----+----+----+
  745. // | 20 | | aa | ac | ad | ae |
  746. // +----+ + + +----+----+
  747. // | 30 | | | | bd | be |
  748. // +----+----+----+----+----+----+----+----+
  749. // | 40 | 42 | ca | cc | cd | ce |
  750. // +----+----+----+----+----+----+----+----+
  751. // | | | | da | dd | de |
  752. // +----+----+----+----+----+----+----+ +
  753. // | | | | ea | eb | ec | ed | |
  754. // +----+----+----+----+----+----+----+----+
  755. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  756. [ '00', '01', { contents: '02', colspan: 3 }, '05', '', '' ],
  757. [ '10', { contents: '11', colspan: 2, rowspan: 3 }, '13', '14', '15', '', '' ],
  758. [ '20', { contents: 'aa', colspan: 2, rowspan: 2 }, { contents: 'ac', rowspan: 2 }, 'ad', 'ae' ],
  759. [ '30', 'bd', 'be' ],
  760. [ { contents: '40', colspan: 2 }, '42', { contents: 'ca', colspan: 2 }, 'cc', 'cd', 'ce' ],
  761. [ '', '', '', { contents: 'da', colspan: 3 }, 'dd', { contents: 'de', rowspan: 2 } ],
  762. [ '', '', '', 'ea', 'eb', 'ec', 'ed' ]
  763. ] ) );
  764. } );
  765. it( 'should fix non-rectangular are on matched table fragment', () => {
  766. // +----+----+----+
  767. // | 00 | 01 | 02 |
  768. // +----+----+ +
  769. // | 10 | 11 | |
  770. // +----+----+----+
  771. // | 20 | 22 |
  772. // +----+----+----+
  773. setModelData( model, modelTable( [
  774. [ '00', '01', { contents: '02', rowspan: 2 } ],
  775. [ '10', '11[]' ],
  776. [ { contents: '20', colspan: 2 }, '22' ]
  777. ] ) );
  778. pasteTable( [
  779. [ 'aa', 'ab' ],
  780. [ 'ba', 'bb' ]
  781. ] );
  782. // +----+----+----+
  783. // | 00 | 01 | 02 |
  784. // +----+----+----+
  785. // | 10 | aa | ab |
  786. // +----+----+----+
  787. // | 20 | ba | bb |
  788. // +----+----+----+
  789. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  790. [ '00', '01', '02' ],
  791. [ '10', 'aa', 'ab' ],
  792. [ '20', 'ba', 'bb' ]
  793. ] ) );
  794. } );
  795. } );
  796. } );
  797. describe( 'pasted table is equal to the selected area', () => {
  798. describe( 'no spans', () => {
  799. it( 'handles simple table paste to a simple table fragment - at the beginning of a table', () => {
  800. tableSelection.setCellSelection(
  801. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  802. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  803. );
  804. pasteTable( [
  805. [ 'aa', 'ab' ],
  806. [ 'ba', 'bb' ]
  807. ] );
  808. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  809. [ 'aa', 'ab', '02', '03' ],
  810. [ 'ba', 'bb', '12', '13' ],
  811. [ '20', '21', '22', '23' ],
  812. [ '30', '31', '32', '33' ]
  813. ] ) );
  814. assertSelectedCells( model, [
  815. [ 1, 1, 0, 0 ],
  816. [ 1, 1, 0, 0 ],
  817. [ 0, 0, 0, 0 ],
  818. [ 0, 0, 0, 0 ]
  819. ] );
  820. } );
  821. it( 'handles simple table paste to a simple table fragment - at the end of a table', () => {
  822. tableSelection.setCellSelection(
  823. modelRoot.getNodeByPath( [ 0, 2, 2 ] ),
  824. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  825. );
  826. pasteTable( [
  827. [ 'aa', 'ab' ],
  828. [ 'ba', 'bb' ]
  829. ] );
  830. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  831. [ '00', '01', '02', '03' ],
  832. [ '10', '11', '12', '13' ],
  833. [ '20', '21', 'aa', 'ab' ],
  834. [ '30', '31', 'ba', 'bb' ]
  835. ] ) );
  836. assertSelectedCells( model, [
  837. [ 0, 0, 0, 0 ],
  838. [ 0, 0, 0, 0 ],
  839. [ 0, 0, 1, 1 ],
  840. [ 0, 0, 1, 1 ]
  841. ] );
  842. } );
  843. it( 'handles simple table paste to a simple table fragment - in the middle of a table', () => {
  844. tableSelection.setCellSelection(
  845. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  846. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  847. );
  848. pasteTable( [
  849. [ 'aa', 'ab' ],
  850. [ 'ba', 'bb' ]
  851. ] );
  852. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  853. [ '00', '01', '02', '03' ],
  854. [ '10', 'aa', 'ab', '13' ],
  855. [ '20', 'ba', 'bb', '23' ],
  856. [ '30', '31', '32', '33' ]
  857. ] ) );
  858. assertSelectedCells( model, [
  859. [ 0, 0, 0, 0 ],
  860. [ 0, 1, 1, 0 ],
  861. [ 0, 1, 1, 0 ],
  862. [ 0, 0, 0, 0 ]
  863. ] );
  864. } );
  865. it( 'handles simple row paste to a simple row fragment - in the middle of a table', () => {
  866. tableSelection.setCellSelection(
  867. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  868. modelRoot.getNodeByPath( [ 0, 1, 2 ] )
  869. );
  870. pasteTable( [
  871. [ 'aa', 'ab' ]
  872. ] );
  873. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  874. [ '00', '01', '02', '03' ],
  875. [ '10', 'aa', 'ab', '13' ],
  876. [ '20', '21', '22', '23' ],
  877. [ '30', '31', '32', '33' ]
  878. ] ) );
  879. assertSelectedCells( model, [
  880. [ 0, 0, 0, 0 ],
  881. [ 0, 1, 1, 0 ],
  882. [ 0, 0, 0, 0 ],
  883. [ 0, 0, 0, 0 ]
  884. ] );
  885. } );
  886. it( 'handles simple column paste to a simple column fragment - in the middle of a table', () => {
  887. tableSelection.setCellSelection(
  888. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  889. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  890. );
  891. pasteTable( [
  892. [ 'aa' ],
  893. [ 'ba' ]
  894. ] );
  895. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  896. [ '00', '01', '02', '03' ],
  897. [ '10', 'aa', '12', '13' ],
  898. [ '20', 'ba', '22', '23' ],
  899. [ '30', '31', '32', '33' ]
  900. ] ) );
  901. assertSelectedCells( model, [
  902. [ 0, 0, 0, 0 ],
  903. [ 0, 1, 0, 0 ],
  904. [ 0, 1, 0, 0 ],
  905. [ 0, 0, 0, 0 ]
  906. ] );
  907. } );
  908. it( 'handles simple table paste to a simple table fragment - whole table selected', () => {
  909. tableSelection.setCellSelection(
  910. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  911. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  912. );
  913. pasteTable( [
  914. [ 'aa', 'ab', 'ac', 'ad' ],
  915. [ 'ba', 'bb', 'bc', 'bd' ],
  916. [ 'ca', 'cb', 'cc', 'cd' ],
  917. [ 'da', 'db', 'dc', 'dd' ]
  918. ] );
  919. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  920. [ 'aa', 'ab', 'ac', 'ad' ],
  921. [ 'ba', 'bb', 'bc', 'bd' ],
  922. [ 'ca', 'cb', 'cc', 'cd' ],
  923. [ 'da', 'db', 'dc', 'dd' ]
  924. ] ) );
  925. assertSelectedCells( model, [
  926. [ 1, 1, 1, 1 ],
  927. [ 1, 1, 1, 1 ],
  928. [ 1, 1, 1, 1 ],
  929. [ 1, 1, 1, 1 ]
  930. ] );
  931. } );
  932. } );
  933. describe( 'pasted table has spans', () => {
  934. it( 'handles pasting table that has cell with colspan', () => {
  935. tableSelection.setCellSelection(
  936. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  937. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  938. );
  939. pasteTable( [
  940. [ { colspan: 2, contents: 'aa' } ],
  941. [ 'ba', 'bb' ]
  942. ] );
  943. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  944. [ '00', '01', '02', '03' ],
  945. [ '10', { colspan: 2, contents: 'aa' }, '13' ],
  946. [ '20', 'ba', 'bb', '23' ],
  947. [ '30', '31', '32', '33' ]
  948. ] ) );
  949. /* eslint-disable no-multi-spaces */
  950. assertSelectedCells( model, [
  951. [ 0, 0, 0, 0 ],
  952. [ 0, 1, 0 ],
  953. [ 0, 1, 1, 0 ],
  954. [ 0, 0, 0, 0 ]
  955. ] );
  956. /* eslint-enable no-multi-spaces */
  957. } );
  958. it( 'handles pasting table that has many cells with various colspan', () => {
  959. tableSelection.setCellSelection(
  960. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  961. modelRoot.getNodeByPath( [ 0, 3, 2 ] )
  962. );
  963. pasteTable( [
  964. [ 'aa', { colspan: 2, contents: 'ab' } ],
  965. [ { colspan: 3, contents: 'ba' } ],
  966. [ 'ca', 'cb', 'cc' ],
  967. [ { colspan: 2, contents: 'da' }, 'dc' ]
  968. ] );
  969. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  970. [ 'aa', { colspan: 2, contents: 'ab' }, '03' ],
  971. [ { colspan: 3, contents: 'ba' }, '13' ],
  972. [ 'ca', 'cb', 'cc', '23' ],
  973. [ { colspan: 2, contents: 'da' }, 'dc', '33' ]
  974. ] ) );
  975. /* eslint-disable no-multi-spaces */
  976. assertSelectedCells( model, [
  977. [ 1, 1, 0 ],
  978. [ 1, 0 ],
  979. [ 1, 1, 1, 0 ],
  980. [ 1, 1, 0 ]
  981. ] );
  982. /* eslint-enable no-multi-spaces */
  983. } );
  984. it( 'handles pasting table that has cell with rowspan', () => {
  985. tableSelection.setCellSelection(
  986. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  987. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  988. );
  989. pasteTable( [
  990. [ { rowspan: 2, contents: 'aa' }, 'ab' ],
  991. [ 'bb' ]
  992. ] );
  993. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  994. [ '00', '01', '02', '03' ],
  995. [ '10', { rowspan: 2, contents: 'aa' }, 'ab', '13' ],
  996. [ '20', 'bb', '23' ],
  997. [ '30', '31', '32', '33' ]
  998. ] ) );
  999. /* eslint-disable no-multi-spaces */
  1000. assertSelectedCells( model, [
  1001. [ 0, 0, 0, 0 ],
  1002. [ 0, 1, 1, 0 ],
  1003. [ 0, 1, 0 ],
  1004. [ 0, 0, 0, 0 ]
  1005. ] );
  1006. /* eslint-enable no-multi-spaces */
  1007. } );
  1008. it( 'handles pasting table that has many cells with various rowspan', () => {
  1009. tableSelection.setCellSelection(
  1010. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1011. modelRoot.getNodeByPath( [ 0, 2, 3 ] )
  1012. );
  1013. pasteTable( [
  1014. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  1015. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  1016. [ 'cc', 'cd' ]
  1017. ] );
  1018. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1019. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  1020. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  1021. [ 'cc', 'cd' ],
  1022. [ '30', '31', '32', '33' ]
  1023. ] ) );
  1024. /* eslint-disable no-multi-spaces */
  1025. assertSelectedCells( model, [
  1026. [ 1, 1, 1, 1 ],
  1027. [ 1, 1 ],
  1028. [ 1, 1 ],
  1029. [ 0, 0, 0, 0 ]
  1030. ] );
  1031. /* eslint-enable no-multi-spaces */
  1032. } );
  1033. it( 'handles pasting multi-spanned table', () => {
  1034. setModelData( model, modelTable( [
  1035. [ '00', '01', '02', '03', '04', '05' ],
  1036. [ '10', '11', '12', '13', '14', '15' ],
  1037. [ '20', '21', '22', '23', '24', '25' ],
  1038. [ '30', '31', '32', '33', '34', '35' ],
  1039. [ '40', '41', '42', '43', '44', '45' ]
  1040. ] ) );
  1041. tableSelection.setCellSelection(
  1042. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1043. modelRoot.getNodeByPath( [ 0, 3, 4 ] )
  1044. );
  1045. // +----+----+----+----+----+
  1046. // | aa | ac | ad | ae |
  1047. // +----+----+----+----+ +
  1048. // | ba | bb | |
  1049. // +----+ +----+
  1050. // | ca | | ce |
  1051. // + +----+----+----+----+
  1052. // | | db | dc | dd |
  1053. // +----+----+----+----+----+
  1054. pasteTable( [
  1055. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 } ],
  1056. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 } ],
  1057. [ { contents: 'ca', rowspan: 2 }, 'ce' ],
  1058. [ 'db', 'dc', { contents: 'dd', colspan: 2 } ]
  1059. ] );
  1060. // +----+----+----+----+----+----+
  1061. // | aa | ac | ad | ae | 05 |
  1062. // +----+----+----+----+ +----+
  1063. // | ba | bb | | 15 |
  1064. // +----+ +----+----+
  1065. // | ca | | ce | 25 |
  1066. // + +----+----+----+----+----+
  1067. // | | db | dc | dd | 35 |
  1068. // +----+----+----+----+----+----+
  1069. // | 40 | 41 | 42 | 43 | 44 | 45 |
  1070. // +----+----+----+----+----+----+
  1071. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1072. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 }, '05' ],
  1073. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 }, '15' ],
  1074. [ { contents: 'ca', rowspan: 2 }, 'ce', '25' ],
  1075. [ 'db', 'dc', { contents: 'dd', colspan: 2 }, '35' ],
  1076. [ '40', '41', '42', '43', '44', '45' ]
  1077. ] ) );
  1078. /* eslint-disable no-multi-spaces */
  1079. assertSelectedCells( model, [
  1080. [ 1, 1, 1, 1, 0 ],
  1081. [ 1, 1, 0 ],
  1082. [ 1, 1, 0 ],
  1083. [ 1, 1, 1, 0 ],
  1084. [ 0, 0, 0, 0, 0, 0 ]
  1085. ] );
  1086. /* eslint-enable no-multi-spaces */
  1087. } );
  1088. } );
  1089. describe( 'content table has spans', () => {
  1090. it( 'handles pasting simple table over a table with colspans (no colspan exceeds selection)', () => {
  1091. setModelData( model, modelTable( [
  1092. [ '00[]', '01', '02', '03' ],
  1093. [ { colspan: 3, contents: '10' }, '13' ],
  1094. [ { colspan: 2, contents: '20' }, '22', '23' ],
  1095. [ '30', '31', { colspan: 2, contents: '31' } ]
  1096. ] ) );
  1097. tableSelection.setCellSelection(
  1098. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1099. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  1100. );
  1101. pasteTable( [
  1102. [ 'aa', 'ab', 'ac' ],
  1103. [ 'ba', 'bb', 'bc' ],
  1104. [ 'ca', 'cb', 'cc' ]
  1105. ] );
  1106. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1107. [ 'aa', 'ab', 'ac', '03' ],
  1108. [ 'ba', 'bb', 'bc', '13' ],
  1109. [ 'ca', 'cb', 'cc', '23' ],
  1110. [ '30', '31', { colspan: 2, contents: '31' } ]
  1111. ] ) );
  1112. /* eslint-disable no-multi-spaces */
  1113. assertSelectedCells( model, [
  1114. [ 1, 1, 1, 0 ],
  1115. [ 1, 1, 1, 0 ],
  1116. [ 1, 1, 1, 0 ],
  1117. [ 0, 0, 0 ]
  1118. ] );
  1119. /* eslint-enable no-multi-spaces */
  1120. } );
  1121. it( 'handles pasting simple table over a table with rowspans (no rowspan exceeds selection)', () => {
  1122. setModelData( model, modelTable( [
  1123. [ '00', { rowspan: 3, contents: '01' }, { rowspan: 2, contents: '02' }, '03' ],
  1124. [ { rowspan: 2, contents: '10' }, '13' ],
  1125. [ '22', '23' ],
  1126. [ '30', '31', '32', '33' ]
  1127. ] ) );
  1128. tableSelection.setCellSelection(
  1129. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1130. modelRoot.getNodeByPath( [ 0, 2, 0 ] )
  1131. );
  1132. pasteTable( [
  1133. [ 'aa', 'ab', 'ac' ],
  1134. [ 'ba', 'bb', 'bc' ],
  1135. [ 'ca', 'cb', 'cc' ]
  1136. ] );
  1137. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1138. [ 'aa', 'ab', 'ac', '03' ],
  1139. [ 'ba', 'bb', 'bc', '13' ],
  1140. [ 'ca', 'cb', 'cc', '23' ],
  1141. [ '30', '31', '32', '33' ]
  1142. ] ) );
  1143. assertSelectedCells( model, [
  1144. [ 1, 1, 1, 0 ],
  1145. [ 1, 1, 1, 0 ],
  1146. [ 1, 1, 1, 0 ],
  1147. [ 0, 0, 0, 0 ]
  1148. ] );
  1149. } );
  1150. it( 'handles pasting simple table over table with multi-spans (no span exceeds selection)', () => {
  1151. // +----+----+----+----+----+----+
  1152. // | 00 | 02 | 03 | 05 |
  1153. // + + + +----+
  1154. // | | | | 15 |
  1155. // +----+----+----+ +----+
  1156. // | 20 | 21 | | 25 |
  1157. // + +----+----+----+----+----+
  1158. // | | 31 | 32 | 34 | 35 |
  1159. // +----+----+----+----+----+----+
  1160. // | 40 | 41 | 42 | 43 | 44 | 45 |
  1161. // +----+----+----+----+----+----+
  1162. setModelData( model, modelTable( [
  1163. [
  1164. { contents: '00', colspan: 2, rowspan: 2 },
  1165. { contents: '02', rowspan: 2 },
  1166. { contents: '03', colspan: 2, rowspan: 3 },
  1167. '05'
  1168. ],
  1169. [ '15' ],
  1170. [ { contents: '20', rowspan: 2 }, { contents: '21', colspan: 2 }, '25' ],
  1171. [ '31', { contents: '32', colspan: 2 }, '34', '35' ],
  1172. [ '40', '41', '42', '43', '44', '45' ]
  1173. ] ) );
  1174. tableSelection.setCellSelection(
  1175. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1176. modelRoot.getNodeByPath( [ 0, 3, 2 ] ) // Cell 34.
  1177. );
  1178. pasteTable( [
  1179. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  1180. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  1181. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  1182. [ 'da', 'db', 'dc', 'dd', 'de' ]
  1183. ] );
  1184. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1185. [ 'aa', 'ab', 'ac', 'ad', 'ae', '05' ],
  1186. [ 'ba', 'bb', 'bc', 'bd', 'be', '15' ],
  1187. [ 'ca', 'cb', 'cc', 'cd', 'ce', '25' ],
  1188. [ 'da', 'db', 'dc', 'dd', 'de', '35' ],
  1189. [ '40', '41', '42', '43', '44', '45' ]
  1190. ] ) );
  1191. assertSelectedCells( model, [
  1192. [ 1, 1, 1, 1, 1, 0 ],
  1193. [ 1, 1, 1, 1, 1, 0 ],
  1194. [ 1, 1, 1, 1, 1, 0 ],
  1195. [ 1, 1, 1, 1, 1, 0 ],
  1196. [ 0, 0, 0, 0, 0, 0 ]
  1197. ] );
  1198. } );
  1199. it( 'handles pasting simple table over a table with rowspan (rowspan before selection)', () => {
  1200. // +----+----+----+----+----+
  1201. // | 00 | 01 | 02 | 03 | 04 |
  1202. // +----+----+----+----+----+
  1203. // | 10 | 11 | 12 | 13 | 14 |
  1204. // +----+ +----+----+----+
  1205. // | 20 | | 22 | 23 | 24 |
  1206. // +----+ +----+----+----+
  1207. // | 30 | | 32 | 33 | 34 |
  1208. // +----+----+----+----+----+
  1209. // | 40 | 41 | 42 | 43 | 44 |
  1210. // +----+----+----+----+----+
  1211. setModelData( model, modelTable( [
  1212. [ '00', '01', '02', '03', '04' ],
  1213. [ '10', { contents: '11', rowspan: 3 }, '12', '13', '14' ],
  1214. [ '20', '22', '23', '24' ],
  1215. [ '30', '32', '33', '34' ],
  1216. [ '40', '41', '42', '43', '44' ]
  1217. ] ) );
  1218. tableSelection.setCellSelection(
  1219. modelRoot.getNodeByPath( [ 0, 1, 2 ] ),
  1220. modelRoot.getNodeByPath( [ 0, 3, 2 ] ) // Cell 33.
  1221. );
  1222. pasteTable( [
  1223. [ 'aa', 'ab' ],
  1224. [ 'ba', 'bb' ],
  1225. [ 'ca', 'cb' ]
  1226. ] );
  1227. // +----+----+----+----+----+
  1228. // | 00 | 01 | 02 | 03 | 04 |
  1229. // +----+----+----+----+----+
  1230. // | 10 | 11 | aa | ab | 14 |
  1231. // +----+ +----+----+----+
  1232. // | 20 | | ba | bb | 24 |
  1233. // +----+ +----+----+----+
  1234. // | 30 | | ca | cb | 34 |
  1235. // +----+----+----+----+----+
  1236. // | 40 | 41 | 42 | 43 | 44 |
  1237. // +----+----+----+----+----+
  1238. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1239. [ '00', '01', '02', '03', '04' ],
  1240. [ '10', { contents: '11', rowspan: 3 }, 'aa', 'ab', '14' ],
  1241. [ '20', 'ba', 'bb', '24' ],
  1242. [ '30', 'ca', 'cb', '34' ],
  1243. [ '40', '41', '42', '43', '44' ]
  1244. ] ) );
  1245. /* eslint-disable no-multi-spaces */
  1246. assertSelectedCells( model, [
  1247. [ 0, 0, 0, 0, 0 ],
  1248. [ 0, 0, 1, 1, 0 ],
  1249. [ 0, 1, 1, 0 ],
  1250. [ 0, 1, 1, 0 ],
  1251. [ 0, 0, 0, 0, 0 ]
  1252. ] );
  1253. /* eslint-enable no-multi-spaces */
  1254. } );
  1255. it( 'handles pasting simple table over a table with rowspans (rowspan before selection)', () => {
  1256. // +----+----+----+----+----+----+
  1257. // | 00 | 01 | 02 | 03 | 04 | 05 |
  1258. // +----+----+----+----+----+----+
  1259. // | 10 | 12 | 13 | 14 | 15 |
  1260. // +----+----+----+----+----+----+
  1261. // | 20 | 23 | 24 | 25 |
  1262. // +----+----+----+----+----+----+
  1263. // | 30 | 31 | 32 | 33 | 34 | 35 |
  1264. // +----+----+----+----+----+----+
  1265. setModelData( model, modelTable( [
  1266. [ '00', '01', '02', '03', '04', '05' ],
  1267. [ { contents: '10', colspan: 2 }, '12', '13', '14', '15' ],
  1268. [ { contents: '20', colspan: 3 }, '23', '24', '25' ],
  1269. [ '30', '31', '32', '33', '34', '35' ]
  1270. ] ) );
  1271. tableSelection.setCellSelection(
  1272. modelRoot.getNodeByPath( [ 0, 1, 2 ] ), // Cell 13.
  1273. modelRoot.getNodeByPath( [ 0, 2, 2 ] ) // Cell 24.
  1274. );
  1275. pasteTable( [
  1276. [ 'aa', 'ab' ],
  1277. [ 'ba', 'bb' ]
  1278. ] );
  1279. // +----+----+----+----+----+----+
  1280. // | 00 | 01 | 02 | 03 | 04 | 05 |
  1281. // +----+----+----+----+----+----+
  1282. // | 10 | 12 | aa | ab | 15 |
  1283. // +----+----+----+----+----+----+
  1284. // | 20 | ba | bb | 25 |
  1285. // +----+----+----+----+----+----+
  1286. // | 30 | 31 | 32 | 33 | 34 | 35 |
  1287. // +----+----+----+----+----+----+
  1288. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1289. [ '00', '01', '02', '03', '04', '05' ],
  1290. [ { contents: '10', colspan: 2 }, '12', 'aa', 'ab', '15' ],
  1291. [ { contents: '20', colspan: 3 }, 'ba', 'bb', '25' ],
  1292. [ '30', '31', '32', '33', '34', '35' ]
  1293. ] ) );
  1294. /* eslint-disable no-multi-spaces */
  1295. assertSelectedCells( model, [
  1296. [ 0, 0, 0, 0, 0, 0 ],
  1297. [ 0, 0, 1, 1, 0 ],
  1298. [ 0, 1, 1, 0 ],
  1299. [ 0, 0, 0, 0, 0, 0 ]
  1300. ] );
  1301. /* eslint-enable no-multi-spaces */
  1302. } );
  1303. it( 'handles pasting table that has cell with colspan (last row in selection is spanned)', () => {
  1304. // +----+----+----+----+
  1305. // | 00 | 01 | 02 | 03 |
  1306. // +----+----+----+----+
  1307. // | 10 | 11 | 13 |
  1308. // + + +----+
  1309. // | | | 23 |
  1310. // +----+----+----+----+
  1311. // | 30 | 31 | 32 | 33 |
  1312. // +----+----+----+----+
  1313. setModelData( model, modelTable( [
  1314. [ '00', '01', '02', '03' ],
  1315. [ { contents: '10', rowspan: 2 }, { contents: '11', colspan: 2, rowspan: 2 }, '13' ],
  1316. [ '23' ],
  1317. [ '30', '31', '32', '33' ]
  1318. ] ) );
  1319. // Select 02 -> 10 (selection 3x3)
  1320. tableSelection.setCellSelection(
  1321. modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
  1322. modelRoot.getNodeByPath( [ 0, 1, 0 ] )
  1323. );
  1324. pasteTable( [
  1325. [ 'aa', 'ab', 'ac' ],
  1326. [ 'ba', 'bb', 'bc' ],
  1327. [ 'ca', 'cb', 'cc' ]
  1328. ] );
  1329. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1330. [ 'aa', 'ab', 'ac', '03' ],
  1331. [ 'ba', 'bb', 'bc', '13' ],
  1332. [ 'ca', 'cb', 'cc', '23' ],
  1333. [ '30', '31', '32', '33' ]
  1334. ] ) );
  1335. assertSelectedCells( model, [
  1336. [ 1, 1, 1, 0 ],
  1337. [ 1, 1, 1, 0 ],
  1338. [ 1, 1, 1, 0 ],
  1339. [ 0, 0, 0, 0 ]
  1340. ] );
  1341. } );
  1342. it( 'handles pasting table that has cell with rowspan (multiple ending rows in the selection are spanned)', () => {
  1343. // +----+----+----+
  1344. // | 00 | 01 | 02 |
  1345. // +----+ + +
  1346. // | 10 | | |
  1347. // +----+ + +
  1348. // | 20 | | |
  1349. // +----+----+----+
  1350. // | 30 | 31 | 32 |
  1351. // +----+----+----+
  1352. setModelData( model, modelTable( [
  1353. [ '00', { contents: '01', rowspan: 3 }, { contents: '02', rowspan: 3 } ],
  1354. [ '10' ],
  1355. [ '20' ],
  1356. [ '30', '31', '32' ]
  1357. ] ) );
  1358. // Select 01 -> 02 (selection 2x2)
  1359. tableSelection.setCellSelection(
  1360. modelRoot.getNodeByPath( [ 0, 0, 1 ] ),
  1361. modelRoot.getNodeByPath( [ 0, 0, 2 ] )
  1362. );
  1363. pasteTable( [
  1364. [ 'aa', 'ab' ],
  1365. [ 'ba', 'bb' ],
  1366. [ 'ca', 'cb' ]
  1367. ] );
  1368. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1369. [ '00', 'aa', 'ab' ],
  1370. [ '10', 'ba', 'bb' ],
  1371. [ '20', 'ca', 'cb' ],
  1372. [ '30', '31', '32' ]
  1373. ] ) );
  1374. assertSelectedCells( model, [
  1375. [ 0, 1, 1 ],
  1376. [ 0, 1, 1 ],
  1377. [ 0, 1, 1 ],
  1378. [ 0, 0, 0 ]
  1379. ] );
  1380. } );
  1381. it( 'handles pasting table that has cell with colspan (last column in selection is spanned)', () => {
  1382. // +----+----+----+----+
  1383. // | 00 | 01 | 03 |
  1384. // +----+----+----+----+
  1385. // | 10 | 11 | 13 |
  1386. // +----+ +----+
  1387. // | 20 | | 23 |
  1388. // +----+----+----+----+
  1389. // | 30 | 31 | 32 | 33 |
  1390. // +----+----+----+----+
  1391. setModelData( model, modelTable( [
  1392. [ '00', { contents: '01', colspan: 2 }, '03' ],
  1393. [ '10', { contents: '11', colspan: 2, rowspan: 2 }, '13' ],
  1394. [ '20', '23' ],
  1395. [ '30', '31', '32', '33' ]
  1396. ] ) );
  1397. // Select 20 -> 01 (selection 3x3)
  1398. tableSelection.setCellSelection(
  1399. modelRoot.getNodeByPath( [ 0, 2, 0 ] ),
  1400. modelRoot.getNodeByPath( [ 0, 0, 1 ] )
  1401. );
  1402. pasteTable( [
  1403. [ 'aa', 'ab', 'ac' ],
  1404. [ 'ba', 'bb', 'bc' ],
  1405. [ 'ca', 'cb', 'cc' ]
  1406. ] );
  1407. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1408. [ 'aa', 'ab', 'ac', '03' ],
  1409. [ 'ba', 'bb', 'bc', '13' ],
  1410. [ 'ca', 'cb', 'cc', '23' ],
  1411. [ '30', '31', '32', '33' ]
  1412. ] ) );
  1413. assertSelectedCells( model, [
  1414. [ 1, 1, 1, 0 ],
  1415. [ 1, 1, 1, 0 ],
  1416. [ 1, 1, 1, 0 ],
  1417. [ 0, 0, 0, 0 ]
  1418. ] );
  1419. } );
  1420. it( 'handles pasting table that has cell with colspan (multiple ending columns in the selection are spanned)', () => {
  1421. // +----+----+----+----+
  1422. // | 00 | 01 | 02 | 03 |
  1423. // +----+----+----+----+
  1424. // | 10 | 13 |
  1425. // +----+----+----+----+
  1426. // | 20 | 23 |
  1427. // +----+----+----+----+
  1428. setModelData( model, modelTable( [
  1429. [ '00', '01', '02', '03' ],
  1430. [ { contents: '10', colspan: 3 }, '13' ],
  1431. [ { contents: '20', colspan: 3 }, '23' ]
  1432. ] ) );
  1433. // Select 10 -> 20 (selection 3x2)
  1434. tableSelection.setCellSelection(
  1435. modelRoot.getNodeByPath( [ 0, 1, 0 ] ),
  1436. modelRoot.getNodeByPath( [ 0, 2, 0 ] )
  1437. );
  1438. pasteTable( [
  1439. [ 'aa', 'ab', 'ac' ],
  1440. [ 'ba', 'bb', 'bc' ]
  1441. ] );
  1442. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1443. [ '00', '01', '02', '03' ],
  1444. [ 'aa', 'ab', 'ac', '13' ],
  1445. [ 'ba', 'bb', 'bc', '23' ]
  1446. ] ) );
  1447. assertSelectedCells( model, [
  1448. [ 0, 0, 0, 0 ],
  1449. [ 1, 1, 1, 0 ],
  1450. [ 1, 1, 1, 0 ]
  1451. ] );
  1452. } );
  1453. } );
  1454. describe( 'content and paste tables have spans', () => {
  1455. it( 'handles pasting colspanned table over table with colspans (no colspan exceeds selection)', () => {
  1456. setModelData( model, modelTable( [
  1457. [ '00[]', '01', '02', '03' ],
  1458. [ { colspan: 3, contents: '10' }, '13' ],
  1459. [ { colspan: 2, contents: '20' }, '22', '23' ],
  1460. [ '30', '31', { colspan: 2, contents: '31' } ]
  1461. ] ) );
  1462. tableSelection.setCellSelection(
  1463. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1464. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  1465. );
  1466. pasteTable( [
  1467. [ 'aa', { colspan: 2, contents: 'ab' } ],
  1468. [ { colspan: 3, contents: 'ba' } ],
  1469. [ 'ca', 'cb', 'cc' ]
  1470. ] );
  1471. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1472. [ 'aa', { colspan: 2, contents: 'ab' }, '03' ],
  1473. [ { colspan: 3, contents: 'ba' }, '13' ],
  1474. [ 'ca', 'cb', 'cc', '23' ],
  1475. [ '30', '31', { colspan: 2, contents: '31' } ]
  1476. ] ) );
  1477. /* eslint-disable no-multi-spaces */
  1478. assertSelectedCells( model, [
  1479. [ 1, 1, 0 ],
  1480. [ 1, 0 ],
  1481. [ 1, 1, 1, 0 ],
  1482. [ 0, 0, 0 ]
  1483. ] );
  1484. /* eslint-enable no-multi-spaces */
  1485. } );
  1486. it( 'handles pasting rowspanned table over table with rowspans (no rowspan exceeds selection)', () => {
  1487. setModelData( model, modelTable( [
  1488. [ { rowspan: 3, contents: '00' }, { rowspan: 2, contents: '01' }, '02', '03' ],
  1489. [ { rowspan: 2, contents: '12' }, '13' ],
  1490. [ '21', '23' ],
  1491. [ '30', '31', '32', '33' ]
  1492. ] ) );
  1493. tableSelection.setCellSelection(
  1494. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1495. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  1496. );
  1497. pasteTable( [
  1498. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  1499. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  1500. [ 'cc', 'cd' ]
  1501. ] );
  1502. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1503. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  1504. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  1505. [ 'cc', 'cd' ],
  1506. [ '30', '31', '32', '33' ]
  1507. ] ) );
  1508. /* eslint-disable no-multi-spaces */
  1509. assertSelectedCells( model, [
  1510. [ 1, 1, 1, 1 ],
  1511. [ 1, 1 ],
  1512. [ 1, 1 ],
  1513. [ 0, 0, 0, 0 ]
  1514. ] );
  1515. /* eslint-enable no-multi-spaces */
  1516. } );
  1517. it( 'handles pasting multi-spanned table over table with multi-spans (no span exceeds selection)', () => {
  1518. // +----+----+----+----+----+----+
  1519. // | 00 | 02 | 03 | 05 |
  1520. // + + + +----+
  1521. // | | | | 15 |
  1522. // +----+----+----+ +----+
  1523. // | 20 | 21 | | 25 |
  1524. // + +----+----+----+----+----+
  1525. // | | 31 | 32 | 34 | 35 |
  1526. // +----+----+----+----+----+----+
  1527. // | 40 | 41 | 42 | 43 | 44 | 45 |
  1528. // +----+----+----+----+----+----+
  1529. setModelData( model, modelTable( [
  1530. [
  1531. { contents: '00', colspan: 2, rowspan: 2 },
  1532. { contents: '02', rowspan: 2 },
  1533. { contents: '03', colspan: 2, rowspan: 3 },
  1534. '05'
  1535. ],
  1536. [ '15' ],
  1537. [ { contents: '20', rowspan: 2 }, { contents: '21', colspan: 2 }, '25' ],
  1538. [ '31', { contents: '32', colspan: 2 }, '34', '35' ],
  1539. [ '40', '41', '42', '43', '44', '45' ]
  1540. ] ) );
  1541. tableSelection.setCellSelection(
  1542. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1543. modelRoot.getNodeByPath( [ 0, 3, 2 ] )
  1544. );
  1545. // +----+----+----+----+----+
  1546. // | aa | ac | ad | ae |
  1547. // +----+----+----+----+ +
  1548. // | ba | bb | |
  1549. // +----+ +----+
  1550. // | ca | | ce |
  1551. // + +----+----+----+----+
  1552. // | | db | dc | dd |
  1553. // +----+----+----+----+----+
  1554. pasteTable( [
  1555. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 } ],
  1556. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 } ],
  1557. [ { contents: 'ca', rowspan: 2 }, 'ce' ],
  1558. [ 'db', 'dc', { contents: 'dd', colspan: 2 } ]
  1559. ] );
  1560. // +----+----+----+----+----+----+
  1561. // | aa | ac | ad | ae | 05 |
  1562. // +----+----+----+----+ +----+
  1563. // | ba | bb | | 15 |
  1564. // +----+ +----+----+
  1565. // | ca | | ce | 25 |
  1566. // + +----+----+----+----+----+
  1567. // | | db | dc | dd | 35 |
  1568. // +----+----+----+----+----+----+
  1569. // | 40 | 41 | 42 | 43 | 44 | 45 |
  1570. // +----+----+----+----+----+----+
  1571. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1572. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 }, '05' ],
  1573. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 }, '15' ],
  1574. [ { contents: 'ca', rowspan: 2 }, 'ce', '25' ],
  1575. [ 'db', 'dc', { contents: 'dd', colspan: 2 }, '35' ],
  1576. [ '40', '41', '42', '43', '44', '45' ]
  1577. ] ) );
  1578. /* eslint-disable no-multi-spaces */
  1579. assertSelectedCells( model, [
  1580. [ 1, 1, 1, 1, 0 ],
  1581. [ 1, 1, 0 ],
  1582. [ 1, 1, 0 ],
  1583. [ 1, 1, 1, 0 ],
  1584. [ 0, 0, 0, 0, 0, 0 ]
  1585. ] );
  1586. /* eslint-enable no-multi-spaces */
  1587. } );
  1588. it( 'handles pasting table that has cell with colspan (last row in selection is spanned)', () => {
  1589. // +----+----+----+----+
  1590. // | 00 | 01 | 02 | 03 |
  1591. // +----+----+----+----+
  1592. // | 10 | 11 | 13 |
  1593. // + + +----+
  1594. // | | | 23 |
  1595. // +----+----+----+----+
  1596. // | 30 | 31 | 32 | 33 |
  1597. // +----+----+----+----+
  1598. setModelData( model, modelTable( [
  1599. [ '00', '01', '02', '03' ],
  1600. [ { contents: '10', rowspan: 2 }, { contents: '11', colspan: 2, rowspan: 2 }, '13' ],
  1601. [ '23' ],
  1602. [ '30', '31', '32', '33' ]
  1603. ] ) );
  1604. // Select 02 -> 10 (selection 3x3)
  1605. tableSelection.setCellSelection(
  1606. modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
  1607. modelRoot.getNodeByPath( [ 0, 1, 0 ] )
  1608. );
  1609. // +----+----+----+
  1610. // | aa | ab | ac |
  1611. // +----+----+----+
  1612. // | ba | bc |
  1613. // + +----+
  1614. // | | cc |
  1615. // +----+----+----+
  1616. pasteTable( [
  1617. [ 'aa', 'ab', 'ac' ],
  1618. [ { contents: 'ba', colspan: 2, rowspan: 2 }, 'bc' ],
  1619. [ 'cc' ]
  1620. ] );
  1621. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1622. [ 'aa', 'ab', 'ac', '03' ],
  1623. [ { contents: 'ba', colspan: 2, rowspan: 2 }, 'bc', '13' ],
  1624. [ 'cc', '23' ],
  1625. [ '30', '31', '32', '33' ]
  1626. ] ) );
  1627. /* eslint-disable no-multi-spaces */
  1628. assertSelectedCells( model, [
  1629. [ 1, 1, 1, 0 ],
  1630. [ 1, 1, 0 ],
  1631. [ 1, 0 ],
  1632. [ 0, 0, 0, 0 ]
  1633. ] );
  1634. /* eslint-enable no-multi-spaces */
  1635. } );
  1636. it( 'handles pasting table that has cell with colspan (last column in selection is spanned)', () => {
  1637. // +----+----+----+----+
  1638. // | 00 | 01 | 03 |
  1639. // +----+----+----+----+
  1640. // | 10 | 11 | 13 |
  1641. // +----+ +----+
  1642. // | 20 | | 23 |
  1643. // +----+----+----+----+
  1644. // | 30 | 31 | 32 | 33 |
  1645. // +----+----+----+----+
  1646. setModelData( model, modelTable( [
  1647. [ '00', { contents: '01', colspan: 2 }, '03' ],
  1648. [ '10', { contents: '11', colspan: 2, rowspan: 2 }, '13' ],
  1649. [ '20', '23' ],
  1650. [ '30', '31', '32', '33' ]
  1651. ] ) );
  1652. // Select 20 -> 01 (selection 3x3)
  1653. tableSelection.setCellSelection(
  1654. modelRoot.getNodeByPath( [ 0, 2, 0 ] ),
  1655. modelRoot.getNodeByPath( [ 0, 0, 1 ] )
  1656. );
  1657. // +----+----+----+
  1658. // | aa | ab | ac |
  1659. // +----+----+----+
  1660. // | ba | bc |
  1661. // + +----+
  1662. // | | cc |
  1663. // +----+----+----+
  1664. pasteTable( [
  1665. [ 'aa', 'ab', 'ac' ],
  1666. [ { contents: 'ba', colspan: 2, rowspan: 2 }, 'bc' ],
  1667. [ 'cc' ]
  1668. ] );
  1669. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1670. [ 'aa', 'ab', 'ac', '03' ],
  1671. [ { contents: 'ba', colspan: 2, rowspan: 2 }, 'bc', '13' ],
  1672. [ 'cc', '23' ],
  1673. [ '30', '31', '32', '33' ]
  1674. ] ) );
  1675. /* eslint-disable no-multi-spaces */
  1676. assertSelectedCells( model, [
  1677. [ 1, 1, 1, 0 ],
  1678. [ 1, 1, 0 ],
  1679. [ 1, 0 ],
  1680. [ 0, 0, 0, 0 ]
  1681. ] );
  1682. /* eslint-enable no-multi-spaces */
  1683. } );
  1684. } );
  1685. describe( 'non-rectangular content table selection', () => {
  1686. it( 'should split cells outside the selected area before pasting (rowspan ends in selection)', () => {
  1687. // +----+----+----+
  1688. // | 00 | 01 | 02 |
  1689. // +----+ +----+
  1690. // | 10 | | 12 |
  1691. // +----+ +----+
  1692. // | 20 | | 22 |
  1693. // +----+ +----+
  1694. // | 30 | | 32 |
  1695. // +----+----+----+
  1696. // | 40 | 41 | 42 |
  1697. // +----+----+----+
  1698. setModelData( model, modelTable( [
  1699. [ '00', { contents: '01', rowspan: 4 }, '02' ],
  1700. [ '10', '12' ],
  1701. [ '20', '22' ],
  1702. [ '30', '32' ],
  1703. [ '40', '41', '42' ]
  1704. ] ) );
  1705. // Select 20 -> 32
  1706. tableSelection.setCellSelection(
  1707. modelRoot.getNodeByPath( [ 0, 2, 0 ] ),
  1708. modelRoot.getNodeByPath( [ 0, 3, 1 ] )
  1709. );
  1710. pasteTable( [
  1711. [ 'aa', 'ab', 'ac' ],
  1712. [ 'ba', 'bb', 'bc' ]
  1713. ] );
  1714. // +----+----+----+
  1715. // | 00 | 01 | 02 |
  1716. // +----+ +----+
  1717. // | 10 | | 12 |
  1718. // +----+----+----+
  1719. // | aa | ab | ac |
  1720. // +----+----+----+
  1721. // | ba | bb | bc |
  1722. // +----+----+----+
  1723. // | 40 | 41 | 42 |
  1724. // +----+----+----+
  1725. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1726. [ '00', { contents: '01', rowspan: 2 }, '02' ],
  1727. [ '10', '12' ],
  1728. [ 'aa', 'ab', 'ac' ],
  1729. [ 'ba', 'bb', 'bc' ],
  1730. [ '40', '41', '42' ]
  1731. ] ) );
  1732. } );
  1733. it( 'should split cells outside the selected area before pasting (rowspan ends after the selection)', () => {
  1734. // +----+----+----+
  1735. // | 00 | 01 | 02 |
  1736. // +----+ +----+
  1737. // | 10 | | 12 |
  1738. // +----+ +----+
  1739. // | 20 | | 22 |
  1740. // +----+ +----+
  1741. // | 30 | | 32 |
  1742. // +----+----+----+
  1743. // | 40 | 41 | 42 |
  1744. // +----+----+----+
  1745. setModelData( model, modelTable( [
  1746. [ '00', { contents: '01', rowspan: 4 }, '02' ],
  1747. [ '10', '12' ],
  1748. [ '20', '22' ],
  1749. [ '30', '32' ],
  1750. [ '40', '41', '42' ]
  1751. ] ) );
  1752. // Select 10 -> 22
  1753. tableSelection.setCellSelection(
  1754. modelRoot.getNodeByPath( [ 0, 1, 0 ] ),
  1755. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  1756. );
  1757. pasteTable( [
  1758. [ 'aa', 'ab', 'ac' ],
  1759. [ 'ba', 'bb', 'bc' ]
  1760. ] );
  1761. // +----+----+----+
  1762. // | 00 | 01 | 02 |
  1763. // +----+----+----+
  1764. // | aa | ab | ac |
  1765. // +----+----+----+
  1766. // | ba | bb | bc |
  1767. // +----+----+----+
  1768. // | 30 | | 32 |
  1769. // +----+----+----+
  1770. // | 40 | 41 | 42 |
  1771. // +----+----+----+
  1772. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1773. [ '00', '01', '02' ],
  1774. [ 'aa', 'ab', 'ac' ],
  1775. [ 'ba', 'bb', 'bc' ],
  1776. [ '30', '', '32' ],
  1777. [ '40', '41', '42' ]
  1778. ] ) );
  1779. } );
  1780. it( 'should split cells inside the selected area before pasting (rowspan ends after the selection)', () => {
  1781. // +----+----+----+
  1782. // | 00 | 01 | 02 |
  1783. // +----+ +----+
  1784. // | 10 | | 12 |
  1785. // +----+ +----+
  1786. // | 20 | | 22 |
  1787. // +----+ +----+
  1788. // | 30 | | 32 |
  1789. // +----+----+----+
  1790. // | 40 | 41 | 42 |
  1791. // +----+----+----+
  1792. setModelData( model, modelTable( [
  1793. [ '00', { contents: '01', rowspan: 4 }, '02' ],
  1794. [ '10', '12' ],
  1795. [ '20', '22' ],
  1796. [ '30', '32' ],
  1797. [ '40', '41', '42' ]
  1798. ] ) );
  1799. // Select 00 -> 12
  1800. tableSelection.setCellSelection(
  1801. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1802. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  1803. );
  1804. pasteTable( [
  1805. [ 'aa', 'ab', 'ac' ],
  1806. [ 'ba', 'bb', 'bc' ]
  1807. ] );
  1808. // +----+----+----+
  1809. // | aa | ab | ac |
  1810. // +----+----+----+
  1811. // | ba | bb | bc |
  1812. // +----+----+----+
  1813. // | 20 | | 22 |
  1814. // +----+ +----+
  1815. // | 30 | | 32 |
  1816. // +----+----+----+
  1817. // | 40 | 41 | 42 |
  1818. // +----+----+----+
  1819. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1820. [ 'aa', 'ab', 'ac' ],
  1821. [ 'ba', 'bb', 'bc' ],
  1822. [ '20', { rowspan: 2, contents: '' }, '22' ],
  1823. [ '30', '32' ],
  1824. [ '40', '41', '42' ]
  1825. ] ) );
  1826. } );
  1827. it( 'should split cells outside the selected area before pasting (colspan ends in selection)', () => {
  1828. // +----+----+----+----+----+
  1829. // | 00 | 01 | 02 | 03 | 04 |
  1830. // +----+----+----+----+----+
  1831. // | 10 | 14 |
  1832. // +----+----+----+----+----+
  1833. // | 20 | 21 | 22 | 23 | 24 |
  1834. // +----+----+----+----+----+
  1835. setModelData( model, modelTable( [
  1836. [ '00', '01', '02', '03', '04' ],
  1837. [ { contents: '10', colspan: 4 }, '14' ],
  1838. [ '20', '21', '22', '23', '24' ]
  1839. ] ) );
  1840. // Select 02 -> 23
  1841. tableSelection.setCellSelection(
  1842. modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
  1843. modelRoot.getNodeByPath( [ 0, 2, 3 ] )
  1844. );
  1845. pasteTable( [
  1846. [ 'aa', 'ab' ],
  1847. [ 'ba', 'bb' ],
  1848. [ 'ca', 'cb' ]
  1849. ] );
  1850. // +----+----+----+----+----+
  1851. // | 00 | 01 | aa | ab | 04 |
  1852. // +----+----+----+----+----+
  1853. // | 10 | ba | bb | 14 |
  1854. // +----+----+----+----+----+
  1855. // | 20 | 21 | ca | cb | 24 |
  1856. // +----+----+----+----+----+
  1857. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1858. [ '00', '01', 'aa', 'ab', '04' ],
  1859. [ { contents: '10', colspan: 2 }, 'ba', 'bb', '14' ],
  1860. [ '20', '21', 'ca', 'cb', '24' ]
  1861. ] ) );
  1862. } );
  1863. it( 'should split cells outside the selected area before pasting (colspan ends after the selection)', () => {
  1864. // +----+----+----+----+----+
  1865. // | 00 | 01 | 02 | 03 | 04 |
  1866. // +----+----+----+----+----+
  1867. // | 10 | 14 |
  1868. // +----+----+----+----+----+
  1869. // | 20 | 21 | 22 | 23 | 24 |
  1870. // +----+----+----+----+----+
  1871. setModelData( model, modelTable( [
  1872. [ '00', '01', '02', '03', '04' ],
  1873. [ { contents: '10', colspan: 4 }, '14' ],
  1874. [ '20', '21', '22', '23', '24' ]
  1875. ] ) );
  1876. // Select 01 -> 22
  1877. tableSelection.setCellSelection(
  1878. modelRoot.getNodeByPath( [ 0, 0, 1 ] ),
  1879. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  1880. );
  1881. pasteTable( [
  1882. [ 'aa', 'ab' ],
  1883. [ 'ba', 'bb' ],
  1884. [ 'ca', 'cb' ]
  1885. ] );
  1886. // +----+----+----+----+----+
  1887. // | 00 | aa | ab | 03 | 04 |
  1888. // +----+----+----+----+----+
  1889. // | 10 | ba | bb | | 14 |
  1890. // +----+----+----+----+----+
  1891. // | 20 | ca | cb | 23 | 24 |
  1892. // +----+----+----+----+----+
  1893. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1894. [ '00', 'aa', 'ab', '03', '04' ],
  1895. [ '10', 'ba', 'bb', '', '14' ],
  1896. [ '20', 'ca', 'cb', '23', '24' ]
  1897. ] ) );
  1898. } );
  1899. it( 'should split cells inside the selected area before pasting (colspan ends after the selection)', () => {
  1900. // +----+----+----+----+----+
  1901. // | 00 | 01 | 02 | 03 | 04 |
  1902. // +----+----+----+----+----+
  1903. // | 10 | 14 |
  1904. // +----+----+----+----+----+
  1905. // | 20 | 21 | 22 | 23 | 24 |
  1906. // +----+----+----+----+----+
  1907. setModelData( model, modelTable( [
  1908. [ '00', '01', '02', '03', '04' ],
  1909. [ { contents: '10', colspan: 4 }, '14' ],
  1910. [ '20', '21', '22', '23', '24' ]
  1911. ] ) );
  1912. // Select 00 -> 21
  1913. tableSelection.setCellSelection(
  1914. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  1915. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  1916. );
  1917. pasteTable( [
  1918. [ 'aa', 'ab' ],
  1919. [ 'ba', 'bb' ],
  1920. [ 'ca', 'cb' ]
  1921. ] );
  1922. // +----+----+----+----+----+
  1923. // | aa | ab | 02 | 03 | 04 |
  1924. // +----+----+----+----+----+
  1925. // | ba | bb | | 14 |
  1926. // +----+----+----+----+----+
  1927. // | ca | cb | 22 | 23 | 24 |
  1928. // +----+----+----+----+----+
  1929. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1930. [ 'aa', 'ab', '02', '03', '04' ],
  1931. [ 'ba', 'bb', { colspan: 2, contents: '' }, '14' ],
  1932. [ 'ca', 'cb', '22', '23', '24' ]
  1933. ] ) );
  1934. } );
  1935. it( 'should split cells anchored outside selection rectangle that overlaps selection (above selection)', () => {
  1936. // +----+----+----+
  1937. // | 00 | 02 |
  1938. // + +----+
  1939. // | | 12 |
  1940. // +----+----+----+
  1941. // | 20 | 21 | 22 |
  1942. // +----+----+----+
  1943. setModelData( model, modelTable( [
  1944. [ { contents: '00', colspan: 2, rowspan: 2 }, '02' ],
  1945. [ '12' ],
  1946. [ '20', '21', '22' ]
  1947. ] ) );
  1948. // Select 21 -> 12
  1949. tableSelection.setCellSelection(
  1950. modelRoot.getNodeByPath( [ 0, 2, 1 ] ),
  1951. modelRoot.getNodeByPath( [ 0, 1, 0 ] )
  1952. );
  1953. pasteTable( [
  1954. [ 'aa', 'ab' ],
  1955. [ 'ba', 'bb' ]
  1956. ] );
  1957. // +----+----+----+
  1958. // | 00 | | 02 |
  1959. // + +----+----+
  1960. // | | aa | ab |
  1961. // +----+----+----+
  1962. // | 20 | ba | bb |
  1963. // +----+----+----+
  1964. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  1965. [ { contents: '00', rowspan: 2 }, '', '02' ],
  1966. [ 'aa', 'ab' ],
  1967. [ '20', 'ba', 'bb' ]
  1968. ] ) );
  1969. } );
  1970. it( 'should split cells anchored outside selection rectangle that overlaps selection (below selection)', () => {
  1971. // +----+----+----+
  1972. // | 00 | 01 | 02 |
  1973. // +----+----+----+
  1974. // | 10 | 12 |
  1975. // + +----+
  1976. // | | 22 |
  1977. // +----+----+----+
  1978. setModelData( model, modelTable( [
  1979. [ '00', '01', '02' ],
  1980. [ { contents: '10', colspan: 2, rowspan: 2 }, '12' ],
  1981. [ '22' ]
  1982. ] ) );
  1983. // Select 01 -> 12
  1984. tableSelection.setCellSelection(
  1985. modelRoot.getNodeByPath( [ 0, 0, 1 ] ),
  1986. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  1987. );
  1988. pasteTable( [
  1989. [ 'aa', 'ab' ],
  1990. [ 'ba', 'bb' ]
  1991. ] );
  1992. // +----+----+----+
  1993. // | 00 | aa | ab |
  1994. // +----+----+----+
  1995. // | 10 | ba | bb |
  1996. // + +----+----+
  1997. // | | | 22 |
  1998. // +----+----+----+
  1999. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2000. [ '00', 'aa', 'ab' ],
  2001. [ { contents: '10', rowspan: 2 }, 'ba', 'bb' ],
  2002. [ '', '22' ]
  2003. ] ) );
  2004. } );
  2005. it( 'should properly handle complex case', () => {
  2006. // +----+----+----+----+----+----+----+
  2007. // | 00 | 03 | 04 |
  2008. // + + +----+----+----+
  2009. // | | | 14 | 15 | 16 |
  2010. // + + +----+----+----+
  2011. // | | | 24 |
  2012. // +----+----+----+----+----+----+----+
  2013. // | 30 | 31 | 33 | 34 | 35 | 36 |
  2014. // + +----+----+ +----+----+----+
  2015. // | | 41 | 42 | | 44 | 45 |
  2016. // + +----+----+ +----+ +
  2017. // | | 51 | 52 | | 54 | |
  2018. // +----+----+----+ +----+ +
  2019. // | 60 | 61 | 62 | | 64 | |
  2020. // +----+----+----+----+----+----+----+
  2021. setModelData( model, modelTable( [
  2022. [ { contents: '00', colspan: 3, rowspan: 3 }, { contents: '03', rowspan: 3 }, { colspan: 3, contents: '04' } ],
  2023. [ '14', '15', '16' ],
  2024. [ { contents: '24', colspan: 3 } ],
  2025. [
  2026. { contents: '30', rowspan: 3 },
  2027. { contents: '31', colspan: 2 },
  2028. { contents: '33', rowspan: 4 },
  2029. '34', '35', '36'
  2030. ],
  2031. [ '41', '42', '44', { contents: '45', colspan: 2, rowspan: 3 } ],
  2032. [ '51', '52', '54' ],
  2033. [ '60', '61', '62', '64' ]
  2034. ] ) );
  2035. // Select 42 -> 24 (3x3 selection)
  2036. tableSelection.setCellSelection(
  2037. modelRoot.getNodeByPath( [ 0, 4, 1 ] ),
  2038. modelRoot.getNodeByPath( [ 0, 2, 0 ] )
  2039. );
  2040. pasteTable( [
  2041. [ 'aa', 'ab', 'ac' ],
  2042. [ 'ba', 'bb', 'bc' ],
  2043. [ 'ca', 'cb', 'cc' ]
  2044. ] );
  2045. // +----+----+----+----+----+----+----+
  2046. // | 00 | | 03 | 04 |
  2047. // + + + +----+----+----+
  2048. // | | | | 14 | 15 | 16 |
  2049. // + +----+----+----+----+----+
  2050. // | | aa | ab | ac | |
  2051. // +----+----+----+----+----+----+----+
  2052. // | 30 | 31 | ba | bb | bc | 35 | 36 |
  2053. // + +----+----+----+----+----+----+
  2054. // | | 41 | ca | cb | cc | 45 |
  2055. // + +----+----+----+----+ +
  2056. // | | 51 | 52 | | 54 | |
  2057. // +----+----+----+ +----+ +
  2058. // | 60 | 61 | 62 | | 64 | |
  2059. // +----+----+----+----+----+----+----+
  2060. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2061. [
  2062. { contents: '00', colspan: 2, rowspan: 3 },
  2063. { contents: '', rowspan: 2 },
  2064. { contents: '03', rowspan: 2 },
  2065. { contents: '04', colspan: 3 }
  2066. ],
  2067. [ '14', '15', '16' ],
  2068. [ 'aa', 'ab', 'ac', { contents: '', colspan: 2 } ],
  2069. [ { contents: '30', rowspan: 3 }, '31', 'ba', 'bb', 'bc', '35', '36' ],
  2070. [ '41', 'ca', 'cb', 'cc', { contents: '45', colspan: 2, rowspan: 3 } ],
  2071. [ '51', '52', { contents: '', rowspan: 2 }, '54' ],
  2072. [ '60', '61', '62', '64' ]
  2073. ] ) );
  2074. } );
  2075. } );
  2076. } );
  2077. describe( 'pasted table is bigger than the selected area', () => {
  2078. describe( 'no spans', () => {
  2079. it( 'handles simple table paste to a simple table fragment - at the beginning of a table', () => {
  2080. tableSelection.setCellSelection(
  2081. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2082. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  2083. );
  2084. pasteTable( [
  2085. [ 'aa', 'ab', 'ac' ],
  2086. [ 'ba', 'bb', 'bc' ],
  2087. [ 'ca', 'cb', 'cc' ]
  2088. ] );
  2089. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2090. [ 'aa', 'ab', '02', '03' ],
  2091. [ 'ba', 'bb', '12', '13' ],
  2092. [ '20', '21', '22', '23' ],
  2093. [ '30', '31', '32', '33' ]
  2094. ] ) );
  2095. assertSelectedCells( model, [
  2096. [ 1, 1, 0, 0 ],
  2097. [ 1, 1, 0, 0 ],
  2098. [ 0, 0, 0, 0 ],
  2099. [ 0, 0, 0, 0 ]
  2100. ] );
  2101. } );
  2102. it( 'handles simple table paste to a simple table fragment - at the end of a table', () => {
  2103. tableSelection.setCellSelection(
  2104. modelRoot.getNodeByPath( [ 0, 2, 2 ] ),
  2105. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  2106. );
  2107. pasteTable( [
  2108. [ 'aa', 'ab', 'ac' ],
  2109. [ 'ba', 'bb', 'bc' ],
  2110. [ 'ca', 'cb', 'cc' ]
  2111. ] );
  2112. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2113. [ '00', '01', '02', '03' ],
  2114. [ '10', '11', '12', '13' ],
  2115. [ '20', '21', 'aa', 'ab' ],
  2116. [ '30', '31', 'ba', 'bb' ]
  2117. ] ) );
  2118. assertSelectedCells( model, [
  2119. [ 0, 0, 0, 0 ],
  2120. [ 0, 0, 0, 0 ],
  2121. [ 0, 0, 1, 1 ],
  2122. [ 0, 0, 1, 1 ]
  2123. ] );
  2124. } );
  2125. it( 'handles simple table paste to a simple table fragment - in the middle of a table', () => {
  2126. tableSelection.setCellSelection(
  2127. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  2128. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  2129. );
  2130. pasteTable( [
  2131. [ 'aa', 'ab', 'ac' ],
  2132. [ 'ba', 'bb', 'bc' ],
  2133. [ 'ca', 'cb', 'cc' ]
  2134. ] );
  2135. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2136. [ '00', '01', '02', '03' ],
  2137. [ '10', 'aa', 'ab', '13' ],
  2138. [ '20', 'ba', 'bb', '23' ],
  2139. [ '30', '31', '32', '33' ]
  2140. ] ) );
  2141. assertSelectedCells( model, [
  2142. [ 0, 0, 0, 0 ],
  2143. [ 0, 1, 1, 0 ],
  2144. [ 0, 1, 1, 0 ],
  2145. [ 0, 0, 0, 0 ]
  2146. ] );
  2147. } );
  2148. it( 'handles paste to a simple row fragment - in the middle of a table', () => {
  2149. tableSelection.setCellSelection(
  2150. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  2151. modelRoot.getNodeByPath( [ 0, 1, 2 ] )
  2152. );
  2153. pasteTable( [
  2154. [ 'aa', 'ab', 'ac' ],
  2155. [ 'ba', 'bb', 'bc' ],
  2156. [ 'ca', 'cb', 'cc' ]
  2157. ] );
  2158. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2159. [ '00', '01', '02', '03' ],
  2160. [ '10', 'aa', 'ab', '13' ],
  2161. [ '20', '21', '22', '23' ],
  2162. [ '30', '31', '32', '33' ]
  2163. ] ) );
  2164. assertSelectedCells( model, [
  2165. [ 0, 0, 0, 0 ],
  2166. [ 0, 1, 1, 0 ],
  2167. [ 0, 0, 0, 0 ],
  2168. [ 0, 0, 0, 0 ]
  2169. ] );
  2170. } );
  2171. it( 'handles paste to a simple column fragment - in the middle of a table', () => {
  2172. tableSelection.setCellSelection(
  2173. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  2174. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  2175. );
  2176. pasteTable( [
  2177. [ 'aa', 'ab', 'ac' ],
  2178. [ 'ba', 'bb', 'bc' ],
  2179. [ 'ca', 'cb', 'cc' ]
  2180. ] );
  2181. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2182. [ '00', '01', '02', '03' ],
  2183. [ '10', 'aa', '12', '13' ],
  2184. [ '20', 'ba', '22', '23' ],
  2185. [ '30', '31', '32', '33' ]
  2186. ] ) );
  2187. assertSelectedCells( model, [
  2188. [ 0, 0, 0, 0 ],
  2189. [ 0, 1, 0, 0 ],
  2190. [ 0, 1, 0, 0 ],
  2191. [ 0, 0, 0, 0 ]
  2192. ] );
  2193. } );
  2194. it( 'handles simple table paste to a simple table fragment - whole table selected', () => {
  2195. tableSelection.setCellSelection(
  2196. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2197. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  2198. );
  2199. pasteTable( [
  2200. [ 'aa', 'ab', 'ac', 'ad', 'ae' ],
  2201. [ 'ba', 'bb', 'bc', 'bd', 'be' ],
  2202. [ 'ca', 'cb', 'cc', 'cd', 'ce' ],
  2203. [ 'da', 'db', 'dc', 'dd', 'de' ],
  2204. [ 'ea', 'eb', 'ec', 'ed', 'ee' ]
  2205. ] );
  2206. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2207. [ 'aa', 'ab', 'ac', 'ad' ],
  2208. [ 'ba', 'bb', 'bc', 'bd' ],
  2209. [ 'ca', 'cb', 'cc', 'cd' ],
  2210. [ 'da', 'db', 'dc', 'dd' ]
  2211. ] ) );
  2212. assertSelectedCells( model, [
  2213. [ 1, 1, 1, 1 ],
  2214. [ 1, 1, 1, 1 ],
  2215. [ 1, 1, 1, 1 ],
  2216. [ 1, 1, 1, 1 ]
  2217. ] );
  2218. } );
  2219. } );
  2220. describe( 'pasted table has spans', () => {
  2221. it( 'handles pasting table that has cell with colspan', () => {
  2222. tableSelection.setCellSelection(
  2223. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  2224. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  2225. );
  2226. pasteTable( [
  2227. [ { colspan: 3, contents: 'aa' } ],
  2228. [ 'ba', 'bb', 'bc' ]
  2229. ] );
  2230. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2231. [ '00', '01', '02', '03' ],
  2232. [ '10', { colspan: 2, contents: 'aa' }, '13' ],
  2233. [ '20', 'ba', 'bb', '23' ],
  2234. [ '30', '31', '32', '33' ]
  2235. ] ) );
  2236. /* eslint-disable no-multi-spaces */
  2237. assertSelectedCells( model, [
  2238. [ 0, 0, 0, 0 ],
  2239. [ 0, 1, 0 ],
  2240. [ 0, 1, 1, 0 ],
  2241. [ 0, 0, 0, 0 ]
  2242. ] );
  2243. /* eslint-enable no-multi-spaces */
  2244. } );
  2245. it( 'handles pasting table that has many cells with various colspan', () => {
  2246. tableSelection.setCellSelection(
  2247. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2248. modelRoot.getNodeByPath( [ 0, 3, 2 ] )
  2249. );
  2250. pasteTable( [
  2251. [ 'aa', { colspan: 3, contents: 'ab' } ],
  2252. [ { colspan: 4, contents: 'ba' } ],
  2253. [ 'ca', 'cb', 'cc', 'cd' ],
  2254. [ { colspan: 2, contents: 'da' }, 'dc', 'dd' ]
  2255. ] );
  2256. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2257. [ 'aa', { colspan: 2, contents: 'ab' }, '03' ],
  2258. [ { colspan: 3, contents: 'ba' }, '13' ],
  2259. [ 'ca', 'cb', 'cc', '23' ],
  2260. [ { colspan: 2, contents: 'da' }, 'dc', '33' ]
  2261. ] ) );
  2262. /* eslint-disable no-multi-spaces */
  2263. assertSelectedCells( model, [
  2264. [ 1, 1, 0 ],
  2265. [ 1, 0 ],
  2266. [ 1, 1, 1, 0 ],
  2267. [ 1, 1, 0 ]
  2268. ] );
  2269. /* eslint-enable no-multi-spaces */
  2270. } );
  2271. it( 'handles pasting table that has cell with rowspan', () => {
  2272. tableSelection.setCellSelection(
  2273. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  2274. modelRoot.getNodeByPath( [ 0, 2, 2 ] )
  2275. );
  2276. pasteTable( [
  2277. [ { rowspan: 3, contents: 'aa' }, 'ab' ],
  2278. [ 'bb' ],
  2279. [ 'cb' ]
  2280. ] );
  2281. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2282. [ '00', '01', '02', '03' ],
  2283. [ '10', { rowspan: 2, contents: 'aa' }, 'ab', '13' ],
  2284. [ '20', 'bb', '23' ],
  2285. [ '30', '31', '32', '33' ]
  2286. ] ) );
  2287. /* eslint-disable no-multi-spaces */
  2288. assertSelectedCells( model, [
  2289. [ 0, 0, 0, 0 ],
  2290. [ 0, 1, 1, 0 ],
  2291. [ 0, 1, 0 ],
  2292. [ 0, 0, 0, 0 ]
  2293. ] );
  2294. /* eslint-enable no-multi-spaces */
  2295. } );
  2296. it( 'handles pasting table that has many cells with various rowspan', () => {
  2297. tableSelection.setCellSelection(
  2298. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2299. modelRoot.getNodeByPath( [ 0, 2, 3 ] )
  2300. );
  2301. pasteTable( [
  2302. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad', 'ae' ],
  2303. [ { rowspan: 3, contents: 'ba' }, 'bd', 'be' ],
  2304. [ 'cc', 'cd', 'ce' ],
  2305. [ 'da', 'db', 'dc', 'dd' ]
  2306. ] );
  2307. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2308. [ 'aa', { rowspan: 3, contents: 'ab' }, { rowspan: 2, contents: 'ac' }, 'ad' ],
  2309. [ { rowspan: 2, contents: 'ba' }, 'bd' ],
  2310. [ 'cc', 'cd' ],
  2311. [ '30', '31', '32', '33' ]
  2312. ] ) );
  2313. /* eslint-disable no-multi-spaces */
  2314. assertSelectedCells( model, [
  2315. [ 1, 1, 1, 1 ],
  2316. [ 1, 1 ],
  2317. [ 1, 1 ],
  2318. [ 0, 0, 0, 0 ]
  2319. ] );
  2320. /* eslint-enable no-multi-spaces */
  2321. } );
  2322. it( 'handles pasting multi-spanned table', () => {
  2323. setModelData( model, modelTable( [
  2324. [ '00', '01', '02', '03', '04', '05' ],
  2325. [ '10', '11', '12', '13', '14', '15' ],
  2326. [ '20', '21', '22', '23', '24', '25' ],
  2327. [ '30', '31', '32', '33', '34', '35' ],
  2328. [ '40', '41', '42', '43', '44', '45' ]
  2329. ] ) );
  2330. tableSelection.setCellSelection(
  2331. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  2332. modelRoot.getNodeByPath( [ 0, 3, 3 ] )
  2333. );
  2334. // +----+----+----+----+----+
  2335. // | aa | ac | ad | ae |
  2336. // +----+----+----+----+ +
  2337. // | ba | bb | |
  2338. // +----+ +----+
  2339. // | ca | | ce |
  2340. // + +----+----+----+----+
  2341. // | | db | dc | dd |
  2342. // +----+----+----+----+----+
  2343. pasteTable( [
  2344. [ { contents: 'aa', colspan: 2 }, 'ac', 'ad', { contents: 'ae', rowspan: 2 } ],
  2345. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 } ],
  2346. [ { contents: 'ca', rowspan: 2 }, 'ce' ],
  2347. [ 'db', 'dc', { contents: 'dd', colspan: 2 } ]
  2348. ] );
  2349. // +----+----+----+----+----+----+
  2350. // | 00 | 01 | 02 | 03 | 04 | 05 |
  2351. // +----+----+----+----+----+----+
  2352. // | 10 | aa | ac | 14 | 15 |
  2353. // +----+----+----+----+----+----+
  2354. // | 20 | ba | bb | 24 | 25 |
  2355. // +----+----+ +----+----+
  2356. // | 30 | ca | | 34 | 35 |
  2357. // +----+----+----+----+----+----+
  2358. // | 40 | 41 | 42 | 43 | 44 | 45 |
  2359. // +----+----+----+----+----+----+
  2360. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2361. [ '00', '01', '02', '03', '04', '05' ],
  2362. [ '10', { contents: 'aa', colspan: 2 }, 'ac', '14', '15' ],
  2363. [ '20', 'ba', { contents: 'bb', colspan: 2, rowspan: 2 }, '24', '25' ],
  2364. [ '30', 'ca', '34', '35' ],
  2365. [ '40', '41', '42', '43', '44', '45' ]
  2366. ] ) );
  2367. /* eslint-disable no-multi-spaces */
  2368. assertSelectedCells( model, [
  2369. [ 0, 0, 0, 0, 0, 0 ],
  2370. [ 0, 1, 1, 0, 0 ],
  2371. [ 0, 1, 1, 0, 0 ],
  2372. [ 0, 1, 0, 0 ],
  2373. [ 0, 0, 0, 0, 0, 0 ]
  2374. ] );
  2375. /* eslint-enable no-multi-spaces */
  2376. } );
  2377. } );
  2378. } );
  2379. describe( 'pasted table is smaller than the selected area', () => {
  2380. describe( 'no spans', () => {
  2381. beforeEach( () => {
  2382. setModelData( model, modelTable( [
  2383. [ '00', '01', '02', '03', '04', '05' ],
  2384. [ '10', '11', '12', '13', '14', '15' ],
  2385. [ '20', '21', '22', '23', '24', '25' ],
  2386. [ '30', '31', '32', '33', '34', '35' ],
  2387. [ '40', '41', '42', '43', '44', '45' ],
  2388. [ '50', '51', '52', '53', '54', '55' ]
  2389. ] ) );
  2390. } );
  2391. it( 'should repeat pasted cells horizontally', () => {
  2392. tableSelection.setCellSelection(
  2393. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2394. modelRoot.getNodeByPath( [ 0, 1, 4 ] )
  2395. );
  2396. pasteTable( [
  2397. [ 'aa', 'ab', 'ac' ],
  2398. [ 'ba', 'bb', 'bc' ],
  2399. [ 'ca', 'cb', 'cc' ]
  2400. ] );
  2401. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2402. [ 'aa', 'ab', 'ac', 'aa', 'ab', '05' ],
  2403. [ 'ba', 'bb', 'bc', 'ba', 'bb', '15' ],
  2404. [ '20', '21', '22', '23', '24', '25' ],
  2405. [ '30', '31', '32', '33', '34', '35' ],
  2406. [ '40', '41', '42', '43', '44', '45' ],
  2407. [ '50', '51', '52', '53', '54', '55' ]
  2408. ] ) );
  2409. assertSelectedCells( model, [
  2410. [ 1, 1, 1, 1, 1, 0 ],
  2411. [ 1, 1, 1, 1, 1, 0 ],
  2412. [ 0, 0, 0, 0, 0, 0 ],
  2413. [ 0, 0, 0, 0, 0, 0 ]
  2414. ] );
  2415. } );
  2416. it( 'should repeat pasted cells vertically', () => {
  2417. tableSelection.setCellSelection(
  2418. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2419. modelRoot.getNodeByPath( [ 0, 4, 1 ] )
  2420. );
  2421. pasteTable( [
  2422. [ 'aa', 'ab', 'ac' ],
  2423. [ 'ba', 'bb', 'bc' ],
  2424. [ 'ca', 'cb', 'cc' ]
  2425. ] );
  2426. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2427. [ 'aa', 'ab', '02', '03', '04', '05' ],
  2428. [ 'ba', 'bb', '12', '13', '14', '15' ],
  2429. [ 'ca', 'cb', '22', '23', '24', '25' ],
  2430. [ 'aa', 'ab', '32', '33', '34', '35' ],
  2431. [ 'ba', 'bb', '42', '43', '44', '45' ],
  2432. [ '50', '51', '52', '53', '54', '55' ]
  2433. ] ) );
  2434. assertSelectedCells( model, [
  2435. [ 1, 1, 0, 0, 0, 0 ],
  2436. [ 1, 1, 0, 0, 0, 0 ],
  2437. [ 1, 1, 0, 0, 0, 0 ],
  2438. [ 1, 1, 0, 0, 0, 0 ],
  2439. [ 1, 1, 0, 0, 0, 0 ],
  2440. [ 0, 0, 0, 0, 0, 0 ]
  2441. ] );
  2442. } );
  2443. it( 'should repeat pasted cells in both directions', () => {
  2444. tableSelection.setCellSelection(
  2445. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2446. modelRoot.getNodeByPath( [ 0, 4, 4 ] )
  2447. );
  2448. pasteTable( [
  2449. [ 'aa', 'ab', 'ac' ],
  2450. [ 'ba', 'bb', 'bc' ],
  2451. [ 'ca', 'cb', 'cc' ]
  2452. ] );
  2453. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2454. [ 'aa', 'ab', 'ac', 'aa', 'ab', '05' ],
  2455. [ 'ba', 'bb', 'bc', 'ba', 'bb', '15' ],
  2456. [ 'ca', 'cb', 'cc', 'ca', 'cb', '25' ],
  2457. [ 'aa', 'ab', 'ac', 'aa', 'ab', '35' ],
  2458. [ 'ba', 'bb', 'bc', 'ba', 'bb', '45' ],
  2459. [ '50', '51', '52', '53', '54', '55' ]
  2460. ] ) );
  2461. assertSelectedCells( model, [
  2462. [ 1, 1, 1, 1, 1, 0 ],
  2463. [ 1, 1, 1, 1, 1, 0 ],
  2464. [ 1, 1, 1, 1, 1, 0 ],
  2465. [ 1, 1, 1, 1, 1, 0 ],
  2466. [ 1, 1, 1, 1, 1, 0 ],
  2467. [ 0, 0, 0, 0, 0, 0 ]
  2468. ] );
  2469. } );
  2470. it( 'should repeat pasted cells in the both directions when pasted on table end', () => {
  2471. tableSelection.setCellSelection(
  2472. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  2473. modelRoot.getNodeByPath( [ 0, 5, 5 ] )
  2474. );
  2475. pasteTable( [
  2476. [ 'aa', 'ab', 'ac' ],
  2477. [ 'ba', 'bb', 'bc' ],
  2478. [ 'ca', 'cb', 'cc' ]
  2479. ] );
  2480. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2481. [ '00', '01', '02', '03', '04', '05' ],
  2482. [ '10', 'aa', 'ab', 'ac', 'aa', 'ab' ],
  2483. [ '20', 'ba', 'bb', 'bc', 'ba', 'bb' ],
  2484. [ '30', 'ca', 'cb', 'cc', 'ca', 'cb' ],
  2485. [ '40', 'aa', 'ab', 'ac', 'aa', 'ab' ],
  2486. [ '50', 'ba', 'bb', 'bc', 'ba', 'bb' ]
  2487. ] ) );
  2488. assertSelectedCells( model, [
  2489. [ 0, 0, 0, 0, 0, 0 ],
  2490. [ 0, 1, 1, 1, 1, 1 ],
  2491. [ 0, 1, 1, 1, 1, 1 ],
  2492. [ 0, 1, 1, 1, 1, 1 ],
  2493. [ 0, 1, 1, 1, 1, 1 ],
  2494. [ 0, 1, 1, 1, 1, 1 ]
  2495. ] );
  2496. } );
  2497. } );
  2498. describe( 'content table has spans', () => {
  2499. beforeEach( () => {
  2500. // +----+----+----+----+----+----+
  2501. // | 00 | 01 | 02 | 03 | 04 | 05 |
  2502. // +----+----+----+ +----+----+
  2503. // | 10 | 11 | 12 | | 14 | 15 |
  2504. // +----+----+ + +----+----+
  2505. // | 20 | 21 | | | 24 | 25 |
  2506. // +----+----+----+ + +----+
  2507. // | 30 | 31 | | | 35 |
  2508. // +----+----+----+----+----+----+
  2509. // | 40 | 44 |
  2510. // +----+----+----+----+ +
  2511. // | 50 | 51 | 52 | |
  2512. // +----+----+----+----+----+----+
  2513. // | 60 | 61 | 62 | 63 | 64 | 65 |
  2514. // +----+----+----+----+----+----+
  2515. setModelData( model, modelTable( [
  2516. [ '00', '01', '02', { contents: '03', rowspan: 4 }, '04', '05' ],
  2517. [ '10', '11', { contents: '12', rowspan: 2 }, '14', '15' ],
  2518. [ '20', '21', { contents: '24', rowspan: 2 }, '25' ],
  2519. [ '30', { contents: '31', colspan: 2 }, '35' ],
  2520. [ { contents: '40', colspan: 4 }, { contents: '44', colspan: 2, rowspan: 2 } ],
  2521. [ '50', '51', { contents: '52', colspan: 2 } ],
  2522. [ '60', '61', '62', '63', '64', '65' ]
  2523. ] ) );
  2524. } );
  2525. it( 'should split spanned cells on the selection edges (vertical spans)', () => {
  2526. tableSelection.setCellSelection(
  2527. modelRoot.getNodeByPath( [ 0, 2, 0 ] ),
  2528. modelRoot.getNodeByPath( [ 0, 4, 1 ] ) // Cell 44.
  2529. );
  2530. pasteTable( [
  2531. [ 'aa', 'ab' ],
  2532. [ 'ba', 'bb' ]
  2533. ] );
  2534. // +----+----+----+----+----+----+
  2535. // | 00 | 01 | 02 | 03 | 04 | 05 |
  2536. // +----+----+----+ +----+----+
  2537. // | 10 | 11 | 12 | | 14 | 15 |
  2538. // +----+----+----+----+----+----+
  2539. // | aa | ab | aa | ab | aa | 25 |
  2540. // +----+----+----+----+----+----+
  2541. // | ba | bb | ba | bb | ba | 35 |
  2542. // +----+----+----+----+----+----+
  2543. // | aa | ab | aa | ab | aa | |
  2544. // +----+----+----+----+----+ +
  2545. // | 50 | 51 | 52 | | |
  2546. // +----+----+----+----+----+----+
  2547. // | 60 | 61 | 62 | 63 | 64 | 65 |
  2548. // +----+----+----+----+----+----+
  2549. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2550. [ '00', '01', '02', { contents: '03', rowspan: 2 }, '04', '05' ],
  2551. [ '10', '11', '12', '14', '15' ],
  2552. [ 'aa', 'ab', 'aa', 'ab', 'aa', '25' ],
  2553. [ 'ba', 'bb', 'ba', 'bb', 'ba', '35' ],
  2554. [ 'aa', 'ab', 'aa', 'ab', 'aa', { contents: '', rowspan: 2 } ],
  2555. [ '50', '51', { contents: '52', colspan: 2 }, '' ],
  2556. [ '60', '61', '62', '63', '64', '65' ]
  2557. ] ) );
  2558. } );
  2559. it( 'should split spanned cells on the selection edges (horizontal spans)', () => {
  2560. tableSelection.setCellSelection(
  2561. modelRoot.getNodeByPath( [ 0, 0, 2 ] ),
  2562. modelRoot.getNodeByPath( [ 0, 4, 1 ] ) // Cell 44.
  2563. );
  2564. pasteTable( [
  2565. [ 'aa', 'ab' ],
  2566. [ 'ba', 'bb' ]
  2567. ] );
  2568. // +----+----+----+----+----+----+
  2569. // | 00 | 01 | aa | ab | aa | 05 |
  2570. // +----+----+----+----+----+----+
  2571. // | 10 | 11 | ba | bb | ba | 15 |
  2572. // +----+----+----+----+----+----+
  2573. // | 20 | 21 | aa | ab | aa | 25 |
  2574. // +----+----+----+----+----+----+
  2575. // | 30 | 31 | ba | bb | ba | 35 |
  2576. // +----+----+----+----+----+----+
  2577. // | 40 | aa | ab | aa | |
  2578. // +----+----+----+----+----+ +
  2579. // | 50 | 51 | 52 | | |
  2580. // +----+----+----+----+----+----+
  2581. // | 60 | 61 | 62 | 63 | 64 | 65 |
  2582. // +----+----+----+----+----+----+
  2583. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2584. [ '00', '01', 'aa', 'ab', 'aa', '05' ],
  2585. [ '10', '11', 'ba', 'bb', 'ba', '15' ],
  2586. [ '20', '21', 'aa', 'ab', 'aa', '25' ],
  2587. [ '30', '31', 'ba', 'bb', 'ba', '35' ],
  2588. [ { contents: '40', colspan: 2 }, 'aa', 'ab', 'aa', { contents: '', rowspan: 2 } ],
  2589. [ '50', '51', { contents: '52', colspan: 2 }, '' ],
  2590. [ '60', '61', '62', '63', '64', '65' ]
  2591. ] ) );
  2592. } );
  2593. } );
  2594. describe( 'pasted table has spans', () => {
  2595. beforeEach( () => {
  2596. setModelData( model, modelTable( [
  2597. [ '00', '01', '02', '03', '04', '05', '06' ],
  2598. [ '10', '11', '12', '13', '14', '15', '16' ],
  2599. [ '20', '21', '22', '23', '24', '25', '26' ],
  2600. [ '30', '31', '32', '33', '34', '35', '36' ],
  2601. [ '40', '41', '42', '43', '44', '45', '46' ],
  2602. [ '50', '51', '52', '53', '54', '55', '56' ],
  2603. [ '60', '61', '62', '63', '64', '65', '66' ],
  2604. [ '70', '71', '72', '73', '74', '75', '76' ],
  2605. [ '80', '81', '82', '83', '84', '85', '86' ]
  2606. ] ) );
  2607. } );
  2608. it( 'should trim overlapping cells', () => {
  2609. tableSelection.setCellSelection(
  2610. modelRoot.getNodeByPath( [ 0, 1, 1 ] ),
  2611. modelRoot.getNodeByPath( [ 0, 8, 6 ] )
  2612. );
  2613. // +----+----+----+----+
  2614. // | aa |
  2615. // +----+----+----+----+
  2616. // | ba | bb |
  2617. // + +----+----+----+
  2618. // | | cb | cc |
  2619. // + + +----+----+
  2620. // | | | dc | dd |
  2621. // + + + +----+
  2622. // | | | | ed |
  2623. // +----+----+----+----+
  2624. pasteTable( [
  2625. [ { contents: 'aa', colspan: 4 } ],
  2626. [ { contents: 'ba', rowspan: 4 }, { contents: 'bb', colspan: 3 } ],
  2627. [ { contents: 'cb', rowspan: 3 }, { contents: 'cc', colspan: 2 } ],
  2628. [ { contents: 'dc', rowspan: 2 }, 'dd' ],
  2629. [ 'ed' ]
  2630. ] );
  2631. // +----+----+----+----+----+----+----+
  2632. // | 00 | 01 | 02 | 03 | 04 | 05 | 06 |
  2633. // +----+----+----+----+----+----+----+
  2634. // | 10 | aa | aa |
  2635. // +----+----+----+----+----+----+----+
  2636. // | 20 | ba | bb | ba | bb |
  2637. // +----+ +----+----+----+ +----+
  2638. // | 30 | | cb | cc | | cb |
  2639. // +----+ + +----+----+ + +
  2640. // | 40 | | | dc | dd | | |
  2641. // +----+ + + +----+ + +
  2642. // | 50 | | | | ed | | |
  2643. // +----+----+----+----+----+----+----+
  2644. // | 60 | aa | aa |
  2645. // +----+----+----+----+----+----+----+
  2646. // | 70 | ba | bb | ba | bb |
  2647. // +----+ +----+----+----+ +----+
  2648. // | 80 | | cb | cc | | cb |
  2649. // +----+----+----+----+----+----+----+
  2650. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2651. [ '00', '01', '02', '03', '04', '05', '06' ],
  2652. [ '10', { contents: 'aa', colspan: 4 }, { contents: 'aa', colspan: 2 } ],
  2653. [ '20', { contents: 'ba', rowspan: 4 }, { contents: 'bb', colspan: 3 }, { contents: 'ba', rowspan: 4 }, 'bb' ],
  2654. [ '30', { contents: 'cb', rowspan: 3 }, { contents: 'cc', colspan: 2 }, { contents: 'cb', rowspan: 3 } ],
  2655. [ '40', { contents: 'dc', rowspan: 2 }, 'dd' ],
  2656. [ '50', 'ed' ],
  2657. [ '60', { contents: 'aa', colspan: 4 }, { contents: 'aa', colspan: 2 } ],
  2658. [ '70', { contents: 'ba', rowspan: 2 }, { contents: 'bb', colspan: 3 }, { contents: 'ba', rowspan: 2 }, 'bb' ],
  2659. [ '80', 'cb', { contents: 'cc', colspan: 2 }, 'cb' ]
  2660. ] ) );
  2661. } );
  2662. } );
  2663. } );
  2664. describe( 'fixing pasted table broken layout', () => {
  2665. it( 'should trim pasted cells\' width if they exceeds table width established by the first row', () => {
  2666. // Select 00 -> 22 (selection 2x3 - equal to expected fixed table)
  2667. tableSelection.setCellSelection(
  2668. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2669. modelRoot.getNodeByPath( [ 0, 2, 1 ] )
  2670. );
  2671. // +----+----+
  2672. // | aa | ab | <- First row establish table width=2.
  2673. // +----+----+----+----+
  2674. // | ba | bb | <- Cell "bb" sticks out by 2 slots.
  2675. // +----+----+----+----+
  2676. // | ca | <- Cell "ca" sticks out by 1 slot.
  2677. // +----+----+----+
  2678. pasteTable( [
  2679. [ 'aa', 'ab' ],
  2680. [ 'ba', { colspan: 3, contents: 'bb' } ],
  2681. [ { colspan: 3, contents: 'ca' } ]
  2682. ] );
  2683. // +----+----+----+----+
  2684. // | aa | ab | 02 | 03 |
  2685. // +----+----+----+----+
  2686. // | ba | bb | 12 | 13 |
  2687. // +----+----+----+----+
  2688. // | ca | 22 | 23 |
  2689. // +----+----+----+----+
  2690. // | 30 | 31 | 32 | 33 |
  2691. // +----+----+----+----+
  2692. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2693. [ 'aa', 'ab', '02', '03' ],
  2694. [ 'ba', 'bb', '12', '13' ],
  2695. [ { contents: 'ca', colspan: 2 }, '22', '23' ],
  2696. [ '30', '31', '32', '33' ]
  2697. ] ) );
  2698. /* eslint-disable no-multi-spaces */
  2699. assertSelectedCells( model, [
  2700. [ 1, 1, 0, 0 ],
  2701. [ 1, 1, 0, 0 ],
  2702. [ 1, 0, 0 ],
  2703. [ 0, 0, 0, 0 ]
  2704. ] );
  2705. /* eslint-enable no-multi-spaces */
  2706. } );
  2707. it( 'should trim pasted cells\' height if they exceeds table height established by the last row', () => {
  2708. // Select 00 -> 12 (selection 3x2 - equal to expected fixed table)
  2709. tableSelection.setCellSelection(
  2710. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2711. modelRoot.getNodeByPath( [ 0, 1, 2 ] )
  2712. );
  2713. // +----+----+----+
  2714. // | aa | ab | ac |
  2715. // +----+----+ +
  2716. // | ba | bb | | <- Last row establish table height=2.
  2717. // +----+ + +
  2718. // | | | <- Cell "ac" sticks out by 1 slot.
  2719. // + +----+
  2720. // | | <- Cell "bb" sticks out by 2 slots.
  2721. // +----+
  2722. pasteTable( [
  2723. [ 'aa', 'ab', { contents: 'ac', rowspan: 3 } ],
  2724. [ 'ba', { contents: 'bb', rowspan: 3 } ]
  2725. ] );
  2726. // +----+----+----+----+
  2727. // | aa | ab | ac | 03 |
  2728. // +----+----+ +----+
  2729. // | ba | bb | | 13 |
  2730. // +----+----+----+----+
  2731. // | 20 | 21 | 22 | 23 |
  2732. // +----+----+----+----+
  2733. // | 30 | 31 | 32 | 33 |
  2734. // +----+----+----+----+
  2735. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2736. [ 'aa', 'ab', { rowspan: 2, contents: 'ac' }, '03' ],
  2737. [ 'ba', 'bb', '13' ],
  2738. [ '20', '21', '22', '23' ],
  2739. [ '30', '31', '32', '33' ]
  2740. ] ) );
  2741. /* eslint-disable no-multi-spaces */
  2742. assertSelectedCells( model, [
  2743. [ 1, 1, 1, 0 ],
  2744. [ 1, 1, 0 ],
  2745. [ 0, 0, 0, 0 ],
  2746. [ 0, 0, 0, 0 ]
  2747. ] );
  2748. /* eslint-enable no-multi-spaces */
  2749. } );
  2750. it( 'should trim pasted cells\' height and width if they exceeds table height and width', () => {
  2751. // Select 00 -> 11 (selection 2x2 - equal to expected fixed table)
  2752. tableSelection.setCellSelection(
  2753. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2754. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  2755. );
  2756. // +----+----+
  2757. // | aa | ab |
  2758. // +----+----+----+----+
  2759. // | ba | bb | <- Cell "bb" sticks out by 2 slots in width and by 1 slot in height.
  2760. // +----+ +
  2761. // | |
  2762. // +----+----+----+
  2763. pasteTable( [
  2764. [ 'aa', 'ab' ],
  2765. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 } ]
  2766. ] );
  2767. // +----+----+----+----+
  2768. // | aa | ab | 02 | 03 |
  2769. // +----+----+----+----+
  2770. // | ba | bb | 12 | 13 |
  2771. // +----+----+----+----+
  2772. // | 20 | 21 | 22 | 23 |
  2773. // +----+----+----+----+
  2774. // | 30 | 31 | 32 | 33 |
  2775. // +----+----+----+----+
  2776. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2777. [ 'aa', 'ab', '02', '03' ],
  2778. [ 'ba', 'bb', '12', '13' ],
  2779. [ '20', '21', '22', '23' ],
  2780. [ '30', '31', '32', '33' ]
  2781. ] ) );
  2782. /* eslint-disable no-multi-spaces */
  2783. assertSelectedCells( model, [
  2784. [ 1, 1, 0, 0 ],
  2785. [ 1, 1, 0, 0 ],
  2786. [ 0, 0, 0, 0 ],
  2787. [ 0, 0, 0, 0 ]
  2788. ] );
  2789. /* eslint-enable no-multi-spaces */
  2790. } );
  2791. it(
  2792. 'should trim pasted cells\' width if they exceeds pasted table width (pasted height is bigger then selection height)',
  2793. () => {
  2794. // Select 00 -> 11 (selection 2x2 - smaller by height than the expected fixed table)
  2795. tableSelection.setCellSelection(
  2796. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2797. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  2798. );
  2799. // +----+----+
  2800. // | aa | ab | <- First row establish table width=2.
  2801. // +----+----+----+----+
  2802. // | ba | bb | <- Cell "bb" sticks out by 2 slots.
  2803. // +----+----+----+----+
  2804. // | ca | <- Cell "ca" sticks out by 1 slot.
  2805. // +----+----+----+
  2806. pasteTable( [
  2807. [ 'aa', 'ab' ],
  2808. [ 'ba', { colspan: 3, contents: 'bb' } ],
  2809. [ { colspan: 3, contents: 'ca' } ]
  2810. ] );
  2811. // +----+----+----+----+
  2812. // | aa | ab | 02 | 03 |
  2813. // +----+----+----+----+
  2814. // | ba | bb | 12 | 13 |
  2815. // +----+----+----+----+
  2816. // | 20 | 21 | 22 | 23 |
  2817. // +----+----+----+----+
  2818. // | 30 | 31 | 32 | 33 |
  2819. // +----+----+----+----+
  2820. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2821. [ 'aa', 'ab', '02', '03' ],
  2822. [ 'ba', 'bb', '12', '13' ],
  2823. [ '20', '21', '22', '23' ],
  2824. [ '30', '31', '32', '33' ]
  2825. ] ) );
  2826. assertSelectedCells( model, [
  2827. [ 1, 1, 0, 0 ],
  2828. [ 1, 1, 0, 0 ],
  2829. [ 0, 0, 0, 0 ],
  2830. [ 0, 0, 0, 0 ]
  2831. ] );
  2832. }
  2833. );
  2834. it(
  2835. 'should trim pasted cells\' height if they exceeds pasted table height (pasted width is bigger then selection width)',
  2836. () => {
  2837. // Select 00 -> 11 (selection 2x2 - smaller by width than the expected fixed table)
  2838. tableSelection.setCellSelection(
  2839. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2840. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  2841. );
  2842. // +----+----+----+
  2843. // | aa | ab | ac |
  2844. // +----+----+ +
  2845. // | ba | bb | | <- Last row establish table height=2.
  2846. // +----+ + +
  2847. // | | | <- Cell "ac" sticks out by 1 slot.
  2848. // + +----+
  2849. // | | <- Cell "bb" sticks out by 2 slots.
  2850. // +----+
  2851. pasteTable( [
  2852. [ 'aa', 'ab', { contents: 'ac', rowspan: 3 } ],
  2853. [ 'ba', { contents: 'bb', rowspan: 3 } ]
  2854. ] );
  2855. // +----+----+----+----+
  2856. // | aa | ab | 02 | 03 |
  2857. // +----+----+----+----+
  2858. // | ba | bb | 12 | 13 |
  2859. // +----+----+----+----+
  2860. // | 20 | 21 | 22 | 23 |
  2861. // +----+----+----+----+
  2862. // | 30 | 31 | 32 | 33 |
  2863. // +----+----+----+----+
  2864. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2865. [ 'aa', 'ab', '02', '03' ],
  2866. [ 'ba', 'bb', '12', '13' ],
  2867. [ '20', '21', '22', '23' ],
  2868. [ '30', '31', '32', '33' ]
  2869. ] ) );
  2870. /* eslint-disable no-multi-spaces */
  2871. assertSelectedCells( model, [
  2872. [ 1, 1, 0, 0 ],
  2873. [ 1, 1, 0, 0 ],
  2874. [ 0, 0, 0, 0 ],
  2875. [ 0, 0, 0, 0 ]
  2876. ] );
  2877. /* eslint-enable no-multi-spaces */
  2878. }
  2879. );
  2880. it(
  2881. `should trim pasted pasted cells' height and width if they exceeds pasted table dimensions
  2882. (pasted table is bigger then selection width)`,
  2883. () => {
  2884. // Select 00 -> 11 (selection 2x2 - smaller than the expected fixed table)
  2885. tableSelection.setCellSelection(
  2886. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2887. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  2888. );
  2889. // +----+----+----+
  2890. // | aa | ab | ac |
  2891. // +----+----+----+----+
  2892. // | ba | bb | <- Cell "bb" sticks out by 1 slots in width and by 1 slot in height.
  2893. // +----+ +
  2894. // | ca | |
  2895. // +----+ +
  2896. // | |
  2897. // +----+----+----+
  2898. pasteTable( [
  2899. [ 'aa', 'ab' ],
  2900. [ 'ba', { contents: 'bb', colspan: 3, rowspan: 2 } ]
  2901. ] );
  2902. // +----+----+----+----+
  2903. // | aa | ab | 02 | 03 |
  2904. // +----+----+----+----+
  2905. // | ba | bb | 12 | 13 |
  2906. // +----+----+----+----+
  2907. // | 20 | 21 | 22 | 23 |
  2908. // +----+----+----+----+
  2909. // | 30 | 31 | 32 | 33 |
  2910. // +----+----+----+----+
  2911. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2912. [ 'aa', 'ab', '02', '03' ],
  2913. [ 'ba', 'bb', '12', '13' ],
  2914. [ '20', '21', '22', '23' ],
  2915. [ '30', '31', '32', '33' ]
  2916. ] ) );
  2917. /* eslint-disable no-multi-spaces */
  2918. assertSelectedCells( model, [
  2919. [ 1, 1, 0, 0 ],
  2920. [ 1, 1, 0, 0 ],
  2921. [ 0, 0, 0, 0 ],
  2922. [ 0, 0, 0, 0 ]
  2923. ] );
  2924. /* eslint-enable no-multi-spaces */
  2925. }
  2926. );
  2927. } );
  2928. } );
  2929. describe( 'Clipboard integration - paste (content scenarios)', () => {
  2930. it( 'handles multiple paragraphs in table cell', async () => {
  2931. await createEditor();
  2932. setModelData( model, modelTable( [
  2933. [ '00', '01', '02' ],
  2934. [ '01', '11', '12' ],
  2935. [ '02', '21', '22' ]
  2936. ] ) );
  2937. tableSelection.setCellSelection(
  2938. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2939. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  2940. );
  2941. pasteTable( [
  2942. [ '<p>a</p><p>a</p><p>a</p>', 'ab' ],
  2943. [ 'ba', 'bb' ]
  2944. ] );
  2945. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2946. [ '<paragraph>a</paragraph><paragraph>a</paragraph><paragraph>a</paragraph>', 'ab', '02' ],
  2947. [ 'ba', 'bb', '12' ],
  2948. [ '02', '21', '22' ]
  2949. ] ) );
  2950. } );
  2951. it( 'handles image in table cell', async () => {
  2952. await createEditor( [ ImageEditing, ImageCaptionEditing ] );
  2953. setModelData( model, modelTable( [
  2954. [ '00', '01', '02' ],
  2955. [ '01', '11', '12' ],
  2956. [ '02', '21', '22' ]
  2957. ] ) );
  2958. tableSelection.setCellSelection(
  2959. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2960. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  2961. );
  2962. pasteTable( [
  2963. [ '<img src="/assets/sample.jpg">', 'ab' ],
  2964. [ 'ba', 'bb' ]
  2965. ] );
  2966. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2967. [ '<image src="/assets/sample.jpg"><caption></caption></image>', 'ab', '02' ],
  2968. [ 'ba', 'bb', '12' ],
  2969. [ '02', '21', '22' ]
  2970. ] ) );
  2971. } );
  2972. it( 'handles mixed nested content in table cell', async () => {
  2973. await createEditor( [ ImageEditing, ImageCaptionEditing, BlockQuoteEditing, HorizontalLineEditing, ListEditing ] );
  2974. setModelData( model, modelTable( [
  2975. [ '00', '01', '02' ],
  2976. [ '01', '11', '12' ],
  2977. [ '02', '21', '22' ]
  2978. ] ) );
  2979. tableSelection.setCellSelection(
  2980. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  2981. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  2982. );
  2983. const img = '<img src="/assets/sample.jpg">';
  2984. const list = '<ul><li>foo</li><li>bar</li></ul>';
  2985. const blockquote = `<blockquote><p>baz</p>${ list }</blockquote>`;
  2986. pasteTable( [
  2987. [ `${ img }${ list }${ blockquote }`, 'ab' ],
  2988. [ 'ba', 'bb' ]
  2989. ] );
  2990. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  2991. [
  2992. '<image src="/assets/sample.jpg"><caption></caption></image>' +
  2993. '<listItem listIndent="0" listType="bulleted">foo</listItem>' +
  2994. '<listItem listIndent="0" listType="bulleted">bar</listItem>' +
  2995. '<blockQuote>' +
  2996. '<paragraph>baz</paragraph>' +
  2997. '<listItem listIndent="0" listType="bulleted">foo</listItem>' +
  2998. '<listItem listIndent="0" listType="bulleted">bar</listItem>' +
  2999. '</blockQuote>',
  3000. 'ab',
  3001. '02' ],
  3002. [ 'ba', 'bb', '12' ],
  3003. [ '02', '21', '22' ]
  3004. ] ) );
  3005. } );
  3006. it( 'handles table cell properties', async () => {
  3007. await createEditor( [ TableCellPropertiesEditing ] );
  3008. setModelData( model, modelTable( [
  3009. [ '00', '01', '02' ],
  3010. [ '01', '11', '12' ],
  3011. [ '02', '21', '22' ]
  3012. ] ) );
  3013. tableSelection.setCellSelection(
  3014. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  3015. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  3016. );
  3017. pasteTable( [
  3018. [ { contents: 'aa', style: 'border:1px solid #f00;background:#ba7;width:1337px' }, 'ab' ],
  3019. [ 'ba', 'bb' ]
  3020. ] );
  3021. const tableCell = model.document.getRoot().getNodeByPath( [ 0, 0, 0 ] );
  3022. expect( tableCell.getAttribute( 'borderColor' ) ).to.deep.equal( {
  3023. top: '#f00',
  3024. right: '#f00',
  3025. bottom: '#f00',
  3026. left: '#f00'
  3027. } );
  3028. expect( tableCell.getAttribute( 'borderStyle' ) ).to.deep.equal( {
  3029. top: 'solid',
  3030. right: 'solid',
  3031. bottom: 'solid',
  3032. left: 'solid'
  3033. } );
  3034. expect( tableCell.getAttribute( 'borderWidth' ) ).to.deep.equal( {
  3035. top: '1px',
  3036. right: '1px',
  3037. bottom: '1px',
  3038. left: '1px'
  3039. } );
  3040. expect( tableCell.getAttribute( 'backgroundColor' ) ).to.equal( '#ba7' );
  3041. expect( tableCell.getAttribute( 'width' ) ).to.equal( '1337px' );
  3042. } );
  3043. it( 'discards table properties', async () => {
  3044. await createEditor( [ TableCellPropertiesEditing ] );
  3045. setModelData( model, modelTable( [
  3046. [ '00', '01', '02' ],
  3047. [ '01', '11', '12' ],
  3048. [ '02', '21', '22' ]
  3049. ] ) );
  3050. tableSelection.setCellSelection(
  3051. modelRoot.getNodeByPath( [ 0, 0, 0 ] ),
  3052. modelRoot.getNodeByPath( [ 0, 1, 1 ] )
  3053. );
  3054. const tableStyle = 'border:1px solid #f00;background:#ba7;width:1337px';
  3055. const pastedTable = `<table style="${ tableStyle }"><tr><td>aa</td><td>ab</td></tr><tr><td>ba</td><td>bb</td></tr></table>`;
  3056. const data = {
  3057. dataTransfer: createDataTransfer(),
  3058. preventDefault: sinon.spy(),
  3059. stopPropagation: sinon.spy()
  3060. };
  3061. data.dataTransfer.setData( 'text/html', pastedTable );
  3062. viewDocument.fire( 'paste', data );
  3063. assertEqualMarkup( getModelData( model, { withoutSelection: true } ), modelTable( [
  3064. [ 'aa', 'ab', '02' ],
  3065. [ 'ba', 'bb', '12' ],
  3066. [ '02', '21', '22' ]
  3067. ] ) );
  3068. } );
  3069. } );
  3070. async function createEditor( extraPlugins = [] ) {
  3071. editor = await ClassicTestEditor.create( element, {
  3072. plugins: [ TableEditing, TableClipboard, Paragraph, Clipboard, ...extraPlugins ]
  3073. } );
  3074. model = editor.model;
  3075. modelRoot = model.document.getRoot();
  3076. viewDocument = editor.editing.view.document;
  3077. tableSelection = editor.plugins.get( 'TableSelection' );
  3078. }
  3079. function pasteTable( tableData ) {
  3080. const data = {
  3081. dataTransfer: createDataTransfer(),
  3082. preventDefault: sinon.spy(),
  3083. stopPropagation: sinon.spy()
  3084. };
  3085. data.dataTransfer.setData( 'text/html', viewTable( tableData ) );
  3086. viewDocument.fire( 'paste', data );
  3087. return data;
  3088. }
  3089. function createDataTransfer() {
  3090. const store = new Map();
  3091. return {
  3092. setData( type, data ) {
  3093. store.set( type, data );
  3094. },
  3095. getData( type ) {
  3096. return store.get( type );
  3097. }
  3098. };
  3099. }
  3100. function setupBatchWatch() {
  3101. const createdBatches = new Set();
  3102. model.on( 'applyOperation', ( evt, [ operation ] ) => {
  3103. if ( operation.isDocumentOperation ) {
  3104. createdBatches.add( operation.batch );
  3105. }
  3106. } );
  3107. return createdBatches;
  3108. }
  3109. } );