8
0

liverange.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  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/core/treemodel/document.js';
  8. import Element from '/ckeditor5/core/treemodel/element.js';
  9. import Position from '/ckeditor5/core/treemodel/position.js';
  10. import LiveRange from '/ckeditor5/core/treemodel/liverange.js';
  11. import Range from '/ckeditor5/core/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, [ 2 ] );
  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. } );
  256. } );
  257. describe( 'should not get transformed if', () => {
  258. let otherRoot;
  259. before( () => {
  260. otherRoot = doc.createRoot( 'otherRoot' );
  261. } );
  262. let live, clone;
  263. beforeEach( () => {
  264. live = new LiveRange( new Position( root, [ 0, 1, 4 ] ), new Position( root, [ 0, 2, 2 ] ) );
  265. clone = Range.createFromRange( live );
  266. } );
  267. afterEach( () => {
  268. live.detach();
  269. } );
  270. describe( 'insertion', () => {
  271. // Technically range will be expanded but the boundaries properties will stay the same.
  272. // Start won't change because insertion is after it.
  273. // End won't change because it is in different node.
  274. it( 'is in the same parent as range start and after it', () => {
  275. let insertRange = new Range( new Position( root, [ 0, 1, 7 ] ), new Position( root, [ 0, 1, 9 ] ) );
  276. doc.fire( 'change', 'insert', { range: insertRange }, null );
  277. expect( live.isEqual( clone ) ).to.be.true;
  278. } );
  279. it( 'is in the same parent as range end and after it', () => {
  280. let insertRange = new Range( new Position( root, [ 0, 2, 7 ] ), new Position( root, [ 0, 2, 9 ] ) );
  281. doc.fire( 'change', 'insert', { range: insertRange }, null );
  282. expect( live.isEqual( clone ) ).to.be.true;
  283. } );
  284. it( 'is to a position after a node from range end path', () => {
  285. let insertRange = new Range( new Position( root, [ 3 ] ), new Position( root, [ 4 ] ) );
  286. doc.fire( 'change', 'insert', { range: insertRange }, null );
  287. expect( live.isEqual( clone ) ).to.be.true;
  288. } );
  289. it( 'is in different root', () => {
  290. let insertRange = new Range( new Position( otherRoot, [ 0, 0 ] ), new Position( otherRoot, [ 0, 2 ] ) );
  291. doc.fire( 'change', 'insert', { range: insertRange }, null );
  292. expect( live.isEqual( clone ) ).to.be.true;
  293. } );
  294. } );
  295. describe( 'range move', () => {
  296. // Technically range will be expanded but the boundaries properties will stay the same.
  297. // Start won't change because insertion is after it.
  298. // End won't change because it is in different node.
  299. it( 'is to the same parent as range start and after it', () => {
  300. let moveSource = new Position( root, [ 4 ] );
  301. let moveRange = new Range( new Position( root, [ 0, 1, 7 ] ), new Position( root, [ 0, 1, 9 ] ) );
  302. let changes = {
  303. range: moveRange,
  304. sourcePosition: moveSource
  305. };
  306. doc.fire( 'change', 'move', changes, null );
  307. expect( live.isEqual( clone ) ).to.be.true;
  308. } );
  309. it( 'is to the same parent as range end and before it', () => {
  310. let moveSource = new Position( root, [ 4 ] );
  311. let moveRange = new Range( new Position( root, [ 0, 2, 3 ] ), new Position( root, [ 0, 2, 5 ] ) );
  312. let changes = {
  313. range: moveRange,
  314. sourcePosition: moveSource
  315. };
  316. doc.fire( 'change', 'move', changes, null );
  317. expect( live.isEqual( clone ) ).to.be.true;
  318. } );
  319. it( 'is to a position after a node from range end path', () => {
  320. let moveSource = new Position( root, [ 4 ] );
  321. let moveRange = new Range( new Position( root, [ 0, 3 ] ), new Position( root, [ 0, 5 ] ) );
  322. let changes = {
  323. range: moveRange,
  324. sourcePosition: moveSource
  325. };
  326. doc.fire( 'change', 'move', changes, null );
  327. expect( live.isEqual( clone ) ).to.be.true;
  328. } );
  329. // Technically range will be shrunk but the boundaries properties will stay the same.
  330. // Start won't change because deletion is after it.
  331. // End won't change because it is in different node.
  332. it( 'is from the same parent as range start and after it', () => {
  333. let moveSource = new Position( root, [ 0, 1, 6 ] );
  334. let moveRange = new Range( new Position( root, [ 4, 0 ] ), new Position( root, [ 4, 3 ] ) );
  335. let changes = {
  336. range: moveRange,
  337. sourcePosition: moveSource
  338. };
  339. doc.fire( 'change', 'move', changes, null );
  340. expect( live.isEqual( clone ) ).to.be.true;
  341. } );
  342. it( 'is from the same parent as range end and after it', () => {
  343. let moveSource = new Position( root, [ 0, 2, 4 ] );
  344. let moveRange = new Range( new Position( root, [ 4, 0 ] ), new Position( root, [ 4, 2 ] ) );
  345. let changes = {
  346. range: moveRange,
  347. sourcePosition: moveSource
  348. };
  349. doc.fire( 'change', 'move', changes, null );
  350. expect( live.isEqual( clone ) ).to.be.true;
  351. } );
  352. it( 'is from a position after a node from range end path', () => {
  353. let moveSource = new Position( root, [ 0, 3 ] );
  354. let moveRange = new Range( new Position( root, [ 5, 0 ] ), new Position( root, [ 5, 1 ] ) );
  355. let changes = {
  356. range: moveRange,
  357. sourcePosition: moveSource
  358. };
  359. doc.fire( 'change', 'move', changes, null );
  360. expect( live.isEqual( clone ) ).to.be.true;
  361. } );
  362. it( 'is to different root', () => {
  363. let moveSource = new Position( root, [ 2 ] );
  364. let moveRange = new Range( new Position( otherRoot, [ 0, 1, 0 ] ), new Position( otherRoot, [ 0, 1, 4 ] ) );
  365. let changes = {
  366. range: moveRange,
  367. sourcePosition: moveSource
  368. };
  369. doc.fire( 'change', 'move', changes, null );
  370. expect( live.isEqual( clone ) ).to.be.true;
  371. } );
  372. it( 'is from different root', () => {
  373. let moveSource = new Position( otherRoot, [ 0, 2, 0 ] );
  374. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 2 ] ) );
  375. let changes = {
  376. range: moveRange,
  377. sourcePosition: moveSource
  378. };
  379. doc.fire( 'change', 'move', changes, null );
  380. expect( live.isEqual( clone ) ).to.be.true;
  381. } );
  382. } );
  383. } );
  384. } );