writer.js 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384
  1. /**
  2. * @license Copyright (c) 2003-2015, 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 Text from '/ckeditor5/core/treeview/text.js';
  10. import Position from '/ckeditor5/core/treeview/position.js';
  11. import Range from '/ckeditor5/core/treeview/range.js';
  12. describe( 'Writer', () => {
  13. /**
  14. * Helper function that is used to test output of writer methods by providing declarative description of the
  15. * expected output.
  16. * Examples:
  17. * test element: `<p>fo[o<b>ba]r</b></p>`
  18. * description: {
  19. * name: 'p',
  20. * instanceOf: Element,
  21. * children:[
  22. * {
  23. * instanceOf: Text,
  24. * data: 'foo',
  25. * rangeStart: 2
  26. * },
  27. * {
  28. * name: 'b'
  29. * instanceOf: Element
  30. * priority: 1,
  31. * children: [
  32. * { instanceOf: Text, data: 'bar', rangeEnd: 2 }
  33. * ]
  34. * }
  35. * ]
  36. * }
  37. *
  38. *
  39. * @param {core.treeView.Writer} writer Writer instance. Used to test priority.
  40. * @param {core.treeView.Range|core.treeView.Position } location Range instance or Position instance.
  41. * Treated as Range when when `rangeStart`, `rangeEnd` is used, treated as Position when `position` is used.
  42. * @param {core.treeView.Node} node Element to check.
  43. * @param {Object} description Object describing expected element and its children.
  44. */
  45. function test( writer, location, node, description ) {
  46. if ( description.instanceOf ) {
  47. expect( node ).to.be.instanceof( description.instanceOf );
  48. }
  49. if ( description.name ) {
  50. expect( node.name ).to.equal( description.name );
  51. }
  52. if ( description.data ) {
  53. expect( node.data ).to.equal( description.data );
  54. }
  55. if ( description.priority !== undefined ) {
  56. expect( writer.getPriority( node ) ).to.equal( description.priority );
  57. }
  58. if ( description.rangeStart !== undefined ) {
  59. expect( location.start.parent ).to.equal( node );
  60. expect( location.start.offset ).to.equal( description.rangeStart );
  61. }
  62. if ( description.attributes ) {
  63. Object.keys( description.attributes ).forEach( ( key ) => {
  64. expect( node.getAttribute( key ) ).to.equal( description.attributes[ key ] );
  65. } );
  66. }
  67. if ( description.rangeEnd !== undefined ) {
  68. expect( location.end.parent ).to.equal( node );
  69. expect( location.end.offset ).to.equal( description.rangeEnd );
  70. }
  71. if ( description.position !== undefined ) {
  72. expect( location.parent ).to.equal( node );
  73. expect( location.offset ).to.equal( description.position );
  74. }
  75. if ( description.children ) {
  76. expect( node.getChildCount() ).to.equal( description.children.length );
  77. description.children.forEach( ( desc, index ) => {
  78. test( writer, location, node.getChild( index ), desc );
  79. } );
  80. }
  81. }
  82. /**
  83. * Helper function that is used to create treeView elements from description object.
  84. *
  85. * @param {core.treeView.Writer} writer Writer instance. Used to set priorities.
  86. * @param {Object} description Description object.
  87. * @param {core.treeView.Range} [range] Optional parameter, used in recurrent calls.
  88. * @param {core.treeView.Position} [position] Optional parameter, used in recurrent calls.
  89. * @returns {Object} Returns object with `node`, `range`, `position` fields, containing created node and, optionally
  90. * range and position if description object contain information about them.
  91. */
  92. function create( writer, description, range, position ) {
  93. const node = new description.instanceOf();
  94. if ( !range ) {
  95. range = Range.createFromParentsAndOffsets( node, 0, node, 0 );
  96. }
  97. if ( !position ) {
  98. position = new Position( node, 0 );
  99. }
  100. if ( description.name ) {
  101. node.name = description.name;
  102. }
  103. if ( description.data ) {
  104. node.data = description.data;
  105. }
  106. if ( description.attributes ) {
  107. Object.keys( description.attributes ).forEach( ( key ) => {
  108. node.setAttribute( key, description.attributes[ key ] );
  109. } );
  110. }
  111. if ( description.priority !== undefined ) {
  112. writer.setPriority( node, description.priority );
  113. }
  114. if ( description.rangeStart !== undefined ) {
  115. range.start.parent = node;
  116. range.start.offset = description.rangeStart;
  117. }
  118. if ( description.rangeEnd !== undefined ) {
  119. range.end.parent = node;
  120. range.end.offset = description.rangeEnd;
  121. }
  122. if ( description.position !== undefined ) {
  123. position.parent = node;
  124. position.offset = description.position;
  125. }
  126. if ( description.children ) {
  127. description.children.forEach( ( desc, index ) => {
  128. const created = create( writer, desc, range, position );
  129. node.insertChildren( index, created.node );
  130. } );
  131. }
  132. return { node, range, position };
  133. }
  134. describe( 'isContainer', () => {
  135. it( 'should return true for container elements', () => {
  136. const containerElement = new Element( 'p' );
  137. const attributeElement = new Element( 'b' );
  138. const writer = new Writer();
  139. writer._priorities.set( attributeElement, 1 );
  140. expect( writer.isContainer( containerElement ) ).to.be.true;
  141. expect( writer.isContainer( attributeElement ) ).to.be.false;
  142. } );
  143. } );
  144. describe( 'isAttribute', () => {
  145. it( 'should return true for container elements', () => {
  146. const containerElement = new Element( 'p' );
  147. const attributeElement = new Element( 'b' );
  148. const writer = new Writer();
  149. writer._priorities.set( attributeElement, 1 );
  150. expect( writer.isAttribute( containerElement ) ).to.be.false;
  151. expect( writer.isAttribute( attributeElement ) ).to.be.true;
  152. } );
  153. } );
  154. describe( 'setPriority', () => {
  155. it( 'sets node priority', () => {
  156. const writer = new Writer();
  157. const nodeMock = {};
  158. writer.setPriority( nodeMock, 10 );
  159. expect( writer._priorities.get( nodeMock ) ).to.equal( 10 );
  160. } );
  161. } );
  162. describe( 'getPriority', () => {
  163. it( 'gets node priority', () => {
  164. const writer = new Writer();
  165. const nodeMock = {};
  166. writer._priorities.set( nodeMock, 12 );
  167. expect( writer.getPriority( nodeMock ) ).to.equal( 12 );
  168. } );
  169. } );
  170. describe( 'breakAttributes', () => {
  171. // <p>{|foobar}</p> -> <p>|{foobar}</p>
  172. it( '<p>{|foobar}</p>', () => {
  173. const writer = new Writer();
  174. const created = create( writer, {
  175. instanceOf: Element,
  176. name: 'p',
  177. children: [
  178. { instanceOf: Text, data: 'foobar', position: 0 }
  179. ]
  180. } );
  181. const newPosition = writer.breakAttributes( created.position );
  182. test( writer, newPosition, created.node, {
  183. instanceOf: Element,
  184. name: 'p',
  185. position: 0,
  186. children: [
  187. { instanceOf: Text, data: 'foobar' }
  188. ]
  189. } );
  190. } );
  191. it( '<p>foo|bar</p>', () => {
  192. // <p>{foo|bar}</p> -> <p>{foo}|{bar}</p>
  193. const writer = new Writer();
  194. const created = create( writer, {
  195. instanceOf: Element,
  196. name: 'p',
  197. children: [
  198. { instanceOf: Text, data: 'foobar', position: 3 }
  199. ]
  200. } );
  201. const newPosition = writer.breakAttributes( created.position );
  202. test( writer, newPosition, created.node, {
  203. instanceOf: Element,
  204. name: 'p',
  205. position: 1,
  206. children: [
  207. { instanceOf: Text, data: 'foo' },
  208. { instanceOf: Text, data: 'bar' }
  209. ]
  210. } );
  211. } );
  212. it( '<p>{foobar|}</p>', () => {
  213. // <p>{foobar|}</p> -> <p>{foobar}|</p>
  214. const writer = new Writer();
  215. const created = create( writer, {
  216. instanceOf: Element,
  217. name: 'p',
  218. children: [
  219. { instanceOf: Text, data: 'foobar', position: 6 }
  220. ]
  221. } );
  222. const newPosition = writer.breakAttributes( created.position );
  223. test( writer, newPosition, created.node, {
  224. instanceOf: Element,
  225. name: 'p',
  226. position: 1,
  227. children: [
  228. { instanceOf: Text, data: 'foobar' }
  229. ]
  230. } );
  231. } );
  232. it( '<p><b>{foo|bar}</b></p>', () => {
  233. // <p><b>{foo|bar}</b></p> -> <p><b>{foo}</b>|<b>{bar}</b></p>
  234. const writer = new Writer();
  235. const created = create( writer, {
  236. instanceOf: Element,
  237. name: 'p',
  238. children: [
  239. {
  240. instanceOf: Element,
  241. name: 'b',
  242. priority: 1,
  243. children: [
  244. { instanceOf: Text, data: 'foobar', position: 3 }
  245. ]
  246. }
  247. ]
  248. } );
  249. const newPosition = writer.breakAttributes( created.position );
  250. test( writer, newPosition, created.node, {
  251. instanceOf: Element,
  252. name: 'p',
  253. position: 1,
  254. children: [
  255. {
  256. instanceOf: Element,
  257. name: 'b',
  258. priority: 1,
  259. children: [
  260. { instanceOf: Text, data: 'foo' }
  261. ]
  262. },
  263. {
  264. instanceOf: Element,
  265. name: 'b',
  266. priority: 1,
  267. children: [
  268. { instanceOf: Text, data: 'bar' }
  269. ]
  270. }
  271. ]
  272. } );
  273. } );
  274. it( '<p><b><u>{|foobar}</u></b></p>', () => {
  275. // <p><b><u>{|foobar}</u></b></p> -> <p>|<b><u>{foobar}</u></b></p>
  276. const writer = new Writer();
  277. const created = create( writer, {
  278. instanceOf: Element,
  279. name: 'p',
  280. children: [
  281. {
  282. instanceOf: Element,
  283. name: 'b',
  284. priority: 1,
  285. children: [
  286. {
  287. instanceOf: Element,
  288. name: 'u',
  289. priority: 1,
  290. children: [
  291. { instanceOf: Text, data: 'foobar', position: 0 }
  292. ]
  293. }
  294. ]
  295. }
  296. ]
  297. } );
  298. const newPosition = writer.breakAttributes( created.position );
  299. test( writer, newPosition, created.node, {
  300. instanceOf: Element,
  301. name: 'p',
  302. position: 0,
  303. children: [
  304. {
  305. instanceOf: Element,
  306. name: 'b',
  307. priority: 1,
  308. children: [
  309. {
  310. instanceOf: Element,
  311. name: 'u',
  312. priority: 1,
  313. children: [
  314. { instanceOf: Text, data: 'foobar' }
  315. ]
  316. }
  317. ]
  318. }
  319. ]
  320. } );
  321. } );
  322. // <p><b><u>{foo|ba}r</u></b></p> -> <p><b><u>{foo}</u></b>|<b></u>{bar}</u></b></p>
  323. it( '<p><b><u>{foo|bar}</u></b></p>', () => {
  324. const writer = new Writer();
  325. const created = create( writer, {
  326. instanceOf: Element,
  327. name: 'p',
  328. children: [
  329. {
  330. instanceOf: Element,
  331. name: 'b',
  332. priority: 1,
  333. children: [
  334. {
  335. instanceOf: Element,
  336. name: 'u',
  337. priority: 1,
  338. children: [
  339. { instanceOf: Text, data: 'foobar', position: 3 }
  340. ]
  341. }
  342. ]
  343. }
  344. ]
  345. } );
  346. const newPosition = writer.breakAttributes( created.position );
  347. test( writer, newPosition, created.node, {
  348. instanceOf: Element,
  349. name: 'p',
  350. position: 1,
  351. children: [
  352. {
  353. instanceOf: Element,
  354. name: 'b',
  355. priority: 1,
  356. children: [
  357. {
  358. instanceOf: Element,
  359. name: 'u',
  360. priority: 1,
  361. children: [
  362. { instanceOf: Text, data: 'foo' }
  363. ]
  364. }
  365. ]
  366. },
  367. {
  368. instanceOf: Element,
  369. name: 'b',
  370. priority: 1,
  371. children: [
  372. {
  373. instanceOf: Element,
  374. name: 'u',
  375. priority: 1,
  376. children: [
  377. { instanceOf: Text, data: 'bar' }
  378. ]
  379. }
  380. ]
  381. }
  382. ]
  383. } );
  384. } );
  385. it( '<p><b><u>{foobar|}</u></b></p>', () => {
  386. // <p><b><u>{foobar|}</u></b></p> -> <p><b><u>{foobar}</u></b>|</p>
  387. const writer = new Writer();
  388. const created = create( writer, {
  389. instanceOf: Element,
  390. name: 'p',
  391. children: [
  392. {
  393. instanceOf: Element,
  394. name: 'b',
  395. priority: 1,
  396. children: [
  397. {
  398. instanceOf: Element,
  399. name: 'u',
  400. priority: 1,
  401. children: [
  402. { instanceOf: Text, data: 'foobar', position: 6 }
  403. ]
  404. }
  405. ]
  406. }
  407. ]
  408. } );
  409. const newPosition = writer.breakAttributes( created.position );
  410. test( writer, newPosition, created.node, {
  411. instanceOf: Element,
  412. name: 'p',
  413. position: 1,
  414. children: [
  415. {
  416. instanceOf: Element,
  417. name: 'b',
  418. priority: 1,
  419. children: [
  420. {
  421. instanceOf: Element,
  422. name: 'u',
  423. priority: 1,
  424. children: [
  425. { instanceOf: Text, data: 'foobar' }
  426. ]
  427. }
  428. ]
  429. }
  430. ]
  431. } );
  432. } );
  433. } );
  434. describe( 'mergeAttributes', () => {
  435. it( 'should not merge if inside text node', () => {
  436. // <p>{fo|obar}</p>
  437. const writer = new Writer();
  438. const description = {
  439. instanceOf: Element,
  440. name: 'p',
  441. children: [
  442. { instanceOf: Text, data: 'foobar', position: 2 }
  443. ]
  444. };
  445. const created = create( writer, description );
  446. const newPosition = writer.mergeAttributes( created.position );
  447. test( writer, newPosition, created.node, description );
  448. } );
  449. it( 'should return same position when inside empty container', () => {
  450. // <p>|</p>
  451. const writer = new Writer();
  452. const description = { instanceOf: Element, name: 'p', position: 0 };
  453. const created = create( writer, description );
  454. const newPosition = writer.mergeAttributes( created.position );
  455. test( writer, newPosition, created.node, description );
  456. } );
  457. it( 'should not merge when position is placed at the beginning of the container', () => {
  458. // <p>|<b></b></p>
  459. const writer = new Writer();
  460. const description = {
  461. instanceOf: Element,
  462. name: 'p',
  463. position: 0,
  464. children: [
  465. {
  466. instanceOf: Element,
  467. name: 'b',
  468. priority: 1
  469. }
  470. ]
  471. };
  472. const created = create( writer, description );
  473. const newPosition = writer.mergeAttributes( created.position );
  474. test( writer, newPosition, created.node, description );
  475. } );
  476. it( 'should not merge when position is placed at the end of the container', () => {
  477. // <p><b></b>|</p>
  478. const writer = new Writer();
  479. const description = {
  480. instanceOf: Element,
  481. name: 'p',
  482. position: 1,
  483. children: [
  484. {
  485. instanceOf: Element,
  486. name: 'b',
  487. priority: 1
  488. }
  489. ]
  490. };
  491. const created = create( writer, description );
  492. const newPosition = writer.mergeAttributes( created.position );
  493. test( writer, newPosition, created.node, description );
  494. } );
  495. it( 'should merge when placed between two text nodes', () => {
  496. // <p>{foo}|{bar}</p> -> <p>{foo|bar}</p>
  497. const writer = new Writer();
  498. const created = create( writer, {
  499. instanceOf: Element,
  500. name: 'p',
  501. position: 1,
  502. children: [
  503. { instanceOf: Text, data: 'foo' },
  504. { instanceOf: Text, data: 'bar' }
  505. ]
  506. } );
  507. const newPosition = writer.mergeAttributes( created.position );
  508. test( writer, newPosition, created.node, {
  509. instanceOf: Element,
  510. name: 'p',
  511. children: [
  512. { instanceOf: Text, data: 'foobar', position: 3 }
  513. ]
  514. } );
  515. } );
  516. it( 'should merge when placed between similar attribute nodes', () => {
  517. // <p><b foo="bar"></b>|<b foo="bar"></b></p> -> <p><b foo="bar">|</b></p>
  518. const writer = new Writer();
  519. const created = create( writer, {
  520. instanceOf: Element,
  521. name: 'p',
  522. position: 1,
  523. children: [
  524. {
  525. instanceOf: Element,
  526. name: 'b',
  527. priority: 1,
  528. attributes: { foo: 'bar' }
  529. },
  530. {
  531. instanceOf: Element,
  532. name: 'b',
  533. priority: 1,
  534. attributes: { foo: 'bar' }
  535. }
  536. ]
  537. } );
  538. const newPosition = writer.mergeAttributes( created.position );
  539. test( writer, newPosition, created.node, {
  540. instanceOf: Element,
  541. name: 'p',
  542. children: [
  543. {
  544. instanceOf: Element,
  545. name: 'b',
  546. priority: 1,
  547. position: 0,
  548. attributes: { foo: 'bar' }
  549. }
  550. ]
  551. } );
  552. } );
  553. it( 'should not merge when placed between non-similar attribute nodes', () => {
  554. // <p><b foo="bar"></b>|<b foo="baz"></b></p> ->
  555. // <p><b foo="bar"></b>|<b foo="baz"></b></p>
  556. const writer = new Writer();
  557. const description = {
  558. instanceOf: Element,
  559. name: 'p',
  560. position: 1,
  561. children: [
  562. {
  563. instanceOf: Element,
  564. name: 'b',
  565. priority: 1,
  566. attributes: { foo: 'bar' }
  567. },
  568. {
  569. instanceOf: Element,
  570. name: 'b',
  571. priority: 1,
  572. attributes: { foo: 'baz' }
  573. }
  574. ]
  575. };
  576. const created = create( writer, description );
  577. const newPosition = writer.mergeAttributes( created.position );
  578. test( writer, newPosition, created.node, description );
  579. } );
  580. it( 'should not merge when placed between similar attribute nodes with different priority', () => {
  581. // <p><b foo="bar"></b>|<b foo="bar"></b></p> -> <p><b foo="bar"></b>|<b foo="bar"></b></p>
  582. const writer = new Writer();
  583. const description = {
  584. instanceOf: Element,
  585. name: 'p',
  586. position: 1,
  587. children: [
  588. {
  589. instanceOf: Element,
  590. name: 'b',
  591. priority: 1,
  592. attributes: { foo: 'bar' }
  593. },
  594. {
  595. instanceOf: Element,
  596. name: 'b',
  597. priority: 2,
  598. attributes: { foo: 'bar' }
  599. }
  600. ]
  601. };
  602. const created = create( writer,description );
  603. const newPosition = writer.mergeAttributes( created.position );
  604. test( writer, newPosition, created.node, description );
  605. } );
  606. it( 'should merge attribute nodes and their contents if possible', () => {
  607. // <p><b foo="bar">{foo}</b>|<b foo="bar">{bar}</b></p>
  608. // <p><b foo="bar">{foo}|{bar}</b></p>
  609. // <p><b foo="bar">{foo|bar}</b></p>
  610. const writer = new Writer();
  611. const created = create( writer, {
  612. instanceOf: Element,
  613. name: 'p',
  614. position: 1,
  615. children: [
  616. {
  617. instanceOf: Element,
  618. name: 'b',
  619. priority: 1,
  620. attributes: { foo: 'bar' },
  621. children: [
  622. { instanceOf: Text, data: 'foo' }
  623. ]
  624. },
  625. {
  626. instanceOf: Element,
  627. name: 'b',
  628. priority: 1,
  629. attributes: { foo: 'bar' },
  630. children: [
  631. { instanceOf: Text, data: 'bar' }
  632. ]
  633. }
  634. ]
  635. } );
  636. const newPosition = writer.mergeAttributes( created.position );
  637. test( writer, newPosition, created.node, {
  638. instanceOf: Element,
  639. name: 'p',
  640. children: [
  641. {
  642. instanceOf: Element,
  643. name: 'b',
  644. priority: 1,
  645. attributes: { foo: 'bar' },
  646. children: [
  647. { instanceOf: Text, data: 'foobar', position: 3 }
  648. ]
  649. }
  650. ]
  651. } );
  652. } );
  653. } );
  654. describe( 'insert', () => {
  655. it( 'should insert text', () => {
  656. // <p>{foo|bar}</p> insert {baz}
  657. // <p>{foo[baz]bar}</p>
  658. const writer = new Writer();
  659. const created = create( writer, {
  660. instanceOf: Element,
  661. name: 'p',
  662. children: [
  663. { instanceOf: Text, data: 'foobar', position: 3 }
  664. ]
  665. } );
  666. const newRange = writer.insert( created.position, new Text( 'baz' ) );
  667. test( writer, newRange, created.node, {
  668. instanceOf: Element,
  669. name: 'p',
  670. children: [
  671. { instanceOf: Text, data: 'foobazbar' }
  672. ]
  673. } );
  674. } );
  675. it( 'should merge attributes', () => {
  676. // <p><b>{foo|bar}</b></p> insert <b>{baz}</b>
  677. // <p><b>{foobazbar}</b></p>
  678. const writer = new Writer();
  679. const text = new Text( 'foobar' );
  680. const b1 = new Element( 'b', null, text );
  681. const p = new Element( 'p', null, b1 );
  682. const position = new Position( text, 3 );
  683. const insertText = new Text( 'baz' );
  684. const b2 = new Element( 'b', null, insertText );
  685. writer.setPriority( b1, 1 );
  686. writer.setPriority( b2, 1 );
  687. writer.insert( position, b2 );
  688. expect( p.getChildCount() ).to.equal( 1 );
  689. const b3 = p.getChild( 0 );
  690. expect( b3 ).to.be.instanceof( Element );
  691. expect( b3.name ).to.equal( 'b' );
  692. expect( b3.getChildCount() ).to.equal( 1 );
  693. const newText = b3.getChild( 0 );
  694. expect( newText ).to.be.instanceof( Text );
  695. expect( newText.data ).to.equal( 'foobazbar' );
  696. } );
  697. } );
  698. describe( 'wrap', () => {
  699. //// <p>[{foobar}]</p>
  700. //// wrap <b>
  701. //// <p>[<b>{foobar}<b>]</p>
  702. //it( 'wraps single text node', () => {
  703. // const writer = new Writer();
  704. // const text = new Text( 'foobar' );
  705. // const p = new Element( 'p', null, [ text ] );
  706. // const b = new Element( 'b' );
  707. // const range = new Range(
  708. // new Position( p, 0 ),
  709. // new Position( p, 1 )
  710. // );
  711. //
  712. // const newRange = writer.wrap( range, b, 1 );
  713. //
  714. // expect( p.getChildCount() ).to.equal( 1 );
  715. // expect( p.getChild( 0 ) ).to.equal( b );
  716. // expect( b.getChildCount() ).to.equal( 1 );
  717. // expect( b.getChild( 0 ) ).to.equal( text );
  718. // expect( text.data ).to.equal( 'foobar' );
  719. // expect( newRange.start.parent ).to.equal( p );
  720. // expect( newRange.start.offset ).to.equal( 0 );
  721. // expect( newRange.end.parent ).to.equal( p );
  722. // expect( newRange.end.offset ).to.equal( 1 );
  723. //} );
  724. //// <p>[{foo]bar}</p>
  725. //// <p>[<b>{foo}</b>]{bar}</p>
  726. //it( 'wraps part of single text node', () => {
  727. // const writer = new Writer();
  728. // const text = new Text( 'foobar' );
  729. // const p = new Element( 'p', null, [ text ] );
  730. // const b = new Element( 'b' );
  731. // const range = new Range(
  732. // new Position( p, 0 ),
  733. // new Position( text, 3 )
  734. // );
  735. //
  736. // const newRange = writer.wrap( range, b, 1 );
  737. // expect( p.getChildCount() ).to.equal( 2 );
  738. // const child1 = p.getChild( 0 );
  739. // const child2 = p.getChild( 1 );
  740. //
  741. // expect( child1 ).to.be.instanceof( Element );
  742. // expect( child1.same( b ) ).to.be.true;
  743. // expect( child1.getChildCount() ).to.equal( 1 );
  744. // const newText = child1.getChild( 0 );
  745. // expect( newText ).to.be.instanceof( Text );
  746. // expect( newText.data ).to.equal( 'foo' );
  747. // expect( child2 ).to.be.instanceof( Text );
  748. // expect( child2.data ).to.equal( 'bar' );
  749. //
  750. // expect( newRange.start.parent ).to.equal( p );
  751. // expect( newRange.start.offset ).to.equal( 0 );
  752. // expect( newRange.end.parent ).to.equal( p );
  753. // expect( newRange.end.offset ).to.equal( 1 );
  754. //} );
  755. //it( 'tests', () => {
  756. // const writer = new Writer();
  757. // const text = new Text( 'foobar' );
  758. // const text2 = new Text( 'bazquix' );
  759. // const u = new Element( 'u', null, [ text2 ] );
  760. // const p = new Element( 'p', null, [ text, u ] );
  761. // const b = new Element( 'b' );
  762. // const range = new Range(
  763. // new Position( p, 0 ),
  764. // new Position( p, 2 )
  765. // );
  766. //
  767. // const newRange = writer.wrap( range, b, 1 );
  768. //
  769. // expect( p.getChildCount() ).to.equal( 1 );
  770. // //expect( p.getChild( 0 ) ).to.equal( b );
  771. // //expect( b.getChildCount() ).to.equal( 1 );
  772. // //expect( b.getChild( 0 ) ).to.equal( text );
  773. // //expect( text.data ).to.equal( 'foobar' );
  774. // //expect( newRange.start.parent ).to.equal( p );
  775. // //expect( newRange.start.offset ).to.equal( 0 );
  776. // //expect( newRange.end.parent ).to.equal( p );
  777. // //expect( newRange.end.offset ).to.equal( 1 );
  778. //} );
  779. } );
  780. describe( 'unwrap', () => {
  781. it( 'should do nothing on single text node', () => {
  782. // <p>[{foobar}]</p>
  783. const writer = new Writer();
  784. const description = {
  785. instanceOf: Element,
  786. name: 'p',
  787. rangeStart: 0,
  788. rangeEnd: 1,
  789. children: [
  790. { instanceOf: Text, data: 'foobar' }
  791. ]
  792. };
  793. const created = create( writer, description );
  794. const b = new Element( 'b' );
  795. writer.setPriority( b, 1 );
  796. const newRange = writer.unwrap( created.range, b );
  797. test( writer, newRange, created.node, description );
  798. } );
  799. it( 'should unwrap single node', () => {
  800. // <p>[<b>{foobar}</b>]<p> -> <p>[{foobar}]</p>
  801. const writer = new Writer();
  802. const created = create( writer, {
  803. instanceOf: Element,
  804. name: 'p',
  805. rangeStart: 0,
  806. rangeEnd: 1,
  807. children: [
  808. {
  809. instanceOf: Element,
  810. priority: 1,
  811. name: 'b',
  812. children: [
  813. { instanceOf: Text, data: 'foobar' }
  814. ]
  815. }
  816. ]
  817. } );
  818. const b = new Element( 'b' );
  819. writer.setPriority( b, 1 );
  820. const newRange = writer.unwrap( created.range, b );
  821. test( writer, newRange, created.node, {
  822. instanceOf: Element,
  823. name: 'p',
  824. rangeStart: 0,
  825. rangeEnd: 1,
  826. children: [
  827. { instanceOf: Text, data: 'foobar' }
  828. ]
  829. } );
  830. } );
  831. it( 'should not unwrap attributes with different priorities #1', () => {
  832. // <p>[<b>{foobar}</b>]<p> -> <p>[<b>{foobar}</b>]</p>
  833. // Unwrapped with <b> but using different priority.
  834. const writer = new Writer();
  835. const description = {
  836. instanceOf: Element,
  837. name: 'p',
  838. rangeStart: 0,
  839. rangeEnd: 1,
  840. children: [
  841. {
  842. instanceOf: Element,
  843. priority: 1,
  844. name: 'b',
  845. children: [
  846. { instanceOf: Text, data: 'foobar' }
  847. ]
  848. }
  849. ]
  850. };
  851. const created = create( writer, description );
  852. const b = new Element( 'b' );
  853. writer.setPriority( b, 2 );
  854. const newRange = writer.unwrap( created.range, b );
  855. test( writer, newRange, created.node, description );
  856. } );
  857. it( 'should not unwrap attributes with different priorities #2', () => {
  858. // <p>[<b>{foo}</b><b>{bar}</b><b>{baz}</b>]<p> -> <p>[{foo}<b>bar</b>{baz}]</p>
  859. // <b> around `bar` has different priority than others.
  860. const writer = new Writer();
  861. const created = create( writer, {
  862. instanceOf: Element,
  863. name: 'p',
  864. rangeStart: 0,
  865. rangeEnd: 3,
  866. children: [
  867. {
  868. instanceOf: Element,
  869. priority: 2,
  870. name: 'b',
  871. children: [
  872. { instanceOf: Text, data: 'foo' }
  873. ]
  874. },
  875. {
  876. instanceOf: Element,
  877. priority: 1,
  878. name: 'b',
  879. children: [
  880. { instanceOf: Text, data: 'bar' }
  881. ]
  882. },
  883. {
  884. instanceOf: Element,
  885. priority: 2,
  886. name: 'b',
  887. children: [
  888. { instanceOf: Text, data: 'baz' }
  889. ]
  890. }
  891. ]
  892. } );
  893. const b = new Element( 'b' );
  894. writer.setPriority( b, 2 );
  895. const newRange = writer.unwrap( created.range, b );
  896. test( writer, newRange, created.node, {
  897. instanceOf: Element,
  898. name: 'p',
  899. rangeStart: 0,
  900. rangeEnd: 3,
  901. children: [
  902. { instanceOf: Text, data: 'foo' },
  903. {
  904. instanceOf: Element,
  905. priority: 1,
  906. name: 'b',
  907. children: [
  908. { instanceOf: Text, data: 'bar' }
  909. ]
  910. },
  911. { instanceOf: Text, data: 'baz' }
  912. ]
  913. } );
  914. } );
  915. it( 'should unwrap part of the node', () => {
  916. // <p>[{baz}<b>{foo]bar}</b><p> -> <p>[{bazfoo}]<b>{bar}</b></p>
  917. const writer = new Writer();
  918. const created = create( writer, {
  919. instanceOf: Element,
  920. name: 'p',
  921. rangeStart: 0,
  922. children: [
  923. { instanceOf: Text, data: 'baz' },
  924. {
  925. instanceOf: Element,
  926. name: 'b',
  927. priority: 1,
  928. children: [
  929. { instanceOf: Text, data: 'foobar', rangeEnd: 3 }
  930. ]
  931. }
  932. ]
  933. } );
  934. const b = new Element( 'b' );
  935. writer.setPriority( b, 1 );
  936. const newRange = writer.unwrap( created.range, b );
  937. test( writer, newRange, created.node, {
  938. instanceOf: Element,
  939. name: 'p',
  940. rangeStart: 0,
  941. rangeEnd: 1,
  942. children: [
  943. { instanceOf: Text, data: 'bazfoo' },
  944. {
  945. instanceOf: Element,
  946. name: 'b',
  947. priority: 1,
  948. children: [
  949. { instanceOf: Text, data: 'bar' }
  950. ]
  951. }
  952. ]
  953. } );
  954. } );
  955. it( 'should unwrap nested attributes', () => {
  956. // <p>[<u><b>{foobar}</b></u>]</p> -> <p>[<u>{foobar}</u>]</p>
  957. const writer = new Writer();
  958. const created = create( writer, {
  959. instanceOf: Element,
  960. name: 'p',
  961. rangeStart: 0,
  962. rangeEnd: 1,
  963. children: [
  964. {
  965. instanceOf: Element,
  966. name: 'u',
  967. priority: 1,
  968. children: [
  969. {
  970. instanceOf: Element,
  971. name: 'b',
  972. priority: 1,
  973. children: [
  974. { instanceOf: Text, data: 'foobar' }
  975. ]
  976. }
  977. ]
  978. }
  979. ]
  980. } );
  981. const b = new Element( 'b' );
  982. writer.setPriority( b, 1 );
  983. const newRange = writer.unwrap( created.range, b );
  984. test( writer, newRange, created.node, {
  985. instanceOf: Element,
  986. name: 'p',
  987. rangeStart: 0,
  988. rangeEnd: 1,
  989. children: [
  990. {
  991. instanceOf: Element,
  992. name: 'u',
  993. priority: 1,
  994. children: [
  995. { instanceOf: Text, data: 'foobar' }
  996. ]
  997. }
  998. ]
  999. } );
  1000. } );
  1001. it( 'should merge unwrapped nodes #1', () => {
  1002. // <p>{foo}<u>{bar}</u>[<b><u>{bazqux}</u></b>]</p> -> <p>{foo}<u>{bar[bazqux}</u>]</p>
  1003. const writer = new Writer();
  1004. const created = create( writer, {
  1005. instanceOf: Element,
  1006. name: 'p',
  1007. rangeStart: 2,
  1008. rangeEnd: 3,
  1009. children: [
  1010. { instanceOf: Text, data: 'foo' },
  1011. {
  1012. instanceOf: Element,
  1013. name: 'u',
  1014. priority: 1,
  1015. children: [
  1016. { instanceOf: Text, data: 'bar' }
  1017. ]
  1018. },
  1019. {
  1020. instanceOf: Element,
  1021. name: 'b',
  1022. priority: 1,
  1023. children: [
  1024. {
  1025. instanceOf: Element,
  1026. name: 'u',
  1027. priority: 1,
  1028. children: [
  1029. { instanceOf: Text, data: 'bazqux' }
  1030. ]
  1031. }
  1032. ]
  1033. }
  1034. ]
  1035. } );
  1036. const b = new Element( 'b' );
  1037. writer.setPriority( b, 1 );
  1038. const newRange = writer.unwrap( created.range, b );
  1039. test( writer, newRange, created.node, {
  1040. instanceOf: Element,
  1041. name: 'p',
  1042. rangeEnd: 2,
  1043. children: [
  1044. {
  1045. instanceOf: Text,
  1046. data: 'foo'
  1047. },
  1048. {
  1049. instanceOf: Element,
  1050. name: 'u',
  1051. priority: 1,
  1052. children: [
  1053. { instanceOf: Text, data: 'barbazqux', rangeStart: 3 }
  1054. ]
  1055. }
  1056. ]
  1057. } );
  1058. } );
  1059. it( 'should merge unwrapped nodes #2', () => {
  1060. // <p>{foo}<u>{bar}</u>[<b><u>{baz]qux}</u></b></p> -> <p>{foo}<u>{bar[baz}</u>]<b><u>{qux}</u></b></p>
  1061. const writer = new Writer();
  1062. const created = create( writer, {
  1063. instanceOf: Element,
  1064. name: 'p',
  1065. rangeStart: 2,
  1066. children: [
  1067. { instanceOf: Text, data: 'foo' },
  1068. {
  1069. instanceOf: Element,
  1070. name: 'u',
  1071. priority: 1,
  1072. children: [
  1073. { instanceOf: Text, data: 'bar' }
  1074. ]
  1075. },
  1076. {
  1077. instanceOf: Element,
  1078. name: 'b',
  1079. priority: 1,
  1080. children: [
  1081. {
  1082. instanceOf: Element,
  1083. name: 'u',
  1084. priority: 1,
  1085. children: [
  1086. { instanceOf: Text, data: 'bazqux', rangeEnd: 3 }
  1087. ]
  1088. }
  1089. ]
  1090. }
  1091. ]
  1092. } );
  1093. const b = new Element( 'b' );
  1094. writer.setPriority( b, 1 );
  1095. const newRange = writer.unwrap( created.range, b );
  1096. test( writer, newRange, created.node, {
  1097. instanceOf: Element,
  1098. name: 'p',
  1099. rangeEnd: 2,
  1100. children: [
  1101. {
  1102. instanceOf: Text,
  1103. data: 'foo'
  1104. },
  1105. {
  1106. instanceOf: Element,
  1107. name: 'u',
  1108. priority: 1,
  1109. children: [
  1110. { instanceOf: Text, data: 'barbaz', rangeStart: 3 }
  1111. ]
  1112. },
  1113. {
  1114. instanceOf: Element,
  1115. name: 'b',
  1116. priority: 1,
  1117. children: [
  1118. {
  1119. instanceOf: Element,
  1120. name: 'u',
  1121. priority: 1,
  1122. children: [
  1123. { instanceOf: Text, data: 'qux' }
  1124. ]
  1125. }
  1126. ]
  1127. }
  1128. ]
  1129. } );
  1130. } );
  1131. it( 'should merge unwrapped nodes #3', () => {
  1132. // <p>{foo}<u>{bar}</u>[<b><u>{baz}</u></b>]<u>qux</u></p> -> <p>{foo}<u>{bar[baz]qux}</u></p>
  1133. const writer = new Writer();
  1134. const created = create( writer, {
  1135. instanceOf: Element,
  1136. name: 'p',
  1137. rangeStart: 2,
  1138. rangeEnd: 3,
  1139. children: [
  1140. { instanceOf: Text, data: 'foo' },
  1141. {
  1142. instanceOf: Element,
  1143. name: 'u',
  1144. priority: 1,
  1145. children: [
  1146. { instanceOf: Text, data: 'bar' }
  1147. ]
  1148. },
  1149. {
  1150. instanceOf: Element,
  1151. name: 'b',
  1152. priority: 1,
  1153. children: [
  1154. {
  1155. instanceOf: Element,
  1156. name: 'u',
  1157. priority: 1,
  1158. children: [
  1159. { instanceOf: Text, data: 'baz' }
  1160. ]
  1161. }
  1162. ]
  1163. },
  1164. {
  1165. instanceOf: Element,
  1166. name: 'u',
  1167. priority: 1,
  1168. children: [
  1169. { instanceOf: Text, data: 'qux' }
  1170. ]
  1171. }
  1172. ]
  1173. } );
  1174. const b = new Element( 'b' );
  1175. writer.setPriority( b, 1 );
  1176. const newRange = writer.unwrap( created.range, b );
  1177. test( writer, newRange, created.node, {
  1178. instanceOf: Element,
  1179. name: 'p',
  1180. children: [
  1181. {
  1182. instanceOf: Text,
  1183. data: 'foo'
  1184. },
  1185. {
  1186. instanceOf: Element,
  1187. name: 'u',
  1188. priority: 1,
  1189. children: [
  1190. { instanceOf: Text, data: 'barbazqux', rangeStart: 3, rangeEnd: 6 }
  1191. ]
  1192. }
  1193. ]
  1194. } );
  1195. } );
  1196. it( 'should merge unwrapped nodes #4', () => {
  1197. // <p>[<u><b>{foo}</b></u><u><b>{bar}</b></u><u><b>{baz}</b></u>]</p> -> <p>[<u>{foobarbaz}</u>]</p>
  1198. const writer = new Writer();
  1199. const created = create( writer, {
  1200. instanceOf: Element,
  1201. name: 'p',
  1202. rangeStart: 0,
  1203. rangeEnd: 3,
  1204. children: [
  1205. {
  1206. instanceOf: Element,
  1207. name: 'b',
  1208. priority: 1,
  1209. children: [
  1210. {
  1211. instanceOf: Element,
  1212. name: 'u',
  1213. priority: 1,
  1214. children: [
  1215. { instanceOf: Text, data: 'foo' }
  1216. ]
  1217. }
  1218. ]
  1219. },
  1220. {
  1221. instanceOf: Element,
  1222. name: 'b',
  1223. priority: 1,
  1224. children: [
  1225. {
  1226. instanceOf: Element,
  1227. name: 'u',
  1228. priority: 1,
  1229. children: [
  1230. { instanceOf: Text, data: 'bar' }
  1231. ]
  1232. }
  1233. ]
  1234. },
  1235. {
  1236. instanceOf: Element,
  1237. name: 'b',
  1238. priority: 1,
  1239. children: [
  1240. {
  1241. instanceOf: Element,
  1242. name: 'u',
  1243. priority: 1,
  1244. children: [
  1245. { instanceOf: Text, data: 'baz' }
  1246. ]
  1247. }
  1248. ]
  1249. }
  1250. ]
  1251. } );
  1252. const b = new Element( 'b' );
  1253. writer.setPriority( b, 1 );
  1254. const newRange = writer.unwrap( created.range, b );
  1255. test( writer, newRange, created.node, {
  1256. instanceOf: Element,
  1257. name: 'p',
  1258. rangeStart: 0,
  1259. rangeEnd: 1,
  1260. children: [
  1261. {
  1262. instanceOf: Element,
  1263. name: 'u',
  1264. priority: 1,
  1265. children: [
  1266. { instanceOf: Text, data: 'foobarbaz' }
  1267. ]
  1268. }
  1269. ]
  1270. } );
  1271. } );
  1272. } );
  1273. } );