8
0

liverange.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* bender-tags: treemodel */
  6. 'use strict';
  7. import Document from '/ckeditor5/engine/treemodel/document.js';
  8. import Element from '/ckeditor5/engine/treemodel/element.js';
  9. import Position from '/ckeditor5/engine/treemodel/position.js';
  10. import LiveRange from '/ckeditor5/engine/treemodel/liverange.js';
  11. import Range from '/ckeditor5/engine/treemodel/range.js';
  12. describe( 'LiveRange', () => {
  13. let doc, root, ul, p;
  14. before( () => {
  15. doc = new Document();
  16. root = doc.createRoot( 'root' );
  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. } );
  108. describe( 'range move', () => {
  109. it( 'is to the same parent as range start and before it', () => {
  110. let moveSource = new Position( root, [ 2 ] );
  111. let moveRange = new Range( new Position( root, [ 0, 1, 0 ] ), new Position( root, [ 0, 1, 4 ] ) );
  112. let changes = {
  113. range: moveRange,
  114. sourcePosition: moveSource
  115. };
  116. doc.fire( 'change', 'move', changes, null );
  117. expect( live.start.path ).to.deep.equal( [ 0, 1, 8 ] );
  118. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  119. } );
  120. it( 'is to the same parent as range end and before it', () => {
  121. let moveSource = new Position( root, [ 3 ] );
  122. let moveRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 4 ] ) );
  123. let changes = {
  124. range: moveRange,
  125. sourcePosition: moveSource
  126. };
  127. doc.fire( 'change', 'move', changes, null );
  128. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  129. expect( live.end.path ).to.deep.equal( [ 0, 2, 6 ] );
  130. } );
  131. it( 'is to a position before a node from range start path', () => {
  132. let moveSource = new Position( root, [ 2 ] );
  133. let moveRange = new Range( new Position( root, [ 0, 0 ] ), new Position( root, [ 0, 2 ] ) );
  134. let changes = {
  135. range: moveRange,
  136. sourcePosition: moveSource
  137. };
  138. doc.fire( 'change', 'move', changes, null );
  139. expect( live.start.path ).to.deep.equal( [ 0, 3, 4 ] );
  140. expect( live.end.path ).to.deep.equal( [ 0, 4, 2 ] );
  141. } );
  142. it( 'is to a position before a node from range end path', () => {
  143. let moveSource = new Position( root, [ 2 ] );
  144. let moveRange = new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 3 ] ) );
  145. let changes = {
  146. range: moveRange,
  147. sourcePosition: moveSource
  148. };
  149. doc.fire( 'change', 'move', changes, null );
  150. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  151. expect( live.end.path ).to.deep.equal( [ 0, 3, 2 ] );
  152. } );
  153. it( 'is from the same parent as range start and before it', () => {
  154. let moveSource = new Position( root, [ 0, 1, 0 ] );
  155. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 3 ] ) );
  156. let changes = {
  157. range: moveRange,
  158. sourcePosition: moveSource
  159. };
  160. doc.fire( 'change', 'move', changes, null );
  161. expect( live.start.path ).to.deep.equal( [ 0, 1, 1 ] );
  162. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  163. } );
  164. it( 'is from the same parent as range end and before it', () => {
  165. let moveSource = new Position( root, [ 0, 2, 0 ] );
  166. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 2 ] ) );
  167. let changes = {
  168. range: moveRange,
  169. sourcePosition: moveSource
  170. };
  171. doc.fire( 'change', 'move', changes, null );
  172. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  173. expect( live.end.path ).to.deep.equal( [ 0, 2, 0 ] );
  174. } );
  175. it( 'is from a position before a node from range start path', () => {
  176. let moveSource = new Position( root, [ 0, 0 ] );
  177. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 1 ] ) );
  178. let changes = {
  179. range: moveRange,
  180. sourcePosition: moveSource
  181. };
  182. doc.fire( 'change', 'move', changes, null );
  183. expect( live.start.path ).to.deep.equal( [ 0, 0, 4 ] );
  184. expect( live.end.path ).to.deep.equal( [ 0, 1, 2 ] );
  185. } );
  186. it( 'intersects on live range left side', () => {
  187. let moveSource = new Position( root, [ 0, 1, 2 ] );
  188. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 4 ] ) );
  189. let changes = {
  190. range: moveRange,
  191. sourcePosition: moveSource
  192. };
  193. doc.fire( 'change', 'move', changes, null );
  194. expect( live.start.path ).to.deep.equal( [ 0, 1, 2 ] );
  195. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  196. } );
  197. it( 'intersects on live range right side', () => {
  198. let moveSource = new Position( root, [ 0, 2, 1 ] );
  199. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 4 ] ) );
  200. let changes = {
  201. range: moveRange,
  202. sourcePosition: moveSource
  203. };
  204. doc.fire( 'change', 'move', changes, null );
  205. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  206. expect( live.end.path ).to.deep.equal( [ 0, 2, 1 ] );
  207. } );
  208. it( 'intersects on live range left side and live range new start is touching moved range end', () => {
  209. let moveSource = new Position( root, [ 0, 1, 0 ] );
  210. let moveRange = new Range( new Position( root, [ 0, 1 ] ), new Position( root, [ 0, 6 ] ) );
  211. let changes = {
  212. range: moveRange,
  213. sourcePosition: moveSource
  214. };
  215. doc.fire( 'change', 'move', changes, null );
  216. expect( live.start.path ).to.deep.equal( [ 0, 5 ] );
  217. expect( live.end.path ).to.deep.equal( [ 0, 7, 2 ] );
  218. } );
  219. it( 'intersects on live range right side and live range new end is touching moved range start', () => {
  220. live.end.offset = 12;
  221. let moveSource = new Position( root, [ 0, 2, 10 ] );
  222. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 5 ] ) );
  223. let changes = {
  224. range: moveRange,
  225. sourcePosition: moveSource
  226. };
  227. doc.fire( 'change', 'move', changes, null );
  228. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  229. expect( live.end.path ).to.deep.equal( [ 0, 3, 2 ] );
  230. } );
  231. it( 'is equal to live range', () => {
  232. live.end.path = [ 0, 1, 7 ];
  233. let moveSource = new Position( root, [ 0, 1, 4 ] );
  234. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 3 ] ) );
  235. let changes = {
  236. range: moveRange,
  237. sourcePosition: moveSource
  238. };
  239. doc.fire( 'change', 'move', changes, null );
  240. expect( live.start.path ).to.deep.equal( [ 0, 3, 0 ] );
  241. expect( live.end.path ).to.deep.equal( [ 0, 3, 3 ] );
  242. } );
  243. it( 'contains live range', () => {
  244. live.end.path = [ 0, 1, 7 ];
  245. let moveSource = new Position( root, [ 0, 1, 3 ] );
  246. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 9 ] ) );
  247. let changes = {
  248. range: moveRange,
  249. sourcePosition: moveSource
  250. };
  251. doc.fire( 'change', 'move', changes, null );
  252. expect( live.start.path ).to.deep.equal( [ 0, 3, 1 ] );
  253. expect( live.end.path ).to.deep.equal( [ 0, 3, 4 ] );
  254. } );
  255. it( 'is inside live range and points to live range', () => {
  256. live.end.path = [ 0, 1, 12 ];
  257. let moveSource = new Position( root, [ 0, 1, 6 ] );
  258. let moveRange = new Range( new Position( root, [ 0, 1, 8 ] ), new Position( root, [ 0, 1, 10 ] ) );
  259. let changes = {
  260. range: moveRange,
  261. sourcePosition: moveSource
  262. };
  263. doc.fire( 'change', 'move', changes, null );
  264. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  265. expect( live.end.path ).to.deep.equal( [ 0, 1, 12 ] );
  266. } );
  267. it( 'is intersecting with live range and points to live range', () => {
  268. live.end.path = [ 0, 1, 12 ];
  269. let moveSource = new Position( root, [ 0, 1, 2 ] );
  270. let moveRange = new Range( new Position( root, [ 0, 1, 5 ] ), new Position( root, [ 0, 1, 9 ] ) );
  271. let changes = {
  272. range: moveRange,
  273. sourcePosition: moveSource
  274. };
  275. doc.fire( 'change', 'move', changes, null );
  276. expect( live.start.path ).to.deep.equal( [ 0, 1, 2 ] );
  277. expect( live.end.path ).to.deep.equal( [ 0, 1, 12 ] );
  278. } );
  279. } );
  280. } );
  281. describe( 'should not get transformed if', () => {
  282. let otherRoot;
  283. before( () => {
  284. otherRoot = doc.createRoot( 'otherRoot' );
  285. } );
  286. let live, clone;
  287. beforeEach( () => {
  288. live = new LiveRange( new Position( root, [ 0, 1, 4 ] ), new Position( root, [ 0, 2, 2 ] ) );
  289. clone = Range.createFromRange( live );
  290. } );
  291. afterEach( () => {
  292. live.detach();
  293. } );
  294. describe( 'insertion', () => {
  295. it( 'is in the same parent as range start and after it', () => {
  296. let insertRange = new Range( new Position( root, [ 0, 1, 7 ] ), new Position( root, [ 0, 1, 9 ] ) );
  297. doc.fire( 'change', 'insert', { range: insertRange }, null );
  298. expect( live.isEqual( clone ) ).to.be.true;
  299. } );
  300. it( 'is in the same parent as range end and after it', () => {
  301. let insertRange = new Range( new Position( root, [ 0, 2, 7 ] ), new Position( root, [ 0, 2, 9 ] ) );
  302. doc.fire( 'change', 'insert', { range: insertRange }, null );
  303. expect( live.isEqual( clone ) ).to.be.true;
  304. } );
  305. it( 'is to a position after a node from range end path', () => {
  306. let insertRange = new Range( new Position( root, [ 3 ] ), new Position( root, [ 4 ] ) );
  307. doc.fire( 'change', 'insert', { range: insertRange }, null );
  308. expect( live.isEqual( clone ) ).to.be.true;
  309. } );
  310. it( 'is in different root', () => {
  311. let insertRange = new Range( new Position( otherRoot, [ 0, 0 ] ), new Position( otherRoot, [ 0, 2 ] ) );
  312. doc.fire( 'change', 'insert', { range: insertRange }, null );
  313. expect( live.isEqual( clone ) ).to.be.true;
  314. } );
  315. } );
  316. describe( 'range move', () => {
  317. it( 'is to the same parent as range start and after it', () => {
  318. let moveSource = new Position( root, [ 4 ] );
  319. let moveRange = new Range( new Position( root, [ 0, 1, 7 ] ), new Position( root, [ 0, 1, 9 ] ) );
  320. let changes = {
  321. range: moveRange,
  322. sourcePosition: moveSource
  323. };
  324. doc.fire( 'change', 'move', changes, null );
  325. expect( live.isEqual( clone ) ).to.be.true;
  326. } );
  327. it( 'is to the same parent as range end and after it', () => {
  328. let moveSource = new Position( root, [ 4 ] );
  329. let moveRange = new Range( new Position( root, [ 0, 2, 3 ] ), new Position( root, [ 0, 2, 5 ] ) );
  330. let changes = {
  331. range: moveRange,
  332. sourcePosition: moveSource
  333. };
  334. doc.fire( 'change', 'move', changes, null );
  335. expect( live.isEqual( clone ) ).to.be.true;
  336. } );
  337. it( 'is to a position after a node from range end path', () => {
  338. let moveSource = new Position( root, [ 4 ] );
  339. let moveRange = new Range( new Position( root, [ 0, 3 ] ), new Position( root, [ 0, 5 ] ) );
  340. let changes = {
  341. range: moveRange,
  342. sourcePosition: moveSource
  343. };
  344. doc.fire( 'change', 'move', changes, null );
  345. expect( live.isEqual( clone ) ).to.be.true;
  346. } );
  347. it( 'is from the same parent as range start and after it', () => {
  348. let moveSource = new Position( root, [ 0, 1, 6 ] );
  349. let moveRange = new Range( new Position( root, [ 4, 0 ] ), new Position( root, [ 4, 3 ] ) );
  350. let changes = {
  351. range: moveRange,
  352. sourcePosition: moveSource
  353. };
  354. doc.fire( 'change', 'move', changes, null );
  355. expect( live.isEqual( clone ) ).to.be.true;
  356. } );
  357. it( 'is from the same parent as range end and after it', () => {
  358. let moveSource = new Position( root, [ 0, 2, 4 ] );
  359. let moveRange = new Range( new Position( root, [ 4, 0 ] ), new Position( root, [ 4, 2 ] ) );
  360. let changes = {
  361. range: moveRange,
  362. sourcePosition: moveSource
  363. };
  364. doc.fire( 'change', 'move', changes, null );
  365. expect( live.isEqual( clone ) ).to.be.true;
  366. } );
  367. it( 'is from a position after a node from range end path', () => {
  368. let moveSource = new Position( root, [ 0, 3 ] );
  369. let moveRange = new Range( new Position( root, [ 5, 0 ] ), new Position( root, [ 5, 1 ] ) );
  370. let changes = {
  371. range: moveRange,
  372. sourcePosition: moveSource
  373. };
  374. doc.fire( 'change', 'move', changes, null );
  375. expect( live.isEqual( clone ) ).to.be.true;
  376. } );
  377. it( 'is to different root', () => {
  378. let moveSource = new Position( root, [ 2 ] );
  379. let moveRange = new Range( new Position( otherRoot, [ 0, 1, 0 ] ), new Position( otherRoot, [ 0, 1, 4 ] ) );
  380. let changes = {
  381. range: moveRange,
  382. sourcePosition: moveSource
  383. };
  384. doc.fire( 'change', 'move', changes, null );
  385. expect( live.isEqual( clone ) ).to.be.true;
  386. } );
  387. it( 'is from different root', () => {
  388. let moveSource = new Position( otherRoot, [ 0, 2, 0 ] );
  389. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 2 ] ) );
  390. let changes = {
  391. range: moveRange,
  392. sourcePosition: moveSource
  393. };
  394. doc.fire( 'change', 'move', changes, null );
  395. expect( live.isEqual( clone ) ).to.be.true;
  396. } );
  397. } );
  398. } );
  399. } );