liverange.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  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 listen to a change event of the document that owns this range', () => {
  47. sinon.spy( LiveRange.prototype, 'listenTo' );
  48. let live = new LiveRange( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) );
  49. live.detach();
  50. expect( live.listenTo.calledWith( doc, 'change' ) ).to.be.true;
  51. LiveRange.prototype.listenTo.restore();
  52. } );
  53. it( 'should stop listening when detached', () => {
  54. sinon.spy( LiveRange.prototype, 'stopListening' );
  55. let live = new LiveRange( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) );
  56. live.detach();
  57. expect( live.stopListening.called ).to.be.true;
  58. LiveRange.prototype.stopListening.restore();
  59. } );
  60. it( 'createFromElement should return LiveRange', () => {
  61. let range = LiveRange.createFromElement( p );
  62. expect( range ).to.be.instanceof( LiveRange );
  63. range.detach();
  64. } );
  65. it( 'createFromParentsAndOffsets should return LiveRange', () => {
  66. let range = LiveRange.createFromParentsAndOffsets( root, 0, p, 2 );
  67. expect( range ).to.be.instanceof( LiveRange );
  68. range.detach();
  69. } );
  70. it( 'createFromPositionAndShift should return LiveRange', () => {
  71. let range = LiveRange.createFromPositionAndShift( new Position( root, [ 0, 1 ] ), 4 );
  72. expect( range ).to.be.instanceof( LiveRange );
  73. range.detach();
  74. } );
  75. it( 'createFromRange should return LiveRange', () => {
  76. let range = LiveRange.createFromRange( new Range( new Position( root, [ 0 ] ), new Position( root, [ 1 ] ) ) );
  77. expect( range ).to.be.instanceof( LiveRange );
  78. range.detach();
  79. } );
  80. // Examples may seem weird when you compare them with the tree structure generated at the beginning of tests.
  81. // Since change event is fired _after_ operation is executed on tree model, you have to imagine that generated
  82. // structure is representing what is _after_ operation is executed. So live LiveRange properties are describing
  83. // virtual tree that is not existing anymore and event ranges are operating on the tree generated above.
  84. describe( 'should get transformed if', () => {
  85. let live;
  86. beforeEach( () => {
  87. live = new LiveRange( new Position( root, [ 0, 1, 4 ] ), new Position( root, [ 0, 2, 2 ] ) );
  88. } );
  89. afterEach( () => {
  90. live.detach();
  91. } );
  92. describe( 'insertion', () => {
  93. it( 'is in the same parent as range start and before it', () => {
  94. let insertRange = new Range( new Position( root, [ 0, 1, 0 ] ), new Position( root, [ 0, 1, 4 ] ) );
  95. doc.fire( 'change', 'insert', { range: insertRange }, null );
  96. expect( live.start.path ).to.deep.equal( [ 0, 1, 8 ] );
  97. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  98. } );
  99. it( 'is in the same parent as range end and before it', () => {
  100. let insertRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 3 ] ) );
  101. doc.fire( 'change', 'insert', { range: insertRange }, null );
  102. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  103. expect( live.end.path ).to.deep.equal( [ 0, 2, 5 ] );
  104. } );
  105. it( 'is at a position before a node from range start path', () => {
  106. let insertRange = new Range( new Position( root, [ 0, 0 ] ), new Position( root, [ 0, 2 ] ) );
  107. doc.fire( 'change', 'insert', { range: insertRange }, null );
  108. expect( live.start.path ).to.deep.equal( [ 0, 3, 4 ] );
  109. expect( live.end.path ).to.deep.equal( [ 0, 4, 2 ] );
  110. } );
  111. it( 'is at a position before a node from range end path', () => {
  112. let insertRange = new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 3 ] ) );
  113. doc.fire( 'change', 'insert', { range: insertRange }, null );
  114. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  115. expect( live.end.path ).to.deep.equal( [ 0, 3, 2 ] );
  116. } );
  117. } );
  118. describe( 'range move', () => {
  119. it( 'is to the same parent as range start and before it', () => {
  120. let moveSource = new Position( root, [ 2 ] );
  121. let moveRange = new Range( new Position( root, [ 0, 1, 0 ] ), new Position( root, [ 0, 1, 4 ] ) );
  122. let changes = {
  123. range: moveRange,
  124. sourcePosition: moveSource
  125. };
  126. doc.fire( 'change', 'move', changes, null );
  127. expect( live.start.path ).to.deep.equal( [ 0, 1, 8 ] );
  128. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  129. } );
  130. it( 'is to the same parent as range end and before it', () => {
  131. let moveSource = new Position( root, [ 2 ] );
  132. let moveRange = new Range( new Position( root, [ 0, 2, 0 ] ), new Position( root, [ 0, 2, 4 ] ) );
  133. let changes = {
  134. range: moveRange,
  135. sourcePosition: moveSource
  136. };
  137. doc.fire( 'change', 'move', changes, 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. let changes = {
  145. range: moveRange,
  146. sourcePosition: moveSource
  147. };
  148. doc.fire( 'change', 'move', changes, null );
  149. expect( live.start.path ).to.deep.equal( [ 0, 3, 4 ] );
  150. expect( live.end.path ).to.deep.equal( [ 0, 4, 2 ] );
  151. } );
  152. it( 'is to a position before a node from range end path', () => {
  153. let moveSource = new Position( root, [ 2 ] );
  154. let moveRange = new Range( new Position( root, [ 0, 2 ] ), new Position( root, [ 0, 3 ] ) );
  155. let changes = {
  156. range: moveRange,
  157. sourcePosition: moveSource
  158. };
  159. doc.fire( 'change', 'move', changes, null );
  160. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  161. expect( live.end.path ).to.deep.equal( [ 0, 3, 2 ] );
  162. } );
  163. it( 'is from the same parent as range start and before it', () => {
  164. let moveSource = new Position( root, [ 0, 1, 0 ] );
  165. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 3 ] ) );
  166. let changes = {
  167. range: moveRange,
  168. sourcePosition: moveSource
  169. };
  170. doc.fire( 'change', 'move', changes, null );
  171. expect( live.start.path ).to.deep.equal( [ 0, 1, 1 ] );
  172. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  173. } );
  174. it( 'is from the same parent as range end and before it', () => {
  175. let moveSource = new Position( root, [ 0, 2, 0 ] );
  176. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 2 ] ) );
  177. let changes = {
  178. range: moveRange,
  179. sourcePosition: moveSource
  180. };
  181. doc.fire( 'change', 'move', changes, null );
  182. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  183. expect( live.end.path ).to.deep.equal( [ 0, 2, 0 ] );
  184. } );
  185. it( 'is from a position before a node from range start path', () => {
  186. let moveSource = new Position( root, [ 0, 0 ] );
  187. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 1 ] ) );
  188. let changes = {
  189. range: moveRange,
  190. sourcePosition: moveSource
  191. };
  192. doc.fire( 'change', 'move', changes, null );
  193. expect( live.start.path ).to.deep.equal( [ 0, 0, 4 ] );
  194. expect( live.end.path ).to.deep.equal( [ 0, 1, 2 ] );
  195. } );
  196. it( 'intersects on live range left side', () => {
  197. let moveSource = new Position( root, [ 0, 1, 2 ] );
  198. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 4 ] ) );
  199. let changes = {
  200. range: moveRange,
  201. sourcePosition: moveSource
  202. };
  203. doc.fire( 'change', 'move', changes, null );
  204. expect( live.start.path ).to.deep.equal( [ 0, 1, 2 ] );
  205. expect( live.end.path ).to.deep.equal( [ 0, 2, 2 ] );
  206. } );
  207. it( 'intersects on live range right side', () => {
  208. let moveSource = new Position( root, [ 0, 2, 1 ] );
  209. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 4 ] ) );
  210. let changes = {
  211. range: moveRange,
  212. sourcePosition: moveSource
  213. };
  214. doc.fire( 'change', 'move', changes, null );
  215. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  216. expect( live.end.path ).to.deep.equal( [ 0, 2, 1 ] );
  217. } );
  218. it( 'intersects on live range left side and live range new start is touching moved range end', () => {
  219. let moveSource = new Position( root, [ 0, 1, 0 ] );
  220. let moveRange = new Range( new Position( root, [ 0, 1 ] ), new Position( root, [ 0, 6 ] ) );
  221. let changes = {
  222. range: moveRange,
  223. sourcePosition: moveSource
  224. };
  225. doc.fire( 'change', 'move', changes, null );
  226. expect( live.start.path ).to.deep.equal( [ 0, 5 ] );
  227. expect( live.end.path ).to.deep.equal( [ 0, 7, 2 ] );
  228. } );
  229. it( 'intersects on live range right side and live range new end is touching moved range start', () => {
  230. live.end.offset = 12;
  231. let moveSource = new Position( root, [ 0, 2, 10 ] );
  232. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 5 ] ) );
  233. let changes = {
  234. range: moveRange,
  235. sourcePosition: moveSource
  236. };
  237. doc.fire( 'change', 'move', changes, null );
  238. expect( live.start.path ).to.deep.equal( [ 0, 1, 4 ] );
  239. expect( live.end.path ).to.deep.equal( [ 0, 3, 2 ] );
  240. } );
  241. it( 'is equal to live range', () => {
  242. live.end.path = [ 0, 1, 7 ];
  243. let moveSource = new Position( root, [ 0, 1, 4 ] );
  244. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 3 ] ) );
  245. let changes = {
  246. range: moveRange,
  247. sourcePosition: moveSource
  248. };
  249. doc.fire( 'change', 'move', changes, null );
  250. expect( live.start.path ).to.deep.equal( [ 0, 3, 0 ] );
  251. expect( live.end.path ).to.deep.equal( [ 0, 3, 3 ] );
  252. } );
  253. it( 'contains live range', () => {
  254. live.end.path = [ 0, 1, 7 ];
  255. let moveSource = new Position( root, [ 0, 1, 3 ] );
  256. let moveRange = new Range( new Position( root, [ 0, 3, 0 ] ), new Position( root, [ 0, 3, 9 ] ) );
  257. let changes = {
  258. range: moveRange,
  259. sourcePosition: moveSource
  260. };
  261. doc.fire( 'change', 'move', changes, null );
  262. expect( live.start.path ).to.deep.equal( [ 0, 3, 1 ] );
  263. expect( live.end.path ).to.deep.equal( [ 0, 3, 4 ] );
  264. } );
  265. } );
  266. } );
  267. describe( 'should not get transformed if', () => {
  268. let otherRoot;
  269. before( () => {
  270. otherRoot = doc.createRoot( 'otherRoot' );
  271. } );
  272. let live, clone;
  273. beforeEach( () => {
  274. live = new LiveRange( new Position( root, [ 0, 1, 4 ] ), new Position( root, [ 0, 2, 2 ] ) );
  275. clone = Range.createFromRange( live );
  276. } );
  277. afterEach( () => {
  278. live.detach();
  279. } );
  280. describe( 'insertion', () => {
  281. // Technically range will be expanded but the boundaries properties will stay the same.
  282. // Start won't change because insertion is after it.
  283. // End won't change because it is in different node.
  284. it( 'is in the same parent as range start and after it', () => {
  285. let insertRange = new Range( new Position( root, [ 0, 1, 7 ] ), new Position( root, [ 0, 1, 9 ] ) );
  286. doc.fire( 'change', 'insert', { range: insertRange }, null );
  287. expect( live.isEqual( clone ) ).to.be.true;
  288. } );
  289. it( 'is in the same parent as range end and after it', () => {
  290. let insertRange = new Range( new Position( root, [ 0, 2, 7 ] ), new Position( root, [ 0, 2, 9 ] ) );
  291. doc.fire( 'change', 'insert', { range: insertRange }, null );
  292. expect( live.isEqual( clone ) ).to.be.true;
  293. } );
  294. it( 'is to a position after a node from range end path', () => {
  295. let insertRange = new Range( new Position( root, [ 3 ] ), new Position( root, [ 4 ] ) );
  296. doc.fire( 'change', 'insert', { range: insertRange }, null );
  297. expect( live.isEqual( clone ) ).to.be.true;
  298. } );
  299. it( 'is in different root', () => {
  300. let insertRange = new Range( new Position( otherRoot, [ 0, 0 ] ), new Position( otherRoot, [ 0, 2 ] ) );
  301. doc.fire( 'change', 'insert', { range: insertRange }, null );
  302. expect( live.isEqual( clone ) ).to.be.true;
  303. } );
  304. } );
  305. describe( 'range move', () => {
  306. // Technically range will be expanded but the boundaries properties will stay the same.
  307. // Start won't change because insertion is after it.
  308. // End won't change because it is in different node.
  309. it( 'is to the same parent as range start and after it', () => {
  310. let moveSource = new Position( root, [ 4 ] );
  311. let moveRange = new Range( new Position( root, [ 0, 1, 7 ] ), new Position( root, [ 0, 1, 9 ] ) );
  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 the same parent as range end and before it', () => {
  320. let moveSource = new Position( root, [ 4 ] );
  321. let moveRange = new Range( new Position( root, [ 0, 2, 3 ] ), new Position( root, [ 0, 2, 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. it( 'is to a position after a node from range end path', () => {
  330. let moveSource = new Position( root, [ 4 ] );
  331. let moveRange = new Range( new Position( root, [ 0, 3 ] ), new Position( root, [ 0, 5 ] ) );
  332. let changes = {
  333. range: moveRange,
  334. sourcePosition: moveSource
  335. };
  336. doc.fire( 'change', 'move', changes, null );
  337. expect( live.isEqual( clone ) ).to.be.true;
  338. } );
  339. // Technically range will be shrunk but the boundaries properties will stay the same.
  340. // Start won't change because deletion is after it.
  341. // End won't change because it is in different node.
  342. it( 'is from the same parent as range start and after it', () => {
  343. let moveSource = new Position( root, [ 0, 1, 6 ] );
  344. let moveRange = new Range( new Position( root, [ 4, 0 ] ), new Position( root, [ 4, 3 ] ) );
  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 the same parent as range end and after it', () => {
  353. let moveSource = new Position( root, [ 0, 2, 4 ] );
  354. let moveRange = new Range( new Position( root, [ 4, 0 ] ), new Position( root, [ 4, 2 ] ) );
  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 from a position after a node from range end path', () => {
  363. let moveSource = new Position( root, [ 0, 3 ] );
  364. let moveRange = new Range( new Position( root, [ 5, 0 ] ), new Position( root, [ 5, 1 ] ) );
  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 to different root', () => {
  373. let moveSource = new Position( root, [ 2 ] );
  374. let moveRange = new Range( new Position( otherRoot, [ 0, 1, 0 ] ), new Position( otherRoot, [ 0, 1, 4 ] ) );
  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. it( 'is from different root', () => {
  383. let moveSource = new Position( otherRoot, [ 0, 2, 0 ] );
  384. let moveRange = new Range( new Position( root, [ 2, 0 ] ), new Position( root, [ 2, 2 ] ) );
  385. let changes = {
  386. range: moveRange,
  387. sourcePosition: moveSource
  388. };
  389. doc.fire( 'change', 'move', changes, null );
  390. expect( live.isEqual( clone ) ).to.be.true;
  391. } );
  392. } );
  393. } );
  394. } );