8
0

treewalker.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* globals document */
  6. /* bender-tags: view */
  7. import Document from 'ckeditor5/engine/view/document.js';
  8. import AttributeElement from 'ckeditor5/engine/view/attributeelement.js';
  9. import ContainerElement from 'ckeditor5/engine/view/containerelement.js';
  10. import Text from 'ckeditor5/engine/view/text.js';
  11. import TreeWalker from 'ckeditor5/engine/view/treewalker.js';
  12. import Position from 'ckeditor5/engine/view/position.js';
  13. import Range from 'ckeditor5/engine/view/range.js';
  14. import CKEditorError from 'ckeditor5/utils/ckeditorerror.js';
  15. describe( 'TreeWalker', () => {
  16. let doc, root, img1, paragraph, bold, textAbcd, charY, img2, charX;
  17. let rootBeginning, rootEnding;
  18. before( () => {
  19. doc = new Document();
  20. root = doc.createRoot( document.createElement( 'div' ) );
  21. // root
  22. // |- img1
  23. // |- p
  24. // |- b
  25. // | |- A
  26. // | |- B
  27. // | |- C
  28. // | |- D
  29. // |
  30. // |- Y
  31. // |
  32. // |- img2
  33. // |
  34. // |- X
  35. textAbcd = new Text( 'abcd' );
  36. bold = new AttributeElement( 'b', null, [ textAbcd ] );
  37. charY = new Text( 'y' );
  38. img2 = new ContainerElement( 'img2' );
  39. charX = new Text( 'x' );
  40. paragraph = new ContainerElement( 'p', null, [ bold, charY, img2, charX ] );
  41. img1 = new ContainerElement( 'img1' );
  42. root.insertChildren( 0, [ img1, paragraph ] );
  43. rootBeginning = new Position( root, 0 );
  44. rootEnding = new Position( root, 2 );
  45. } );
  46. afterEach( () => {
  47. doc.destroy();
  48. } );
  49. describe( 'constructor()', () => {
  50. it( 'should throw if neither boundaries nor starting position is set', () => {
  51. expect( () => {
  52. new TreeWalker();
  53. } ).to.throw( CKEditorError, /^view-tree-walker-no-start-position/ );
  54. expect( () => {
  55. new TreeWalker( {} );
  56. } ).to.throw( CKEditorError, /^view-tree-walker-no-start-position/ );
  57. expect( () => {
  58. new TreeWalker( { singleCharacters: true } );
  59. } ).to.throw( CKEditorError, /^view-tree-walker-no-start-position/ );
  60. } );
  61. it( 'should throw if walking direction is unknown', () => {
  62. expect( () => {
  63. new TreeWalker( { startPosition: rootBeginning, direction: 'unknown' } );
  64. } ).to.throw( CKEditorError, /^view-tree-walker-unknown-direction/ );
  65. } );
  66. } );
  67. describe( 'iterate from start position `startPosition`', () => {
  68. let expected;
  69. beforeEach( () => {
  70. expected = [
  71. {
  72. type: 'elementStart',
  73. item: img1,
  74. previousPosition: new Position( root, 0 ),
  75. nextPosition: new Position( img1, 0 )
  76. },
  77. {
  78. type: 'elementEnd',
  79. item: img1,
  80. previousPosition: new Position( img1, 0 ),
  81. nextPosition: new Position( root, 1 )
  82. },
  83. {
  84. type: 'elementStart',
  85. item: paragraph,
  86. previousPosition: new Position( root, 1 ),
  87. nextPosition: new Position( paragraph, 0 )
  88. },
  89. {
  90. type: 'elementStart',
  91. item: bold,
  92. previousPosition: new Position( paragraph, 0 ),
  93. nextPosition: new Position( bold, 0 )
  94. },
  95. {
  96. type: 'text',
  97. text: 'abcd',
  98. previousPosition: new Position( bold, 0 ),
  99. nextPosition: new Position( bold, 1 )
  100. },
  101. {
  102. type: 'elementEnd',
  103. item: bold,
  104. previousPosition: new Position( bold, 1 ),
  105. nextPosition: new Position( paragraph, 1 )
  106. },
  107. {
  108. type: 'text',
  109. text: 'y',
  110. previousPosition: new Position( paragraph, 1 ),
  111. nextPosition: new Position( paragraph, 2 )
  112. },
  113. {
  114. type: 'elementStart',
  115. item: img2,
  116. previousPosition: new Position( paragraph, 2 ),
  117. nextPosition: new Position( img2, 0 )
  118. },
  119. {
  120. type: 'elementEnd',
  121. item: img2,
  122. previousPosition: new Position( img2, 0 ),
  123. nextPosition: new Position( paragraph, 3 )
  124. },
  125. {
  126. type: 'text',
  127. text: 'x',
  128. previousPosition: new Position( paragraph, 3 ),
  129. nextPosition: new Position( paragraph, 4 )
  130. },
  131. {
  132. type: 'elementEnd',
  133. item: paragraph,
  134. previousPosition: new Position( paragraph, 4 ),
  135. nextPosition: new Position( root, 2 )
  136. }
  137. ];
  138. } );
  139. it( 'should provide iterator interface with default forward direction', () => {
  140. let iterator = new TreeWalker( { startPosition: rootBeginning } );
  141. let i = 0;
  142. for ( let value of iterator ) {
  143. expectValue( value, expected[ i++ ] );
  144. }
  145. expect( i ).to.equal( expected.length );
  146. } );
  147. it( 'should provide iterator interface with forward direction', () => {
  148. let iterator = new TreeWalker( { startPosition: rootBeginning, direction: 'forward' } );
  149. let i = 0;
  150. for ( let value of iterator ) {
  151. expectValue( value, expected[ i++ ] );
  152. }
  153. expect( i ).to.equal( expected.length );
  154. } );
  155. it( 'should provide iterator interface which backward direction', () => {
  156. let iterator = new TreeWalker( { startPosition: rootEnding, direction: 'backward' } );
  157. let i = expected.length;
  158. for ( let value of iterator ) {
  159. expectValue( value, expected[ --i ], { direction: 'backward' } );
  160. }
  161. expect( i ).to.equal( 0 );
  162. } );
  163. it( 'should start iterating at the startPosition witch is not a root bound', () => {
  164. let iterator = new TreeWalker( { startPosition: new Position( root, 1 ) } );
  165. let i = 2;
  166. for ( let value of iterator ) {
  167. expectValue( value, expected[ i++ ] );
  168. }
  169. expect( i ).to.equal( expected.length );
  170. } );
  171. it( 'should start iterating at the startPosition witch is not a root bound, going backward', () => {
  172. let expected = [
  173. {
  174. type: 'elementStart',
  175. item: img1,
  176. previousPosition: new Position( root, 0 ),
  177. nextPosition: new Position( img1, 0 )
  178. },
  179. {
  180. type: 'elementEnd',
  181. item: img1,
  182. previousPosition: new Position( img1, 0 ),
  183. nextPosition: new Position( root, 1 )
  184. }
  185. ];
  186. let iterator = new TreeWalker( { startPosition: new Position( root, 1 ), direction: 'backward' } );
  187. let i = expected.length;
  188. for ( let value of iterator ) {
  189. expectValue( value, expected[ --i ], { direction: 'backward' } );
  190. }
  191. expect( i ).to.equal( 0 );
  192. } );
  193. } );
  194. describe( 'iterate trough the range `boundary`', () => {
  195. describe( 'range starts between elements', () => {
  196. let expected, range;
  197. before( () => {
  198. expected = [
  199. {
  200. type: 'elementStart',
  201. item: paragraph,
  202. previousPosition: new Position( root, 1 ),
  203. nextPosition: new Position( paragraph, 0 )
  204. },
  205. {
  206. type: 'elementStart',
  207. item: bold,
  208. previousPosition: new Position( paragraph, 0 ),
  209. nextPosition: new Position( bold, 0 )
  210. },
  211. {
  212. type: 'text',
  213. text: 'abcd',
  214. previousPosition: new Position( bold, 0 ),
  215. nextPosition: new Position( bold, 1 )
  216. },
  217. {
  218. type: 'elementEnd',
  219. item: bold,
  220. previousPosition: new Position( bold, 1 ),
  221. nextPosition: new Position( paragraph, 1 )
  222. },
  223. {
  224. type: 'text',
  225. text: 'y',
  226. previousPosition: new Position( paragraph, 1 ),
  227. nextPosition: new Position( paragraph, 2 )
  228. },
  229. {
  230. type: 'elementStart',
  231. item: img2,
  232. previousPosition: new Position( paragraph, 2 ),
  233. nextPosition: new Position( img2, 0 )
  234. },
  235. {
  236. type: 'elementEnd',
  237. item: img2,
  238. previousPosition: new Position( img2, 0 ),
  239. nextPosition: new Position( paragraph, 3 )
  240. }
  241. ];
  242. range = Range.createFromParentsAndOffsets( root, 1, paragraph, 3 );
  243. } );
  244. it( 'should iterating over the range', () => {
  245. let iterator = new TreeWalker( { boundaries: range } );
  246. let i = 0;
  247. for ( let value of iterator ) {
  248. expectValue( value, expected[ i++ ] );
  249. }
  250. expect( i ).to.equal( expected.length );
  251. } );
  252. it( 'should iterating over the range going backward', () => {
  253. let iterator = new TreeWalker( { boundaries: range, direction: 'backward' } );
  254. let i = expected.length;
  255. for ( let value of iterator ) {
  256. expectValue( value, expected[ --i ], { direction: 'backward' } );
  257. }
  258. expect( i ).to.equal( 0 );
  259. } );
  260. } );
  261. describe( 'range starts inside the text', () => {
  262. let expected, range;
  263. before( () => {
  264. expected = [
  265. {
  266. type: 'text',
  267. text: 'bcd',
  268. previousPosition: new Position( textAbcd, 1 ),
  269. nextPosition: new Position( bold, 1 )
  270. },
  271. {
  272. type: 'elementEnd',
  273. item: bold,
  274. previousPosition: new Position( bold, 1 ),
  275. nextPosition: new Position( paragraph, 1 )
  276. },
  277. {
  278. type: 'text',
  279. text: 'y',
  280. previousPosition: new Position( paragraph, 1 ),
  281. nextPosition: new Position( paragraph, 2 )
  282. },
  283. {
  284. type: 'elementStart',
  285. item: img2,
  286. previousPosition: new Position( paragraph, 2 ),
  287. nextPosition: new Position( img2, 0 )
  288. },
  289. {
  290. type: 'elementEnd',
  291. item: img2,
  292. previousPosition: new Position( img2, 0 ),
  293. nextPosition: new Position( paragraph, 3 )
  294. }
  295. ];
  296. range = Range.createFromParentsAndOffsets( textAbcd, 1, paragraph, 3 );
  297. } );
  298. it( 'should return part of the text', () => {
  299. let iterator = new TreeWalker( { boundaries: range } );
  300. let i = 0;
  301. for ( let value of iterator ) {
  302. expectValue( value, expected[ i++ ] );
  303. }
  304. expect( i ).to.equal( expected.length );
  305. } );
  306. it( 'should return part of the text going backward', () => {
  307. let iterator = new TreeWalker( {
  308. boundaries: range,
  309. direction: 'backward'
  310. }
  311. );
  312. let i = expected.length;
  313. for ( let value of iterator ) {
  314. expectValue( value, expected[ --i ], { direction: 'backward' } );
  315. }
  316. expect( i ).to.equal( 0 );
  317. } );
  318. } );
  319. describe( 'range ends inside the text', () => {
  320. let expected, range;
  321. before( () => {
  322. expected = [
  323. {
  324. type: 'elementStart',
  325. item: img1,
  326. previousPosition: new Position( root, 0 ),
  327. nextPosition: new Position( img1, 0 )
  328. },
  329. {
  330. type: 'elementEnd',
  331. item: img1,
  332. previousPosition: new Position( img1, 0 ),
  333. nextPosition: new Position( root, 1 )
  334. },
  335. {
  336. type: 'elementStart',
  337. item: paragraph,
  338. previousPosition: new Position( root, 1 ),
  339. nextPosition: new Position( paragraph, 0 )
  340. },
  341. {
  342. type: 'elementStart',
  343. item: bold,
  344. previousPosition: new Position( paragraph, 0 ),
  345. nextPosition: new Position( bold, 0 )
  346. },
  347. {
  348. type: 'text',
  349. text: 'ab',
  350. previousPosition: new Position( bold, 0 ),
  351. nextPosition: new Position( textAbcd, 2 )
  352. }
  353. ];
  354. range = new Range( rootBeginning, new Position( textAbcd, 2 ) );
  355. } );
  356. it( 'should return part of the text', () => {
  357. let iterator = new TreeWalker( { boundaries: range } );
  358. let i = 0;
  359. for ( let value of iterator ) {
  360. expectValue( value, expected[ i++ ] );
  361. }
  362. expect( i ).to.equal( expected.length );
  363. } );
  364. it( 'should return part of the text going backward', () => {
  365. let iterator = new TreeWalker( {
  366. boundaries: range,
  367. startPosition: range.end,
  368. direction: 'backward'
  369. } );
  370. let i = expected.length;
  371. for ( let value of iterator ) {
  372. expectValue( value, expected[ --i ], { direction: 'backward' } );
  373. }
  374. expect( i ).to.equal( 0 );
  375. } );
  376. } );
  377. describe( 'range starts and ends inside the same text', () => {
  378. let expected, range;
  379. before( () => {
  380. expected = [
  381. {
  382. type: 'text',
  383. text: 'bc',
  384. previousPosition: new Position( textAbcd, 1 ),
  385. nextPosition: new Position( textAbcd, 3 )
  386. }
  387. ];
  388. range = new Range( new Position( textAbcd, 1 ), new Position( textAbcd, 3 ) );
  389. } );
  390. it( 'should return part of the text', () => {
  391. let iterator = new TreeWalker( { boundaries: range } );
  392. let i = 0;
  393. for ( let value of iterator ) {
  394. expectValue( value, expected[ i++ ] );
  395. }
  396. expect( i ).to.equal( expected.length );
  397. } );
  398. it( 'should return part of the text going backward', () => {
  399. let iterator = new TreeWalker( {
  400. boundaries: range,
  401. startPosition: range.end,
  402. direction: 'backward'
  403. } );
  404. let i = expected.length;
  405. for ( let value of iterator ) {
  406. expectValue( value, expected[ --i ], { direction: 'backward' } );
  407. }
  408. expect( i ).to.equal( 0 );
  409. } );
  410. } );
  411. describe( 'custom start position', () => {
  412. it( 'should iterating from the start position', () => {
  413. let expected = [
  414. {
  415. type: 'text',
  416. text: 'y',
  417. previousPosition: new Position( paragraph, 1 ),
  418. nextPosition: new Position( paragraph, 2 )
  419. },
  420. {
  421. type: 'elementStart',
  422. item: img2,
  423. previousPosition: new Position( paragraph, 2 ),
  424. nextPosition: new Position( img2, 0 )
  425. },
  426. {
  427. type: 'elementEnd',
  428. item: img2,
  429. previousPosition: new Position( img2, 0 ),
  430. nextPosition: new Position( paragraph, 3 )
  431. }
  432. ];
  433. let range = Range.createFromParentsAndOffsets( bold, 1, paragraph, 3 );
  434. let iterator = new TreeWalker( {
  435. boundaries: range,
  436. startPosition: new Position( paragraph, 1 )
  437. } );
  438. let i = 0;
  439. for ( let value of iterator ) {
  440. expectValue( value, expected[ i++ ] );
  441. }
  442. expect( i ).to.equal( expected.length );
  443. } );
  444. it( 'should iterating from the start position going backward', () => {
  445. let expected = [
  446. {
  447. type: 'text',
  448. text: 'bcd',
  449. previousPosition: new Position( textAbcd, 1 ),
  450. nextPosition: new Position( bold, 1 )
  451. },
  452. {
  453. type: 'elementEnd',
  454. item: bold,
  455. previousPosition: new Position( bold, 1 ),
  456. nextPosition: new Position( paragraph, 1 )
  457. },
  458. {
  459. type: 'text',
  460. text: 'y',
  461. previousPosition: new Position( paragraph, 1 ),
  462. nextPosition: new Position( paragraph, 2 )
  463. }
  464. ];
  465. let range = new Range( new Position( textAbcd, 1 ), new Position( paragraph, 3 ) );
  466. let iterator = new TreeWalker( {
  467. boundaries: range,
  468. startPosition: new Position( paragraph, 2 ),
  469. direction: 'backward'
  470. } );
  471. let i = expected.length;
  472. for ( let value of iterator ) {
  473. expectValue( value, expected[ --i ], { direction: 'backward' } );
  474. }
  475. expect( i ).to.equal( 0 );
  476. } );
  477. } );
  478. } );
  479. describe( 'iterate by every single characters `singleCharacter`', () => {
  480. describe( 'whole root', () => {
  481. let expected;
  482. before( () => {
  483. expected = [
  484. {
  485. type: 'elementStart',
  486. item: img1,
  487. previousPosition: new Position( root, 0 ),
  488. nextPosition: new Position( img1, 0 )
  489. },
  490. {
  491. type: 'elementEnd',
  492. item: img1,
  493. previousPosition: new Position( img1, 0 ),
  494. nextPosition: new Position( root, 1 )
  495. },
  496. {
  497. type: 'elementStart',
  498. item: paragraph,
  499. previousPosition: new Position( root, 1 ),
  500. nextPosition: new Position( paragraph, 0 )
  501. },
  502. {
  503. type: 'elementStart',
  504. item: bold,
  505. previousPosition: new Position( paragraph, 0 ),
  506. nextPosition: new Position( bold, 0 )
  507. },
  508. {
  509. type: 'text',
  510. text: 'a',
  511. previousPosition: new Position( bold, 0 ),
  512. nextPosition: new Position( textAbcd, 1 )
  513. },
  514. {
  515. type: 'text',
  516. text: 'b',
  517. previousPosition: new Position( textAbcd, 1 ),
  518. nextPosition: new Position( textAbcd, 2 )
  519. },
  520. {
  521. type: 'text',
  522. text: 'c',
  523. previousPosition: new Position( textAbcd, 2 ),
  524. nextPosition: new Position( textAbcd, 3 )
  525. },
  526. {
  527. type: 'text',
  528. text: 'd',
  529. previousPosition: new Position( textAbcd, 3 ),
  530. nextPosition: new Position( bold, 1 )
  531. },
  532. {
  533. type: 'elementEnd',
  534. item: bold,
  535. previousPosition: new Position( bold, 1 ),
  536. nextPosition: new Position( paragraph, 1 )
  537. },
  538. {
  539. type: 'text',
  540. text: 'y',
  541. previousPosition: new Position( paragraph, 1 ),
  542. nextPosition: new Position( paragraph, 2 )
  543. },
  544. {
  545. type: 'elementStart',
  546. item: img2,
  547. previousPosition: new Position( paragraph, 2 ),
  548. nextPosition: new Position( img2, 0 )
  549. },
  550. {
  551. type: 'elementEnd',
  552. item: img2,
  553. previousPosition: new Position( img2, 0 ),
  554. nextPosition: new Position( paragraph, 3 )
  555. },
  556. {
  557. type: 'text',
  558. text: 'x',
  559. previousPosition: new Position( paragraph, 3 ),
  560. nextPosition: new Position( paragraph, 4 )
  561. },
  562. {
  563. type: 'elementEnd',
  564. item: paragraph,
  565. previousPosition: new Position( paragraph, 4 ),
  566. nextPosition: new Position( root, 2 )
  567. }
  568. ];
  569. } );
  570. it( 'should return single characters', () => {
  571. let iterator = new TreeWalker( { startPosition: rootBeginning, singleCharacters: true } );
  572. let i = 0;
  573. for ( let value of iterator ) {
  574. expectValue( value, expected[ i++ ] );
  575. }
  576. expect( i ).to.equal( expected.length );
  577. } );
  578. it( 'should return single characters going backward', () => {
  579. let iterator = new TreeWalker( {
  580. startPosition: rootEnding,
  581. singleCharacters: true,
  582. direction: 'backward'
  583. } );
  584. let i = expected.length;
  585. for ( let value of iterator ) {
  586. expectValue( value, expected[ --i ], { direction: 'backward' } );
  587. }
  588. expect( i ).to.equal( 0 );
  589. } );
  590. } );
  591. describe( 'range', () => {
  592. let range, expected;
  593. before( () => {
  594. expected = [
  595. {
  596. type: 'text',
  597. text: 'a',
  598. previousPosition: new Position( bold, 0 ),
  599. nextPosition: new Position( textAbcd, 1 )
  600. },
  601. {
  602. type: 'text',
  603. text: 'b',
  604. previousPosition: new Position( textAbcd, 1 ),
  605. nextPosition: new Position( textAbcd, 2 )
  606. },
  607. {
  608. type: 'text',
  609. text: 'c',
  610. previousPosition: new Position( textAbcd, 2 ),
  611. nextPosition: new Position( textAbcd, 3 )
  612. },
  613. {
  614. type: 'text',
  615. text: 'd',
  616. previousPosition: new Position( textAbcd, 3 ),
  617. nextPosition: new Position( bold, 1 )
  618. },
  619. {
  620. type: 'elementEnd',
  621. item: bold,
  622. previousPosition: new Position( bold, 1 ),
  623. nextPosition: new Position( paragraph, 1 )
  624. },
  625. {
  626. type: 'text',
  627. text: 'y',
  628. previousPosition: new Position( paragraph, 1 ),
  629. nextPosition: new Position( paragraph, 2 )
  630. },
  631. {
  632. type: 'elementStart',
  633. item: img2,
  634. previousPosition: new Position( paragraph, 2 ),
  635. nextPosition: new Position( img2, 0 )
  636. }
  637. ];
  638. range = new Range( new Position( bold, 0 ), new Position( img2, 0 ) );
  639. } );
  640. it( 'should respect boundaries', () => {
  641. let iterator = new TreeWalker( { boundaries: range, singleCharacters: true } );
  642. let i = 0;
  643. for ( let value of iterator ) {
  644. expectValue( value, expected[ i++ ] );
  645. }
  646. expect( i ).to.equal( expected.length );
  647. } );
  648. it( 'should respect boundaries going backward', () => {
  649. let iterator = new TreeWalker( {
  650. boundaries: range,
  651. singleCharacters: true,
  652. direction: 'backward'
  653. } );
  654. let i = expected.length;
  655. for ( let value of iterator ) {
  656. expectValue( value, expected[ --i ], { direction: 'backward' } );
  657. }
  658. expect( i ).to.equal( 0 );
  659. } );
  660. } );
  661. } );
  662. describe( 'iterate omitting child nodes and elementEnd `shallow`', () => {
  663. let expected;
  664. before( () => {
  665. expected = [
  666. {
  667. type: 'elementStart',
  668. item: img1,
  669. previousPosition: new Position( root, 0 ),
  670. nextPosition: new Position( root, 1 )
  671. },
  672. {
  673. type: 'elementStart',
  674. item: paragraph,
  675. previousPosition: new Position( root, 1 ),
  676. nextPosition: new Position( root, 2 )
  677. }
  678. ];
  679. } );
  680. it( 'should not enter elements', () => {
  681. let iterator = new TreeWalker( { startPosition: rootBeginning, shallow: true } );
  682. let i = 0;
  683. for ( let value of iterator ) {
  684. expectValue( value, expected[ i++ ] );
  685. }
  686. expect( i ).to.equal( expected.length );
  687. } );
  688. it( 'should not enter elements going backward', () => {
  689. let iterator = new TreeWalker( { startPosition: rootEnding, shallow: true, direction: 'backward' } );
  690. let i = expected.length;
  691. for ( let value of iterator ) {
  692. expectValue( value, expected[ --i ], { direction: 'backward' } );
  693. }
  694. expect( i ).to.equal( 0 );
  695. } );
  696. } );
  697. describe( 'iterate omitting elementEnd `ignoreElementEnd`', () => {
  698. describe( 'merged text', () => {
  699. let expected;
  700. before( () => {
  701. expected = [
  702. {
  703. type: 'elementStart',
  704. item: img1,
  705. previousPosition: new Position( root, 0 ),
  706. nextPosition: new Position( img1, 0 )
  707. },
  708. {
  709. type: 'elementStart',
  710. item: paragraph,
  711. previousPosition: new Position( root, 1 ),
  712. nextPosition: new Position( paragraph, 0 )
  713. },
  714. {
  715. type: 'elementStart',
  716. item: bold,
  717. previousPosition: new Position( paragraph, 0 ),
  718. nextPosition: new Position( bold, 0 )
  719. },
  720. {
  721. type: 'text',
  722. text: 'abcd',
  723. previousPosition: new Position( bold, 0 ),
  724. nextPosition: new Position( bold, 1 )
  725. },
  726. {
  727. type: 'text',
  728. text: 'y',
  729. previousPosition: new Position( paragraph, 1 ),
  730. nextPosition: new Position( paragraph, 2 )
  731. },
  732. {
  733. type: 'elementStart',
  734. item: img2,
  735. previousPosition: new Position( paragraph, 2 ),
  736. nextPosition: new Position( img2, 0 )
  737. },
  738. {
  739. type: 'text',
  740. text: 'x',
  741. previousPosition: new Position( paragraph, 3 ),
  742. nextPosition: new Position( paragraph, 4 )
  743. }
  744. ];
  745. } );
  746. it( 'should iterate ignoring elementEnd', () => {
  747. let iterator = new TreeWalker( { startPosition: rootBeginning, ignoreElementEnd: true } );
  748. let i = 0;
  749. for ( let value of iterator ) {
  750. expectValue( value, expected[ i++ ] );
  751. }
  752. expect( i ).to.equal( expected.length );
  753. } );
  754. it( 'should iterate ignoring elementEnd going backward', () => {
  755. let iterator = new TreeWalker( {
  756. startPosition: rootEnding,
  757. ignoreElementEnd: true,
  758. direction: 'backward'
  759. } );
  760. let i = expected.length;
  761. for ( let value of iterator ) {
  762. expectValue( value, expected[ --i ], { direction: 'backward' } );
  763. }
  764. expect( i ).to.equal( 0 );
  765. } );
  766. } );
  767. describe( 'single character', () => {
  768. let expected;
  769. before( () => {
  770. expected = [
  771. {
  772. type: 'elementStart',
  773. item: img1,
  774. previousPosition: new Position( root, 0 ),
  775. nextPosition: new Position( img1, 0 )
  776. },
  777. {
  778. type: 'elementStart',
  779. item: paragraph,
  780. previousPosition: new Position( root, 1 ),
  781. nextPosition: new Position( paragraph, 0 )
  782. },
  783. {
  784. type: 'elementStart',
  785. item: bold,
  786. previousPosition: new Position( paragraph, 0 ),
  787. nextPosition: new Position( bold, 0 )
  788. },
  789. {
  790. type: 'text',
  791. text: 'a',
  792. previousPosition: new Position( bold, 0 ),
  793. nextPosition: new Position( textAbcd, 1 )
  794. },
  795. {
  796. type: 'text',
  797. text: 'b',
  798. previousPosition: new Position( textAbcd, 1 ),
  799. nextPosition: new Position( textAbcd, 2 )
  800. },
  801. {
  802. type: 'text',
  803. text: 'c',
  804. previousPosition: new Position( textAbcd, 2 ),
  805. nextPosition: new Position( textAbcd, 3 )
  806. },
  807. {
  808. type: 'text',
  809. text: 'd',
  810. previousPosition: new Position( textAbcd, 3 ),
  811. nextPosition: new Position( bold, 1 )
  812. },
  813. {
  814. type: 'text',
  815. text: 'y',
  816. previousPosition: new Position( paragraph, 1 ),
  817. nextPosition: new Position( paragraph, 2 )
  818. },
  819. {
  820. type: 'elementStart',
  821. item: img2,
  822. previousPosition: new Position( paragraph, 2 ),
  823. nextPosition: new Position( img2, 0 )
  824. },
  825. {
  826. type: 'text',
  827. text: 'x',
  828. previousPosition: new Position( paragraph, 3 ),
  829. nextPosition: new Position( paragraph, 4 )
  830. }
  831. ];
  832. } );
  833. it( 'should return single characters ignoring elementEnd', () => {
  834. let iterator = new TreeWalker( {
  835. startPosition: rootBeginning,
  836. singleCharacters: true,
  837. ignoreElementEnd: true
  838. } );
  839. let i = 0;
  840. for ( let value of iterator ) {
  841. expectValue( value, expected[ i++ ] );
  842. }
  843. expect( i ).to.equal( expected.length );
  844. } );
  845. it( 'should return single characters ignoring elementEnd going backward', () => {
  846. let iterator = new TreeWalker( {
  847. startPosition: rootEnding,
  848. singleCharacters: true,
  849. ignoreElementEnd: true,
  850. direction: 'backward'
  851. } );
  852. let i = expected.length;
  853. for ( let value of iterator ) {
  854. expectValue( value, expected[ --i ], { direction: 'backward' } );
  855. }
  856. expect( i ).to.equal( 0 );
  857. } );
  858. } );
  859. } );
  860. } );
  861. function expectValue( value, expected, options = {} ) {
  862. let expectedPreviousPosition, expectedNextPosition;
  863. if ( options.direction == 'backward' ) {
  864. expectedNextPosition = expected.previousPosition;
  865. expectedPreviousPosition = expected.nextPosition;
  866. } else {
  867. expectedNextPosition = expected.nextPosition;
  868. expectedPreviousPosition = expected.previousPosition;
  869. }
  870. expect( value.type ).to.equal( expected.type );
  871. expect( value.previousPosition ).to.deep.equal( expectedPreviousPosition );
  872. expect( value.nextPosition ).to.deep.equal( expectedNextPosition );
  873. if ( value.type == 'text' ) {
  874. expectText( value, expected );
  875. } else if ( value.type == 'elementStart' ) {
  876. expectStart( value, expected );
  877. } else if ( value.type == 'elementEnd' ) {
  878. expectEnd( value, expected );
  879. }
  880. }
  881. function expectText( value, expected ) {
  882. expect( value.item.data ).to.equal( expected.text );
  883. expect( value.length ).to.equal( value.item.data.length );
  884. }
  885. function expectStart( value, expected ) {
  886. expect( value.item ).to.equal( expected.item );
  887. expect( value.length ).to.equal( 1 );
  888. }
  889. function expectEnd( value, expected ) {
  890. expect( value.item ).to.equal( expected.item );
  891. expect( value.length ).to.be.undefined;
  892. }