treewalker.js 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* globals document */
  6. import Document from '../../src/view/document';
  7. import DocumentFragment from '../../src/view/documentfragment';
  8. import AttributeElement from '../../src/view/attributeelement';
  9. import ContainerElement from '../../src/view/containerelement';
  10. import Text from '../../src/view/text';
  11. import TreeWalker from '../../src/view/treewalker';
  12. import Position from '../../src/view/position';
  13. import Range from '../../src/view/range';
  14. import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
  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. it( 'should iterate over document fragment', () => {
  861. const foo = new Text( 'foo' );
  862. const bar = new Text( 'bar' );
  863. const p = new ContainerElement( 'p', null, foo );
  864. const b = new AttributeElement( 'b', null, bar );
  865. const docFrag = new DocumentFragment( [ p, b ] );
  866. const iterator = new TreeWalker( {
  867. startPosition: new Position( docFrag, 0 ),
  868. ignoreElementEnd: true
  869. } );
  870. const nodes = Array.from( iterator ).map( ( step ) => step.item );
  871. expect( nodes ).to.deep.equal( [ p, foo, b, bar ] );
  872. } );
  873. } );
  874. function expectValue( value, expected, options = {} ) {
  875. let expectedPreviousPosition, expectedNextPosition;
  876. if ( options.direction == 'backward' ) {
  877. expectedNextPosition = expected.previousPosition;
  878. expectedPreviousPosition = expected.nextPosition;
  879. } else {
  880. expectedNextPosition = expected.nextPosition;
  881. expectedPreviousPosition = expected.previousPosition;
  882. }
  883. expect( value.type ).to.equal( expected.type );
  884. expect( value.previousPosition ).to.deep.equal( expectedPreviousPosition );
  885. expect( value.nextPosition ).to.deep.equal( expectedNextPosition );
  886. if ( value.type == 'text' ) {
  887. expectText( value, expected );
  888. } else if ( value.type == 'elementStart' ) {
  889. expectStart( value, expected );
  890. } else if ( value.type == 'elementEnd' ) {
  891. expectEnd( value, expected );
  892. }
  893. }
  894. function expectText( value, expected ) {
  895. expect( value.item.data ).to.equal( expected.text );
  896. expect( value.length ).to.equal( value.item.data.length );
  897. }
  898. function expectStart( value, expected ) {
  899. expect( value.item ).to.equal( expected.item );
  900. expect( value.length ).to.equal( 1 );
  901. }
  902. function expectEnd( value, expected ) {
  903. expect( value.item ).to.equal( expected.item );
  904. expect( value.length ).to.be.undefined;
  905. }