8
0

selection-post-fixer.js 28 KB

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