writer.js 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175
  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.rengeEnd !== undefined ) {
  68. expect( location.end.parent ).to.equal( node );
  69. expect( location.end.offset ).to.equal( description.rengeEnd );
  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. const writer = new Writer();
  834. const description = {
  835. instanceOf: Element,
  836. name: 'p',
  837. rangeStart: 0,
  838. rangeEnd: 1,
  839. children: [
  840. {
  841. instanceOf: Element,
  842. priority: 1,
  843. name: 'b',
  844. children: [
  845. { instanceOf: Text, data: 'foobar' }
  846. ]
  847. }
  848. ]
  849. };
  850. const created = create( writer, description );
  851. const b = new Element( 'b' );
  852. writer.setPriority( b, 2 );
  853. const newRange = writer.unwrap( created.range, b );
  854. test( writer, newRange, created.node, description );
  855. } );
  856. it( 'should not unwrap attributes with different priorities #2', () => {
  857. // <p>[<b>{foo}</b><b>{bar}</b><b>{baz}</b>]<p> -> <p>[{foo}<b>bar</b>{baz}]</p>
  858. const writer = new Writer();
  859. const created = create( writer, {
  860. instanceOf: Element,
  861. name: 'p',
  862. rangeStart: 0,
  863. rangeEnd: 3,
  864. children: [
  865. {
  866. instanceOf: Element,
  867. priority: 2,
  868. name: 'b',
  869. children: [
  870. { instanceOf: Text, data: 'foo' }
  871. ]
  872. },
  873. {
  874. instanceOf: Element,
  875. priority: 1,
  876. name: 'b',
  877. children: [
  878. { instanceOf: Text, data: 'bar' }
  879. ]
  880. },
  881. {
  882. instanceOf: Element,
  883. priority: 2,
  884. name: 'b',
  885. children: [
  886. { instanceOf: Text, data: 'baz' }
  887. ]
  888. }
  889. ]
  890. } );
  891. const b = new Element( 'b' );
  892. writer.setPriority( b, 2 );
  893. const newRange = writer.unwrap( created.range, b );
  894. test( writer, newRange, created.node, {
  895. instanceOf: Element,
  896. name: 'p',
  897. rangeStart: 0,
  898. rangeEnd: 3,
  899. children: [
  900. { instanceOf: Text, data: 'foo' },
  901. {
  902. instanceOf: Element,
  903. priority: 1,
  904. name: 'b',
  905. children: [
  906. { instanceOf: Text, data: 'bar' }
  907. ]
  908. },
  909. { instanceOf: Text, data: 'baz' }
  910. ]
  911. } );
  912. } );
  913. it( 'should unwrap part of the node', () => {
  914. // <p>[{baz}<b>{foo]bar}</b><p> -> <p>[{bazfoo}]<b>{bar}</b></p>
  915. const writer = new Writer();
  916. const created = create( writer, {
  917. instanceOf: Element,
  918. name: 'p',
  919. rangeStart: 0,
  920. children: [
  921. { instanceOf: Text, data: 'baz' },
  922. {
  923. instanceOf: Element,
  924. name: 'b',
  925. priority: 1,
  926. children: [
  927. { instanceOf: Text, data: 'foobar', rangeEnd: 3 }
  928. ]
  929. }
  930. ]
  931. } );
  932. const b = new Element( 'b' );
  933. writer.setPriority( b, 1 );
  934. const newRange = writer.unwrap( created.range, b );
  935. test( writer, newRange, created.node, {
  936. instanceOf: Element,
  937. name: 'p',
  938. rangeStart: 0,
  939. rangeEnd: 1,
  940. children: [
  941. { instanceOf: Text, data: 'bazfoo' },
  942. {
  943. instanceOf: Element,
  944. name: 'b',
  945. priority: 1,
  946. children: [
  947. { instanceOf: Text, data: 'bar' }
  948. ]
  949. }
  950. ]
  951. } );
  952. } );
  953. it( 'should unwrap nested attributes', () => {
  954. // <p>[<u><b>{foobar}</b></u>]</p> -> <p>[<u>{foobar}</u>]</p>
  955. const writer = new Writer();
  956. const created = create( writer, {
  957. instanceOf: Element,
  958. name: 'p',
  959. rangeStart: 0,
  960. rangeEnd: 1,
  961. children: [
  962. {
  963. instanceOf: Element,
  964. name: 'u',
  965. priority: 1,
  966. children: [
  967. {
  968. instanceOf: Element,
  969. name: 'b',
  970. priority: 1,
  971. children: [
  972. { instanceOf: Text, data: 'foobar' }
  973. ]
  974. }
  975. ]
  976. }
  977. ]
  978. } );
  979. const b = new Element( 'b' );
  980. writer.setPriority( b, 1 );
  981. const newRange = writer.unwrap( created.range, b );
  982. test( writer, newRange, created.node, {
  983. instanceOf: Element,
  984. name: 'p',
  985. rangeStart: 0,
  986. rangeEnd: 1,
  987. children: [
  988. {
  989. instanceOf: Element,
  990. name: 'u',
  991. priority: 1,
  992. children: [
  993. { instanceOf: Text, data: 'foobar' }
  994. ]
  995. }
  996. ]
  997. } );
  998. } );
  999. it( 'should merge unwrapped nodes', () => {
  1000. // <p>{foo}<u>{bar}</u>[<b><u>{baz]qux}</u></b></p> -> <p>{foo}<u>{bar[baz}</u>]<b><u>{qux}</u></b></p>
  1001. const writer = new Writer();
  1002. const created = create( writer, {
  1003. instanceOf: Element,
  1004. name: 'p',
  1005. rangeStart: 2,
  1006. children: [
  1007. { instanceOf: Text, data: 'foo' },
  1008. {
  1009. instanceOf: Element,
  1010. name: 'u',
  1011. priority: 1,
  1012. children: [
  1013. { instanceOf: Text, data: 'bar' }
  1014. ]
  1015. },
  1016. {
  1017. instanceOf: Element,
  1018. name: 'b',
  1019. priority: 1,
  1020. children: [
  1021. {
  1022. instanceOf: Element,
  1023. name: 'u',
  1024. priority: 1,
  1025. children: [
  1026. { instanceOf: Text, data: 'bazqux', rangeEnd: 3 }
  1027. ]
  1028. }
  1029. ]
  1030. }
  1031. ]
  1032. } );
  1033. const b = new Element( 'b' );
  1034. writer.setPriority( b, 1 );
  1035. const newRange = writer.unwrap( created.range, b );
  1036. test( writer, newRange, created.node, {
  1037. instanceOf: Element,
  1038. name: 'p',
  1039. rangeEnd: 2,
  1040. children: [
  1041. {
  1042. instanceOf: Text,
  1043. data: 'foo'
  1044. },
  1045. {
  1046. instanceOf: Element,
  1047. name: 'u',
  1048. priority: 1,
  1049. children: [
  1050. { instanceOf: Text, data: 'barbaz', rangeStart: 3 }
  1051. ]
  1052. },
  1053. {
  1054. instanceOf: Element,
  1055. name: 'b',
  1056. priority: 1,
  1057. children: [
  1058. {
  1059. instanceOf: Element,
  1060. name: 'u',
  1061. priority: 1,
  1062. children: [
  1063. { instanceOf: Text, data: 'qux' }
  1064. ]
  1065. }
  1066. ]
  1067. }
  1068. ]
  1069. } );
  1070. } );
  1071. } );
  1072. } );