wrap.js 11 KB

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