liverange.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. /**
  2. * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* bender-tags: treemodel */
  6. 'use strict';
  7. const modules = bender.amd.require(
  8. 'treemodel/document',
  9. 'treemodel/element',
  10. 'treemodel/position',
  11. 'treemodel/liverange',
  12. 'treemodel/range',
  13. 'emittermixin'
  14. );
  15. describe( 'LiveRange', () => {
  16. let Document, Element, Position, LiveRange, Range, EmitterMixin;
  17. let doc, root, ul, p;
  18. before( () => {
  19. Document = modules[ 'treemodel/document' ];
  20. Element = modules[ 'treemodel/element' ];
  21. Position = modules[ 'treemodel/position' ];
  22. LiveRange = modules[ 'treemodel/liverange' ];
  23. Range = modules[ 'treemodel/range' ];
  24. EmitterMixin = modules.emittermixin;
  25. doc = new Document();
  26. root = doc.createRoot( 'root' );
  27. let lis = [
  28. new Element( 'li', [], 'aaaaaaaaaa' ),
  29. new Element( 'li', [], 'bbbbbbbbbb' ),
  30. new Element( 'li', [], 'cccccccccc' ),
  31. new Element( 'li', [], 'dddddddddd' ),
  32. new Element( 'li', [], 'eeeeeeeeee' ),
  33. new Element( 'li', [], 'ffffffffff' ),
  34. new Element( 'li', [], 'gggggggggg' ),
  35. new Element( 'li', [], 'hhhhhhhhhh' )
  36. ];
  37. ul = new Element( 'ul', [], lis );
  38. p = new Element( 'p', [], 'qwertyuiop' );
  39. root.insertChildren( 0, [ ul, p, 'xyzxyz' ] );
  40. } );
  41. it( 'should be an instance of Range', () => {
  42. let live = new LiveRange( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) );
  43. live.detach();
  44. expect( live ).to.be.instanceof( Range );
  45. } );
  46. it( 'should return instance of Range when cloned', () => {
  47. let live = new LiveRange( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) );
  48. let clone = live.clone();
  49. expect( clone ).not.to.be.instanceof( LiveRange );
  50. live.detach();
  51. } );
  52. it( 'should return instance of LiveRange when cloned with flag set to true', () => {
  53. let live = new LiveRange( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) );
  54. let clone = live.clone( true );
  55. expect( clone ).to.be.instanceof( LiveRange );
  56. live.detach();
  57. clone.detach();
  58. } );
  59. it( 'should listen to a change event of the document that owns this range', () => {
  60. sinon.spy( LiveRange.prototype, 'listenTo' );
  61. let live = new LiveRange( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) );
  62. live.detach();
  63. expect( live.listenTo.calledWith( doc, 'change' ) ).to.be.true;
  64. LiveRange.prototype.listenTo.restore();
  65. } );
  66. it( 'should stop listening when detached', () => {
  67. sinon.spy( LiveRange.prototype, 'stopListening' );
  68. let live = new LiveRange( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) );
  69. live.detach();
  70. expect( live.stopListening.called ).to.be.true;
  71. LiveRange.prototype.stopListening.restore();
  72. } );
  73. it( 'createFromElement should return LiveRange', () => {
  74. let range = LiveRange.createFromElement( p );
  75. expect( range ).to.be.instanceof( LiveRange );
  76. range.detach();
  77. } );
  78. it( 'createFromParentsAndOffsets should return LiveRange', () => {
  79. let range = LiveRange.createFromParentsAndOffsets( root, 0, p, 2 );
  80. expect( range ).to.be.instanceof( LiveRange );
  81. range.detach();
  82. } );
  83. it( 'createFromPositionAndShift should return LiveRange', () => {
  84. let range = LiveRange.createFromPositionAndShift( new Position( root, [ 0, 1 ] ), 4 );
  85. expect( range ).to.be.instanceof( LiveRange );
  86. range.detach();
  87. } );
  88. // Examples may be weird when you compare them with the tree structure generated at the beginning of tests.
  89. // Since change event is fired _after_ operation is executed on tree model, you have to imagine that generated
  90. // structure is representing what is _after_ operation is executed. So live LiveRange properties are describing
  91. // virtual tree that is not existing anymore and event ranges are operating on the tree generated above.
  92. describe( 'should get transformed if', () => {
  93. let live;
  94. beforeEach( () => {
  95. live = new LiveRange( new Position( root, [ 0, 1, 4 ] ), new Position( root, [ 0, 2, 2 ] ) );
  96. } );
  97. afterEach( () => {
  98. live.detach();
  99. } );
  100. describe( 'insertion', () => {
  101. it( 'is in the same parent as range start and before it', () => {
  102. let insertRange = new Range( new Position( root, [ 0, 1, 0 ] ), new Position( root, [ 0, 1, 4 ] ) );
  103. doc.fire( 'change', 'insert', { range: insertRange }, null );
  104. expect( live.start.path ).to.deep.equal( [ 0, 1, 8 ] );
  105. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  106. } );
  107. it( 'is in the same parent as range end and before it', () => {
  108. let insertRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 3 ] ) );
  109. doc.fire( 'change', 'insert', { range: insertRange }, null );
  110. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  111. expect( live.end.path ).to.deep.equal( [ 0, 2, 5 ] );
  112. } );
  113. it( 'is at a position before a node from range start path', () => {
  114. let insertRange = new Range( new Position( root, [ 0, 0 ] ), new Position( root, [ 0, 2 ] ) );
  115. doc.fire( 'change', 'insert', { range: insertRange }, null );
  116. expect( live.start.path ).to.deep.equal( [ 0, 3, 4 ] );
  117. expect( live.end.path ).to.deep.equal( [ 0, 4, 2 ] );
  118. } );
  119. it( 'is at a position before a node from range end path', () => {
  120. let insertRange = new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 3 ] ) );
  121. doc.fire( 'change', 'insert', { range: insertRange }, null );
  122. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  123. expect( live.end.path ).to.deep.equal( [ 0, 3, 2 ] );
  124. } );
  125. } );
  126. describe( 'range move', () => {
  127. it( 'is to the same parent as range start and before it', () => {
  128. let moveSource = new Position( root, [ 2 ] );
  129. let moveRange = new Range( new Position( root, [ 0, 1, 0 ] ), new Position( root, [ 0, 1, 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, 8 ] );
  136. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  137. } );
  138. it( 'is to the same parent as range end and before it', () => {
  139. let moveSource = new Position( root, [ 2 ] );
  140. let moveRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 4 ] ) );
  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, 1, 4 ] );
  147. expect( live.end.path ).to.deep.equal( [ 0, 2, 6 ] );
  148. } );
  149. it( 'is to a position before a node from range start path', () => {
  150. let moveSource = new Position( root, [ 2 ] );
  151. let moveRange = new Range( new Position( root, [ 0, 0 ] ), new Position( root, [ 0, 2 ] ) );
  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, 3, 4 ] );
  158. expect( live.end.path ).to.deep.equal( [ 0, 4, 2 ] );
  159. } );
  160. it( 'is to a position before a node from range end path', () => {
  161. let moveSource = new Position( root, [ 2 ] );
  162. let moveRange = new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 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, 4 ] );
  169. expect( live.end.path ).to.deep.equal( [ 0, 3, 2 ] );
  170. } );
  171. it( 'is from the same parent as range start and before it', () => {
  172. let moveSource = new Position( root, [ 0, 1, 0 ] );
  173. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 3 ] ) );
  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, 1 ] );
  180. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  181. } );
  182. it( 'is from the same parent as range end and before it', () => {
  183. let moveSource = new Position( root, [ 0, 2, 0 ] );
  184. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 2 ] ) );
  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, 1, 4 ] );
  191. expect( live.end.path ).to.deep.equal( [ 0, 2, 0 ] );
  192. } );
  193. it( 'is from a position before a node from range start path', () => {
  194. let moveSource = new Position( root, [ 0, 0 ] );
  195. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 1 ] ) );
  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, 0, 4 ] );
  202. expect( live.end.path ).to.deep.equal( [ 0, 1, 2 ] );
  203. } );
  204. it( 'intersects on live range left side', () => {
  205. let moveSource = new Position( root, [ 0, 1, 2 ] );
  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, 2 ] );
  213. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  214. } );
  215. it( 'intersects on live range right side', () => {
  216. let moveSource = new Position( root, [ 0, 2, 1 ] );
  217. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 4 ] ) );
  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, 1, 4 ] );
  224. expect( live.end.path ).to.deep.equal( [ 0, 2, 1 ] );
  225. } );
  226. it( 'intersects on live range left side and live range new start is touching moved range end', () => {
  227. let moveSource = new Position( root, [ 0, 1, 0 ] );
  228. let moveRange = new Range( new Position( root, [ 0, 1 ] ), new Position( root, [ 0, 6 ] ) );
  229. let changes = {
  230. range: moveRange,
  231. sourcePosition: moveSource
  232. };
  233. doc.fire( 'change', 'move', changes, null );
  234. expect( live.start.path ).to.deep.equal( [ 0, 5 ] );
  235. expect( live.end.path ).to.deep.equal( [ 0, 7, 2 ] );
  236. } );
  237. it( 'intersects on live range right side and live range new end is touching moved range start', () => {
  238. live.end.offset = 12;
  239. let moveSource = new Position( root, [ 0, 2, 10 ] );
  240. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 5 ] ) );
  241. let changes = {
  242. range: moveRange,
  243. sourcePosition: moveSource
  244. };
  245. doc.fire( 'change', 'move', changes, null );
  246. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  247. expect( live.end.path ).to.deep.equal( [ 0, 3, 2 ] );
  248. } );
  249. it( 'is equal to live range', () => {
  250. live.end.path = [ 0, 1, 7 ];
  251. let moveSource = new Position( root, [ 0, 1, 4 ] );
  252. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 3 ] ) );
  253. let changes = {
  254. range: moveRange,
  255. sourcePosition: moveSource
  256. };
  257. doc.fire( 'change', 'move', changes, null );
  258. expect( live.start.path ).to.deep.equal( [ 0, 3, 0 ] );
  259. expect( live.end.path ).to.deep.equal( [ 0, 3, 3 ] );
  260. } );
  261. it( 'contains live range', () => {
  262. live.end.path = [ 0, 1, 7 ];
  263. let moveSource = new Position( root, [ 0, 1, 3 ] );
  264. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 9 ] ) );
  265. let changes = {
  266. range: moveRange,
  267. sourcePosition: moveSource
  268. };
  269. doc.fire( 'change', 'move', changes, null );
  270. expect( live.start.path ).to.deep.equal( [ 0, 3, 1 ] );
  271. expect( live.end.path ).to.deep.equal( [ 0, 3, 4 ] );
  272. } );
  273. } );
  274. } );
  275. describe( 'should not get transformed if', () => {
  276. let otherRoot;
  277. before( () => {
  278. otherRoot = doc.createRoot( 'otherRoot' );
  279. } );
  280. let live, clone;
  281. beforeEach( () => {
  282. live = new LiveRange( new Position( root, [ 0, 1, 4 ] ), new Position( root, [ 0, 2, 2 ] ) );
  283. clone = live.clone();
  284. } );
  285. afterEach( () => {
  286. live.detach();
  287. } );
  288. describe( 'insertion', () => {
  289. // Technically range will be expanded but the boundaries properties will stay the same.
  290. // Start won't change because insertion is after it.
  291. // End won't change because it is in different node.
  292. it( 'is in the same parent as range start and after it', () => {
  293. let insertRange = new Range( new Position( root, [ 0, 1, 7 ] ), new Position( root, [ 0, 1, 9 ] ) );
  294. doc.fire( 'change', 'insert', { range: insertRange }, null );
  295. expect( live.isEqual( clone ) ).to.be.true;
  296. } );
  297. it( 'is in the same parent as range end and after it', () => {
  298. let insertRange = new Range( new Position( root, [ 0, 2, 7 ] ), new Position( root, [ 0, 2, 9 ] ) );
  299. doc.fire( 'change', 'insert', { range: insertRange }, null );
  300. expect( live.isEqual( clone ) ).to.be.true;
  301. } );
  302. it( 'is to a position after a node from range end path', () => {
  303. let insertRange = new Range( new Position( root, [ 3 ] ), new Position( root, [ 4 ] ) );
  304. doc.fire( 'change', 'insert', { range: insertRange }, null );
  305. expect( live.isEqual( clone ) ).to.be.true;
  306. } );
  307. it( 'is in different root', () => {
  308. let insertRange = new Range( new Position( otherRoot, [ 0, 0 ] ), new Position( otherRoot, [ 0, 2 ] ) );
  309. doc.fire( 'change', 'insert', { range: insertRange }, null );
  310. expect( live.isEqual( clone ) ).to.be.true;
  311. } );
  312. } );
  313. describe( 'range move', () => {
  314. // Technically range will be expanded but the boundaries properties will stay the same.
  315. // Start won't change because insertion is after it.
  316. // End won't change because it is in different node.
  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 before 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. // Technically range will be shrunk but the boundaries properties will stay the same.
  348. // Start won't change because deletion is after it.
  349. // End won't change because it is in different node.
  350. it( 'is from the same parent as range start and after it', () => {
  351. let moveSource = new Position( root, [ 0, 1, 6 ] );
  352. let moveRange = new Range( new Position( root, [ 4, 0 ] ), new Position( root, [ 4, 3 ] ) );
  353. let changes = {
  354. range: moveRange,
  355. sourcePosition: moveSource
  356. };
  357. doc.fire( 'change', 'move', changes, null );
  358. expect( live.isEqual( clone ) ).to.be.true;
  359. } );
  360. it( 'is from the same parent as range end and after it', () => {
  361. let moveSource = new Position( root, [ 0, 2, 4 ] );
  362. let moveRange = new Range( new Position( root, [ 4, 0 ] ), new Position( root, [ 4, 2 ] ) );
  363. let changes = {
  364. range: moveRange,
  365. sourcePosition: moveSource
  366. };
  367. doc.fire( 'change', 'move', changes, null );
  368. expect( live.isEqual( clone ) ).to.be.true;
  369. } );
  370. it( 'is from a position after a node from range end path', () => {
  371. let moveSource = new Position( root, [ 0, 3 ] );
  372. let moveRange = new Range( new Position( root, [ 5, 0 ] ), new Position( root, [ 5, 1 ] ) );
  373. let changes = {
  374. range: moveRange,
  375. sourcePosition: moveSource
  376. };
  377. doc.fire( 'change', 'move', changes, null );
  378. expect( live.isEqual( clone ) ).to.be.true;
  379. } );
  380. it( 'is to different root', () => {
  381. let moveSource = new Position( root, [ 2 ] );
  382. let moveRange = new Range( new Position( otherRoot, [ 0, 1, 0 ] ), new Position( otherRoot, [ 0, 1, 4 ] ) );
  383. let changes = {
  384. range: moveRange,
  385. sourcePosition: moveSource
  386. };
  387. doc.fire( 'change', 'move', changes, null );
  388. expect( live.isEqual( clone ) ).to.be.true;
  389. } );
  390. it( 'is from different root', () => {
  391. let moveSource = new Position( otherRoot, [ 0, 2, 0 ] );
  392. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 2 ] ) );
  393. let changes = {
  394. range: moveRange,
  395. sourcePosition: moveSource
  396. };
  397. doc.fire( 'change', 'move', changes, null );
  398. expect( live.isEqual( clone ) ).to.be.true;
  399. } );
  400. } );
  401. } );
  402. } );