wrap.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  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/engine/treeview/writer.js';
  8. import ContainerElement from '/ckeditor5/engine/treeview/containerelement.js';
  9. import AttributeElement from '/ckeditor5/engine/treeview/attributeelement.js';
  10. import Position from '/ckeditor5/engine/treeview/position.js';
  11. import Range from '/ckeditor5/engine/treeview/range.js';
  12. import Text from '/ckeditor5/engine/treeview/text.js';
  13. import utils from '/tests/engine/treeview/writer/_utils/utils.js';
  14. import CKEditorError from '/ckeditor5/utils/ckeditorerror.js';
  15. describe( 'Writer', () => {
  16. const create = utils.create;
  17. const test = utils.test;
  18. let writer;
  19. beforeEach( () => {
  20. writer = new Writer();
  21. } );
  22. describe( 'wrap', () => {
  23. it( 'should do nothing on collapsed ranges', () => {
  24. const description = {
  25. instanceOf: ContainerElement,
  26. name: 'p',
  27. children: [
  28. { instanceOf: Text, data: 'foo', rangeStart: 1, rangeEnd: 1 }
  29. ]
  30. };
  31. const created = create( writer, description );
  32. const newRange = writer.wrap( created.range, new AttributeElement( 'b' ), 1 );
  33. test( writer, newRange, created.node, description );
  34. } );
  35. it( 'wraps single text node', () => {
  36. // <p>[{foobar}]</p>
  37. // wrap <b>
  38. // <p>[<b>{foobar}<b>]</p>
  39. const created = create( writer, {
  40. instanceOf: ContainerElement,
  41. name: 'p',
  42. rangeStart: 0,
  43. rangeEnd: 1,
  44. children: [
  45. { instanceOf: Text, data: 'foobar' }
  46. ]
  47. } );
  48. const b = new AttributeElement( 'b' );
  49. const newRange = writer.wrap( created.range, b, 1 );
  50. test( writer, newRange, created.node, {
  51. instanceOf: ContainerElement,
  52. name: 'p',
  53. rangeStart: 0,
  54. rangeEnd: 1,
  55. children: [
  56. {
  57. instanceOf: AttributeElement,
  58. name: 'b',
  59. priority: 1,
  60. children: [
  61. { instanceOf: Text, data: 'foobar' }
  62. ]
  63. }
  64. ]
  65. } );
  66. } );
  67. it( 'should throw error when range placed in two containers', () => {
  68. const container1 = new ContainerElement( 'p' );
  69. const container2 = new ContainerElement( 'p' );
  70. const range = new Range(
  71. new Position( container1, 0 ),
  72. new Position( container2, 1 )
  73. );
  74. const b = new AttributeElement( 'b' );
  75. expect( () => {
  76. writer.wrap( range, b, 1 );
  77. } ).to.throw( CKEditorError, 'treeview-writer-invalid-range-container' );
  78. } );
  79. it( 'wraps part of a single text node #1', () => {
  80. // <p>[{foo]bar}</p>
  81. // wrap with <b>
  82. // <p>[<b>{foo}</b>]{bar}</p>
  83. const created = create( writer, {
  84. instanceOf: ContainerElement,
  85. name: 'p',
  86. rangeStart: 0,
  87. children: [
  88. { instanceOf: Text, data: 'foobar', rangeEnd: 3 }
  89. ]
  90. } );
  91. const b = new AttributeElement( 'b' );
  92. const newRange = writer.wrap( created.range, b, 2 );
  93. test( writer, newRange, created.node, {
  94. instanceOf: ContainerElement,
  95. name: 'p',
  96. rangeStart: 0,
  97. rangeEnd: 1,
  98. children: [
  99. {
  100. instanceOf: AttributeElement,
  101. name: 'b',
  102. priority: 2,
  103. children: [
  104. { instanceOf: Text, data: 'foo' }
  105. ]
  106. },
  107. { instanceOf: Text, data: 'bar' }
  108. ]
  109. } );
  110. } );
  111. it( 'wraps part of a single text node #2', () => {
  112. // <p>{[foo]bar}</p>
  113. // wrap with <b>
  114. // <p>[<b>{foo}</b>]{bar}</p>
  115. const created = create( writer, {
  116. instanceOf: ContainerElement,
  117. name: 'p',
  118. children: [
  119. { instanceOf: Text, data: 'foobar', rangeStart: 0, rangeEnd: 3 }
  120. ]
  121. } );
  122. const b = new AttributeElement( 'b' );
  123. const newRange = writer.wrap( created.range, b, 2 );
  124. test( writer, newRange, created.node, {
  125. instanceOf: ContainerElement,
  126. name: 'p',
  127. rangeStart: 0,
  128. rangeEnd: 1,
  129. children: [
  130. {
  131. instanceOf: AttributeElement,
  132. name: 'b',
  133. priority: 2,
  134. children: [
  135. { instanceOf: Text, data: 'foo' }
  136. ]
  137. },
  138. { instanceOf: Text, data: 'bar' }
  139. ]
  140. } );
  141. } );
  142. it( 'wraps part of a single text node #3', () => {
  143. // <p>{foo[bar]}</p>
  144. // wrap with <b>
  145. // <p>{foo}[<b>{bar}</b>]</p>
  146. const created = create( writer, {
  147. instanceOf: ContainerElement,
  148. name: 'p',
  149. children: [
  150. { instanceOf: Text, data: 'foobar', rangeStart: 3, rangeEnd: 6 }
  151. ]
  152. } );
  153. const b = new AttributeElement( 'b' );
  154. const newRange = writer.wrap( created.range, b, 2 );
  155. test( writer, newRange, created.node, {
  156. instanceOf: ContainerElement,
  157. name: 'p',
  158. rangeStart: 1,
  159. rangeEnd: 2,
  160. children: [
  161. { instanceOf: Text, data: 'foo' },
  162. {
  163. instanceOf: AttributeElement,
  164. name: 'b',
  165. priority: 2,
  166. children: [
  167. { instanceOf: Text, data: 'bar' }
  168. ]
  169. }
  170. ]
  171. } );
  172. } );
  173. it( 'should not wrap inside nested containers', () => {
  174. // <div>[{foobar}<p>{baz}</p>]</div>
  175. // wrap with <b>
  176. // <div>[<b>{foobar}</b><p>{baz}</p>]</div>
  177. const created = create( writer, {
  178. instanceOf: ContainerElement,
  179. name: 'div',
  180. rangeStart: 0,
  181. rangeEnd: 2,
  182. children: [
  183. { instanceOf: Text, data: 'foobar' },
  184. {
  185. instanceOf: ContainerElement,
  186. name: 'p',
  187. children: [
  188. { instanceOf: Text, data: 'baz' }
  189. ]
  190. }
  191. ]
  192. } );
  193. const newRange = writer.wrap( created.range, new AttributeElement( 'b' ), 1 );
  194. test( writer, newRange, created.node, {
  195. instanceOf: ContainerElement,
  196. name: 'div',
  197. rangeStart: 0,
  198. rangeEnd: 2,
  199. children: [
  200. {
  201. instanceOf: AttributeElement,
  202. name: 'b',
  203. priority: 1,
  204. children: [
  205. { instanceOf: Text, data: 'foobar' }
  206. ]
  207. },
  208. {
  209. instanceOf: ContainerElement,
  210. name: 'p',
  211. children: [
  212. { instanceOf: Text, data: 'baz' }
  213. ]
  214. }
  215. ]
  216. } );
  217. } );
  218. it( 'wraps according to priorities', () => {
  219. // <p>[<u>{foobar}</u>]</p>
  220. // wrap with <b> that has higher priority than <u>
  221. // <p>[<u><b>{foobar}</b></u>]</p>
  222. const created = create( writer, {
  223. instanceOf: ContainerElement,
  224. name: 'p',
  225. rangeStart: 0,
  226. rangeEnd: 1,
  227. children: [
  228. {
  229. instanceOf: AttributeElement,
  230. name: 'u',
  231. priority: 1,
  232. children: [
  233. { instanceOf: Text, data: 'foobar' }
  234. ]
  235. }
  236. ]
  237. } );
  238. const b = new AttributeElement( 'b' );
  239. const newRange = writer.wrap( created.range, b, 2 );
  240. test( writer, newRange, created.node, {
  241. instanceOf: ContainerElement,
  242. name: 'p',
  243. rangeStart: 0,
  244. rangeEnd: 1,
  245. children: [
  246. {
  247. instanceOf: AttributeElement,
  248. name: 'u',
  249. priority: 1,
  250. children: [
  251. {
  252. instanceOf: AttributeElement,
  253. name: 'b',
  254. priority: 2,
  255. children: [
  256. { instanceOf: Text, data: 'foobar' }
  257. ]
  258. }
  259. ]
  260. }
  261. ]
  262. } );
  263. } );
  264. it( 'merges wrapped nodes #1', () => {
  265. // <p>[<b>{foo}</b>{bar}<b>{baz}</b>]</p>
  266. // wrap with <b>
  267. // <p>[<b>{foobarbaz}</b>]</p>
  268. const created = create( writer, {
  269. instanceOf: ContainerElement,
  270. name: 'p',
  271. rangeStart: 0,
  272. rangeEnd: 3,
  273. children: [
  274. {
  275. instanceOf: AttributeElement,
  276. name: 'b',
  277. priority: 1,
  278. children: [
  279. { instanceOf: Text, data: 'foo' }
  280. ]
  281. },
  282. { instanceOf: Text, data: 'bar' },
  283. {
  284. instanceOf: AttributeElement,
  285. name: 'b',
  286. priority: 1,
  287. children: [
  288. { instanceOf: Text, data: 'baz' }
  289. ]
  290. }
  291. ]
  292. } );
  293. const b = new AttributeElement( 'b' );
  294. const newRange = writer.wrap( created.range, b, 1 );
  295. test( writer, newRange, created.node, {
  296. instanceOf: ContainerElement,
  297. name: 'p',
  298. rangeStart: 0,
  299. rangeEnd: 1,
  300. children: [
  301. {
  302. instanceOf: AttributeElement,
  303. name: 'b',
  304. priority: 1,
  305. children: [
  306. { instanceOf: Text, data: 'foobarbaz' }
  307. ]
  308. }
  309. ]
  310. } );
  311. } );
  312. it( 'merges wrapped nodes #2', () => {
  313. // <p><b>{foo}</b>[{bar]baz}</p>
  314. // wrap with <b>
  315. // <p><b>{foo[bar}</b>]{baz}</p>
  316. const created = create( writer, {
  317. instanceOf: ContainerElement,
  318. name: 'p',
  319. rangeStart: 1,
  320. children: [
  321. {
  322. instanceOf: AttributeElement,
  323. name: 'b',
  324. priority: 1,
  325. children: [
  326. { instanceOf: Text, data: 'foo' }
  327. ]
  328. },
  329. { instanceOf: Text, data: 'barbaz', rangeEnd: 3 }
  330. ]
  331. } );
  332. const newRange = writer.wrap( created.range, new AttributeElement( 'b' ), 1 );
  333. test( writer, newRange, created.node, {
  334. instanceOf: ContainerElement,
  335. name: 'p',
  336. rangeEnd: 1,
  337. children: [
  338. {
  339. instanceOf: AttributeElement,
  340. name: 'b',
  341. priority: 1,
  342. children: [
  343. { instanceOf: Text, data: 'foobar', rangeStart: 3 }
  344. ]
  345. },
  346. { instanceOf: Text, data: 'baz' }
  347. ]
  348. } );
  349. } );
  350. it( 'merges wrapped nodes #3', () => {
  351. // <p><b>{foobar}</b>[{baz}]</p>
  352. // wrap with <b>
  353. // <p><b>{foobar[baz}</b>]</p>
  354. const created = create( writer, {
  355. instanceOf: ContainerElement,
  356. name: 'p',
  357. rangeStart: 1,
  358. rangeEnd: 2,
  359. children: [
  360. {
  361. instanceOf: AttributeElement,
  362. name: 'b',
  363. priority: 1,
  364. children: [
  365. { instanceOf: Text, data: 'foobar' }
  366. ]
  367. },
  368. { instanceOf: Text, data: 'baz', rangeEnd: 3 }
  369. ]
  370. } );
  371. const newRange = writer.wrap( created.range, new AttributeElement( 'b' ), 1 );
  372. test( writer, newRange, created.node, {
  373. instanceOf: ContainerElement,
  374. name: 'p',
  375. rangeEnd: 1,
  376. children: [
  377. {
  378. instanceOf: AttributeElement,
  379. name: 'b',
  380. priority: 1,
  381. children: [
  382. { instanceOf: Text, data: 'foobarbaz', rangeStart: 6 }
  383. ]
  384. }
  385. ]
  386. } );
  387. } );
  388. it( 'merges wrapped nodes #4', () => {
  389. // <p>[{foo}<i>{bar}</i>]{baz}</p>
  390. // wrap with <b>
  391. // <p>[<b>{foo}<i>{bar}</i></b>]{baz}</p>
  392. const created = create( writer, {
  393. instanceOf: ContainerElement,
  394. name: 'p',
  395. rangeStart: 0,
  396. rangeEnd: 2,
  397. children: [
  398. { instanceOf: Text, data: 'foo' },
  399. {
  400. instanceOf: AttributeElement,
  401. name: 'i',
  402. priority: 1,
  403. children: [
  404. { instanceOf: Text, data: 'bar' }
  405. ]
  406. },
  407. { instanceOf: Text, data: 'baz' }
  408. ]
  409. } );
  410. const newRange = writer.wrap( created.range, new AttributeElement( 'b' ), 1 );
  411. test( writer, newRange, created.node, {
  412. instanceOf: ContainerElement,
  413. name: 'p',
  414. rangeStart: 0,
  415. rangeEnd: 1,
  416. children: [
  417. {
  418. instanceOf: AttributeElement,
  419. name: 'b',
  420. priority: 1,
  421. children: [
  422. { instanceOf: Text, data: 'foo' },
  423. {
  424. instanceOf: AttributeElement,
  425. name: 'i',
  426. priority: 1,
  427. children: [
  428. { instanceOf: Text, data: 'bar' }
  429. ]
  430. }
  431. ]
  432. },
  433. { instanceOf: Text, data: 'baz' }
  434. ]
  435. } );
  436. } );
  437. it( 'merges wrapped nodes #5', () => {
  438. // <p>[{foo}<i>{bar}</i>{baz}]</p>
  439. // wrap with <b>, that has higher priority than <i>
  440. // <p>[<b>{foo}</b><i><b>{bar}</b></i><b>{baz}</b>]</p>
  441. const created = create( writer, {
  442. instanceOf: ContainerElement,
  443. name: 'p',
  444. rangeStart: 0,
  445. rangeEnd: 3,
  446. children: [
  447. { instanceOf: Text, data: 'foo' },
  448. {
  449. instanceOf: AttributeElement,
  450. name: 'i',
  451. priority: 1,
  452. children: [
  453. { instanceOf: Text, data: 'bar' }
  454. ]
  455. },
  456. { instanceOf: Text, data: 'baz' }
  457. ]
  458. } );
  459. const newRange = writer.wrap( created.range, new AttributeElement( 'b' ), 2 );
  460. test( writer, newRange, created.node, {
  461. instanceOf: ContainerElement,
  462. name: 'p',
  463. rangeStart: 0,
  464. rangeEnd: 3,
  465. children: [
  466. {
  467. instanceOf: AttributeElement,
  468. name: 'b',
  469. priority: 2,
  470. children: [
  471. { instanceOf: Text, data: 'foo' }
  472. ]
  473. },
  474. {
  475. instanceOf: AttributeElement,
  476. name: 'i',
  477. priority: 1,
  478. children: [
  479. {
  480. instanceOf: AttributeElement,
  481. name: 'b',
  482. priority: 2,
  483. children: [
  484. { instanceOf: Text, data: 'bar' }
  485. ]
  486. }
  487. ]
  488. },
  489. {
  490. instanceOf: AttributeElement,
  491. name: 'b',
  492. priority: 2,
  493. children: [
  494. { instanceOf: Text, data: 'baz' }
  495. ]
  496. }
  497. ]
  498. } );
  499. } );
  500. } );
  501. } );