unwrap.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* bender-tags: treeview */
  6. 'use strict';
  7. import Writer from '/ckeditor5/core/treeview/writer.js';
  8. import Element from '/ckeditor5/core/treeview/element.js';
  9. import Position from '/ckeditor5/core/treeview/position.js';
  10. import Range from '/ckeditor5/core/treeview/range.js';
  11. import Text from '/ckeditor5/core/treeview/text.js';
  12. import utils from '/tests/core/treeview/writer/_utils/utils.js';
  13. import CKEditorError from '/ckeditor5/utils/ckeditorerror.js';
  14. describe( 'Writer', () => {
  15. const create = utils.create;
  16. const test = utils.test;
  17. let writer;
  18. beforeEach( () => {
  19. writer = new Writer();
  20. } );
  21. describe( 'unwrap', () => {
  22. it( 'should do nothing on collapsed ranges', () => {
  23. const description = {
  24. instanceOf: Element,
  25. name: 'p',
  26. children: [
  27. { instanceOf: Text, data: 'foo', rangeStart: 1, rangeEnd: 1 }
  28. ]
  29. };
  30. const created = create( writer, description );
  31. const newRange = writer.unwrap( created.range, new Element( 'b' ), 1 );
  32. test( writer, newRange, created.node, description );
  33. } );
  34. it( 'should do nothing on single text node', () => {
  35. // <p>[{foobar}]</p>
  36. const description = {
  37. instanceOf: Element,
  38. name: 'p',
  39. rangeStart: 0,
  40. rangeEnd: 1,
  41. children: [
  42. { instanceOf: Text, data: 'foobar' }
  43. ]
  44. };
  45. const created = create( writer, description );
  46. const b = new Element( 'b' );
  47. writer.setPriority( b, 1 );
  48. const newRange = writer.unwrap( created.range, b );
  49. test( writer, newRange, created.node, description );
  50. } );
  51. it( 'should throw error when range placed in two containers', () => {
  52. const container1 = new Element( 'p' );
  53. const container2 = new Element( 'p' );
  54. const range = new Range(
  55. new Position( container1, 0 ),
  56. new Position( container2, 1 )
  57. );
  58. const b = new Element( 'b' );
  59. expect( () => {
  60. writer.unwrap( range, b, 1 );
  61. } ).to.throw( CKEditorError, 'treeview-writer-invalid-range-container' );
  62. } );
  63. it( 'should unwrap single node', () => {
  64. // <p>[<b>{foobar}</b>]<p> -> <p>[{foobar}]</p>
  65. const created = create( writer, {
  66. instanceOf: Element,
  67. name: 'p',
  68. rangeStart: 0,
  69. rangeEnd: 1,
  70. children: [
  71. {
  72. instanceOf: Element,
  73. priority: 1,
  74. name: 'b',
  75. children: [
  76. { instanceOf: Text, data: 'foobar' }
  77. ]
  78. }
  79. ]
  80. } );
  81. const b = new Element( 'b' );
  82. writer.setPriority( b, 1 );
  83. const newRange = writer.unwrap( created.range, b );
  84. test( writer, newRange, created.node, {
  85. instanceOf: Element,
  86. name: 'p',
  87. rangeStart: 0,
  88. rangeEnd: 1,
  89. children: [
  90. { instanceOf: Text, data: 'foobar' }
  91. ]
  92. } );
  93. } );
  94. it( 'should not unwrap attributes with different priorities #1', () => {
  95. // <p>[<b>{foobar}</b>]<p> -> <p>[<b>{foobar}</b>]</p>
  96. // Unwrapped with <b> but using different priority.
  97. const description = {
  98. instanceOf: Element,
  99. name: 'p',
  100. rangeStart: 0,
  101. rangeEnd: 1,
  102. children: [
  103. {
  104. instanceOf: Element,
  105. priority: 1,
  106. name: 'b',
  107. children: [
  108. { instanceOf: Text, data: 'foobar' }
  109. ]
  110. }
  111. ]
  112. };
  113. const created = create( writer, description );
  114. const b = new Element( 'b' );
  115. writer.setPriority( b, 2 );
  116. const newRange = writer.unwrap( created.range, b );
  117. test( writer, newRange, created.node, description );
  118. } );
  119. it( 'should not unwrap attributes with different priorities #2', () => {
  120. // <p>[<b>{foo}</b><b>{bar}</b><b>{baz}</b>]<p> -> <p>[{foo}<b>bar</b>{baz}]</p>
  121. // <b> around `bar` has different priority than others.
  122. const created = create( writer, {
  123. instanceOf: Element,
  124. name: 'p',
  125. rangeStart: 0,
  126. rangeEnd: 3,
  127. children: [
  128. {
  129. instanceOf: Element,
  130. priority: 2,
  131. name: 'b',
  132. children: [
  133. { instanceOf: Text, data: 'foo' }
  134. ]
  135. },
  136. {
  137. instanceOf: Element,
  138. priority: 1,
  139. name: 'b',
  140. children: [
  141. { instanceOf: Text, data: 'bar' }
  142. ]
  143. },
  144. {
  145. instanceOf: Element,
  146. priority: 2,
  147. name: 'b',
  148. children: [
  149. { instanceOf: Text, data: 'baz' }
  150. ]
  151. }
  152. ]
  153. } );
  154. const b = new Element( 'b' );
  155. writer.setPriority( b, 2 );
  156. const newRange = writer.unwrap( created.range, b );
  157. test( writer, newRange, created.node, {
  158. instanceOf: Element,
  159. name: 'p',
  160. rangeStart: 0,
  161. rangeEnd: 3,
  162. children: [
  163. { instanceOf: Text, data: 'foo' },
  164. {
  165. instanceOf: Element,
  166. priority: 1,
  167. name: 'b',
  168. children: [
  169. { instanceOf: Text, data: 'bar' }
  170. ]
  171. },
  172. { instanceOf: Text, data: 'baz' }
  173. ]
  174. } );
  175. } );
  176. it( 'should unwrap part of the node', () => {
  177. // <p>[{baz}<b>{foo]bar}</b><p> -> <p>[{bazfoo}]<b>{bar}</b></p>
  178. const created = create( writer, {
  179. instanceOf: Element,
  180. name: 'p',
  181. rangeStart: 0,
  182. children: [
  183. { instanceOf: Text, data: 'baz' },
  184. {
  185. instanceOf: Element,
  186. name: 'b',
  187. priority: 1,
  188. children: [
  189. { instanceOf: Text, data: 'foobar', rangeEnd: 3 }
  190. ]
  191. }
  192. ]
  193. } );
  194. const b = new Element( 'b' );
  195. writer.setPriority( b, 1 );
  196. const newRange = writer.unwrap( created.range, b );
  197. test( writer, newRange, created.node, {
  198. instanceOf: Element,
  199. name: 'p',
  200. rangeStart: 0,
  201. rangeEnd: 1,
  202. children: [
  203. { instanceOf: Text, data: 'bazfoo' },
  204. {
  205. instanceOf: Element,
  206. name: 'b',
  207. priority: 1,
  208. children: [
  209. { instanceOf: Text, data: 'bar' }
  210. ]
  211. }
  212. ]
  213. } );
  214. } );
  215. it( 'should unwrap nested attributes', () => {
  216. // <p>[<u><b>{foobar}</b></u>]</p> -> <p>[<u>{foobar}</u>]</p>
  217. const created = create( writer, {
  218. instanceOf: Element,
  219. name: 'p',
  220. rangeStart: 0,
  221. rangeEnd: 1,
  222. children: [
  223. {
  224. instanceOf: Element,
  225. name: 'u',
  226. priority: 1,
  227. children: [
  228. {
  229. instanceOf: Element,
  230. name: 'b',
  231. priority: 1,
  232. children: [
  233. { instanceOf: Text, data: 'foobar' }
  234. ]
  235. }
  236. ]
  237. }
  238. ]
  239. } );
  240. const b = new Element( 'b' );
  241. writer.setPriority( b, 1 );
  242. const newRange = writer.unwrap( created.range, b );
  243. test( writer, newRange, created.node, {
  244. instanceOf: Element,
  245. name: 'p',
  246. rangeStart: 0,
  247. rangeEnd: 1,
  248. children: [
  249. {
  250. instanceOf: Element,
  251. name: 'u',
  252. priority: 1,
  253. children: [
  254. { instanceOf: Text, data: 'foobar' }
  255. ]
  256. }
  257. ]
  258. } );
  259. } );
  260. it( 'should merge unwrapped nodes #1', () => {
  261. // <p>{foo}[<b>{bar}</b>]{bom}</p> -> <p>{foo[bar]bom}</p>
  262. const created = create( writer, {
  263. instanceOf: Element,
  264. name: 'p',
  265. rangeStart: 1,
  266. rangeEnd: 2,
  267. children: [
  268. { instanceOf: Text, data: 'foo' },
  269. {
  270. instanceOf: Element,
  271. name: 'b',
  272. priority: 1,
  273. children: [
  274. { instanceOf: Text, data: 'bar' }
  275. ]
  276. },
  277. { instanceOf: Text, data: 'bom' }
  278. ]
  279. } );
  280. const b = new Element( 'b' );
  281. writer.setPriority( b, 1 );
  282. const newRange = writer.unwrap( created.range, b );
  283. test( writer, newRange, created.node, {
  284. instanceOf: Element,
  285. name: 'p',
  286. children: [
  287. {
  288. instanceOf: Text,
  289. data: 'foobarbom',
  290. rangeStart: 3,
  291. rangeEnd: 6
  292. }
  293. ]
  294. } );
  295. } );
  296. it( 'should merge unwrapped nodes #2', () => {
  297. // <p>{foo}<u>{bar}</u>[<b><u>{bazqux}</u></b>]</p> -> <p>{foo}<u>{bar[bazqux}</u>]</p>
  298. const created = create( writer, {
  299. instanceOf: Element,
  300. name: 'p',
  301. rangeStart: 2,
  302. rangeEnd: 3,
  303. children: [
  304. { instanceOf: Text, data: 'foo' },
  305. {
  306. instanceOf: Element,
  307. name: 'u',
  308. priority: 1,
  309. children: [
  310. { instanceOf: Text, data: 'bar' }
  311. ]
  312. },
  313. {
  314. instanceOf: Element,
  315. name: 'b',
  316. priority: 1,
  317. children: [
  318. {
  319. instanceOf: Element,
  320. name: 'u',
  321. priority: 1,
  322. children: [
  323. { instanceOf: Text, data: 'bazqux' }
  324. ]
  325. }
  326. ]
  327. }
  328. ]
  329. } );
  330. const b = new Element( 'b' );
  331. writer.setPriority( b, 1 );
  332. const newRange = writer.unwrap( created.range, b );
  333. test( writer, newRange, created.node, {
  334. instanceOf: Element,
  335. name: 'p',
  336. rangeEnd: 2,
  337. children: [
  338. {
  339. instanceOf: Text,
  340. data: 'foo'
  341. },
  342. {
  343. instanceOf: Element,
  344. name: 'u',
  345. priority: 1,
  346. children: [
  347. { instanceOf: Text, data: 'barbazqux', rangeStart: 3 }
  348. ]
  349. }
  350. ]
  351. } );
  352. } );
  353. it( 'should merge unwrapped nodes #3', () => {
  354. // <p>{foo}<u>{bar}</u>[<b><u>{baz]qux}</u></b></p> -> <p>{foo}<u>{bar[baz}</u>]<b><u>{qux}</u></b></p>
  355. const created = create( writer, {
  356. instanceOf: Element,
  357. name: 'p',
  358. rangeStart: 2,
  359. children: [
  360. { instanceOf: Text, data: 'foo' },
  361. {
  362. instanceOf: Element,
  363. name: 'u',
  364. priority: 1,
  365. children: [
  366. { instanceOf: Text, data: 'bar' }
  367. ]
  368. },
  369. {
  370. instanceOf: Element,
  371. name: 'b',
  372. priority: 1,
  373. children: [
  374. {
  375. instanceOf: Element,
  376. name: 'u',
  377. priority: 1,
  378. children: [
  379. { instanceOf: Text, data: 'bazqux', rangeEnd: 3 }
  380. ]
  381. }
  382. ]
  383. }
  384. ]
  385. } );
  386. const b = new Element( 'b' );
  387. writer.setPriority( b, 1 );
  388. const newRange = writer.unwrap( created.range, b );
  389. test( writer, newRange, created.node, {
  390. instanceOf: Element,
  391. name: 'p',
  392. rangeEnd: 2,
  393. children: [
  394. {
  395. instanceOf: Text,
  396. data: 'foo'
  397. },
  398. {
  399. instanceOf: Element,
  400. name: 'u',
  401. priority: 1,
  402. children: [
  403. { instanceOf: Text, data: 'barbaz', rangeStart: 3 }
  404. ]
  405. },
  406. {
  407. instanceOf: Element,
  408. name: 'b',
  409. priority: 1,
  410. children: [
  411. {
  412. instanceOf: Element,
  413. name: 'u',
  414. priority: 1,
  415. children: [
  416. { instanceOf: Text, data: 'qux' }
  417. ]
  418. }
  419. ]
  420. }
  421. ]
  422. } );
  423. } );
  424. it( 'should merge unwrapped nodes #4', () => {
  425. // <p>{foo}<u>{bar}</u>[<b><u>{baz}</u></b>]<u>qux</u></p> -> <p>{foo}<u>{bar[baz]qux}</u></p>
  426. const created = create( writer, {
  427. instanceOf: Element,
  428. name: 'p',
  429. rangeStart: 2,
  430. rangeEnd: 3,
  431. children: [
  432. { instanceOf: Text, data: 'foo' },
  433. {
  434. instanceOf: Element,
  435. name: 'u',
  436. priority: 1,
  437. children: [
  438. { instanceOf: Text, data: 'bar' }
  439. ]
  440. },
  441. {
  442. instanceOf: Element,
  443. name: 'b',
  444. priority: 1,
  445. children: [
  446. {
  447. instanceOf: Element,
  448. name: 'u',
  449. priority: 1,
  450. children: [
  451. { instanceOf: Text, data: 'baz' }
  452. ]
  453. }
  454. ]
  455. },
  456. {
  457. instanceOf: Element,
  458. name: 'u',
  459. priority: 1,
  460. children: [
  461. { instanceOf: Text, data: 'qux' }
  462. ]
  463. }
  464. ]
  465. } );
  466. const b = new Element( 'b' );
  467. writer.setPriority( b, 1 );
  468. const newRange = writer.unwrap( created.range, b );
  469. test( writer, newRange, created.node, {
  470. instanceOf: Element,
  471. name: 'p',
  472. children: [
  473. {
  474. instanceOf: Text,
  475. data: 'foo'
  476. },
  477. {
  478. instanceOf: Element,
  479. name: 'u',
  480. priority: 1,
  481. children: [
  482. { instanceOf: Text, data: 'barbazqux', rangeStart: 3, rangeEnd: 6 }
  483. ]
  484. }
  485. ]
  486. } );
  487. } );
  488. it( 'should merge unwrapped nodes #5', () => {
  489. // <p>[<b><u>{foo}</u></b><b><u>{bar}</u></b><b><u>{baz}</u></b>]</p> -> <p>[<u>{foobarbaz}</u>]</p>
  490. const created = create( writer, {
  491. instanceOf: Element,
  492. name: 'p',
  493. rangeStart: 0,
  494. rangeEnd: 3,
  495. children: [
  496. {
  497. instanceOf: Element,
  498. name: 'b',
  499. priority: 1,
  500. children: [
  501. {
  502. instanceOf: Element,
  503. name: 'u',
  504. priority: 1,
  505. children: [
  506. { instanceOf: Text, data: 'foo' }
  507. ]
  508. }
  509. ]
  510. },
  511. {
  512. instanceOf: Element,
  513. name: 'b',
  514. priority: 1,
  515. children: [
  516. {
  517. instanceOf: Element,
  518. name: 'u',
  519. priority: 1,
  520. children: [
  521. { instanceOf: Text, data: 'bar' }
  522. ]
  523. }
  524. ]
  525. },
  526. {
  527. instanceOf: Element,
  528. name: 'b',
  529. priority: 1,
  530. children: [
  531. {
  532. instanceOf: Element,
  533. name: 'u',
  534. priority: 1,
  535. children: [
  536. { instanceOf: Text, data: 'baz' }
  537. ]
  538. }
  539. ]
  540. }
  541. ]
  542. } );
  543. const b = new Element( 'b' );
  544. writer.setPriority( b, 1 );
  545. const newRange = writer.unwrap( created.range, b );
  546. test( writer, newRange, created.node, {
  547. instanceOf: Element,
  548. name: 'p',
  549. rangeStart: 0,
  550. rangeEnd: 1,
  551. children: [
  552. {
  553. instanceOf: Element,
  554. name: 'u',
  555. priority: 1,
  556. children: [
  557. { instanceOf: Text, data: 'foobarbaz' }
  558. ]
  559. }
  560. ]
  561. } );
  562. } );
  563. it( 'should unwrap mixed ranges #1', () => {
  564. // <p>[<u><b>{foo}]</b></u></p> -> <p>[<u>{foo}</u>]</p
  565. const created = create( writer, {
  566. instanceOf: Element,
  567. name: 'p',
  568. rangeStart: 0,
  569. children: [
  570. {
  571. instanceOf: Element,
  572. name: 'u',
  573. priority: 1,
  574. children: [
  575. {
  576. instanceOf: Element,
  577. name: 'b',
  578. priority: 1,
  579. rangeEnd: 1,
  580. children: [
  581. { instanceOf: Text, data: 'foo' }
  582. ]
  583. }
  584. ]
  585. }
  586. ]
  587. } );
  588. const b = new Element( 'b' );
  589. writer.setPriority( b, 1 );
  590. const newRange = writer.unwrap( created.range, b );
  591. test( writer, newRange, created.node, {
  592. instanceOf: Element,
  593. name: 'p',
  594. rangeStart: 0,
  595. rangeEnd: 1,
  596. children: [
  597. {
  598. instanceOf: Element,
  599. name: 'u',
  600. priority: 1,
  601. children: [
  602. { instanceOf: Text, data: 'foo' }
  603. ]
  604. }
  605. ]
  606. } );
  607. } );
  608. it( 'should unwrap mixed ranges #2', () => {
  609. // <p>[<u><b>{foo]}</b></u></p> -> <p>[<u>{foo}</u>]</p
  610. const created = create( writer, {
  611. instanceOf: Element,
  612. name: 'p',
  613. rangeStart: 0,
  614. children: [
  615. {
  616. instanceOf: Element,
  617. name: 'u',
  618. priority: 1,
  619. children: [
  620. {
  621. instanceOf: Element,
  622. name: 'b',
  623. priority: 1,
  624. children: [
  625. { instanceOf: Text, data: 'foo', rangeEnd: 3 }
  626. ]
  627. }
  628. ]
  629. }
  630. ]
  631. } );
  632. const b = new Element( 'b' );
  633. writer.setPriority( b, 1 );
  634. const newRange = writer.unwrap( created.range, b );
  635. test( writer, newRange, created.node, {
  636. instanceOf: Element,
  637. name: 'p',
  638. rangeStart: 0,
  639. rangeEnd: 1,
  640. children: [
  641. {
  642. instanceOf: Element,
  643. name: 'u',
  644. priority: 1,
  645. children: [
  646. { instanceOf: Text, data: 'foo' }
  647. ]
  648. }
  649. ]
  650. } );
  651. } );
  652. } );
  653. } );