writer.js 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842
  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. import CKEditorError from '/ckeditor5/core/ckeditorerror.js';
  13. describe( 'Writer', () => {
  14. /**
  15. * Helper function that is used to test output of writer methods by providing declarative description of the
  16. * expected output.
  17. * Examples:
  18. * test element: `<p>fo[o<b>ba]r</b></p>`
  19. * description: {
  20. * name: 'p',
  21. * instanceOf: Element,
  22. * children:[
  23. * {
  24. * instanceOf: Text,
  25. * data: 'foo',
  26. * rangeStart: 2
  27. * },
  28. * {
  29. * name: 'b'
  30. * instanceOf: Element
  31. * priority: 1,
  32. * children: [
  33. * { instanceOf: Text, data: 'bar', rangeEnd: 2 }
  34. * ]
  35. * }
  36. * ]
  37. * }
  38. *
  39. *
  40. * @param {core.treeView.Writer} writer Writer instance. Used to test priority.
  41. * @param {core.treeView.Range|core.treeView.Position } location Range instance or Position instance.
  42. * Treated as Range when when `rangeStart`, `rangeEnd` is used, treated as Position when `position` is used.
  43. * @param {core.treeView.Node} node Element to check.
  44. * @param {Object} description Object describing expected element and its children.
  45. */
  46. function test( writer, location, node, description ) {
  47. if ( description.instanceOf ) {
  48. expect( node ).to.be.instanceof( description.instanceOf );
  49. }
  50. if ( description.name ) {
  51. expect( description.name ).to.equal( node.name );
  52. }
  53. if ( description.data ) {
  54. expect( description.data ).to.equal( node.data );
  55. }
  56. if ( description.priority !== undefined ) {
  57. expect( description.priority ).to.equal( writer.getPriority( node ) );
  58. }
  59. if ( description.rangeStart !== undefined ) {
  60. expect( node ).to.equal( location.start.parent );
  61. expect( description.rangeStart ).to.equal( location.start.offset );
  62. }
  63. if ( description.rangeEnd !== undefined ) {
  64. expect( node ).to.equal( location.end.parent );
  65. expect( description.rangeEnd ).to.equal( location.end.offset );
  66. }
  67. if ( description.attributes ) {
  68. Object.keys( description.attributes ).forEach( ( key ) => {
  69. expect( description.attributes[ key ] ).to.equal( node.getAttribute( key ) );
  70. } );
  71. }
  72. if ( description.position !== undefined ) {
  73. expect( node ).to.equal( location.parent );
  74. expect( description.position ).to.equal( location.offset );
  75. }
  76. if ( description.children ) {
  77. expect( description.children.length ).to.equal( node.getChildCount() );
  78. description.children.forEach( ( desc, index ) => {
  79. test( writer, location, node.getChild( index ), desc );
  80. } );
  81. }
  82. }
  83. /**
  84. * Helper function that is used to create treeView elements from description object.
  85. *
  86. * @param {core.treeView.Writer} writer Writer instance. Used to set priorities.
  87. * @param {Object} description Description object.
  88. * @param {core.treeView.Range} [range] Optional parameter, used in recurrent calls.
  89. * @param {core.treeView.Position} [position] Optional parameter, used in recurrent calls.
  90. * @returns {Object} Returns object with `node`, `range`, `position` fields, containing created node and, optionally
  91. * range and position if description object contain information about them.
  92. */
  93. function create( writer, description, range, position ) {
  94. const node = new description.instanceOf();
  95. if ( !range ) {
  96. range = Range.createFromParentsAndOffsets( node, 0, node, 0 );
  97. }
  98. if ( !position ) {
  99. position = new Position( node, 0 );
  100. }
  101. if ( description.name ) {
  102. node.name = description.name;
  103. }
  104. if ( description.data ) {
  105. node.data = description.data;
  106. }
  107. if ( description.attributes ) {
  108. Object.keys( description.attributes ).forEach( ( key ) => {
  109. node.setAttribute( key, description.attributes[ key ] );
  110. } );
  111. }
  112. if ( description.priority !== undefined ) {
  113. writer.setPriority( node, description.priority );
  114. }
  115. if ( description.rangeStart !== undefined ) {
  116. range.start.parent = node;
  117. range.start.offset = description.rangeStart;
  118. }
  119. if ( description.rangeEnd !== undefined ) {
  120. range.end.parent = node;
  121. range.end.offset = description.rangeEnd;
  122. }
  123. if ( description.position !== undefined ) {
  124. position.parent = node;
  125. position.offset = description.position;
  126. }
  127. if ( description.children ) {
  128. description.children.forEach( ( desc, index ) => {
  129. const created = create( writer, desc, range, position );
  130. node.insertChildren( index, created.node );
  131. } );
  132. }
  133. return { node, range, position };
  134. }
  135. describe( 'isContainer', () => {
  136. it( 'should return true for container elements', () => {
  137. const containerElement = new Element( 'p' );
  138. const attributeElement = new Element( 'b' );
  139. const writer = new Writer();
  140. writer._priorities.set( attributeElement, 1 );
  141. expect( writer.isContainer( containerElement ) ).to.be.true;
  142. expect( writer.isContainer( attributeElement ) ).to.be.false;
  143. } );
  144. } );
  145. describe( 'isAttribute', () => {
  146. it( 'should return true for container elements', () => {
  147. const containerElement = new Element( 'p' );
  148. const attributeElement = new Element( 'b' );
  149. const writer = new Writer();
  150. writer._priorities.set( attributeElement, 1 );
  151. expect( writer.isAttribute( containerElement ) ).to.be.false;
  152. expect( writer.isAttribute( attributeElement ) ).to.be.true;
  153. } );
  154. } );
  155. describe( 'setPriority', () => {
  156. it( 'sets node priority', () => {
  157. const writer = new Writer();
  158. const nodeMock = {};
  159. writer.setPriority( nodeMock, 10 );
  160. expect( writer._priorities.get( nodeMock ) ).to.equal( 10 );
  161. } );
  162. } );
  163. describe( 'getPriority', () => {
  164. it( 'gets node priority', () => {
  165. const writer = new Writer();
  166. const nodeMock = {};
  167. writer._priorities.set( nodeMock, 12 );
  168. expect( writer.getPriority( nodeMock ) ).to.equal( 12 );
  169. } );
  170. } );
  171. describe( 'getParentContainer', () => {
  172. it( 'should return parent container of the node', () => {
  173. const writer = new Writer();
  174. const text = new Text( 'foobar' );
  175. const b = new Element( 'b', null, [ text ] );
  176. const parent = new Element( 'p', null, [ b ] );
  177. writer.setPriority( b, 1 );
  178. const container = writer.getParentContainer( new Position( text, 0 ) );
  179. expect( container ).to.equal( parent );
  180. } );
  181. it( 'should return null if no parent container', () => {
  182. const writer = new Writer();
  183. const text = new Text( 'foobar' );
  184. const b = new Element( 'b', null, [ text ] );
  185. writer.setPriority( b, 1 );
  186. const container = writer.getParentContainer( new Position( text, 0 ) );
  187. expect( container ).to.equal( null );
  188. } );
  189. } );
  190. describe( 'breakAttributes', () => {
  191. // <p>{|foobar}</p> -> <p>|{foobar}</p>
  192. it( '<p>{|foobar}</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: 0 }
  199. ]
  200. } );
  201. const newPosition = writer.breakAttributes( created.position );
  202. test( writer, newPosition, created.node, {
  203. instanceOf: Element,
  204. name: 'p',
  205. position: 0,
  206. children: [
  207. { instanceOf: Text, data: 'foobar' }
  208. ]
  209. } );
  210. } );
  211. it( '<p>foo|bar</p>', () => {
  212. // <p>{foo|bar}</p> -> <p>{foo}|{bar}</p>
  213. const writer = new Writer();
  214. const created = create( writer, {
  215. instanceOf: Element,
  216. name: 'p',
  217. children: [
  218. { instanceOf: Text, data: 'foobar', position: 3 }
  219. ]
  220. } );
  221. const newPosition = writer.breakAttributes( created.position );
  222. test( writer, newPosition, created.node, {
  223. instanceOf: Element,
  224. name: 'p',
  225. position: 1,
  226. children: [
  227. { instanceOf: Text, data: 'foo' },
  228. { instanceOf: Text, data: 'bar' }
  229. ]
  230. } );
  231. } );
  232. it( '<p>{foobar|}</p>', () => {
  233. // <p>{foobar|}</p> -> <p>{foobar}|</p>
  234. const writer = new Writer();
  235. const created = create( writer, {
  236. instanceOf: Element,
  237. name: 'p',
  238. children: [
  239. { instanceOf: Text, data: 'foobar', position: 6 }
  240. ]
  241. } );
  242. const newPosition = writer.breakAttributes( created.position );
  243. test( writer, newPosition, created.node, {
  244. instanceOf: Element,
  245. name: 'p',
  246. position: 1,
  247. children: [
  248. { instanceOf: Text, data: 'foobar' }
  249. ]
  250. } );
  251. } );
  252. it( '<p><b>{foo|bar}</b></p>', () => {
  253. // <p><b>{foo|bar}</b></p> -> <p><b>{foo}</b>|<b>{bar}</b></p>
  254. const writer = new Writer();
  255. const created = create( writer, {
  256. instanceOf: Element,
  257. name: 'p',
  258. children: [
  259. {
  260. instanceOf: Element,
  261. name: 'b',
  262. priority: 1,
  263. children: [
  264. { instanceOf: Text, data: 'foobar', position: 3 }
  265. ]
  266. }
  267. ]
  268. } );
  269. const newPosition = writer.breakAttributes( created.position );
  270. test( writer, newPosition, created.node, {
  271. instanceOf: Element,
  272. name: 'p',
  273. position: 1,
  274. children: [
  275. {
  276. instanceOf: Element,
  277. name: 'b',
  278. priority: 1,
  279. children: [
  280. { instanceOf: Text, data: 'foo' }
  281. ]
  282. },
  283. {
  284. instanceOf: Element,
  285. name: 'b',
  286. priority: 1,
  287. children: [
  288. { instanceOf: Text, data: 'bar' }
  289. ]
  290. }
  291. ]
  292. } );
  293. } );
  294. it( '<p><b><u>{|foobar}</u></b></p>', () => {
  295. // <p><b><u>{|foobar}</u></b></p> -> <p>|<b><u>{foobar}</u></b></p>
  296. const writer = new Writer();
  297. const created = create( writer, {
  298. instanceOf: Element,
  299. name: 'p',
  300. children: [
  301. {
  302. instanceOf: Element,
  303. name: 'b',
  304. priority: 1,
  305. children: [
  306. {
  307. instanceOf: Element,
  308. name: 'u',
  309. priority: 1,
  310. children: [
  311. { instanceOf: Text, data: 'foobar', position: 0 }
  312. ]
  313. }
  314. ]
  315. }
  316. ]
  317. } );
  318. const newPosition = writer.breakAttributes( created.position );
  319. test( writer, newPosition, created.node, {
  320. instanceOf: Element,
  321. name: 'p',
  322. position: 0,
  323. children: [
  324. {
  325. instanceOf: Element,
  326. name: 'b',
  327. priority: 1,
  328. children: [
  329. {
  330. instanceOf: Element,
  331. name: 'u',
  332. priority: 1,
  333. children: [
  334. { instanceOf: Text, data: 'foobar' }
  335. ]
  336. }
  337. ]
  338. }
  339. ]
  340. } );
  341. } );
  342. // <p><b><u>{foo|ba}r</u></b></p> -> <p><b><u>{foo}</u></b>|<b></u>{bar}</u></b></p>
  343. it( '<p><b><u>{foo|bar}</u></b></p>', () => {
  344. const writer = new Writer();
  345. const created = create( writer, {
  346. instanceOf: Element,
  347. name: 'p',
  348. children: [
  349. {
  350. instanceOf: Element,
  351. name: 'b',
  352. priority: 1,
  353. children: [
  354. {
  355. instanceOf: Element,
  356. name: 'u',
  357. priority: 1,
  358. children: [
  359. { instanceOf: Text, data: 'foobar', position: 3 }
  360. ]
  361. }
  362. ]
  363. }
  364. ]
  365. } );
  366. const newPosition = writer.breakAttributes( created.position );
  367. test( writer, newPosition, created.node, {
  368. instanceOf: Element,
  369. name: 'p',
  370. position: 1,
  371. children: [
  372. {
  373. instanceOf: Element,
  374. name: 'b',
  375. priority: 1,
  376. children: [
  377. {
  378. instanceOf: Element,
  379. name: 'u',
  380. priority: 1,
  381. children: [
  382. { instanceOf: Text, data: 'foo' }
  383. ]
  384. }
  385. ]
  386. },
  387. {
  388. instanceOf: Element,
  389. name: 'b',
  390. priority: 1,
  391. children: [
  392. {
  393. instanceOf: Element,
  394. name: 'u',
  395. priority: 1,
  396. children: [
  397. { instanceOf: Text, data: 'bar' }
  398. ]
  399. }
  400. ]
  401. }
  402. ]
  403. } );
  404. } );
  405. it( '<p><b><u>{foobar|}</u></b></p>', () => {
  406. // <p><b><u>{foobar|}</u></b></p> -> <p><b><u>{foobar}</u></b>|</p>
  407. const writer = new Writer();
  408. const created = create( writer, {
  409. instanceOf: Element,
  410. name: 'p',
  411. children: [
  412. {
  413. instanceOf: Element,
  414. name: 'b',
  415. priority: 1,
  416. children: [
  417. {
  418. instanceOf: Element,
  419. name: 'u',
  420. priority: 1,
  421. children: [
  422. { instanceOf: Text, data: 'foobar', position: 6 }
  423. ]
  424. }
  425. ]
  426. }
  427. ]
  428. } );
  429. const newPosition = writer.breakAttributes( created.position );
  430. test( writer, newPosition, created.node, {
  431. instanceOf: Element,
  432. name: 'p',
  433. position: 1,
  434. children: [
  435. {
  436. instanceOf: Element,
  437. name: 'b',
  438. priority: 1,
  439. children: [
  440. {
  441. instanceOf: Element,
  442. name: 'u',
  443. priority: 1,
  444. children: [
  445. { instanceOf: Text, data: 'foobar' }
  446. ]
  447. }
  448. ]
  449. }
  450. ]
  451. } );
  452. } );
  453. } );
  454. describe( 'mergeAttributes', () => {
  455. it( 'should not merge if inside text node', () => {
  456. // <p>{fo|obar}</p>
  457. const writer = new Writer();
  458. const description = {
  459. instanceOf: Element,
  460. name: 'p',
  461. children: [
  462. { instanceOf: Text, data: 'foobar', position: 2 }
  463. ]
  464. };
  465. const created = create( writer, description );
  466. const newPosition = writer.mergeAttributes( created.position );
  467. test( writer, newPosition, created.node, description );
  468. } );
  469. it( 'should not merge if between containers', () => {
  470. // <div><p>{foo}</p>|<p>{bar}</p></div>
  471. const writer = new Writer();
  472. const description = {
  473. instanceOf: Element,
  474. name: 'div',
  475. position: 1,
  476. children: [
  477. {
  478. instanceOf: Element,
  479. name: 'p',
  480. children: [
  481. { instanceOf: Text, data: 'foo' }
  482. ]
  483. },
  484. {
  485. instanceOf: Element,
  486. name: 'p',
  487. children: [
  488. { instanceOf: Text, data: 'bar' }
  489. ]
  490. }
  491. ]
  492. };
  493. const created = create( writer, description );
  494. const newPosition = writer.mergeAttributes( created.position );
  495. test( writer, newPosition, created.node, description );
  496. } );
  497. it( 'should return same position when inside empty container', () => {
  498. // <p>|</p>
  499. const writer = new Writer();
  500. const description = { instanceOf: Element, name: 'p', position: 0 };
  501. const created = create( writer, description );
  502. const newPosition = writer.mergeAttributes( created.position );
  503. test( writer, newPosition, created.node, description );
  504. } );
  505. it( 'should not merge when position is placed at the beginning of the container', () => {
  506. // <p>|<b></b></p>
  507. const writer = new Writer();
  508. const description = {
  509. instanceOf: Element,
  510. name: 'p',
  511. position: 0,
  512. children: [
  513. {
  514. instanceOf: Element,
  515. name: 'b',
  516. priority: 1
  517. }
  518. ]
  519. };
  520. const created = create( writer, description );
  521. const newPosition = writer.mergeAttributes( created.position );
  522. test( writer, newPosition, created.node, description );
  523. } );
  524. it( 'should not merge when position is placed at the end of the container', () => {
  525. // <p><b></b>|</p>
  526. const writer = new Writer();
  527. const description = {
  528. instanceOf: Element,
  529. name: 'p',
  530. position: 1,
  531. children: [
  532. {
  533. instanceOf: Element,
  534. name: 'b',
  535. priority: 1
  536. }
  537. ]
  538. };
  539. const created = create( writer, description );
  540. const newPosition = writer.mergeAttributes( created.position );
  541. test( writer, newPosition, created.node, description );
  542. } );
  543. it( 'should merge when placed between two text nodes', () => {
  544. // <p>{foo}|{bar}</p> -> <p>{foo|bar}</p>
  545. const writer = new Writer();
  546. const created = create( writer, {
  547. instanceOf: Element,
  548. name: 'p',
  549. position: 1,
  550. children: [
  551. { instanceOf: Text, data: 'foo' },
  552. { instanceOf: Text, data: 'bar' }
  553. ]
  554. } );
  555. const newPosition = writer.mergeAttributes( created.position );
  556. test( writer, newPosition, created.node, {
  557. instanceOf: Element,
  558. name: 'p',
  559. children: [
  560. { instanceOf: Text, data: 'foobar', position: 3 }
  561. ]
  562. } );
  563. } );
  564. it( 'should merge when placed between similar attribute nodes', () => {
  565. // <p><b foo="bar"></b>|<b foo="bar"></b></p> -> <p><b foo="bar">|</b></p>
  566. const writer = new Writer();
  567. const created = create( writer, {
  568. instanceOf: Element,
  569. name: 'p',
  570. position: 1,
  571. children: [
  572. {
  573. instanceOf: Element,
  574. name: 'b',
  575. priority: 1,
  576. attributes: { foo: 'bar' }
  577. },
  578. {
  579. instanceOf: Element,
  580. name: 'b',
  581. priority: 1,
  582. attributes: { foo: 'bar' }
  583. }
  584. ]
  585. } );
  586. const newPosition = writer.mergeAttributes( created.position );
  587. test( writer, newPosition, created.node, {
  588. instanceOf: Element,
  589. name: 'p',
  590. children: [
  591. {
  592. instanceOf: Element,
  593. name: 'b',
  594. priority: 1,
  595. position: 0,
  596. attributes: { foo: 'bar' }
  597. }
  598. ]
  599. } );
  600. } );
  601. it( 'should not merge when placed between non-similar attribute nodes', () => {
  602. // <p><b foo="bar"></b>|<b foo="baz"></b></p> ->
  603. // <p><b foo="bar"></b>|<b foo="baz"></b></p>
  604. const writer = new Writer();
  605. const description = {
  606. instanceOf: Element,
  607. name: 'p',
  608. position: 1,
  609. children: [
  610. {
  611. instanceOf: Element,
  612. name: 'b',
  613. priority: 1,
  614. attributes: { foo: 'bar' }
  615. },
  616. {
  617. instanceOf: Element,
  618. name: 'b',
  619. priority: 1,
  620. attributes: { foo: 'baz' }
  621. }
  622. ]
  623. };
  624. const created = create( writer, description );
  625. const newPosition = writer.mergeAttributes( created.position );
  626. test( writer, newPosition, created.node, description );
  627. } );
  628. it( 'should not merge when placed between similar attribute nodes with different priority', () => {
  629. // <p><b foo="bar"></b>|<b foo="bar"></b></p> -> <p><b foo="bar"></b>|<b foo="bar"></b></p>
  630. const writer = new Writer();
  631. const description = {
  632. instanceOf: Element,
  633. name: 'p',
  634. position: 1,
  635. children: [
  636. {
  637. instanceOf: Element,
  638. name: 'b',
  639. priority: 1,
  640. attributes: { foo: 'bar' }
  641. },
  642. {
  643. instanceOf: Element,
  644. name: 'b',
  645. priority: 2,
  646. attributes: { foo: 'bar' }
  647. }
  648. ]
  649. };
  650. const created = create( writer,description );
  651. const newPosition = writer.mergeAttributes( created.position );
  652. test( writer, newPosition, created.node, description );
  653. } );
  654. it( 'should merge attribute nodes and their contents if possible', () => {
  655. // <p><b foo="bar">{foo}</b>|<b foo="bar">{bar}</b></p>
  656. // <p><b foo="bar">{foo}|{bar}</b></p>
  657. // <p><b foo="bar">{foo|bar}</b></p>
  658. const writer = new Writer();
  659. const created = create( writer, {
  660. instanceOf: Element,
  661. name: 'p',
  662. position: 1,
  663. children: [
  664. {
  665. instanceOf: Element,
  666. name: 'b',
  667. priority: 1,
  668. attributes: { foo: 'bar' },
  669. children: [
  670. { instanceOf: Text, data: 'foo' }
  671. ]
  672. },
  673. {
  674. instanceOf: Element,
  675. name: 'b',
  676. priority: 1,
  677. attributes: { foo: 'bar' },
  678. children: [
  679. { instanceOf: Text, data: 'bar' }
  680. ]
  681. }
  682. ]
  683. } );
  684. const newPosition = writer.mergeAttributes( created.position );
  685. test( writer, newPosition, created.node, {
  686. instanceOf: Element,
  687. name: 'p',
  688. children: [
  689. {
  690. instanceOf: Element,
  691. name: 'b',
  692. priority: 1,
  693. attributes: { foo: 'bar' },
  694. children: [
  695. { instanceOf: Text, data: 'foobar', position: 3 }
  696. ]
  697. }
  698. ]
  699. } );
  700. } );
  701. } );
  702. describe( 'insert', () => {
  703. //it( 'should insert text', () => {
  704. // // <p>{foo|bar}</p> insert {baz}
  705. // // <p>{foo[baz]bar}</p>
  706. // const writer = new Writer();
  707. // const created = create( writer, {
  708. // instanceOf: Element,
  709. // name: 'p',
  710. // children: [
  711. // { instanceOf: Text, data: 'foobar', position: 3 }
  712. // ]
  713. // } );
  714. //
  715. // const newRange = writer.insert( created.position, new Text( 'baz' ) );
  716. // test( writer, newRange, created.node, {
  717. // instanceOf: Element,
  718. // name: 'p',
  719. // children: [
  720. // { instanceOf: Text, data: 'foobazbar' }
  721. // ]
  722. // } );
  723. //} );
  724. //
  725. //it( 'should merge attributes', () => {
  726. // // <p><b>{foo|bar}</b></p> insert <b>{baz}</b>
  727. // // <p><b>{foobazbar}</b></p>
  728. // const writer = new Writer();
  729. // const text = new Text( 'foobar' );
  730. // const b1 = new Element( 'b', null, text );
  731. // const p = new Element( 'p', null, b1 );
  732. // const position = new Position( text, 3 );
  733. // const insertText = new Text( 'baz' );
  734. // const b2 = new Element( 'b', null, insertText );
  735. //
  736. // writer.setPriority( b1, 1 );
  737. // writer.setPriority( b2, 1 );
  738. //
  739. // writer.insert( position, b2 );
  740. //
  741. // expect( p.getChildCount() ).to.equal( 1 );
  742. // const b3 = p.getChild( 0 );
  743. // expect( b3 ).to.be.instanceof( Element );
  744. // expect( b3.name ).to.equal( 'b' );
  745. // expect( b3.getChildCount() ).to.equal( 1 );
  746. // const newText = b3.getChild( 0 );
  747. // expect( newText ).to.be.instanceof( Text );
  748. // expect( newText.data ).to.equal( 'foobazbar' );
  749. //} );
  750. } );
  751. describe( 'wrap', () => {
  752. // TODO: tests - different priorities
  753. // TODO: tests - empty containers left
  754. // TODO: merge with elements outside range at the ends
  755. it( 'should do nothing on collapsed ranges', () => {
  756. const writer = new Writer();
  757. const description = {
  758. instanceOf: Element,
  759. name: 'p',
  760. children: [
  761. { instanceOf: Text, data: 'foo', rangeStart: 1, rangeEnd: 1 }
  762. ]
  763. };
  764. const created = create( writer, description );
  765. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  766. test( writer, newRange, created.node, description );
  767. } );
  768. it( 'wraps single text node', () => {
  769. // <p>[{foobar}]</p>
  770. // wrap <b>
  771. // <p>[<b>{foobar}<b>]</p>
  772. const writer = new Writer();
  773. const created = create( writer, {
  774. instanceOf: Element,
  775. name: 'p',
  776. rangeStart: 0,
  777. rangeEnd: 1,
  778. children: [
  779. { instanceOf: Text, data: 'foobar' }
  780. ]
  781. } );
  782. const b = new Element( 'b' );
  783. const newRange = writer.wrap( created.range, b, 1 );
  784. test( writer, newRange, created.node, {
  785. instanceOf: Element,
  786. name: 'p',
  787. rangeStart: 0,
  788. rangeEnd: 1,
  789. children: [
  790. {
  791. instanceOf: Element,
  792. name: 'b',
  793. priority: 1,
  794. children: [
  795. { instanceOf: Text, data: 'foobar' }
  796. ]
  797. }
  798. ]
  799. } );
  800. } );
  801. it( 'should throw error when range placed in two containers', () => {
  802. const writer = new Writer();
  803. const container1 = new Element( 'p' );
  804. const container2 = new Element( 'p' );
  805. const range = new Range(
  806. new Position( container1, 0 ),
  807. new Position( container2, 1 )
  808. );
  809. const b = new Element( 'b' );
  810. expect( () => {
  811. writer.wrap( range, b, 1 );
  812. } ).to.throw( CKEditorError, 'treeview-writer-unwrap-invalid-range' );
  813. } );
  814. it( 'wraps part of single text node', () => {
  815. // <p>[{foo]bar}</p>
  816. // wrap with <b>
  817. // <p>[<b>{foo}</b>]{bar}</p>
  818. const writer = new Writer();
  819. const created = create( writer, {
  820. instanceOf: Element,
  821. name: 'p',
  822. rangeStart: 0,
  823. children: [
  824. { instanceOf: Text, data: 'foobar', rangeEnd: 3 }
  825. ]
  826. } );
  827. const b = new Element( 'b' );
  828. const newRange = writer.wrap( created.range, b, 2 );
  829. test( writer, newRange, created.node, {
  830. instanceOf: Element,
  831. name: 'p',
  832. rangeStart: 0,
  833. rangeEnd: 1,
  834. children: [
  835. {
  836. instanceOf: Element,
  837. name: 'b',
  838. priority: 2,
  839. children: [
  840. { instanceOf: Text, data: 'foo' }
  841. ]
  842. },
  843. { instanceOf: Text, data: 'bar' }
  844. ]
  845. } );
  846. } );
  847. it( 'should not wrap inside nested containers', () => {
  848. // <div>[{foobar}<p>{baz}</p>]</div>
  849. // wrap with <b>
  850. // <div>[<b>{foobar}</b><p>{baz}</p>]</div>
  851. const writer = new Writer();
  852. const created = create( writer, {
  853. instanceOf: Element,
  854. name: 'div',
  855. rangeStart: 0,
  856. rangeEnd: 2,
  857. children: [
  858. { instanceOf: Text, data: 'foobar' },
  859. {
  860. instanceOf: Element,
  861. name: 'p',
  862. children: [
  863. { instanceOf: Text, data: 'baz' }
  864. ]
  865. }
  866. ]
  867. } );
  868. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  869. test( writer, newRange, created.node, {
  870. instanceOf: Element,
  871. name: 'div',
  872. rangeStart: 0,
  873. rangeEnd: 2,
  874. children: [
  875. {
  876. instanceOf: Element,
  877. name: 'b',
  878. priority: 1,
  879. children: [
  880. { instanceOf: Text, data: 'foobar' }
  881. ]
  882. },
  883. {
  884. instanceOf: Element,
  885. name: 'p',
  886. children: [
  887. { instanceOf: Text, data: 'baz' }
  888. ]
  889. }
  890. ]
  891. } );
  892. } );
  893. it( 'wraps according to priorities', () => {
  894. // <p>[<u>{foobar}</u>]</p>
  895. // wrap with <b> that has higher priority than <u>
  896. // <p>[<u><b>{foobar}</b></u>]</p>
  897. const writer = new Writer();
  898. const created = create( writer, {
  899. instanceOf: Element,
  900. name: 'p',
  901. rangeStart: 0,
  902. rangeEnd: 1,
  903. children: [
  904. {
  905. instanceOf: Element,
  906. name: 'u',
  907. priority: 1,
  908. children: [
  909. { instanceOf: Text, data: 'foobar' }
  910. ]
  911. }
  912. ]
  913. } );
  914. const b = new Element( 'b' );
  915. const newRange = writer.wrap( created.range, b, 2 );
  916. test( writer, newRange, created.node, {
  917. instanceOf: Element,
  918. name: 'p',
  919. rangeStart: 0,
  920. rangeEnd: 1,
  921. children: [
  922. {
  923. instanceOf: Element,
  924. name: 'u',
  925. priority: 1,
  926. children: [
  927. {
  928. instanceOf: Element,
  929. name: 'b',
  930. priority: 2,
  931. children: [
  932. { instanceOf: Text, data: 'foobar' }
  933. ]
  934. }
  935. ]
  936. }
  937. ]
  938. } );
  939. } );
  940. it( 'merges wrapped nodes #1', () => {
  941. // <p>[<b>{foo}</b>{bar}<b>{baz}</b>]</p>
  942. // wrap with <b>
  943. // <p>[<b>{foobarbaz}</b>]</p>
  944. const writer = new Writer();
  945. const created = create( writer, {
  946. instanceOf: Element,
  947. name: 'p',
  948. rangeStart: 0,
  949. rangeEnd: 3,
  950. children: [
  951. {
  952. instanceOf: Element,
  953. name: 'b',
  954. priority: 1,
  955. children: [
  956. { instanceOf: Text, data: 'foo' }
  957. ]
  958. },
  959. { instanceOf: Text, data: 'bar' },
  960. {
  961. instanceOf: Element,
  962. name: 'b',
  963. priority: 1,
  964. children: [
  965. { instanceOf: Text, data: 'baz' }
  966. ]
  967. }
  968. ]
  969. } );
  970. const b = new Element( 'b' );
  971. const newRange = writer.wrap( created.range, b, 1 );
  972. test( writer, newRange, created.node, {
  973. instanceOf: Element,
  974. name: 'p',
  975. rangeStart: 0,
  976. rangeEnd: 1,
  977. children: [
  978. {
  979. instanceOf: Element,
  980. name: 'b',
  981. priority: 1,
  982. children: [
  983. { instanceOf: Text, data: 'foobarbaz' }
  984. ]
  985. }
  986. ]
  987. } );
  988. } );
  989. it( 'merges wrapped nodes #2', () => {
  990. // <p><b>{foo}</b>[{bar]baz}</p>
  991. // wrap with <b>
  992. // <p><b>{foo[bar}</b>]{baz}</p>
  993. const writer = new Writer();
  994. const created = create( writer, {
  995. instanceOf: Element,
  996. name: 'p',
  997. rangeStart: 1,
  998. children: [
  999. {
  1000. instanceOf: Element,
  1001. name: 'b',
  1002. priority: 1,
  1003. children: [
  1004. { instanceOf: Text, data: 'foo' }
  1005. ]
  1006. },
  1007. { instanceOf: Text, data: 'barbaz', rangeEnd: 3 }
  1008. ]
  1009. } );
  1010. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  1011. test( writer, newRange, created.node, {
  1012. instanceOf: Element,
  1013. name: 'p',
  1014. rangeEnd: 1,
  1015. children: [
  1016. {
  1017. instanceOf: Element,
  1018. name: 'b',
  1019. priority: 1,
  1020. children: [
  1021. { instanceOf: Text, data: 'foobar', rangeStart: 3 }
  1022. ]
  1023. },
  1024. { instanceOf: Text, data: 'baz' }
  1025. ]
  1026. } );
  1027. } );
  1028. it( 'merges wrapped nodes #3', () => {
  1029. // <p><b>{foobar}</b>[{baz}]</p>
  1030. // wrap with <b>
  1031. // <p><b>{foobar[baz}</b>]</p>
  1032. const writer = new Writer();
  1033. const created = create( writer, {
  1034. instanceOf: Element,
  1035. name: 'p',
  1036. rangeStart: 1,
  1037. rangeEnd: 2,
  1038. children: [
  1039. {
  1040. instanceOf: Element,
  1041. name: 'b',
  1042. priority: 1,
  1043. children: [
  1044. { instanceOf: Text, data: 'foobar' }
  1045. ]
  1046. },
  1047. { instanceOf: Text, data: 'baz', rangeEnd: 3 }
  1048. ]
  1049. } );
  1050. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  1051. test( writer, newRange, created.node, {
  1052. instanceOf: Element,
  1053. name: 'p',
  1054. rangeEnd: 1,
  1055. children: [
  1056. {
  1057. instanceOf: Element,
  1058. name: 'b',
  1059. priority: 1,
  1060. children: [
  1061. { instanceOf: Text, data: 'foobarbaz', rangeStart: 6 }
  1062. ]
  1063. }
  1064. ]
  1065. } );
  1066. } );
  1067. it( 'merges wrapped nodes #4', () => {
  1068. // <p>[{foo}<i>{bar}</i>]{baz}</p>
  1069. // wrap with <b>
  1070. // <p>[<b>{foo}<i>{bar}</i></b>]{baz}</p>
  1071. const writer = new Writer();
  1072. const created = create( writer, {
  1073. instanceOf: Element,
  1074. name: 'p',
  1075. rangeStart: 0,
  1076. rangeEnd: 2,
  1077. children: [
  1078. { instanceOf: Text, data: 'foo' },
  1079. {
  1080. instanceOf: Element,
  1081. name: 'i',
  1082. priority: 1,
  1083. children: [
  1084. { instanceOf: Text, data: 'bar' }
  1085. ]
  1086. },
  1087. { instanceOf: Text, data: 'baz' }
  1088. ]
  1089. } );
  1090. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  1091. test( writer, newRange, created.node, {
  1092. instanceOf: Element,
  1093. name: 'p',
  1094. rangeStart: 0,
  1095. rangeEnd: 1,
  1096. children: [
  1097. {
  1098. instanceOf: Element,
  1099. name: 'b',
  1100. priority: 1,
  1101. children: [
  1102. { instanceOf: Text, data: 'foo' },
  1103. {
  1104. instanceOf: Element,
  1105. name: 'i',
  1106. priority: 1,
  1107. children: [
  1108. { instanceOf: Text, data: 'bar' }
  1109. ]
  1110. }
  1111. ]
  1112. },
  1113. { instanceOf: Text, data: 'baz' }
  1114. ]
  1115. } );
  1116. } );
  1117. it( 'merges wrapped nodes #5', () => {
  1118. // <p>[{foo}<i>{bar}</i>{baz}]</p>
  1119. // wrap with <b>, that has higher priority than <i>
  1120. // <p>[<b>{foo}</b><i><b>{bar}</b></i><b>{baz}</b>]</p>
  1121. const writer = new Writer();
  1122. const created = create( writer, {
  1123. instanceOf: Element,
  1124. name: 'p',
  1125. rangeStart: 0,
  1126. rangeEnd: 3,
  1127. children: [
  1128. { instanceOf: Text, data: 'foo' },
  1129. {
  1130. instanceOf: Element,
  1131. name: 'i',
  1132. priority: 1,
  1133. children: [
  1134. { instanceOf: Text, data: 'bar' }
  1135. ]
  1136. },
  1137. { instanceOf: Text, data: 'baz' }
  1138. ]
  1139. } );
  1140. const newRange = writer.wrap( created.range, new Element( 'b' ), 2 );
  1141. test( writer, newRange, created.node, {
  1142. instanceOf: Element,
  1143. name: 'p',
  1144. rangeStart: 0,
  1145. rangeEnd: 3,
  1146. children: [
  1147. {
  1148. instanceOf: Element,
  1149. name: 'b',
  1150. priority: 2,
  1151. children: [
  1152. { instanceOf: Text, data: 'foo' }
  1153. ]
  1154. },
  1155. {
  1156. instanceOf: Element,
  1157. name: 'i',
  1158. priority: 1,
  1159. children: [
  1160. {
  1161. instanceOf: Element,
  1162. name: 'b',
  1163. priority: 2,
  1164. children: [
  1165. { instanceOf: Text, data: 'bar' }
  1166. ]
  1167. }
  1168. ]
  1169. },
  1170. {
  1171. instanceOf: Element,
  1172. name: 'b',
  1173. priority: 2,
  1174. children: [
  1175. { instanceOf: Text, data: 'baz' }
  1176. ]
  1177. },
  1178. ]
  1179. } );
  1180. } );
  1181. } );
  1182. describe( 'unwrap', () => {
  1183. it( 'should do nothing on collapsed ranges', () => {
  1184. const writer = new Writer();
  1185. const description = {
  1186. instanceOf: Element,
  1187. name: 'p',
  1188. children: [
  1189. { instanceOf: Text, data: 'foo', rangeStart: 1, rangeEnd: 1 }
  1190. ]
  1191. };
  1192. const created = create( writer, description );
  1193. const newRange = writer.unwrap( created.range, new Element( 'b' ), 1 );
  1194. test( writer, newRange, created.node, description );
  1195. } );
  1196. it( 'should do nothing on single text node', () => {
  1197. // <p>[{foobar}]</p>
  1198. const writer = new Writer();
  1199. const description = {
  1200. instanceOf: Element,
  1201. name: 'p',
  1202. rangeStart: 0,
  1203. rangeEnd: 1,
  1204. children: [
  1205. { instanceOf: Text, data: 'foobar' }
  1206. ]
  1207. };
  1208. const created = create( writer, description );
  1209. const b = new Element( 'b' );
  1210. writer.setPriority( b, 1 );
  1211. const newRange = writer.unwrap( created.range, b );
  1212. test( writer, newRange, created.node, description );
  1213. } );
  1214. it( 'should throw error when range placed in two containers', () => {
  1215. const writer = new Writer();
  1216. const container1 = new Element( 'p' );
  1217. const container2 = new Element( 'p' );
  1218. const range = new Range(
  1219. new Position( container1, 0 ),
  1220. new Position( container2, 1 )
  1221. );
  1222. const b = new Element( 'b' );
  1223. expect( () => {
  1224. writer.unwrap( range, b, 1 );
  1225. } ).to.throw( CKEditorError, 'treeview-writer-unwrap-invalid-range' );
  1226. } );
  1227. it( 'should unwrap single node', () => {
  1228. // <p>[<b>{foobar}</b>]<p> -> <p>[{foobar}]</p>
  1229. const writer = new Writer();
  1230. const created = create( writer, {
  1231. instanceOf: Element,
  1232. name: 'p',
  1233. rangeStart: 0,
  1234. rangeEnd: 1,
  1235. children: [
  1236. {
  1237. instanceOf: Element,
  1238. priority: 1,
  1239. name: 'b',
  1240. children: [
  1241. { instanceOf: Text, data: 'foobar' }
  1242. ]
  1243. }
  1244. ]
  1245. } );
  1246. const b = new Element( 'b' );
  1247. writer.setPriority( b, 1 );
  1248. const newRange = writer.unwrap( created.range, b );
  1249. test( writer, newRange, created.node, {
  1250. instanceOf: Element,
  1251. name: 'p',
  1252. rangeStart: 0,
  1253. rangeEnd: 1,
  1254. children: [
  1255. { instanceOf: Text, data: 'foobar' }
  1256. ]
  1257. } );
  1258. } );
  1259. it( 'should not unwrap attributes with different priorities #1', () => {
  1260. // <p>[<b>{foobar}</b>]<p> -> <p>[<b>{foobar}</b>]</p>
  1261. // Unwrapped with <b> but using different priority.
  1262. const writer = new Writer();
  1263. const description = {
  1264. instanceOf: Element,
  1265. name: 'p',
  1266. rangeStart: 0,
  1267. rangeEnd: 1,
  1268. children: [
  1269. {
  1270. instanceOf: Element,
  1271. priority: 1,
  1272. name: 'b',
  1273. children: [
  1274. { instanceOf: Text, data: 'foobar' }
  1275. ]
  1276. }
  1277. ]
  1278. };
  1279. const created = create( writer, description );
  1280. const b = new Element( 'b' );
  1281. writer.setPriority( b, 2 );
  1282. const newRange = writer.unwrap( created.range, b );
  1283. test( writer, newRange, created.node, description );
  1284. } );
  1285. it( 'should not unwrap attributes with different priorities #2', () => {
  1286. // <p>[<b>{foo}</b><b>{bar}</b><b>{baz}</b>]<p> -> <p>[{foo}<b>bar</b>{baz}]</p>
  1287. // <b> around `bar` has different priority than others.
  1288. const writer = new Writer();
  1289. const created = create( writer, {
  1290. instanceOf: Element,
  1291. name: 'p',
  1292. rangeStart: 0,
  1293. rangeEnd: 3,
  1294. children: [
  1295. {
  1296. instanceOf: Element,
  1297. priority: 2,
  1298. name: 'b',
  1299. children: [
  1300. { instanceOf: Text, data: 'foo' }
  1301. ]
  1302. },
  1303. {
  1304. instanceOf: Element,
  1305. priority: 1,
  1306. name: 'b',
  1307. children: [
  1308. { instanceOf: Text, data: 'bar' }
  1309. ]
  1310. },
  1311. {
  1312. instanceOf: Element,
  1313. priority: 2,
  1314. name: 'b',
  1315. children: [
  1316. { instanceOf: Text, data: 'baz' }
  1317. ]
  1318. }
  1319. ]
  1320. } );
  1321. const b = new Element( 'b' );
  1322. writer.setPriority( b, 2 );
  1323. const newRange = writer.unwrap( created.range, b );
  1324. test( writer, newRange, created.node, {
  1325. instanceOf: Element,
  1326. name: 'p',
  1327. rangeStart: 0,
  1328. rangeEnd: 3,
  1329. children: [
  1330. { instanceOf: Text, data: 'foo' },
  1331. {
  1332. instanceOf: Element,
  1333. priority: 1,
  1334. name: 'b',
  1335. children: [
  1336. { instanceOf: Text, data: 'bar' }
  1337. ]
  1338. },
  1339. { instanceOf: Text, data: 'baz' }
  1340. ]
  1341. } );
  1342. } );
  1343. it( 'should unwrap part of the node', () => {
  1344. // <p>[{baz}<b>{foo]bar}</b><p> -> <p>[{bazfoo}]<b>{bar}</b></p>
  1345. const writer = new Writer();
  1346. const created = create( writer, {
  1347. instanceOf: Element,
  1348. name: 'p',
  1349. rangeStart: 0,
  1350. children: [
  1351. { instanceOf: Text, data: 'baz' },
  1352. {
  1353. instanceOf: Element,
  1354. name: 'b',
  1355. priority: 1,
  1356. children: [
  1357. { instanceOf: Text, data: 'foobar', rangeEnd: 3 }
  1358. ]
  1359. }
  1360. ]
  1361. } );
  1362. const b = new Element( 'b' );
  1363. writer.setPriority( b, 1 );
  1364. const newRange = writer.unwrap( created.range, b );
  1365. test( writer, newRange, created.node, {
  1366. instanceOf: Element,
  1367. name: 'p',
  1368. rangeStart: 0,
  1369. rangeEnd: 1,
  1370. children: [
  1371. { instanceOf: Text, data: 'bazfoo' },
  1372. {
  1373. instanceOf: Element,
  1374. name: 'b',
  1375. priority: 1,
  1376. children: [
  1377. { instanceOf: Text, data: 'bar' }
  1378. ]
  1379. }
  1380. ]
  1381. } );
  1382. } );
  1383. it( 'should unwrap nested attributes', () => {
  1384. // <p>[<u><b>{foobar}</b></u>]</p> -> <p>[<u>{foobar}</u>]</p>
  1385. const writer = new Writer();
  1386. const created = create( writer, {
  1387. instanceOf: Element,
  1388. name: 'p',
  1389. rangeStart: 0,
  1390. rangeEnd: 1,
  1391. children: [
  1392. {
  1393. instanceOf: Element,
  1394. name: 'u',
  1395. priority: 1,
  1396. children: [
  1397. {
  1398. instanceOf: Element,
  1399. name: 'b',
  1400. priority: 1,
  1401. children: [
  1402. { instanceOf: Text, data: 'foobar' }
  1403. ]
  1404. }
  1405. ]
  1406. }
  1407. ]
  1408. } );
  1409. const b = new Element( 'b' );
  1410. writer.setPriority( b, 1 );
  1411. const newRange = writer.unwrap( created.range, b );
  1412. test( writer, newRange, created.node, {
  1413. instanceOf: Element,
  1414. name: 'p',
  1415. rangeStart: 0,
  1416. rangeEnd: 1,
  1417. children: [
  1418. {
  1419. instanceOf: Element,
  1420. name: 'u',
  1421. priority: 1,
  1422. children: [
  1423. { instanceOf: Text, data: 'foobar' }
  1424. ]
  1425. }
  1426. ]
  1427. } );
  1428. } );
  1429. it( 'should merge unwrapped nodes #1', () => {
  1430. // <p>{foo}<u>{bar}</u>[<b><u>{bazqux}</u></b>]</p> -> <p>{foo}<u>{bar[bazqux}</u>]</p>
  1431. const writer = new Writer();
  1432. const created = create( writer, {
  1433. instanceOf: Element,
  1434. name: 'p',
  1435. rangeStart: 2,
  1436. rangeEnd: 3,
  1437. children: [
  1438. { instanceOf: Text, data: 'foo' },
  1439. {
  1440. instanceOf: Element,
  1441. name: 'u',
  1442. priority: 1,
  1443. children: [
  1444. { instanceOf: Text, data: 'bar' }
  1445. ]
  1446. },
  1447. {
  1448. instanceOf: Element,
  1449. name: 'b',
  1450. priority: 1,
  1451. children: [
  1452. {
  1453. instanceOf: Element,
  1454. name: 'u',
  1455. priority: 1,
  1456. children: [
  1457. { instanceOf: Text, data: 'bazqux' }
  1458. ]
  1459. }
  1460. ]
  1461. }
  1462. ]
  1463. } );
  1464. const b = new Element( 'b' );
  1465. writer.setPriority( b, 1 );
  1466. const newRange = writer.unwrap( created.range, b );
  1467. test( writer, newRange, created.node, {
  1468. instanceOf: Element,
  1469. name: 'p',
  1470. rangeEnd: 2,
  1471. children: [
  1472. {
  1473. instanceOf: Text,
  1474. data: 'foo'
  1475. },
  1476. {
  1477. instanceOf: Element,
  1478. name: 'u',
  1479. priority: 1,
  1480. children: [
  1481. { instanceOf: Text, data: 'barbazqux', rangeStart: 3 }
  1482. ]
  1483. }
  1484. ]
  1485. } );
  1486. } );
  1487. it( 'should merge unwrapped nodes #2', () => {
  1488. // <p>{foo}<u>{bar}</u>[<b><u>{baz]qux}</u></b></p> -> <p>{foo}<u>{bar[baz}</u>]<b><u>{qux}</u></b></p>
  1489. const writer = new Writer();
  1490. const created = create( writer, {
  1491. instanceOf: Element,
  1492. name: 'p',
  1493. rangeStart: 2,
  1494. children: [
  1495. { instanceOf: Text, data: 'foo' },
  1496. {
  1497. instanceOf: Element,
  1498. name: 'u',
  1499. priority: 1,
  1500. children: [
  1501. { instanceOf: Text, data: 'bar' }
  1502. ]
  1503. },
  1504. {
  1505. instanceOf: Element,
  1506. name: 'b',
  1507. priority: 1,
  1508. children: [
  1509. {
  1510. instanceOf: Element,
  1511. name: 'u',
  1512. priority: 1,
  1513. children: [
  1514. { instanceOf: Text, data: 'bazqux', rangeEnd: 3 }
  1515. ]
  1516. }
  1517. ]
  1518. }
  1519. ]
  1520. } );
  1521. const b = new Element( 'b' );
  1522. writer.setPriority( b, 1 );
  1523. const newRange = writer.unwrap( created.range, b );
  1524. test( writer, newRange, created.node, {
  1525. instanceOf: Element,
  1526. name: 'p',
  1527. rangeEnd: 2,
  1528. children: [
  1529. {
  1530. instanceOf: Text,
  1531. data: 'foo'
  1532. },
  1533. {
  1534. instanceOf: Element,
  1535. name: 'u',
  1536. priority: 1,
  1537. children: [
  1538. { instanceOf: Text, data: 'barbaz', rangeStart: 3 }
  1539. ]
  1540. },
  1541. {
  1542. instanceOf: Element,
  1543. name: 'b',
  1544. priority: 1,
  1545. children: [
  1546. {
  1547. instanceOf: Element,
  1548. name: 'u',
  1549. priority: 1,
  1550. children: [
  1551. { instanceOf: Text, data: 'qux' }
  1552. ]
  1553. }
  1554. ]
  1555. }
  1556. ]
  1557. } );
  1558. } );
  1559. it( 'should merge unwrapped nodes #3', () => {
  1560. // <p>{foo}<u>{bar}</u>[<b><u>{baz}</u></b>]<u>qux</u></p> -> <p>{foo}<u>{bar[baz]qux}</u></p>
  1561. const writer = new Writer();
  1562. const created = create( writer, {
  1563. instanceOf: Element,
  1564. name: 'p',
  1565. rangeStart: 2,
  1566. rangeEnd: 3,
  1567. children: [
  1568. { instanceOf: Text, data: 'foo' },
  1569. {
  1570. instanceOf: Element,
  1571. name: 'u',
  1572. priority: 1,
  1573. children: [
  1574. { instanceOf: Text, data: 'bar' }
  1575. ]
  1576. },
  1577. {
  1578. instanceOf: Element,
  1579. name: 'b',
  1580. priority: 1,
  1581. children: [
  1582. {
  1583. instanceOf: Element,
  1584. name: 'u',
  1585. priority: 1,
  1586. children: [
  1587. { instanceOf: Text, data: 'baz' }
  1588. ]
  1589. }
  1590. ]
  1591. },
  1592. {
  1593. instanceOf: Element,
  1594. name: 'u',
  1595. priority: 1,
  1596. children: [
  1597. { instanceOf: Text, data: 'qux' }
  1598. ]
  1599. }
  1600. ]
  1601. } );
  1602. const b = new Element( 'b' );
  1603. writer.setPriority( b, 1 );
  1604. const newRange = writer.unwrap( created.range, b );
  1605. test( writer, newRange, created.node, {
  1606. instanceOf: Element,
  1607. name: 'p',
  1608. children: [
  1609. {
  1610. instanceOf: Text,
  1611. data: 'foo'
  1612. },
  1613. {
  1614. instanceOf: Element,
  1615. name: 'u',
  1616. priority: 1,
  1617. children: [
  1618. { instanceOf: Text, data: 'barbazqux', rangeStart: 3, rangeEnd: 6 }
  1619. ]
  1620. }
  1621. ]
  1622. } );
  1623. } );
  1624. it( 'should merge unwrapped nodes #4', () => {
  1625. // <p>[<u><b>{foo}</b></u><u><b>{bar}</b></u><u><b>{baz}</b></u>]</p> -> <p>[<u>{foobarbaz}</u>]</p>
  1626. const writer = new Writer();
  1627. const created = create( writer, {
  1628. instanceOf: Element,
  1629. name: 'p',
  1630. rangeStart: 0,
  1631. rangeEnd: 3,
  1632. children: [
  1633. {
  1634. instanceOf: Element,
  1635. name: 'b',
  1636. priority: 1,
  1637. children: [
  1638. {
  1639. instanceOf: Element,
  1640. name: 'u',
  1641. priority: 1,
  1642. children: [
  1643. { instanceOf: Text, data: 'foo' }
  1644. ]
  1645. }
  1646. ]
  1647. },
  1648. {
  1649. instanceOf: Element,
  1650. name: 'b',
  1651. priority: 1,
  1652. children: [
  1653. {
  1654. instanceOf: Element,
  1655. name: 'u',
  1656. priority: 1,
  1657. children: [
  1658. { instanceOf: Text, data: 'bar' }
  1659. ]
  1660. }
  1661. ]
  1662. },
  1663. {
  1664. instanceOf: Element,
  1665. name: 'b',
  1666. priority: 1,
  1667. children: [
  1668. {
  1669. instanceOf: Element,
  1670. name: 'u',
  1671. priority: 1,
  1672. children: [
  1673. { instanceOf: Text, data: 'baz' }
  1674. ]
  1675. }
  1676. ]
  1677. }
  1678. ]
  1679. } );
  1680. const b = new Element( 'b' );
  1681. writer.setPriority( b, 1 );
  1682. const newRange = writer.unwrap( created.range, b );
  1683. test( writer, newRange, created.node, {
  1684. instanceOf: Element,
  1685. name: 'p',
  1686. rangeStart: 0,
  1687. rangeEnd: 1,
  1688. children: [
  1689. {
  1690. instanceOf: Element,
  1691. name: 'u',
  1692. priority: 1,
  1693. children: [
  1694. { instanceOf: Text, data: 'foobarbaz' }
  1695. ]
  1696. }
  1697. ]
  1698. } );
  1699. } );
  1700. } );
  1701. } );