8
0

liverange.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  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', 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', 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', 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', 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. doc.fire( 'change', 'move', moveRange, moveSource, null );
  131. expect( live.start.path ).to.deep.equal( [ 0, 1, 8 ] );
  132. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  133. } );
  134. it( 'is to the same parent as range end and before it', () => {
  135. let moveSource = new Position( root, [ 2 ] );
  136. let moveRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 4 ] ) );
  137. doc.fire( 'change', 'move', moveRange, moveSource, null );
  138. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  139. expect( live.end.path ).to.deep.equal( [ 0, 2, 6 ] );
  140. } );
  141. it( 'is to a position before a node from range start path', () => {
  142. let moveSource = new Position( root, [ 2 ] );
  143. let moveRange = new Range( new Position( root, [ 0, 0 ] ), new Position( root, [ 0, 2 ] ) );
  144. doc.fire( 'change', 'move', moveRange, moveSource, null );
  145. expect( live.start.path ).to.deep.equal( [ 0, 3, 4 ] );
  146. expect( live.end.path ).to.deep.equal( [ 0, 4, 2 ] );
  147. } );
  148. it( 'is to a position before a node from range end path', () => {
  149. let moveSource = new Position( root, [ 2 ] );
  150. let moveRange = new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 3 ] ) );
  151. doc.fire( 'change', 'move', moveRange, moveSource, null );
  152. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  153. expect( live.end.path ).to.deep.equal( [ 0, 3, 2 ] );
  154. } );
  155. it( 'is from the same parent as range start and before it', () => {
  156. let moveSource = new Position( root, [ 0, 1, 0 ] );
  157. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 3 ] ) );
  158. doc.fire( 'change', 'move', moveRange, moveSource, null );
  159. expect( live.start.path ).to.deep.equal( [ 0, 1, 1 ] );
  160. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  161. } );
  162. it( 'is from the same parent as range end and before it', () => {
  163. let moveSource = new Position( root, [ 0, 2, 0 ] );
  164. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 2 ] ) );
  165. doc.fire( 'change', 'move', moveRange, moveSource, null );
  166. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  167. expect( live.end.path ).to.deep.equal( [ 0, 2, 0 ] );
  168. } );
  169. it( 'is from a position before a node from range start path', () => {
  170. let moveSource = new Position( root, [ 0, 0 ] );
  171. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 1 ] ) );
  172. doc.fire( 'change', 'move', moveRange, moveSource, null );
  173. expect( live.start.path ).to.deep.equal( [ 0, 0, 4 ] );
  174. expect( live.end.path ).to.deep.equal( [ 0, 1, 2 ] );
  175. } );
  176. it( 'intersects on live range left side', () => {
  177. let moveSource = new Position( root, [ 0, 1, 2 ] );
  178. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 4 ] ) );
  179. doc.fire( 'change', 'move', moveRange, moveSource, null );
  180. expect( live.start.path ).to.deep.equal( [ 0, 1, 2 ] );
  181. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  182. } );
  183. it( 'intersects on live range right side', () => {
  184. let moveSource = new Position( root, [ 0, 2, 1 ] );
  185. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 4 ] ) );
  186. doc.fire( 'change', 'move', moveRange, moveSource, null );
  187. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  188. expect( live.end.path ).to.deep.equal( [ 0, 2, 1 ] );
  189. } );
  190. it( 'intersects on live range left side and live range new start is touching moved range end', () => {
  191. let moveSource = new Position( root, [ 0, 1, 0 ] );
  192. let moveRange = new Range( new Position( root, [ 0, 1 ] ), new Position( root, [ 0, 6 ] ) );
  193. doc.fire( 'change', 'move', moveRange, moveSource, null );
  194. expect( live.start.path ).to.deep.equal( [ 0, 5 ] );
  195. expect( live.end.path ).to.deep.equal( [ 0, 7, 2 ] );
  196. } );
  197. it( 'intersects on live range right side and live range new end is touching moved range start', () => {
  198. live.end.offset = 12;
  199. let moveSource = new Position( root, [ 0, 2, 10 ] );
  200. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 5 ] ) );
  201. doc.fire( 'change', 'move', moveRange, moveSource, null );
  202. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  203. expect( live.end.path ).to.deep.equal( [ 0, 3, 2 ] );
  204. } );
  205. it( 'is equal to live range', () => {
  206. live.end.path = [ 0, 1, 7 ];
  207. let moveSource = new Position( root, [ 0, 1, 4 ] );
  208. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 3 ] ) );
  209. doc.fire( 'change', 'move', moveRange, moveSource, null );
  210. expect( live.start.path ).to.deep.equal( [ 0, 3, 0 ] );
  211. expect( live.end.path ).to.deep.equal( [ 0, 3, 3 ] );
  212. } );
  213. it( 'contains live range', () => {
  214. live.end.path = [ 0, 1, 7 ];
  215. let moveSource = new Position( root, [ 0, 1, 3 ] );
  216. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 9 ] ) );
  217. doc.fire( 'change', 'move', moveRange, moveSource, null );
  218. expect( live.start.path ).to.deep.equal( [ 0, 3, 1 ] );
  219. expect( live.end.path ).to.deep.equal( [ 0, 3, 4 ] );
  220. } );
  221. } );
  222. } );
  223. describe( 'should not get transformed if', () => {
  224. let otherRoot;
  225. before( () => {
  226. otherRoot = doc.createRoot( 'otherRoot' );
  227. } );
  228. let live, clone;
  229. beforeEach( () => {
  230. live = new LiveRange( new Position( root, [ 0, 1, 4 ] ), new Position( root, [ 0, 2, 2 ] ) );
  231. clone = live.clone();
  232. } );
  233. afterEach( () => {
  234. live.detach();
  235. } );
  236. describe( 'insertion', () => {
  237. // Technically range will be expanded but the boundaries properties will stay the same.
  238. // Start won't change because insertion is after it.
  239. // End won't change because it is in different node.
  240. it( 'is in the same parent as range start and after it', () => {
  241. let insertRange = new Range( new Position( root, [ 0, 1, 7 ] ), new Position( root, [ 0, 1, 9 ] ) );
  242. doc.fire( 'change', 'insert', insertRange, null );
  243. expect( live.isEqual( clone ) ).to.be.true;
  244. } );
  245. it( 'is in the same parent as range end and after it', () => {
  246. let insertRange = new Range( new Position( root, [ 0, 2, 7 ] ), new Position( root, [ 0, 2, 9 ] ) );
  247. doc.fire( 'change', 'insert', insertRange, null );
  248. expect( live.isEqual( clone ) ).to.be.true;
  249. } );
  250. it( 'is to a position after a node from range end path', () => {
  251. let insertRange = new Range( new Position( root, [ 3 ] ), new Position( root, [ 4 ] ) );
  252. doc.fire( 'change', 'insert', insertRange, null );
  253. expect( live.isEqual( clone ) ).to.be.true;
  254. } );
  255. it( 'is in different root', () => {
  256. let insertRange = new Range( new Position( otherRoot, [ 0, 0 ] ), new Position( otherRoot, [ 0, 2 ] ) );
  257. doc.fire( 'change', 'insert', insertRange, null );
  258. expect( live.isEqual( clone ) ).to.be.true;
  259. } );
  260. } );
  261. describe( 'range move', () => {
  262. // Technically range will be expanded but the boundaries properties will stay the same.
  263. // Start won't change because insertion is after it.
  264. // End won't change because it is in different node.
  265. it( 'is to the same parent as range start and after it', () => {
  266. let moveSource = new Position( root, [ 4 ] );
  267. let moveRange = new Range( new Position( root, [ 0, 1, 7 ] ), new Position( root, [ 0, 1, 9 ] ) );
  268. doc.fire( 'change', 'move', moveRange, moveSource, null );
  269. expect( live.isEqual( clone ) ).to.be.true;
  270. } );
  271. it( 'is to the same parent as range end and before it', () => {
  272. let moveSource = new Position( root, [ 4 ] );
  273. let moveRange = new Range( new Position( root, [ 0, 2, 3 ] ), new Position( root, [ 0, 2, 5 ] ) );
  274. doc.fire( 'change', 'move', moveRange, moveSource, null );
  275. expect( live.isEqual( clone ) ).to.be.true;
  276. } );
  277. it( 'is to a position after a node from range end path', () => {
  278. let moveSource = new Position( root, [ 4 ] );
  279. let moveRange = new Range( new Position( root, [ 0, 3 ] ), new Position( root, [ 0, 5 ] ) );
  280. doc.fire( 'change', 'move', moveRange, moveSource, null );
  281. expect( live.isEqual( clone ) ).to.be.true;
  282. } );
  283. // Technically range will be shrunk but the boundaries properties will stay the same.
  284. // Start won't change because deletion is after it.
  285. // End won't change because it is in different node.
  286. it( 'is from the same parent as range start and after it', () => {
  287. let moveSource = new Position( root, [ 0, 1, 6 ] );
  288. let moveRange = new Range( new Position( root, [ 4, 0 ] ), new Position( root, [ 4, 3 ] ) );
  289. doc.fire( 'change', 'move', moveRange, moveSource, null );
  290. expect( live.isEqual( clone ) ).to.be.true;
  291. } );
  292. it( 'is from the same parent as range end and after it', () => {
  293. let moveSource = new Position( root, [ 0, 2, 4 ] );
  294. let moveRange = new Range( new Position( root, [ 4, 0 ] ), new Position( root, [ 4, 2 ] ) );
  295. doc.fire( 'change', 'move', moveRange, moveSource, null );
  296. expect( live.isEqual( clone ) ).to.be.true;
  297. } );
  298. it( 'is from a position after a node from range end path', () => {
  299. let moveSource = new Position( root, [ 0, 3 ] );
  300. let moveRange = new Range( new Position( root, [ 5, 0 ] ), new Position( root, [ 5, 1 ] ) );
  301. doc.fire( 'change', 'move', moveRange, moveSource, null );
  302. expect( live.isEqual( clone ) ).to.be.true;
  303. } );
  304. it( 'is to different root', () => {
  305. it( 'is to the same parent as range start and before it', () => {
  306. let moveSource = new Position( root, [ 2 ] );
  307. let moveRange = new Range( new Position( otherRoot, [ 0, 1, 0 ] ), new Position( otherRoot, [ 0, 1, 4 ] ) );
  308. doc.fire( 'change', 'move', moveRange, moveSource, null );
  309. expect( live.isEqual( clone ) ).to.be.true;
  310. } );
  311. } );
  312. it( 'is from different root', () => {
  313. let moveSource = new Position( otherRoot, [ 0, 2, 0 ] );
  314. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 2 ] ) );
  315. doc.fire( 'change', 'move', moveRange, moveSource, null );
  316. expect( live.isEqual( clone ) ).to.be.true;
  317. } );
  318. } );
  319. } );
  320. } );