selection-post-fixer.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import Model from '../../../src/model/model';
  6. import ModelPosition from '../../../src/model/position';
  7. import ModelRange from '../../../src/model/range';
  8. import { injectSelectionPostFixer } from '../../../src/model/utils/selection-post-fixer';
  9. import { getData as getModelData, setData as setModelData } from '../../../src/dev-utils/model';
  10. describe( 'Selection post-fixer', () => {
  11. describe( 'injectSelectionPostFixer()', () => {
  12. it( 'is a function', () => {
  13. expect( injectSelectionPostFixer ).to.be.a( 'function' );
  14. } );
  15. } );
  16. describe( 'injected behavior', () => {
  17. let model, modelRoot;
  18. beforeEach( () => {
  19. model = new Model();
  20. modelRoot = model.document.createRoot();
  21. model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
  22. model.schema.register( 'table', {
  23. allowWhere: '$block',
  24. allowAttributes: [ 'headingRows', 'headingColumns' ],
  25. isLimit: true,
  26. isObject: true
  27. } );
  28. model.schema.register( 'tableRow', {
  29. allowIn: 'table',
  30. isLimit: true
  31. } );
  32. model.schema.register( 'tableCell', {
  33. allowIn: 'tableRow',
  34. allowAttributes: [ 'colspan', 'rowspan' ],
  35. isLimit: 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.register( 'caption', {
  44. allowIn: 'image',
  45. allowContentOf: '$block',
  46. isLimit: true
  47. } );
  48. model.schema.register( 'inlineWidget', {
  49. isObject: true,
  50. allowIn: [ '$block', '$clipboardHolder' ]
  51. } );
  52. model.schema.register( 'figure', {
  53. allowIn: '$root',
  54. allowAttributes: [ 'name', 'title' ]
  55. } );
  56. } );
  57. it( 'should not crash if there is no correct position for model selection', () => {
  58. setModelData( model, '' );
  59. expect( getModelData( model ) ).to.equal( '[]' );
  60. } );
  61. it( 'should react to structure changes', () => {
  62. setModelData( model, '<paragraph>[]foo</paragraph><image></image>' );
  63. model.change( writer => {
  64. writer.remove( modelRoot.getChild( 0 ) );
  65. } );
  66. expect( getModelData( model ) ).to.equal( '[<image></image>]' );
  67. } );
  68. it( 'should react to selection changes', () => {
  69. setModelData( model, '<paragraph>[]foo</paragraph><image></image>' );
  70. // <paragraph>foo</paragraph>[]<image></image>
  71. model.change( writer => {
  72. writer.setSelection(
  73. ModelRange.createFromParentsAndOffsets( modelRoot, 1, modelRoot, 1 )
  74. );
  75. } );
  76. expect( getModelData( model ) ).to.equal( '<paragraph>foo[]</paragraph><image></image>' );
  77. } );
  78. describe( 'non-collapsed selection - table scenarios', () => {
  79. beforeEach( () => {
  80. setModelData( model,
  81. '<paragraph>[]foo</paragraph>' +
  82. '<table>' +
  83. '<tableRow>' +
  84. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  85. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  86. '</tableRow>' +
  87. '</table>' +
  88. '<paragraph>bar</paragraph>'
  89. );
  90. } );
  91. it( 'should fix #1', () => {
  92. // <paragraph>f[oo</paragraph><table><tableRow><tableCell></tableCell>]<tableCell>...
  93. model.change( writer => {
  94. writer.setSelection( ModelRange.createFromParentsAndOffsets(
  95. modelRoot.getChild( 0 ), 1,
  96. modelRoot.getChild( 1 ).getChild( 0 ), 1
  97. ) );
  98. } );
  99. expect( getModelData( model ) ).to.equal(
  100. '<paragraph>f[oo</paragraph>' +
  101. '<table>' +
  102. '<tableRow>' +
  103. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  104. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  105. '</tableRow>' +
  106. '</table>]' +
  107. '<paragraph>bar</paragraph>'
  108. );
  109. } );
  110. it( 'should fix #2', () => {
  111. // ...<table><tableRow><tableCell></tableCell>[<tableCell></tableCell></tableRow></table><paragraph>b]ar</paragraph>
  112. model.change( writer => {
  113. writer.setSelection( ModelRange.createFromParentsAndOffsets(
  114. modelRoot.getChild( 1 ).getChild( 0 ), 1,
  115. modelRoot.getChild( 2 ), 1
  116. ) );
  117. } );
  118. expect( getModelData( model ) ).to.equal(
  119. '<paragraph>foo</paragraph>' +
  120. '[<table>' +
  121. '<tableRow>' +
  122. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  123. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  124. '</tableRow>' +
  125. '</table>' +
  126. '<paragraph>b]ar</paragraph>'
  127. );
  128. } );
  129. it( 'should fix #3', () => {
  130. // <paragraph>f[oo</paragraph><table>]<tableRow>...
  131. model.change( writer => {
  132. writer.setSelection( ModelRange.createFromParentsAndOffsets(
  133. modelRoot.getChild( 0 ), 1,
  134. modelRoot.getChild( 1 ), 0
  135. ) );
  136. } );
  137. expect( getModelData( model ) ).to.equal(
  138. '<paragraph>f[oo</paragraph>' +
  139. '<table>' +
  140. '<tableRow>' +
  141. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  142. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  143. '</tableRow>' +
  144. '</table>]' +
  145. '<paragraph>bar</paragraph>'
  146. );
  147. } );
  148. it( 'should fix #4', () => {
  149. // <paragraph>foo</paragraph><table><tableRow><tableCell>a[aa</tableCell><tableCell>b]bb</tableCell>
  150. model.change( writer => {
  151. writer.setSelection( ModelRange.createFromParentsAndOffsets(
  152. modelRoot.getNodeByPath( [ 1, 0, 0, 0 ] ), 1,
  153. modelRoot.getNodeByPath( [ 1, 0, 1, 0 ] ), 2
  154. ) );
  155. } );
  156. expect( getModelData( model ) ).to.equal(
  157. '<paragraph>foo</paragraph>' +
  158. '[<table>' +
  159. '<tableRow>' +
  160. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  161. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  162. '</tableRow>' +
  163. '</table>]' +
  164. '<paragraph>bar</paragraph>'
  165. );
  166. } );
  167. it( 'should fix #5', () => {
  168. setModelData( model,
  169. '<paragraph>foo</paragraph>' +
  170. '<table>' +
  171. '<tableRow>' +
  172. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  173. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  174. '</tableRow>' +
  175. '</table>' +
  176. '[]' +
  177. '<table>' +
  178. '<tableRow>' +
  179. '<tableCell><paragraph>xxx</paragraph></tableCell>' +
  180. '<tableCell><paragraph>yyy</paragraph></tableCell>' +
  181. '</tableRow>' +
  182. '</table>' +
  183. '<paragraph>baz</paragraph>'
  184. );
  185. expect( getModelData( model ) ).to.equal(
  186. '<paragraph>foo</paragraph>' +
  187. '[<table>' +
  188. '<tableRow>' +
  189. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  190. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  191. '</tableRow>' +
  192. '</table>]' +
  193. '<table>' +
  194. '<tableRow>' +
  195. '<tableCell><paragraph>xxx</paragraph></tableCell>' +
  196. '<tableCell><paragraph>yyy</paragraph></tableCell>' +
  197. '</tableRow>' +
  198. '</table>' +
  199. '<paragraph>baz</paragraph>'
  200. );
  201. } );
  202. // There's a chance that this and the following test will not be up to date with
  203. // how the table feature is really implemented once we'll introduce row/cells/columns selection
  204. // in which case all these elements will need to be marked as objects.
  205. it( 'should fix #6 (element selection of not an object)', () => {
  206. setModelData( model,
  207. '<paragraph>foo</paragraph>' +
  208. '<table>' +
  209. '[<tableRow>' +
  210. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  211. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  212. '</tableRow>]' +
  213. '</table>' +
  214. '<paragraph>baz</paragraph>'
  215. );
  216. expect( getModelData( model ) ).to.equal(
  217. '<paragraph>foo</paragraph>' +
  218. '[<table>' +
  219. '<tableRow>' +
  220. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  221. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  222. '</tableRow>' +
  223. '</table>]' +
  224. '<paragraph>baz</paragraph>'
  225. );
  226. } );
  227. it( 'should fix #7 (element selection of non-objects)', () => {
  228. setModelData( model,
  229. '<paragraph>foo</paragraph>' +
  230. '<table>' +
  231. '[<tableRow>' +
  232. '<tableCell><paragraph>1</paragraph></tableCell>' +
  233. '<tableCell><paragraph>2</paragraph></tableCell>' +
  234. '</tableRow>' +
  235. '<tableRow>' +
  236. '<tableCell><paragraph>3</paragraph></tableCell>' +
  237. '<tableCell><paragraph>4</paragraph></tableCell>]' +
  238. '</tableRow>' +
  239. '<tableRow>' +
  240. '<tableCell><paragraph>5</paragraph></tableCell>' +
  241. '<tableCell><paragraph>6</paragraph></tableCell>' +
  242. '</tableRow>' +
  243. '</table>' +
  244. '<paragraph>baz</paragraph>'
  245. );
  246. expect( getModelData( model ) ).to.equal(
  247. '<paragraph>foo</paragraph>' +
  248. '[<table>' +
  249. '<tableRow>' +
  250. '<tableCell><paragraph>1</paragraph></tableCell><tableCell><paragraph>2</paragraph></tableCell>' +
  251. '</tableRow>' +
  252. '<tableRow>' +
  253. '<tableCell><paragraph>3</paragraph></tableCell><tableCell><paragraph>4</paragraph></tableCell>' +
  254. '</tableRow>' +
  255. '<tableRow>' +
  256. '<tableCell><paragraph>5</paragraph></tableCell><tableCell><paragraph>6</paragraph></tableCell>' +
  257. '</tableRow>' +
  258. '</table>]' +
  259. '<paragraph>baz</paragraph>'
  260. );
  261. } );
  262. it( 'should fix #8 (cross-limit selection which starts in a non-limit elements)', () => {
  263. model.schema.extend( 'paragraph', { allowIn: 'tableCell' } );
  264. setModelData( model,
  265. '<paragraph>foo</paragraph>' +
  266. '<table>' +
  267. '<tableRow>' +
  268. '<tableCell><paragraph>f[oo</paragraph></tableCell>' +
  269. '<tableCell><paragraph>b]ar</paragraph></tableCell>' +
  270. '</tableRow>' +
  271. '</table>' +
  272. '<paragraph>baz</paragraph>'
  273. );
  274. expect( getModelData( model ) ).to.equal(
  275. '<paragraph>foo</paragraph>' +
  276. '[<table>' +
  277. '<tableRow>' +
  278. '<tableCell><paragraph>foo</paragraph></tableCell>' +
  279. '<tableCell><paragraph>bar</paragraph></tableCell>' +
  280. '</tableRow>' +
  281. '</table>]' +
  282. '<paragraph>baz</paragraph>'
  283. );
  284. } );
  285. it( 'should not fix #1 (selection after a table)', () => {
  286. setModelData( model,
  287. '<paragraph>foo</paragraph>' +
  288. '<table>' +
  289. '<tableRow>' +
  290. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  291. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  292. '</tableRow>' +
  293. '</table>' +
  294. '<paragraph>b[ar</paragraph>' +
  295. '<paragraph>ba]z</paragraph>'
  296. );
  297. expect( getModelData( model ) ).to.equal(
  298. '<paragraph>foo</paragraph>' +
  299. '<table>' +
  300. '<tableRow>' +
  301. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  302. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  303. '</tableRow>' +
  304. '</table>' +
  305. '<paragraph>b[ar</paragraph>' +
  306. '<paragraph>ba]z</paragraph>'
  307. );
  308. } );
  309. it( 'should fix multiple ranges #1', () => {
  310. model.change( writer => {
  311. const ranges = [
  312. new ModelRange( new ModelPosition( modelRoot, [ 0, 1 ] ), new ModelPosition( modelRoot, [ 1, 0 ] ) ),
  313. new ModelRange( new ModelPosition( modelRoot, [ 1, 0, 0, 0 ] ), new ModelPosition( modelRoot, [ 1, 1 ] ) )
  314. ];
  315. writer.setSelection( ranges );
  316. } );
  317. expect( getModelData( model ) ).to.equal(
  318. '<paragraph>f[oo</paragraph>' +
  319. '<table>' +
  320. '<tableRow>' +
  321. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  322. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  323. '</tableRow>' +
  324. '</table>]' +
  325. '<paragraph>bar</paragraph>'
  326. );
  327. } );
  328. it( 'should fix multiple ranges #2', () => {
  329. model.change( writer => {
  330. const ranges = [
  331. new ModelRange( new ModelPosition( modelRoot, [ 0, 1 ] ), new ModelPosition( modelRoot, [ 1, 0 ] ) ),
  332. new ModelRange( new ModelPosition( modelRoot, [ 1, 0, 0, 0 ] ), new ModelPosition( modelRoot, [ 2, 2 ] ) )
  333. ];
  334. writer.setSelection( ranges );
  335. } );
  336. expect( getModelData( model ) ).to.equal(
  337. '<paragraph>f[oo</paragraph>' +
  338. '<table>' +
  339. '<tableRow>' +
  340. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  341. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  342. '</tableRow>' +
  343. '</table>' +
  344. '<paragraph>ba]r</paragraph>'
  345. );
  346. } );
  347. it( 'should fix multiple ranges #3', () => {
  348. setModelData( model,
  349. '<paragraph>foo</paragraph>' +
  350. '<table>' +
  351. '<tableRow>' +
  352. '<tableCell><paragraph>[aaa</paragraph></tableCell>' +
  353. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  354. '</tableRow>' +
  355. '<tableRow>]' +
  356. '<tableCell><paragraph>[aaa</paragraph></tableCell>' +
  357. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  358. '</tableRow>' +
  359. '<tableRow>]' +
  360. '<tableCell><paragraph>[aaa</paragraph></tableCell>' +
  361. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  362. '</tableRow>' +
  363. '<tableRow>]' +
  364. '<tableCell><paragraph>[aaa</paragraph></tableCell>' +
  365. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  366. '</tableRow>' +
  367. '</table>' +
  368. '<paragraph>b]az</paragraph>'
  369. );
  370. expect( getModelData( model ) ).to.equal(
  371. '<paragraph>foo</paragraph>' +
  372. '[<table>' +
  373. '<tableRow>' +
  374. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  375. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  376. '</tableRow>' +
  377. '<tableRow>' +
  378. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  379. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  380. '</tableRow>' +
  381. '<tableRow>' +
  382. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  383. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  384. '</tableRow>' +
  385. '<tableRow>' +
  386. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  387. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  388. '</tableRow>' +
  389. '</table>' +
  390. '<paragraph>b]az</paragraph>'
  391. );
  392. } );
  393. it( 'should not fix multiple ranges #1 - not overlapping ranges', () => {
  394. model.change( writer => {
  395. const ranges = [
  396. new ModelRange( new ModelPosition( modelRoot, [ 0, 1 ] ), new ModelPosition( modelRoot, [ 1, 0 ] ) ),
  397. new ModelRange( new ModelPosition( modelRoot, [ 1, 0, 0, 0 ] ), new ModelPosition( modelRoot, [ 2, 1 ] ) ),
  398. new ModelRange( new ModelPosition( modelRoot, [ 2, 2 ] ), new ModelPosition( modelRoot, [ 2, 3 ] ) )
  399. ];
  400. writer.setSelection( ranges );
  401. } );
  402. expect( getModelData( model ) ).to.equal(
  403. '<paragraph>f[oo</paragraph>' +
  404. '<table>' +
  405. '<tableRow>' +
  406. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  407. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  408. '</tableRow>' +
  409. '</table>' +
  410. '<paragraph>b]a[r]</paragraph>'
  411. );
  412. } );
  413. it( 'should allow selection on block - limit element', () => {
  414. model.schema.extend( '$block', { allowIn: 'tableCell' } );
  415. setModelData( model,
  416. '<table>' +
  417. '<tableRow><tableCell>[<paragraph>aaa</paragraph>]</tableCell></tableRow>' +
  418. '</table>'
  419. );
  420. expect( getModelData( model ) ).to.equal(
  421. '<table>' +
  422. '<tableRow><tableCell><paragraph>[aaa]</paragraph></tableCell></tableRow>' +
  423. '</table>'
  424. );
  425. } );
  426. } );
  427. describe( 'non-collapsed selection - image scenarios', () => {
  428. beforeEach( () => {
  429. setModelData( model,
  430. '<paragraph>[]foo</paragraph>' +
  431. '<image>' +
  432. '<caption>xxx</caption>' +
  433. '</image>' +
  434. '<paragraph>bar</paragraph>'
  435. );
  436. } );
  437. it( 'should fix #1 (crossing object and limit boundaries)', () => {
  438. model.change( writer => {
  439. // <paragraph>f[oo</paragraph><image><caption>x]xx</caption>...
  440. writer.setSelection( ModelRange.createFromParentsAndOffsets(
  441. modelRoot.getChild( 0 ), 1,
  442. modelRoot.getChild( 1 ).getChild( 0 ), 1
  443. ) );
  444. } );
  445. expect( getModelData( model ) ).to.equal(
  446. '<paragraph>f[oo</paragraph>' +
  447. '<image>' +
  448. '<caption>xxx</caption>' +
  449. '</image>]' +
  450. '<paragraph>bar</paragraph>'
  451. );
  452. } );
  453. it( 'should fix #2 (crossing object boundary)', () => {
  454. model.change( writer => {
  455. // <paragraph>f[oo</paragraph><image>]<caption>xxx</caption>...
  456. writer.setSelection( ModelRange.createFromParentsAndOffsets(
  457. modelRoot.getChild( 0 ), 1,
  458. modelRoot.getChild( 1 ), 0
  459. ) );
  460. } );
  461. expect( getModelData( model ) ).to.equal(
  462. '<paragraph>f[oo</paragraph>' +
  463. '<image>' +
  464. '<caption>xxx</caption>' +
  465. '</image>]' +
  466. '<paragraph>bar</paragraph>'
  467. );
  468. } );
  469. it( 'should fix #3 (crossing object boundary)', () => {
  470. model.change( writer => {
  471. // <paragraph>f[oo</paragraph><image><caption>xxx</caption>]</image>...
  472. writer.setSelection( ModelRange.createFromParentsAndOffsets(
  473. modelRoot.getChild( 0 ), 1,
  474. modelRoot.getChild( 1 ), 1
  475. ) );
  476. } );
  477. expect( getModelData( model ) ).to.equal(
  478. '<paragraph>f[oo</paragraph>' +
  479. '<image>' +
  480. '<caption>xxx</caption>' +
  481. '</image>]' +
  482. '<paragraph>bar</paragraph>'
  483. );
  484. } );
  485. it( 'should fix #4 (element selection of not an object)', () => {
  486. model.change( writer => {
  487. // <paragraph>foo</paragraph><image>[<caption>xxx</caption>]</image>...
  488. writer.setSelection( ModelRange.createFromParentsAndOffsets(
  489. modelRoot.getChild( 1 ), 0,
  490. modelRoot.getChild( 1 ), 1
  491. ) );
  492. } );
  493. expect( getModelData( model ) ).to.equal(
  494. '<paragraph>foo</paragraph>' +
  495. '[<image>' +
  496. '<caption>xxx</caption>' +
  497. '</image>]' +
  498. '<paragraph>bar</paragraph>'
  499. );
  500. } );
  501. it( 'should not fix #1 (element selection of an object)', () => {
  502. model.change( writer => {
  503. // <paragraph>foo</paragraph>[<image><caption>xxx</caption></image>]...
  504. writer.setSelection( ModelRange.createFromParentsAndOffsets(
  505. modelRoot, 1,
  506. modelRoot, 2
  507. ) );
  508. } );
  509. expect( getModelData( model ) ).to.equal(
  510. '<paragraph>foo</paragraph>' +
  511. '[<image>' +
  512. '<caption>xxx</caption>' +
  513. '</image>]' +
  514. '<paragraph>bar</paragraph>'
  515. );
  516. } );
  517. it( 'should not fix #2 (inside a limit)', () => {
  518. model.change( writer => {
  519. const caption = modelRoot.getChild( 1 ).getChild( 0 );
  520. // <paragraph>foo</paragraph><image><caption>[xxx]</caption></image>...
  521. writer.setSelection( ModelRange.createFromParentsAndOffsets(
  522. caption, 0,
  523. caption, 3
  524. ) );
  525. } );
  526. expect( getModelData( model ) ).to.equal(
  527. '<paragraph>foo</paragraph>' +
  528. '<image>' +
  529. '<caption>[xxx]</caption>' +
  530. '</image>' +
  531. '<paragraph>bar</paragraph>'
  532. );
  533. } );
  534. it( 'should not fix #3 (inside a limit - partial text selection)', () => {
  535. model.change( writer => {
  536. const caption = modelRoot.getChild( 1 ).getChild( 0 );
  537. // <paragraph>foo</paragraph><image><caption>[xx]x</caption></image>...
  538. writer.setSelection( ModelRange.createFromParentsAndOffsets(
  539. caption, 0,
  540. caption, 2
  541. ) );
  542. } );
  543. expect( getModelData( model ) ).to.equal(
  544. '<paragraph>foo</paragraph>' +
  545. '<image>' +
  546. '<caption>[xx]x</caption>' +
  547. '</image>' +
  548. '<paragraph>bar</paragraph>'
  549. );
  550. } );
  551. it( 'should not fix #4 (inside a limit - partial text selection)', () => {
  552. model.change( writer => {
  553. const caption = modelRoot.getChild( 1 ).getChild( 0 );
  554. // <paragraph>foo</paragraph><image><caption>x[xx]</caption></image>...
  555. writer.setSelection( ModelRange.createFromParentsAndOffsets(
  556. caption, 1,
  557. caption, 3
  558. ) );
  559. } );
  560. expect( getModelData( model ) ).to.equal(
  561. '<paragraph>foo</paragraph>' +
  562. '<image>' +
  563. '<caption>x[xx]</caption>' +
  564. '</image>' +
  565. '<paragraph>bar</paragraph>'
  566. );
  567. } );
  568. it( 'should not fix #5 (selection in root on non limit element that doesn\'t allow text)', () => {
  569. setModelData( model,
  570. '[<figure></figure>]'
  571. );
  572. expect( getModelData( model ) ).to.equal(
  573. '[<figure></figure>]'
  574. );
  575. } );
  576. } );
  577. describe( 'non-collapsed selection - other scenarios', () => {
  578. it( 'should fix #1 (element selection of not an object)', () => {
  579. setModelData( model,
  580. '<paragraph>aaa</paragraph>' +
  581. '[<paragraph>bbb</paragraph>]' +
  582. '<paragraph>ccc</paragraph>'
  583. );
  584. expect( getModelData( model ) ).to.equal(
  585. '<paragraph>aaa</paragraph>' +
  586. '<paragraph>[bbb]</paragraph>' +
  587. '<paragraph>ccc</paragraph>'
  588. );
  589. } );
  590. it( 'should fix #2 (elements selection of not an object)', () => {
  591. setModelData( model,
  592. '<paragraph>aaa</paragraph>' +
  593. '[<paragraph>bbb</paragraph>' +
  594. '<paragraph>ccc</paragraph>]'
  595. );
  596. expect( getModelData( model ) ).to.equal(
  597. '<paragraph>aaa</paragraph>' +
  598. '<paragraph>[bbb</paragraph>' +
  599. '<paragraph>ccc]</paragraph>'
  600. );
  601. } );
  602. it( 'should fix #3 (partial selection of not an object)', () => {
  603. setModelData( model,
  604. '<paragraph>aaa</paragraph>' +
  605. '[<paragraph>bbb</paragraph>' +
  606. '<paragraph>ccc]</paragraph>'
  607. );
  608. expect( getModelData( model ) ).to.equal(
  609. '<paragraph>aaa</paragraph>' +
  610. '<paragraph>[bbb</paragraph>' +
  611. '<paragraph>ccc]</paragraph>'
  612. );
  613. } );
  614. it( 'should fix #4 (partial selection of not an object)', () => {
  615. setModelData( model,
  616. '<paragraph>aaa</paragraph>' +
  617. '<paragraph>b[bb</paragraph>]' +
  618. '<paragraph>ccc</paragraph>'
  619. );
  620. expect( getModelData( model ) ).to.equal(
  621. '<paragraph>aaa</paragraph>' +
  622. '<paragraph>b[bb]</paragraph>' +
  623. '<paragraph>ccc</paragraph>'
  624. );
  625. } );
  626. it( 'should fix #5 (partial selection of not an object)', () => {
  627. setModelData( model,
  628. '<paragraph>aaa</paragraph>' +
  629. '[<paragraph>bb]b</paragraph>' +
  630. '<paragraph>ccc</paragraph>'
  631. );
  632. expect( getModelData( model ) ).to.equal(
  633. '<paragraph>aaa</paragraph>' +
  634. '<paragraph>[bb]b</paragraph>' +
  635. '<paragraph>ccc</paragraph>'
  636. );
  637. } );
  638. it( 'should fix #6 (selection must not cross a limit element; starts in a root)', () => {
  639. model.schema.register( 'a', { isLimit: true, allowIn: '$root' } );
  640. model.schema.register( 'b', { isLimit: true, allowIn: 'a' } );
  641. model.schema.register( 'c', { allowIn: 'b' } );
  642. model.schema.extend( '$text', { allowIn: 'c' } );
  643. setModelData( model,
  644. '<a><b><c>[</c></b></a>]'
  645. );
  646. expect( getModelData( model ) ).to.equal( '[<a><b><c></c></b></a>]' );
  647. } );
  648. it( 'should fix #7 (selection must not cross a limit element; ends in a root)', () => {
  649. model.schema.register( 'a', { isLimit: true, allowIn: '$root' } );
  650. model.schema.register( 'b', { isLimit: true, allowIn: 'a' } );
  651. model.schema.register( 'c', { allowIn: 'b' } );
  652. model.schema.extend( '$text', { allowIn: 'c' } );
  653. setModelData( model,
  654. '[<a><b><c>]</c></b></a>'
  655. );
  656. expect( getModelData( model ) ).to.equal( '[<a><b><c></c></b></a>]' );
  657. } );
  658. it( 'should fix #8 (selection must not cross a limit element; starts in a non-limit)', () => {
  659. model.schema.register( 'div', { allowIn: '$root' } );
  660. model.schema.register( 'a', { isLimit: true, allowIn: 'div' } );
  661. model.schema.register( 'b', { isLimit: true, allowIn: 'a' } );
  662. model.schema.register( 'c', { allowIn: 'b' } );
  663. model.schema.extend( '$text', { allowIn: 'c' } );
  664. setModelData( model,
  665. '<div>[<a><b><c>]</c></b></a></div>'
  666. );
  667. expect( getModelData( model ) ).to.equal( '<div>[<a><b><c></c></b></a>]</div>' );
  668. } );
  669. it( 'should fix #9 (selection must not cross a limit element; ends in a non-limit)', () => {
  670. model.schema.register( 'div', { allowIn: '$root' } );
  671. model.schema.register( 'a', { isLimit: true, allowIn: 'div' } );
  672. model.schema.register( 'b', { isLimit: true, allowIn: 'a' } );
  673. model.schema.register( 'c', { allowIn: 'b' } );
  674. model.schema.extend( '$text', { allowIn: 'c' } );
  675. setModelData( model,
  676. '<div><a><b><c>[</c></b></a>]</div>'
  677. );
  678. expect( getModelData( model ) ).to.equal( '<div>[<a><b><c></c></b></a>]</div>' );
  679. } );
  680. it( 'should not fix #1 (selection on text node)', () => {
  681. setModelData( model, '<paragraph>foob[a]r</paragraph>', { lastRangeBackward: true } );
  682. expect( getModelData( model ) ).to.equal( '<paragraph>foob[a]r</paragraph>' );
  683. } );
  684. it( 'should not fix #2 (inline widget selected)', () => {
  685. setModelData( model,
  686. '<paragraph>[<inlineWidget></inlineWidget>]</paragraph>'
  687. );
  688. expect( getModelData( model ) ).to.equal(
  689. '<paragraph>[<inlineWidget></inlineWidget>]</paragraph>'
  690. );
  691. } );
  692. it( 'should not fix #3 (text around inline widget)', () => {
  693. setModelData( model,
  694. '<paragraph>fo[o<inlineWidget></inlineWidget>b]ar</paragraph>'
  695. );
  696. expect( getModelData( model ) ).to.equal(
  697. '<paragraph>fo[o<inlineWidget></inlineWidget>b]ar</paragraph>'
  698. );
  699. } );
  700. } );
  701. describe( 'collapsed selection', () => {
  702. beforeEach( () => {
  703. setModelData( model,
  704. '<paragraph>[]foo</paragraph>' +
  705. '<table>' +
  706. '<tableRow>' +
  707. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  708. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  709. '</tableRow>' +
  710. '</table>' +
  711. '<paragraph>bar</paragraph>'
  712. );
  713. } );
  714. it( 'should fix #1', () => {
  715. // <table>[]<tableRow>...
  716. model.change( writer => {
  717. writer.setSelection(
  718. ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 1 ), 0, modelRoot.getChild( 1 ), 0 )
  719. );
  720. } );
  721. expect( getModelData( model ) ).to.equal(
  722. '<paragraph>foo[]</paragraph>' +
  723. '<table>' +
  724. '<tableRow>' +
  725. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  726. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  727. '</tableRow>' +
  728. '</table>' +
  729. '<paragraph>bar</paragraph>'
  730. );
  731. } );
  732. it( 'should fix #2', () => {
  733. // <table><tableRow>[]<tableCell>...
  734. model.change( writer => {
  735. const row = modelRoot.getChild( 1 ).getChild( 0 );
  736. writer.setSelection(
  737. ModelRange.createFromParentsAndOffsets( row, 0, row, 0 )
  738. );
  739. } );
  740. expect( getModelData( model ) ).to.equal(
  741. '<paragraph>foo</paragraph>' +
  742. '<table>' +
  743. '<tableRow>' +
  744. '<tableCell><paragraph>[]aaa</paragraph></tableCell>' +
  745. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  746. '</tableRow>' +
  747. '</table>' +
  748. '<paragraph>bar</paragraph>'
  749. );
  750. } );
  751. it( 'should not fix multiple ranges #1', () => {
  752. // []<paragraph>foo</paragraph>[]<table>...
  753. model.change( writer => {
  754. writer.setSelection(
  755. [
  756. ModelRange.createFromParentsAndOffsets( modelRoot, 0, modelRoot, 0 ),
  757. ModelRange.createFromParentsAndOffsets( modelRoot, 1, modelRoot, 1 )
  758. ]
  759. );
  760. } );
  761. expect( getModelData( model ) ).to.equal(
  762. '<paragraph>[]foo[]</paragraph>' +
  763. '<table>' +
  764. '<tableRow>' +
  765. '<tableCell><paragraph>aaa</paragraph></tableCell>' +
  766. '<tableCell><paragraph>bbb</paragraph></tableCell>' +
  767. '</tableRow>' +
  768. '</table>' +
  769. '<paragraph>bar</paragraph>'
  770. );
  771. } );
  772. } );
  773. } );
  774. } );