8
0

selection-post-fixer.js 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377
  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. import Model from '../../../src/model/model';
  6. import { injectSelectionPostFixer } from '../../../src/model/utils/selection-post-fixer';
  7. import { getData as getModelData, setData as setModelData } from '../../../src/dev-utils/model';
  8. import { assertEqualMarkup } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
  9. describe( 'Selection post-fixer', () => {
  10. describe( 'injectSelectionPostFixer()', () => {
  11. it( 'is a function', () => {
  12. expect( injectSelectionPostFixer ).to.be.a( 'function' );
  13. } );
  14. } );
  15. describe( 'injected behavior', () => {
  16. let model, modelRoot;
  17. beforeEach( () => {
  18. model = new Model();
  19. modelRoot = model.document.createRoot();
  20. model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
  21. model.schema.register( 'table', {
  22. allowWhere: '$block',
  23. allowAttributes: [ 'headingRows', 'headingColumns' ],
  24. isLimit: true,
  25. isObject: true
  26. } );
  27. model.schema.register( 'tableRow', {
  28. allowIn: 'table',
  29. isLimit: true
  30. } );
  31. model.schema.register( 'tableCell', {
  32. allowIn: 'tableRow',
  33. allowAttributes: [ 'colspan', 'rowspan' ],
  34. isLimit: true,
  35. isSelectable: true
  36. } );
  37. model.schema.extend( '$block', { allowIn: 'tableCell' } );
  38. model.schema.register( 'image', {
  39. isObject: true,
  40. isBlock: true,
  41. allowWhere: '$block'
  42. } );
  43. model.schema.extend( '$block', { allowIn: 'tableCell' } );
  44. model.schema.register( 'caption', {
  45. allowIn: 'image',
  46. allowContentOf: '$block',
  47. isLimit: true
  48. } );
  49. model.schema.register( 'inlineWidget', {
  50. isObject: true,
  51. allowIn: [ '$block', '$clipboardHolder' ]
  52. } );
  53. model.schema.register( 'figure', {
  54. allowIn: '$root',
  55. allowAttributes: [ 'name', 'title' ]
  56. } );
  57. } );
  58. it( 'should not crash if there is no correct position for model selection', () => {
  59. setModelData( model, '' );
  60. expect( getModelData( model ) ).to.equal( '[]' );
  61. } );
  62. it( 'should react to structure changes', () => {
  63. setModelData( model, '<paragraph>[]foo</paragraph><image></image>' );
  64. model.change( writer => {
  65. writer.remove( modelRoot.getChild( 0 ) );
  66. } );
  67. expect( getModelData( model ) ).to.equal( '[<image></image>]' );
  68. } );
  69. it( 'should react to selection changes', () => {
  70. setModelData( model, '<paragraph>[]foo</paragraph><image></image>' );
  71. // <paragraph>foo</paragraph>[]<image></image>
  72. model.change( writer => {
  73. writer.setSelection(
  74. writer.createRange( writer.createPositionAt( modelRoot, 1 ), writer.createPositionAt( modelRoot, 1 ) )
  75. );
  76. } );
  77. expect( getModelData( model ) ).to.equal( '<paragraph>foo[]</paragraph><image></image>' );
  78. } );
  79. describe( 'selection - table scenarios', () => {
  80. beforeEach( () => {
  81. setModelData( model,
  82. '<paragraph>[]foo</paragraph>' +
  83. '<table>' +
  84. '<tableRow>' +
  85. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  86. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  87. '</tableRow>' +
  88. '</table>' +
  89. '<paragraph>bar</paragraph>'
  90. );
  91. } );
  92. it( 'should fix #1 (range start outside table, end on table cell)', () => {
  93. // <paragraph>f[oo</paragraph><table><tableRow><tableCell></tableCell>]<tableCell>...
  94. model.change( writer => {
  95. writer.setSelection( writer.createRange(
  96. writer.createPositionAt( modelRoot.getChild( 0 ), 1 ),
  97. writer.createPositionAt( modelRoot.getChild( 1 ).getChild( 0 ), 1 )
  98. ) );
  99. } );
  100. expect( getModelData( model ) ).to.equal(
  101. '<paragraph>f[oo</paragraph>' +
  102. '<table>' +
  103. '<tableRow>' +
  104. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  105. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  106. '</tableRow>' +
  107. '</table>]' +
  108. '<paragraph>bar</paragraph>'
  109. );
  110. } );
  111. it( 'should fix #2 (range start on table cell, end outside table)', () => {
  112. // ...<table><tableRow><tableCell></tableCell>[<tableCell></tableCell></tableRow></table><paragraph>b]ar</paragraph>
  113. model.change( writer => {
  114. writer.setSelection( writer.createRange(
  115. writer.createPositionAt( modelRoot.getChild( 1 ).getChild( 0 ), 1 ),
  116. writer.createPositionAt( modelRoot.getChild( 2 ), 1 )
  117. ) );
  118. } );
  119. expect( getModelData( model ) ).to.equal(
  120. '<paragraph>foo</paragraph>' +
  121. '[<table>' +
  122. '<tableRow>' +
  123. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  124. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  125. '</tableRow>' +
  126. '</table>' +
  127. '<paragraph>b]ar</paragraph>'
  128. );
  129. } );
  130. it( 'should fix #3', () => {
  131. // <paragraph>f[oo</paragraph><table>]<tableRow>...
  132. model.change( writer => {
  133. writer.setSelection( writer.createRange(
  134. writer.createPositionAt( modelRoot.getChild( 0 ), 1 ),
  135. writer.createPositionAt( modelRoot.getChild( 1 ), 0 )
  136. ) );
  137. } );
  138. expect( getModelData( model ) ).to.equal(
  139. '<paragraph>f[oo</paragraph>' +
  140. '<table>' +
  141. '<tableRow>' +
  142. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  143. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  144. '</tableRow>' +
  145. '</table>]' +
  146. '<paragraph>bar</paragraph>'
  147. );
  148. } );
  149. it( 'should fix #4', () => {
  150. // <paragraph>foo</paragraph><table><tableRow><tableCell>a[aa</tableCell><tableCell>b]bb</tableCell>
  151. model.change( writer => {
  152. writer.setSelection( writer.createRange(
  153. writer.createPositionAt( modelRoot.getNodeByPath( [ 1, 0, 0, 0 ] ), 1 ),
  154. writer.createPositionAt( modelRoot.getNodeByPath( [ 1, 0, 1, 0 ] ), 2 )
  155. ) );
  156. } );
  157. expect( getModelData( model ) ).to.equal(
  158. '<paragraph>foo</paragraph>' +
  159. '[<table>' +
  160. '<tableRow>' +
  161. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  162. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  163. '</tableRow>' +
  164. '</table>]' +
  165. '<paragraph>bar</paragraph>'
  166. );
  167. } );
  168. it( 'should fix #5 (collapsed selection between tables)', () => {
  169. setModelData( model,
  170. '<paragraph>foo</paragraph>' +
  171. '<table>' +
  172. '<tableRow>' +
  173. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  174. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  175. '</tableRow>' +
  176. '</table>' +
  177. '[]' +
  178. '<table>' +
  179. '<tableRow>' +
  180. '<tableCell><paragraph>xxx</paragraph></tableCell>' +
  181. '<tableCell><paragraph>yyy</paragraph></tableCell>' +
  182. '</tableRow>' +
  183. '</table>' +
  184. '<paragraph>baz</paragraph>'
  185. );
  186. assertEqualMarkup( getModelData( model ),
  187. '<paragraph>foo</paragraph>' +
  188. '[<table>' +
  189. '<tableRow>' +
  190. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  191. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  192. '</tableRow>' +
  193. '</table>]' +
  194. '<table>' +
  195. '<tableRow>' +
  196. '<tableCell><paragraph>xxx</paragraph></tableCell>' +
  197. '<tableCell><paragraph>yyy</paragraph></tableCell>' +
  198. '</tableRow>' +
  199. '</table>' +
  200. '<paragraph>baz</paragraph>'
  201. );
  202. } );
  203. // There's a chance that this and the following test will not be up to date with
  204. // how the table feature is really implemented once we'll introduce row/cells/columns selection
  205. // in which case all these elements will need to be marked as objects.
  206. it( 'should fix #6 (element selection of not an object)', () => {
  207. setModelData( model,
  208. '<paragraph>foo</paragraph>' +
  209. '<table>' +
  210. '[<tableRow>' +
  211. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  212. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  213. '</tableRow>]' +
  214. '</table>' +
  215. '<paragraph>baz</paragraph>'
  216. );
  217. expect( getModelData( model ) ).to.equal(
  218. '<paragraph>foo</paragraph>' +
  219. '[<table>' +
  220. '<tableRow>' +
  221. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  222. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  223. '</tableRow>' +
  224. '</table>]' +
  225. '<paragraph>baz</paragraph>'
  226. );
  227. } );
  228. it( 'should fix #7 (element selection of non-objects)', () => {
  229. setModelData( model,
  230. '<paragraph>foo</paragraph>' +
  231. '<table>' +
  232. '[<tableRow>' +
  233. '<tableCell><paragraph>1</paragraph></tableCell>' +
  234. '<tableCell><paragraph>2</paragraph></tableCell>' +
  235. '</tableRow>' +
  236. '<tableRow>' +
  237. '<tableCell><paragraph>3</paragraph></tableCell>' +
  238. '<tableCell><paragraph>4</paragraph></tableCell>]' +
  239. '</tableRow>' +
  240. '<tableRow>' +
  241. '<tableCell><paragraph>5</paragraph></tableCell>' +
  242. '<tableCell><paragraph>6</paragraph></tableCell>' +
  243. '</tableRow>' +
  244. '</table>' +
  245. '<paragraph>baz</paragraph>'
  246. );
  247. expect( getModelData( model ) ).to.equal(
  248. '<paragraph>foo</paragraph>' +
  249. '[<table>' +
  250. '<tableRow>' +
  251. '<tableCell><paragraph>1</paragraph></tableCell>' +
  252. '<tableCell><paragraph>2</paragraph></tableCell>' +
  253. '</tableRow>' +
  254. '<tableRow>' +
  255. '<tableCell><paragraph>3</paragraph></tableCell>' +
  256. '<tableCell><paragraph>4</paragraph></tableCell>' +
  257. '</tableRow>' +
  258. '<tableRow>' +
  259. '<tableCell><paragraph>5</paragraph></tableCell>' +
  260. '<tableCell><paragraph>6</paragraph></tableCell>' +
  261. '</tableRow>' +
  262. '</table>]' +
  263. '<paragraph>baz</paragraph>'
  264. );
  265. } );
  266. it( 'should fix #8 (cross-limit selection which starts in a non-limit elements)', () => {
  267. model.schema.extend( 'paragraph', { allowIn: 'tableCell' } );
  268. setModelData( model,
  269. '<paragraph>foo</paragraph>' +
  270. '<table>' +
  271. '<tableRow>' +
  272. '<tableCell><paragraph>f[oo</paragraph></tableCell>' +
  273. '<tableCell><paragraph>b]ar</paragraph></tableCell>' +
  274. '</tableRow>' +
  275. '</table>' +
  276. '<paragraph>baz</paragraph>'
  277. );
  278. expect( getModelData( model ) ).to.equal(
  279. '<paragraph>foo</paragraph>' +
  280. '[<table>' +
  281. '<tableRow>' +
  282. '<tableCell><paragraph>foo</paragraph></tableCell>' +
  283. '<tableCell><paragraph>bar</paragraph></tableCell>' +
  284. '</tableRow>' +
  285. '</table>]' +
  286. '<paragraph>baz</paragraph>'
  287. );
  288. } );
  289. it( 'should not fix #1 (selection over paragraphs outside table)', () => {
  290. setModelData( model,
  291. '<paragraph>foo</paragraph>' +
  292. '<table>' +
  293. '<tableRow>' +
  294. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  295. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  296. '</tableRow>' +
  297. '</table>' +
  298. '<paragraph>b[ar</paragraph>' +
  299. '<paragraph>ba]z</paragraph>'
  300. );
  301. expect( getModelData( model ) ).to.equal(
  302. '<paragraph>foo</paragraph>' +
  303. '<table>' +
  304. '<tableRow>' +
  305. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  306. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  307. '</tableRow>' +
  308. '</table>' +
  309. '<paragraph>b[ar</paragraph>' +
  310. '<paragraph>ba]z</paragraph>'
  311. );
  312. } );
  313. it( 'should not fix #2 (selection over image in table)', () => {
  314. setModelData( model,
  315. '<paragraph>foo</paragraph>' +
  316. '<table>' +
  317. '<tableRow>' +
  318. '<tableCell><paragraph>foo</paragraph><image></image></tableCell>' +
  319. '<tableCell><paragraph>[]bbb</paragraph></tableCell>' +
  320. '</tableRow>' +
  321. '</table>'
  322. );
  323. model.change( writer => {
  324. const image = model.document.getRoot().getNodeByPath( [ 1, 0, 0, 1 ] );
  325. writer.setSelection( writer.createRangeOn( image ) );
  326. } );
  327. expect( getModelData( model ) ).to.equal(
  328. '<paragraph>foo</paragraph>' +
  329. '<table>' +
  330. '<tableRow>' +
  331. '<tableCell><paragraph>foo</paragraph>[<image></image>]</tableCell>' +
  332. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  333. '</tableRow>' +
  334. '</table>'
  335. );
  336. } );
  337. it( 'should not fix #3 (selection over paragraph & image in table)', () => {
  338. setModelData( model,
  339. '<paragraph>foo</paragraph>' +
  340. '<table>' +
  341. '<tableRow>' +
  342. '<tableCell><paragraph>foo</paragraph><image></image></tableCell>' +
  343. '<tableCell><paragraph>[]bbb</paragraph></tableCell>' +
  344. '</tableRow>' +
  345. '</table>'
  346. );
  347. model.change( writer => {
  348. const tableCell = model.document.getRoot().getNodeByPath( [ 1, 0, 0 ] );
  349. writer.setSelection( writer.createRangeIn( tableCell ) );
  350. } );
  351. expect( getModelData( model ) ).to.equal(
  352. '<paragraph>foo</paragraph>' +
  353. '<table>' +
  354. '<tableRow>' +
  355. '<tableCell><paragraph>[foo</paragraph><image></image>]</tableCell>' +
  356. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  357. '</tableRow>' +
  358. '</table>'
  359. );
  360. } );
  361. it( 'should not fix #4 (selection over image & paragraph in table)', () => {
  362. setModelData( model,
  363. '<paragraph>foo</paragraph>' +
  364. '<table>' +
  365. '<tableRow>' +
  366. '<tableCell><image></image><paragraph>foo</paragraph></tableCell>' +
  367. '<tableCell><paragraph>[]bbb</paragraph></tableCell>' +
  368. '</tableRow>' +
  369. '</table>'
  370. );
  371. model.change( writer => {
  372. const tableCell = model.document.getRoot().getNodeByPath( [ 1, 0, 0 ] );
  373. writer.setSelection( writer.createRangeIn( tableCell ) );
  374. } );
  375. expect( getModelData( model ) ).to.equal(
  376. '<paragraph>foo</paragraph>' +
  377. '<table>' +
  378. '<tableRow>' +
  379. '<tableCell>[<image></image><paragraph>foo]</paragraph></tableCell>' +
  380. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  381. '</tableRow>' +
  382. '</table>'
  383. );
  384. } );
  385. it( 'should not fix #5 (selection over blockQuote in table)', () => {
  386. model.schema.register( 'blockQuote', {
  387. allowWhere: '$block',
  388. allowContentOf: '$root'
  389. } );
  390. setModelData( model,
  391. '<paragraph>foo</paragraph>' +
  392. '<table>' +
  393. '<tableRow>' +
  394. '<tableCell><blockQuote><paragraph>foo</paragraph></blockQuote></tableCell>' +
  395. '<tableCell><paragraph>[]bbb</paragraph></tableCell>' +
  396. '</tableRow>' +
  397. '</table>'
  398. );
  399. model.change( writer => {
  400. const tableCell = model.document.getRoot().getNodeByPath( [ 1, 0, 0 ] );
  401. writer.setSelection( writer.createRangeIn( tableCell ) );
  402. } );
  403. expect( getModelData( model ) ).to.equal(
  404. '<paragraph>foo</paragraph>' +
  405. '<table>' +
  406. '<tableRow>' +
  407. '<tableCell><blockQuote><paragraph>[foo]</paragraph></blockQuote></tableCell>' +
  408. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  409. '</tableRow>' +
  410. '</table>'
  411. );
  412. } );
  413. it( 'should fix multiple ranges #1', () => {
  414. model.change( writer => {
  415. const ranges = [
  416. writer.createRange(
  417. writer.createPositionFromPath( modelRoot, [ 0, 1 ] ),
  418. writer.createPositionFromPath( modelRoot, [ 1, 0 ] )
  419. ),
  420. writer.createRange(
  421. writer.createPositionFromPath( modelRoot, [ 1, 0, 0, 0 ] ),
  422. writer.createPositionFromPath( modelRoot, [ 1, 1 ] )
  423. )
  424. ];
  425. writer.setSelection( ranges );
  426. } );
  427. expect( getModelData( model ) ).to.equal(
  428. '<paragraph>f[oo</paragraph>' +
  429. '<table>' +
  430. '<tableRow>' +
  431. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  432. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  433. '</tableRow>' +
  434. '</table>]' +
  435. '<paragraph>bar</paragraph>'
  436. );
  437. } );
  438. it( 'should fix multiple ranges #2', () => {
  439. model.change( writer => {
  440. const ranges = [
  441. writer.createRange(
  442. writer.createPositionFromPath( modelRoot, [ 0, 1 ] ),
  443. writer.createPositionFromPath( modelRoot, [ 1, 0 ] )
  444. ),
  445. writer.createRange(
  446. writer.createPositionFromPath( modelRoot, [ 1, 0, 0, 0 ] ),
  447. writer.createPositionFromPath( modelRoot, [ 2, 2 ] )
  448. )
  449. ];
  450. writer.setSelection( ranges );
  451. } );
  452. expect( getModelData( model ) ).to.equal(
  453. '<paragraph>f[oo</paragraph>' +
  454. '<table>' +
  455. '<tableRow>' +
  456. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  457. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  458. '</tableRow>' +
  459. '</table>' +
  460. '<paragraph>ba]r</paragraph>'
  461. );
  462. } );
  463. it( 'should fix multiple ranges #3', () => {
  464. setModelData( model,
  465. '<paragraph>foo</paragraph>' +
  466. '<table>' +
  467. '<tableRow>' +
  468. '<tableCell><paragraph>[aaa</paragraph></tableCell>' +
  469. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  470. '</tableRow>' +
  471. '<tableRow>]' +
  472. '<tableCell><paragraph>[aaa</paragraph></tableCell>' +
  473. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  474. '</tableRow>' +
  475. '<tableRow>]' +
  476. '<tableCell><paragraph>[aaa</paragraph></tableCell>' +
  477. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  478. '</tableRow>' +
  479. '<tableRow>]' +
  480. '<tableCell><paragraph>[aaa</paragraph></tableCell>' +
  481. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  482. '</tableRow>' +
  483. '</table>' +
  484. '<paragraph>b]az</paragraph>'
  485. );
  486. expect( getModelData( model ) ).to.equal(
  487. '<paragraph>foo</paragraph>' +
  488. '[<table>' +
  489. '<tableRow>' +
  490. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  491. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  492. '</tableRow>' +
  493. '<tableRow>' +
  494. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  495. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  496. '</tableRow>' +
  497. '<tableRow>' +
  498. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  499. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  500. '</tableRow>' +
  501. '<tableRow>' +
  502. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  503. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  504. '</tableRow>' +
  505. '</table>' +
  506. '<paragraph>b]az</paragraph>'
  507. );
  508. } );
  509. it( 'should not fix multiple ranges #1 (not overlapping ranges)', () => {
  510. model.change( writer => {
  511. const ranges = [
  512. writer.createRange(
  513. writer.createPositionFromPath( modelRoot, [ 0, 1 ] ),
  514. writer.createPositionFromPath( modelRoot, [ 1, 0 ] )
  515. ),
  516. writer.createRange(
  517. writer.createPositionFromPath( modelRoot, [ 1, 0, 0, 0 ] ),
  518. writer.createPositionFromPath( modelRoot, [ 2, 1 ] )
  519. ),
  520. writer.createRange(
  521. writer.createPositionFromPath( modelRoot, [ 2, 2 ] ),
  522. writer.createPositionFromPath( modelRoot, [ 2, 3 ] )
  523. )
  524. ];
  525. writer.setSelection( ranges );
  526. } );
  527. expect( getModelData( model ) ).to.equal(
  528. '<paragraph>f[oo</paragraph>' +
  529. '<table>' +
  530. '<tableRow>' +
  531. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  532. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  533. '</tableRow>' +
  534. '</table>' +
  535. '<paragraph>b]a[r]</paragraph>'
  536. );
  537. } );
  538. it( 'should not fix multiple ranges on objects (table selection)', () => {
  539. setModelData( model,
  540. '<table>' +
  541. '<tableRow>' +
  542. '[<tableCell><paragraph>a</paragraph></tableCell>]' +
  543. '[<tableCell><paragraph>b</paragraph></tableCell>]' +
  544. '</tableRow>' +
  545. '<tableRow>' +
  546. '[<tableCell><paragraph>c</paragraph></tableCell>]' +
  547. '<tableCell><paragraph>d</paragraph></tableCell>' +
  548. '</tableRow>' +
  549. '</table>'
  550. );
  551. assertEqualMarkup( getModelData( model ),
  552. '<table>' +
  553. '<tableRow>' +
  554. '[<tableCell><paragraph>a</paragraph></tableCell>]' +
  555. '[<tableCell><paragraph>b</paragraph></tableCell>]' +
  556. '</tableRow>' +
  557. '<tableRow>' +
  558. '[<tableCell><paragraph>c</paragraph></tableCell>]' +
  559. '<tableCell><paragraph>d</paragraph></tableCell>' +
  560. '</tableRow>' +
  561. '</table>'
  562. );
  563. } );
  564. it( 'should fix selection on block', () => {
  565. model.schema.extend( '$block', { allowIn: 'tableCell' } );
  566. setModelData( model,
  567. '<table>' +
  568. '<tableRow><tableCell>[<paragraph>aaa</paragraph>]</tableCell></tableRow>' +
  569. '</table>'
  570. );
  571. assertEqualMarkup( getModelData( model ),
  572. '<table>' +
  573. '<tableRow><tableCell><paragraph>[aaa]</paragraph></tableCell></tableRow>' +
  574. '</table>'
  575. );
  576. } );
  577. it( 'should allow multi-range selection on non-continues blocks (column selected)', () => {
  578. setModelData( model,
  579. '<table>' +
  580. '<tableRow>' +
  581. '[<tableCell><paragraph>A1</paragraph></tableCell>]' +
  582. '<tableCell><paragraph>B1</paragraph></tableCell>' +
  583. '</tableRow>' +
  584. '<tableRow>' +
  585. '[<tableCell><paragraph>A2</paragraph></tableCell>]' +
  586. '<tableCell><paragraph>B2</paragraph></tableCell>' +
  587. '</tableRow>' +
  588. '<tableRow>' +
  589. '[<tableCell><paragraph>A3</paragraph></tableCell>]' +
  590. '<tableCell><paragraph>B3</paragraph></tableCell>' +
  591. '</tableRow>' +
  592. '</table>'
  593. );
  594. assertEqualMarkup( getModelData( model ),
  595. '<table>' +
  596. '<tableRow>' +
  597. '[<tableCell><paragraph>A1</paragraph></tableCell>]' +
  598. '<tableCell><paragraph>B1</paragraph></tableCell>' +
  599. '</tableRow>' +
  600. '<tableRow>' +
  601. '[<tableCell><paragraph>A2</paragraph></tableCell>]' +
  602. '<tableCell><paragraph>B2</paragraph></tableCell>' +
  603. '</tableRow>' +
  604. '<tableRow>' +
  605. '[<tableCell><paragraph>A3</paragraph></tableCell>]' +
  606. '<tableCell><paragraph>B3</paragraph></tableCell>' +
  607. '</tableRow>' +
  608. '</table>'
  609. );
  610. } );
  611. it( 'should allow multi-range selection on continues blocks (row selected)', () => {
  612. setModelData( model,
  613. '<table>' +
  614. '<tableRow>' +
  615. '<tableCell><paragraph>A1</paragraph></tableCell>' +
  616. '<tableCell><paragraph>B1</paragraph></tableCell>' +
  617. '<tableCell><paragraph>C1</paragraph></tableCell>' +
  618. '</tableRow>' +
  619. '<tableRow>' +
  620. '[<tableCell><paragraph>A2</paragraph></tableCell>]' +
  621. '[<tableCell><paragraph>B2</paragraph></tableCell>]' +
  622. '[<tableCell><paragraph>C2</paragraph></tableCell>]' +
  623. '</tableRow>' +
  624. '<tableRow>' +
  625. '<tableCell><paragraph>A3</paragraph></tableCell>' +
  626. '<tableCell><paragraph>B3</paragraph></tableCell>' +
  627. '<tableCell><paragraph>C3</paragraph></tableCell>' +
  628. '</tableRow>' +
  629. '</table>'
  630. );
  631. assertEqualMarkup( getModelData( model ),
  632. '<table>' +
  633. '<tableRow>' +
  634. '<tableCell><paragraph>A1</paragraph></tableCell>' +
  635. '<tableCell><paragraph>B1</paragraph></tableCell>' +
  636. '<tableCell><paragraph>C1</paragraph></tableCell>' +
  637. '</tableRow>' +
  638. '<tableRow>' +
  639. '[<tableCell><paragraph>A2</paragraph></tableCell>]' +
  640. '[<tableCell><paragraph>B2</paragraph></tableCell>]' +
  641. '[<tableCell><paragraph>C2</paragraph></tableCell>]' +
  642. '</tableRow>' +
  643. '<tableRow>' +
  644. '<tableCell><paragraph>A3</paragraph></tableCell>' +
  645. '<tableCell><paragraph>B3</paragraph></tableCell>' +
  646. '<tableCell><paragraph>C3</paragraph></tableCell>' +
  647. '</tableRow>' +
  648. '</table>'
  649. );
  650. } );
  651. it( 'should allow multi-range selection with mixed continues/non-continues blocks (part of table selected)', () => {
  652. setModelData( model,
  653. '<table>' +
  654. '<tableRow>' +
  655. '<tableCell><paragraph>A1</paragraph></tableCell>' +
  656. '<tableCell><paragraph>B1</paragraph></tableCell>' +
  657. '<tableCell><paragraph>C1</paragraph></tableCell>' +
  658. '</tableRow>' +
  659. '<tableRow>' +
  660. '<tableCell><paragraph>A2</paragraph></tableCell>' +
  661. '[<tableCell><paragraph>B2</paragraph></tableCell>]' +
  662. '[<tableCell><paragraph>C2</paragraph></tableCell>]' +
  663. '</tableRow>' +
  664. '<tableRow>' +
  665. '<tableCell><paragraph>A3</paragraph></tableCell>' +
  666. '[<tableCell><paragraph>B3</paragraph></tableCell>]' +
  667. '[<tableCell><paragraph>C3</paragraph></tableCell>]' +
  668. '</tableRow>' +
  669. '</table>'
  670. );
  671. assertEqualMarkup( getModelData( model ),
  672. '<table>' +
  673. '<tableRow>' +
  674. '<tableCell><paragraph>A1</paragraph></tableCell>' +
  675. '<tableCell><paragraph>B1</paragraph></tableCell>' +
  676. '<tableCell><paragraph>C1</paragraph></tableCell>' +
  677. '</tableRow>' +
  678. '<tableRow>' +
  679. '<tableCell><paragraph>A2</paragraph></tableCell>' +
  680. '[<tableCell><paragraph>B2</paragraph></tableCell>]' +
  681. '[<tableCell><paragraph>C2</paragraph></tableCell>]' +
  682. '</tableRow>' +
  683. '<tableRow>' +
  684. '<tableCell><paragraph>A3</paragraph></tableCell>' +
  685. '[<tableCell><paragraph>B3</paragraph></tableCell>]' +
  686. '[<tableCell><paragraph>C3</paragraph></tableCell>]' +
  687. '</tableRow>' +
  688. '</table>'
  689. );
  690. } );
  691. it( 'should not fix ranges in multi-range selection (each range set differently - but valid)', () => {
  692. setModelData( model,
  693. '<paragraph>[foo]</paragraph>' +
  694. '<table>' +
  695. '<tableRow>' +
  696. '[<tableCell><paragraph>aaa</paragraph></tableCell>]' +
  697. '<tableCell><paragraph>[bbb]</paragraph></tableCell>' +
  698. '</tableRow>' +
  699. '</table>'
  700. );
  701. assertEqualMarkup( getModelData( model ),
  702. '<paragraph>[foo]</paragraph>' +
  703. '<table>' +
  704. '<tableRow>' +
  705. '[<tableCell><paragraph>aaa</paragraph></tableCell>]' +
  706. '<tableCell><paragraph>[bbb]</paragraph></tableCell>' +
  707. '</tableRow>' +
  708. '</table>'
  709. );
  710. } );
  711. it( 'should fix partially wrong selection (last range is post-fixed on whole table)', () => {
  712. setModelData( model,
  713. '<table>' +
  714. '<tableRow>' +
  715. '[<tableCell><paragraph>aaa</paragraph></tableCell>]' +
  716. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  717. '[<tableCell><paragraph>ccc</paragraph></tableCell>' +
  718. '</tableRow>]' +
  719. '</table>'
  720. );
  721. assertEqualMarkup( getModelData( model ),
  722. '[<table>' +
  723. '<tableRow>' +
  724. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  725. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  726. '<tableCell><paragraph>ccc</paragraph></tableCell>' +
  727. '</tableRow>' +
  728. '</table>]'
  729. );
  730. } );
  731. it( 'should fix partially wrong selection (first range is post-fixed on whole table)', () => {
  732. setModelData( model,
  733. '<table>' +
  734. '[<tableRow>' +
  735. '<tableCell><paragraph>aaa</paragraph></tableCell>]' +
  736. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  737. '[<tableCell><paragraph>ccc</paragraph></tableCell>]' +
  738. '</tableRow>' +
  739. '</table>'
  740. );
  741. assertEqualMarkup( getModelData( model ),
  742. '[<table>' +
  743. '<tableRow>' +
  744. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  745. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  746. '<tableCell><paragraph>ccc</paragraph></tableCell>' +
  747. '</tableRow>' +
  748. '</table>]'
  749. );
  750. } );
  751. it( 'should not reset the selection if the final range is the same as the initial one', () => {
  752. setModelData( model,
  753. '<table>' +
  754. '<tableRow>' +
  755. '<tableCell>[<image></image>]</tableCell>' +
  756. '</tableRow>' +
  757. '</table>'
  758. );
  759. // Setting a selection attribute will trigger the post-fixer. However, because this
  760. // action does not affect ranges, the post-fixer should not set a new selection and,
  761. // in consequence, should not clear the selection attribute (like it normally would when
  762. // a new selection is set).
  763. // https://github.com/ckeditor/ckeditor5/issues/6693
  764. model.change( writer => {
  765. writer.setSelectionAttribute( 'foo', 'bar' );
  766. } );
  767. assertEqualMarkup( getModelData( model ),
  768. '<table>' +
  769. '<tableRow>' +
  770. '<tableCell>[<image></image>]</tableCell>' +
  771. '</tableRow>' +
  772. '</table>'
  773. );
  774. expect( model.document.selection.hasAttribute( 'foo' ) ).to.be.true;
  775. } );
  776. } );
  777. describe( 'non-collapsed selection - image scenarios', () => {
  778. beforeEach( () => {
  779. setModelData( model,
  780. '<paragraph>[]foo</paragraph>' +
  781. '<image>' +
  782. '<caption>xxx</caption>' +
  783. '</image>' +
  784. '<paragraph>bar</paragraph>'
  785. );
  786. } );
  787. it( 'should fix #1 (crossing object and limit boundaries)', () => {
  788. model.change( writer => {
  789. // <paragraph>f[oo</paragraph><image><caption>x]xx</caption>...
  790. writer.setSelection( writer.createRange(
  791. writer.createPositionAt( modelRoot.getChild( 0 ), 1 ),
  792. writer.createPositionAt( modelRoot.getChild( 1 ).getChild( 0 ), 1 )
  793. ) );
  794. } );
  795. expect( getModelData( model ) ).to.equal(
  796. '<paragraph>f[oo</paragraph>' +
  797. '<image>' +
  798. '<caption>xxx</caption>' +
  799. '</image>]' +
  800. '<paragraph>bar</paragraph>'
  801. );
  802. } );
  803. it( 'should fix #2 (crossing object boundary)', () => {
  804. model.change( writer => {
  805. // <paragraph>f[oo</paragraph><image>]<caption>xxx</caption>...
  806. writer.setSelection( writer.createRange(
  807. writer.createPositionAt( modelRoot.getChild( 0 ), 1 ),
  808. writer.createPositionAt( modelRoot.getChild( 1 ), 0 )
  809. ) );
  810. } );
  811. expect( getModelData( model ) ).to.equal(
  812. '<paragraph>f[oo</paragraph>' +
  813. '<image>' +
  814. '<caption>xxx</caption>' +
  815. '</image>]' +
  816. '<paragraph>bar</paragraph>'
  817. );
  818. } );
  819. it( 'should fix #3 (crossing object boundary)', () => {
  820. model.change( writer => {
  821. // <paragraph>f[oo</paragraph><image><caption>xxx</caption>]</image>...
  822. writer.setSelection( writer.createRange(
  823. writer.createPositionAt( modelRoot.getChild( 0 ), 1 ),
  824. writer.createPositionAt( modelRoot.getChild( 1 ), 1 )
  825. ) );
  826. } );
  827. expect( getModelData( model ) ).to.equal(
  828. '<paragraph>f[oo</paragraph>' +
  829. '<image>' +
  830. '<caption>xxx</caption>' +
  831. '</image>]' +
  832. '<paragraph>bar</paragraph>'
  833. );
  834. } );
  835. it( 'should fix #4 (element selection of not an object)', () => {
  836. model.change( writer => {
  837. // <paragraph>foo</paragraph><image>[<caption>xxx</caption>]</image>...
  838. writer.setSelection( writer.createRange(
  839. writer.createPositionAt( modelRoot.getChild( 1 ), 0 ),
  840. writer.createPositionAt( modelRoot.getChild( 1 ), 1 )
  841. ) );
  842. } );
  843. expect( getModelData( model ) ).to.equal(
  844. '<paragraph>foo</paragraph>' +
  845. '[<image>' +
  846. '<caption>xxx</caption>' +
  847. '</image>]' +
  848. '<paragraph>bar</paragraph>'
  849. );
  850. } );
  851. it( 'should not fix #1 (element selection of an object)', () => {
  852. model.change( writer => {
  853. // <paragraph>foo</paragraph>[<image><caption>xxx</caption></image>]...
  854. writer.setSelection( writer.createRange(
  855. writer.createPositionAt( modelRoot, 1 ),
  856. writer.createPositionAt( modelRoot, 2 )
  857. ) );
  858. } );
  859. expect( getModelData( model ) ).to.equal(
  860. '<paragraph>foo</paragraph>' +
  861. '[<image>' +
  862. '<caption>xxx</caption>' +
  863. '</image>]' +
  864. '<paragraph>bar</paragraph>'
  865. );
  866. } );
  867. it( 'should not fix #2 (inside a limit)', () => {
  868. model.change( writer => {
  869. const caption = modelRoot.getChild( 1 ).getChild( 0 );
  870. // <paragraph>foo</paragraph><image><caption>[xxx]</caption></image>...
  871. writer.setSelection( writer.createRange(
  872. writer.createPositionAt( caption, 0 ),
  873. writer.createPositionAt( caption, 3 )
  874. ) );
  875. } );
  876. expect( getModelData( model ) ).to.equal(
  877. '<paragraph>foo</paragraph>' +
  878. '<image>' +
  879. '<caption>[xxx]</caption>' +
  880. '</image>' +
  881. '<paragraph>bar</paragraph>'
  882. );
  883. } );
  884. it( 'should not fix #3 (inside a limit - partial text selection)', () => {
  885. model.change( writer => {
  886. const caption = modelRoot.getChild( 1 ).getChild( 0 );
  887. // <paragraph>foo</paragraph><image><caption>[xx]x</caption></image>...
  888. writer.setSelection( writer.createRange(
  889. writer.createPositionAt( caption, 0 ),
  890. writer.createPositionAt( caption, 2 )
  891. ) );
  892. } );
  893. expect( getModelData( model ) ).to.equal(
  894. '<paragraph>foo</paragraph>' +
  895. '<image>' +
  896. '<caption>[xx]x</caption>' +
  897. '</image>' +
  898. '<paragraph>bar</paragraph>'
  899. );
  900. } );
  901. it( 'should not fix #4 (inside a limit - partial text selection)', () => {
  902. model.change( writer => {
  903. const caption = modelRoot.getChild( 1 ).getChild( 0 );
  904. // <paragraph>foo</paragraph><image><caption>x[xx]</caption></image>...
  905. writer.setSelection( writer.createRange(
  906. writer.createPositionAt( caption, 1 ),
  907. writer.createPositionAt( caption, 3 )
  908. ) );
  909. } );
  910. expect( getModelData( model ) ).to.equal(
  911. '<paragraph>foo</paragraph>' +
  912. '<image>' +
  913. '<caption>x[xx]</caption>' +
  914. '</image>' +
  915. '<paragraph>bar</paragraph>'
  916. );
  917. } );
  918. it( 'should not fix #5 (selection in root on non limit element that doesn\'t allow text)', () => {
  919. setModelData( model,
  920. '[<figure></figure>]'
  921. );
  922. expect( getModelData( model ) ).to.equal(
  923. '[<figure></figure>]'
  924. );
  925. } );
  926. } );
  927. describe( 'non-collapsed selection - other scenarios', () => {
  928. it( 'should fix #1 (element selection of not an object)', () => {
  929. setModelData( model,
  930. '<paragraph>aaa</paragraph>' +
  931. '[<paragraph>bbb</paragraph>]' +
  932. '<paragraph>ccc</paragraph>'
  933. );
  934. expect( getModelData( model ) ).to.equal(
  935. '<paragraph>aaa</paragraph>' +
  936. '<paragraph>[bbb]</paragraph>' +
  937. '<paragraph>ccc</paragraph>'
  938. );
  939. } );
  940. it( 'should fix #2 (elements selection of not an object)', () => {
  941. setModelData( model,
  942. '<paragraph>aaa</paragraph>' +
  943. '[<paragraph>bbb</paragraph>' +
  944. '<paragraph>ccc</paragraph>]'
  945. );
  946. expect( getModelData( model ) ).to.equal(
  947. '<paragraph>aaa</paragraph>' +
  948. '<paragraph>[bbb</paragraph>' +
  949. '<paragraph>ccc]</paragraph>'
  950. );
  951. } );
  952. it( 'should fix #3 (partial selection of not an object)', () => {
  953. setModelData( model,
  954. '<paragraph>aaa</paragraph>' +
  955. '[<paragraph>bbb</paragraph>' +
  956. '<paragraph>ccc]</paragraph>'
  957. );
  958. expect( getModelData( model ) ).to.equal(
  959. '<paragraph>aaa</paragraph>' +
  960. '<paragraph>[bbb</paragraph>' +
  961. '<paragraph>ccc]</paragraph>'
  962. );
  963. } );
  964. it( 'should fix #4 (partial selection of not an object)', () => {
  965. setModelData( model,
  966. '<paragraph>aaa</paragraph>' +
  967. '<paragraph>b[bb</paragraph>]' +
  968. '<paragraph>ccc</paragraph>'
  969. );
  970. expect( getModelData( model ) ).to.equal(
  971. '<paragraph>aaa</paragraph>' +
  972. '<paragraph>b[bb]</paragraph>' +
  973. '<paragraph>ccc</paragraph>'
  974. );
  975. } );
  976. it( 'should fix #5 (partial selection of not an object)', () => {
  977. setModelData( model,
  978. '<paragraph>aaa</paragraph>' +
  979. '[<paragraph>bb]b</paragraph>' +
  980. '<paragraph>ccc</paragraph>'
  981. );
  982. expect( getModelData( model ) ).to.equal(
  983. '<paragraph>aaa</paragraph>' +
  984. '<paragraph>[bb]b</paragraph>' +
  985. '<paragraph>ccc</paragraph>'
  986. );
  987. } );
  988. it( 'should fix #6 (selection must not cross a limit element; starts in a root)', () => {
  989. model.schema.register( 'a', { isLimit: true, allowIn: '$root' } );
  990. model.schema.register( 'b', { isLimit: true, allowIn: 'a' } );
  991. model.schema.register( 'c', { allowIn: 'b' } );
  992. model.schema.extend( '$text', { allowIn: 'c' } );
  993. setModelData( model,
  994. '<a><b><c>[</c></b></a>]'
  995. );
  996. expect( getModelData( model ) ).to.equal( '[<a><b><c></c></b></a>]' );
  997. } );
  998. it( 'should fix #7 (selection must not cross a limit element; ends in a root)', () => {
  999. model.schema.register( 'a', { isLimit: true, allowIn: '$root' } );
  1000. model.schema.register( 'b', { isLimit: true, allowIn: 'a' } );
  1001. model.schema.register( 'c', { allowIn: 'b' } );
  1002. model.schema.extend( '$text', { allowIn: 'c' } );
  1003. setModelData( model,
  1004. '[<a><b><c>]</c></b></a>'
  1005. );
  1006. expect( getModelData( model ) ).to.equal( '[<a><b><c></c></b></a>]' );
  1007. } );
  1008. it( 'should fix #8 (selection must not cross a limit element; starts in a non-limit)', () => {
  1009. model.schema.register( 'div', { allowIn: '$root' } );
  1010. model.schema.register( 'a', { isLimit: true, allowIn: 'div' } );
  1011. model.schema.register( 'b', { isLimit: true, allowIn: 'a' } );
  1012. model.schema.register( 'c', { allowIn: 'b' } );
  1013. model.schema.extend( '$text', { allowIn: 'c' } );
  1014. setModelData( model,
  1015. '<div>[<a><b><c>]</c></b></a></div>'
  1016. );
  1017. expect( getModelData( model ) ).to.equal( '<div>[<a><b><c></c></b></a>]</div>' );
  1018. } );
  1019. it( 'should fix #9 (selection must not cross a limit element; ends in a non-limit)', () => {
  1020. model.schema.register( 'div', { allowIn: '$root' } );
  1021. model.schema.register( 'a', { isLimit: true, allowIn: 'div' } );
  1022. model.schema.register( 'b', { isLimit: true, allowIn: 'a' } );
  1023. model.schema.register( 'c', { allowIn: 'b' } );
  1024. model.schema.extend( '$text', { allowIn: 'c' } );
  1025. setModelData( model,
  1026. '<div><a><b><c>[</c></b></a>]</div>'
  1027. );
  1028. expect( getModelData( model ) ).to.equal( '<div>[<a><b><c></c></b></a>]</div>' );
  1029. } );
  1030. it( 'should not fix #1 (selection on text node)', () => {
  1031. setModelData( model, '<paragraph>foob[a]r</paragraph>', { lastRangeBackward: true } );
  1032. expect( getModelData( model ) ).to.equal( '<paragraph>foob[a]r</paragraph>' );
  1033. } );
  1034. it( 'should not fix #2 (inline widget selected)', () => {
  1035. setModelData( model,
  1036. '<paragraph>[<inlineWidget></inlineWidget>]</paragraph>'
  1037. );
  1038. expect( getModelData( model ) ).to.equal(
  1039. '<paragraph>[<inlineWidget></inlineWidget>]</paragraph>'
  1040. );
  1041. } );
  1042. it( 'should not fix #3 (text around inline widget)', () => {
  1043. setModelData( model,
  1044. '<paragraph>fo[o<inlineWidget></inlineWidget>b]ar</paragraph>'
  1045. );
  1046. expect( getModelData( model ) ).to.equal(
  1047. '<paragraph>fo[o<inlineWidget></inlineWidget>b]ar</paragraph>'
  1048. );
  1049. } );
  1050. it( 'should not fix #4 (object in object)', () => {
  1051. model.schema.register( 'div', {
  1052. allowIn: [ '$root', 'div' ],
  1053. isObject: true
  1054. } );
  1055. setModelData( model, '<div>[<div></div>]</div>' );
  1056. model.change( writer => {
  1057. const innerDiv = model.document.getRoot().getNodeByPath( [ 0, 0 ] );
  1058. writer.setSelection( writer.createRangeOn( innerDiv ) );
  1059. } );
  1060. expect( getModelData( model ) ).to.equal( '<div>[<div></div>]</div>' );
  1061. } );
  1062. it( 'should not fix #5 (selection starts before a selectable, which is not an object)', () => {
  1063. model.schema.register( 'div', { allowIn: '$root' } );
  1064. model.schema.register( 'selectable', { isSelectable: true, allowIn: 'div' } );
  1065. model.schema.extend( '$text', { allowIn: 'selectable' } );
  1066. setModelData( model,
  1067. '<div>[<selectable>foo]</selectable></div>'
  1068. );
  1069. expect( getModelData( model ) ).to.equal( '<div>[<selectable>foo]</selectable></div>' );
  1070. } );
  1071. it( 'should not fix #6 (selection ends after before a selectable, which is not an object)', () => {
  1072. model.schema.register( 'div', { allowIn: '$root' } );
  1073. model.schema.register( 'selectable', { isSelectable: true, allowIn: 'div' } );
  1074. model.schema.extend( '$text', { allowIn: 'selectable' } );
  1075. setModelData( model,
  1076. '<div><selectable>[foo</selectable>]</div>'
  1077. );
  1078. expect( getModelData( model ) ).to.equal( '<div><selectable>[foo</selectable>]</div>' );
  1079. } );
  1080. } );
  1081. describe( 'non-collapsed selection - inline widget scenarios', () => {
  1082. beforeEach( () => {
  1083. model.schema.register( 'placeholder', {
  1084. allowWhere: '$text',
  1085. isInline: true
  1086. } );
  1087. } );
  1088. it( 'should fix selection that ends in inline element', () => {
  1089. setModelData( model, '<paragraph>aaa[<placeholder>]</placeholder>bbb</paragraph>' );
  1090. expect( getModelData( model ) ).to.equal( '<paragraph>aaa[]<placeholder></placeholder>bbb</paragraph>' );
  1091. } );
  1092. it( 'should fix selection that starts in inline element', () => {
  1093. setModelData( model, '<paragraph>aaa<placeholder>[</placeholder>]bbb</paragraph>' );
  1094. expect( getModelData( model ) ).to.equal( '<paragraph>aaa<placeholder></placeholder>[]bbb</paragraph>' );
  1095. } );
  1096. it( 'should fix selection that ends in inline element that is also an object', () => {
  1097. model.schema.extend( 'placeholder', {
  1098. isObject: true
  1099. } );
  1100. setModelData( model, '<paragraph>aaa[<placeholder>]</placeholder>bbb</paragraph>' );
  1101. expect( getModelData( model ) ).to.equal( '<paragraph>aaa[<placeholder></placeholder>]bbb</paragraph>' );
  1102. } );
  1103. it( 'should fix selection that starts in inline element that is also an object', () => {
  1104. model.schema.extend( 'placeholder', {
  1105. isObject: true
  1106. } );
  1107. setModelData( model, '<paragraph>aaa<placeholder>[</placeholder>]bbb</paragraph>' );
  1108. expect( getModelData( model ) ).to.equal( '<paragraph>aaa[<placeholder></placeholder>]bbb</paragraph>' );
  1109. } );
  1110. } );
  1111. describe( 'collapsed selection', () => {
  1112. beforeEach( () => {
  1113. setModelData( model,
  1114. '<paragraph>foo</paragraph>' +
  1115. '<table>' +
  1116. '<tableRow>' +
  1117. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  1118. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  1119. '</tableRow>' +
  1120. '</table>' +
  1121. '<paragraph>bar</paragraph>'
  1122. );
  1123. } );
  1124. it( 'should fix #1 (selection in limit element & before limit element)', () => {
  1125. // <table>[]<tableRow>...
  1126. model.change( writer => {
  1127. writer.setSelection(
  1128. writer.createRange( writer.createPositionAt( modelRoot.getChild( 1 ), 0 ) )
  1129. );
  1130. } );
  1131. expect( getModelData( model ) ).to.equal(
  1132. '<paragraph>foo</paragraph>' +
  1133. '<table>' +
  1134. '<tableRow>' +
  1135. '<tableCell><paragraph>[]aaa</paragraph></tableCell>' +
  1136. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  1137. '</tableRow>' +
  1138. '</table>' +
  1139. '<paragraph>bar</paragraph>'
  1140. );
  1141. } );
  1142. it( 'should fix #2 (selection in limit element & before limit+object element)', () => {
  1143. // <table><tableRow>[]<tableCell>...
  1144. model.change( writer => {
  1145. const row = modelRoot.getChild( 1 ).getChild( 0 );
  1146. writer.setSelection(
  1147. writer.createRange( writer.createPositionAt( row, 0 ) )
  1148. );
  1149. } );
  1150. expect( getModelData( model ) ).to.equal(
  1151. '<paragraph>foo</paragraph>' +
  1152. '<table>' +
  1153. '<tableRow>' +
  1154. '<tableCell><paragraph>[]aaa</paragraph></tableCell>' +
  1155. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  1156. '</tableRow>' +
  1157. '</table>' +
  1158. '<paragraph>bar</paragraph>'
  1159. );
  1160. } );
  1161. it( 'should fix #3 (selection inside object element and before block element)', () => {
  1162. setModelData( model,
  1163. '<paragraph>foo</paragraph>' +
  1164. '<table>' +
  1165. '<tableRow>' +
  1166. '<tableCell>[]<paragraph>aaa</paragraph></tableCell>' +
  1167. '</tableRow>' +
  1168. '</table>' +
  1169. '<paragraph>bar</paragraph>'
  1170. );
  1171. assertEqualMarkup( getModelData( model ),
  1172. '<paragraph>foo</paragraph>' +
  1173. '<table>' +
  1174. '<tableRow>' +
  1175. '<tableCell><paragraph>[]aaa</paragraph></tableCell>' +
  1176. '</tableRow>' +
  1177. '</table>' +
  1178. '<paragraph>bar</paragraph>'
  1179. );
  1180. } );
  1181. it( 'should fix multiple ranges outside block element (but not merge them)', () => {
  1182. setModelData( model,
  1183. '[]<paragraph>foo</paragraph>[]' +
  1184. '<table>' +
  1185. '<tableRow>' +
  1186. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  1187. '</tableRow>' +
  1188. '</table>' +
  1189. '<paragraph>bar</paragraph>'
  1190. );
  1191. assertEqualMarkup( getModelData( model ),
  1192. '<paragraph>[]foo[]</paragraph>' +
  1193. '<table>' +
  1194. '<tableRow>' +
  1195. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  1196. '</tableRow>' +
  1197. '</table>' +
  1198. '<paragraph>bar</paragraph>'
  1199. );
  1200. } );
  1201. } );
  1202. } );
  1203. } );