treewalker.js 25 KB

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