liverange.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* bender-tags: model */
  6. 'use strict';
  7. import Document from '/ckeditor5/engine/model/document.js';
  8. import Element from '/ckeditor5/engine/model/element.js';
  9. import Position from '/ckeditor5/engine/model/position.js';
  10. import LiveRange from '/ckeditor5/engine/model/liverange.js';
  11. import Range from '/ckeditor5/engine/model/range.js';
  12. describe( 'LiveRange', () => {
  13. let doc, root, ul, p;
  14. before( () => {
  15. doc = new Document();
  16. root = doc.createRoot();
  17. let lis = [
  18. new Element( 'li', [], 'aaaaaaaaaa' ),
  19. new Element( 'li', [], 'bbbbbbbbbb' ),
  20. new Element( 'li', [], 'cccccccccc' ),
  21. new Element( 'li', [], 'dddddddddd' ),
  22. new Element( 'li', [], 'eeeeeeeeee' ),
  23. new Element( 'li', [], 'ffffffffff' ),
  24. new Element( 'li', [], 'gggggggggg' ),
  25. new Element( 'li', [], 'hhhhhhhhhh' )
  26. ];
  27. ul = new Element( 'ul', [], lis );
  28. p = new Element( 'p', [], 'qwertyuiop' );
  29. root.insertChildren( 0, [ ul, p, 'xyzxyz' ] );
  30. } );
  31. it( 'should be an instance of Range', () => {
  32. let live = new LiveRange( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) );
  33. live.detach();
  34. expect( live ).to.be.instanceof( Range );
  35. } );
  36. it( 'should listen to a change event of the document that owns this range', () => {
  37. sinon.spy( LiveRange.prototype, 'listenTo' );
  38. let live = new LiveRange( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) );
  39. live.detach();
  40. expect( live.listenTo.calledWith( doc, 'change' ) ).to.be.true;
  41. LiveRange.prototype.listenTo.restore();
  42. } );
  43. it( 'should stop listening when detached', () => {
  44. sinon.spy( LiveRange.prototype, 'stopListening' );
  45. let live = new LiveRange( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) );
  46. live.detach();
  47. expect( live.stopListening.called ).to.be.true;
  48. LiveRange.prototype.stopListening.restore();
  49. } );
  50. it( 'createFromElement should return LiveRange', () => {
  51. let range = LiveRange.createFromElement( p );
  52. expect( range ).to.be.instanceof( LiveRange );
  53. range.detach();
  54. } );
  55. it( 'createFromParentsAndOffsets should return LiveRange', () => {
  56. let range = LiveRange.createFromParentsAndOffsets( root, 0, p, 2 );
  57. expect( range ).to.be.instanceof( LiveRange );
  58. range.detach();
  59. } );
  60. it( 'createFromPositionAndShift should return LiveRange', () => {
  61. let range = LiveRange.createFromPositionAndShift( new Position( root, [ 0, 1 ] ), 4 );
  62. expect( range ).to.be.instanceof( LiveRange );
  63. range.detach();
  64. } );
  65. it( 'createFromRange should return LiveRange', () => {
  66. let range = LiveRange.createFromRange( new Range( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) ) );
  67. expect( range ).to.be.instanceof( LiveRange );
  68. range.detach();
  69. } );
  70. // Examples may seem weird when you compare them with the tree structure generated at the beginning of tests.
  71. // Since change event is fired _after_ operation is executed on tree model, you have to imagine that generated
  72. // structure is representing what is _after_ operation is executed. So live LiveRange properties are describing
  73. // virtual tree that is not existing anymore and event ranges are operating on the tree generated above.
  74. describe( 'should get transformed if', () => {
  75. let live;
  76. beforeEach( () => {
  77. live = new LiveRange( new Position( root, [ 0, 1, 4 ] ), new Position( root, [ 0, 2, 2 ] ) );
  78. } );
  79. afterEach( () => {
  80. live.detach();
  81. } );
  82. describe( 'insertion', () => {
  83. it( 'is in the same parent as range start and before it', () => {
  84. let insertRange = new Range( new Position( root, [ 0, 1, 0 ] ), new Position( root, [ 0, 1, 4 ] ) );
  85. doc.fire( 'change', 'insert', { range: insertRange }, null );
  86. expect( live.start.path ).to.deep.equal( [ 0, 1, 8 ] );
  87. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  88. } );
  89. it( 'is in the same parent as range end and before it', () => {
  90. let insertRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 3 ] ) );
  91. doc.fire( 'change', 'insert', { range: insertRange }, null );
  92. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  93. expect( live.end.path ).to.deep.equal( [ 0, 2, 5 ] );
  94. } );
  95. it( 'is at a position before a node from range start path', () => {
  96. let insertRange = new Range( new Position( root, [ 0, 0 ] ), new Position( root, [ 0, 2 ] ) );
  97. doc.fire( 'change', 'insert', { range: insertRange }, null );
  98. expect( live.start.path ).to.deep.equal( [ 0, 3, 4 ] );
  99. expect( live.end.path ).to.deep.equal( [ 0, 4, 2 ] );
  100. } );
  101. it( 'is at a position before a node from range end path', () => {
  102. let insertRange = new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 3 ] ) );
  103. doc.fire( 'change', 'insert', { range: insertRange }, null );
  104. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  105. expect( live.end.path ).to.deep.equal( [ 0, 3, 2 ] );
  106. } );
  107. it( 'is at the live range start position and live range is collapsed', () => {
  108. live.end.path = [ 0, 1, 4 ];
  109. let insertRange = new Range( new Position( root, [ 0, 1, 4 ] ), new Position( root, [ 0, 1, 8 ] ) );
  110. doc.fire( 'change', 'insert', { range: insertRange }, null );
  111. expect( live.start.path ).to.deep.equal( [ 0, 1, 8 ] );
  112. expect( live.end.path ).to.deep.equal( [ 0, 1, 8 ] );
  113. } );
  114. } );
  115. describe( 'range move', () => {
  116. it( 'is to the same parent as range start and before it', () => {
  117. let moveSource = new Position( root, [ 2 ] );
  118. let moveRange = new Range( new Position( root, [ 0, 1, 0 ] ), new Position( root, [ 0, 1, 4 ] ) );
  119. let changes = {
  120. range: moveRange,
  121. sourcePosition: moveSource
  122. };
  123. doc.fire( 'change', 'move', changes, null );
  124. expect( live.start.path ).to.deep.equal( [ 0, 1, 8 ] );
  125. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  126. } );
  127. it( 'is to the same parent as range end and before it', () => {
  128. let moveSource = new Position( root, [ 3 ] );
  129. let moveRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 4 ] ) );
  130. let changes = {
  131. range: moveRange,
  132. sourcePosition: moveSource
  133. };
  134. doc.fire( 'change', 'move', changes, null );
  135. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  136. expect( live.end.path ).to.deep.equal( [ 0, 2, 6 ] );
  137. } );
  138. it( 'is to a position before a node from range start path', () => {
  139. let moveSource = new Position( root, [ 2 ] );
  140. let moveRange = new Range( new Position( root, [ 0, 0 ] ), new Position( root, [ 0, 2 ] ) );
  141. let changes = {
  142. range: moveRange,
  143. sourcePosition: moveSource
  144. };
  145. doc.fire( 'change', 'move', changes, null );
  146. expect( live.start.path ).to.deep.equal( [ 0, 3, 4 ] );
  147. expect( live.end.path ).to.deep.equal( [ 0, 4, 2 ] );
  148. } );
  149. it( 'is to a position before a node from range end path', () => {
  150. let moveSource = new Position( root, [ 2 ] );
  151. let moveRange = new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 3 ] ) );
  152. let changes = {
  153. range: moveRange,
  154. sourcePosition: moveSource
  155. };
  156. doc.fire( 'change', 'move', changes, null );
  157. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  158. expect( live.end.path ).to.deep.equal( [ 0, 3, 2 ] );
  159. } );
  160. it( 'is from the same parent as range start and before it', () => {
  161. let moveSource = new Position( root, [ 0, 1, 0 ] );
  162. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 3 ] ) );
  163. let changes = {
  164. range: moveRange,
  165. sourcePosition: moveSource
  166. };
  167. doc.fire( 'change', 'move', changes, null );
  168. expect( live.start.path ).to.deep.equal( [ 0, 1, 1 ] );
  169. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  170. } );
  171. it( 'is from the same parent as range end and before it', () => {
  172. let moveSource = new Position( root, [ 0, 2, 0 ] );
  173. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 2 ] ) );
  174. let changes = {
  175. range: moveRange,
  176. sourcePosition: moveSource
  177. };
  178. doc.fire( 'change', 'move', changes, null );
  179. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  180. expect( live.end.path ).to.deep.equal( [ 0, 2, 0 ] );
  181. } );
  182. it( 'is from a position before a node from range start path', () => {
  183. let moveSource = new Position( root, [ 0, 0 ] );
  184. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 1 ] ) );
  185. let changes = {
  186. range: moveRange,
  187. sourcePosition: moveSource
  188. };
  189. doc.fire( 'change', 'move', changes, null );
  190. expect( live.start.path ).to.deep.equal( [ 0, 0, 4 ] );
  191. expect( live.end.path ).to.deep.equal( [ 0, 1, 2 ] );
  192. } );
  193. it( 'intersects on live range left side', () => {
  194. let moveSource = new Position( root, [ 0, 1, 2 ] );
  195. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 4 ] ) );
  196. let changes = {
  197. range: moveRange,
  198. sourcePosition: moveSource
  199. };
  200. doc.fire( 'change', 'move', changes, null );
  201. expect( live.start.path ).to.deep.equal( [ 0, 1, 2 ] );
  202. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  203. } );
  204. it( 'intersects on live range right side', () => {
  205. let moveSource = new Position( root, [ 0, 2, 1 ] );
  206. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 4 ] ) );
  207. let changes = {
  208. range: moveRange,
  209. sourcePosition: moveSource
  210. };
  211. doc.fire( 'change', 'move', changes, null );
  212. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  213. expect( live.end.path ).to.deep.equal( [ 0, 2, 1 ] );
  214. } );
  215. it( 'intersects on live range left side and live range new start is touching moved range end', () => {
  216. let moveSource = new Position( root, [ 0, 1, 0 ] );
  217. let moveRange = new Range( new Position( root, [ 0, 1 ] ), new Position( root, [ 0, 6 ] ) );
  218. let changes = {
  219. range: moveRange,
  220. sourcePosition: moveSource
  221. };
  222. doc.fire( 'change', 'move', changes, null );
  223. expect( live.start.path ).to.deep.equal( [ 0, 5 ] );
  224. expect( live.end.path ).to.deep.equal( [ 0, 7, 2 ] );
  225. } );
  226. it( 'intersects on live range right side and live range new end is touching moved range start', () => {
  227. live.end.offset = 12;
  228. let moveSource = new Position( root, [ 0, 2, 10 ] );
  229. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 5 ] ) );
  230. let changes = {
  231. range: moveRange,
  232. sourcePosition: moveSource
  233. };
  234. doc.fire( 'change', 'move', changes, null );
  235. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  236. expect( live.end.path ).to.deep.equal( [ 0, 3, 2 ] );
  237. } );
  238. it( 'is equal to live range', () => {
  239. live.end.path = [ 0, 1, 7 ];
  240. let moveSource = new Position( root, [ 0, 1, 4 ] );
  241. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 3 ] ) );
  242. let changes = {
  243. range: moveRange,
  244. sourcePosition: moveSource
  245. };
  246. doc.fire( 'change', 'move', changes, null );
  247. expect( live.start.path ).to.deep.equal( [ 0, 3, 0 ] );
  248. expect( live.end.path ).to.deep.equal( [ 0, 3, 3 ] );
  249. } );
  250. it( 'contains live range', () => {
  251. live.end.path = [ 0, 1, 7 ];
  252. let moveSource = new Position( root, [ 0, 1, 3 ] );
  253. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 9 ] ) );
  254. let changes = {
  255. range: moveRange,
  256. sourcePosition: moveSource
  257. };
  258. doc.fire( 'change', 'move', changes, null );
  259. expect( live.start.path ).to.deep.equal( [ 0, 3, 1 ] );
  260. expect( live.end.path ).to.deep.equal( [ 0, 3, 4 ] );
  261. } );
  262. it( 'is inside live range and points to live range', () => {
  263. live.end.path = [ 0, 1, 12 ];
  264. let moveSource = new Position( root, [ 0, 1, 6 ] );
  265. let moveRange = new Range( new Position( root, [ 0, 1, 8 ] ), new Position( root, [ 0, 1, 10 ] ) );
  266. let changes = {
  267. range: moveRange,
  268. sourcePosition: moveSource
  269. };
  270. doc.fire( 'change', 'move', changes, null );
  271. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  272. expect( live.end.path ).to.deep.equal( [ 0, 1, 12 ] );
  273. } );
  274. it( 'is intersecting with live range and points to live range', () => {
  275. live.end.path = [ 0, 1, 12 ];
  276. let moveSource = new Position( root, [ 0, 1, 2 ] );
  277. let moveRange = new Range( new Position( root, [ 0, 1, 5 ] ), new Position( root, [ 0, 1, 9 ] ) );
  278. let changes = {
  279. range: moveRange,
  280. sourcePosition: moveSource
  281. };
  282. doc.fire( 'change', 'move', changes, null );
  283. expect( live.start.path ).to.deep.equal( [ 0, 1, 2 ] );
  284. expect( live.end.path ).to.deep.equal( [ 0, 1, 12 ] );
  285. } );
  286. } );
  287. } );
  288. describe( 'should not get transformed if', () => {
  289. let otherRoot;
  290. before( () => {
  291. otherRoot = doc.createRoot( '$root', 'otherRoot' );
  292. } );
  293. let live, clone;
  294. beforeEach( () => {
  295. live = new LiveRange( new Position( root, [ 0, 1, 4 ] ), new Position( root, [ 0, 2, 2 ] ) );
  296. clone = Range.createFromRange( live );
  297. } );
  298. afterEach( () => {
  299. live.detach();
  300. } );
  301. describe( 'insertion', () => {
  302. it( 'is in the same parent as range start and after it', () => {
  303. let insertRange = new Range( new Position( root, [ 0, 1, 7 ] ), new Position( root, [ 0, 1, 9 ] ) );
  304. doc.fire( 'change', 'insert', { range: insertRange }, null );
  305. expect( live.isEqual( clone ) ).to.be.true;
  306. } );
  307. it( 'is in the same parent as range end and after it', () => {
  308. let insertRange = new Range( new Position( root, [ 0, 2, 7 ] ), new Position( root, [ 0, 2, 9 ] ) );
  309. doc.fire( 'change', 'insert', { range: insertRange }, null );
  310. expect( live.isEqual( clone ) ).to.be.true;
  311. } );
  312. it( 'is to a position after a node from range end path', () => {
  313. let insertRange = new Range( new Position( root, [ 3 ] ), new Position( root, [ 4 ] ) );
  314. doc.fire( 'change', 'insert', { range: insertRange }, null );
  315. expect( live.isEqual( clone ) ).to.be.true;
  316. } );
  317. it( 'is in different root', () => {
  318. let insertRange = new Range( new Position( otherRoot, [ 0, 0 ] ), new Position( otherRoot, [ 0, 2 ] ) );
  319. doc.fire( 'change', 'insert', { range: insertRange }, null );
  320. expect( live.isEqual( clone ) ).to.be.true;
  321. } );
  322. } );
  323. describe( 'range move', () => {
  324. it( 'is to the same parent as range start and after it', () => {
  325. let moveSource = new Position( root, [ 4 ] );
  326. let moveRange = new Range( new Position( root, [ 0, 1, 7 ] ), new Position( root, [ 0, 1, 9 ] ) );
  327. let changes = {
  328. range: moveRange,
  329. sourcePosition: moveSource
  330. };
  331. doc.fire( 'change', 'move', changes, null );
  332. expect( live.isEqual( clone ) ).to.be.true;
  333. } );
  334. it( 'is to the same parent as range end and after it', () => {
  335. let moveSource = new Position( root, [ 4 ] );
  336. let moveRange = new Range( new Position( root, [ 0, 2, 3 ] ), new Position( root, [ 0, 2, 5 ] ) );
  337. let changes = {
  338. range: moveRange,
  339. sourcePosition: moveSource
  340. };
  341. doc.fire( 'change', 'move', changes, null );
  342. expect( live.isEqual( clone ) ).to.be.true;
  343. } );
  344. it( 'is to a position after a node from range end path', () => {
  345. let moveSource = new Position( root, [ 4 ] );
  346. let moveRange = new Range( new Position( root, [ 0, 3 ] ), new Position( root, [ 0, 5 ] ) );
  347. let changes = {
  348. range: moveRange,
  349. sourcePosition: moveSource
  350. };
  351. doc.fire( 'change', 'move', changes, null );
  352. expect( live.isEqual( clone ) ).to.be.true;
  353. } );
  354. it( 'is from the same parent as range start and after it', () => {
  355. let moveSource = new Position( root, [ 0, 1, 6 ] );
  356. let moveRange = new Range( new Position( root, [ 4, 0 ] ), new Position( root, [ 4, 3 ] ) );
  357. let changes = {
  358. range: moveRange,
  359. sourcePosition: moveSource
  360. };
  361. doc.fire( 'change', 'move', changes, null );
  362. expect( live.isEqual( clone ) ).to.be.true;
  363. } );
  364. it( 'is from the same parent as range end and after it', () => {
  365. let moveSource = new Position( root, [ 0, 2, 4 ] );
  366. let moveRange = new Range( new Position( root, [ 4, 0 ] ), new Position( root, [ 4, 2 ] ) );
  367. let changes = {
  368. range: moveRange,
  369. sourcePosition: moveSource
  370. };
  371. doc.fire( 'change', 'move', changes, null );
  372. expect( live.isEqual( clone ) ).to.be.true;
  373. } );
  374. it( 'is from a position after a node from range end path', () => {
  375. let moveSource = new Position( root, [ 0, 3 ] );
  376. let moveRange = new Range( new Position( root, [ 5, 0 ] ), new Position( root, [ 5, 1 ] ) );
  377. let changes = {
  378. range: moveRange,
  379. sourcePosition: moveSource
  380. };
  381. doc.fire( 'change', 'move', changes, null );
  382. expect( live.isEqual( clone ) ).to.be.true;
  383. } );
  384. it( 'is to different root', () => {
  385. let moveSource = new Position( root, [ 2 ] );
  386. let moveRange = new Range( new Position( otherRoot, [ 0, 1, 0 ] ), new Position( otherRoot, [ 0, 1, 4 ] ) );
  387. let changes = {
  388. range: moveRange,
  389. sourcePosition: moveSource
  390. };
  391. doc.fire( 'change', 'move', changes, null );
  392. expect( live.isEqual( clone ) ).to.be.true;
  393. } );
  394. it( 'is from different root', () => {
  395. let moveSource = new Position( otherRoot, [ 0, 2, 0 ] );
  396. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 2 ] ) );
  397. let changes = {
  398. range: moveRange,
  399. sourcePosition: moveSource
  400. };
  401. doc.fire( 'change', 'move', changes, null );
  402. expect( live.isEqual( clone ) ).to.be.true;
  403. } );
  404. } );
  405. } );
  406. } );