writer.js 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730
  1. /**
  2. * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* globals beforeEach, describe, it */
  6. /* bender-tags: treeview */
  7. 'use strict';
  8. import Writer from '/ckeditor5/core/treeview/writer.js';
  9. import Element from '/ckeditor5/core/treeview/element.js';
  10. import Text from '/ckeditor5/core/treeview/text.js';
  11. import Position from '/ckeditor5/core/treeview/position.js';
  12. import Range from '/ckeditor5/core/treeview/range.js';
  13. import CKEditorError from '/ckeditor5/core/ckeditorerror.js';
  14. describe( 'Writer', () => {
  15. let writer;
  16. beforeEach( () => {
  17. writer = new Writer();
  18. } );
  19. /**
  20. * Helper function that is used to test output of writer methods by providing declarative description of the
  21. * expected output.
  22. * Examples:
  23. * test element: `<p>fo[o<b>ba]r</b></p>`
  24. * description: {
  25. * name: 'p',
  26. * instanceOf: Element,
  27. * children:[
  28. * {
  29. * instanceOf: Text,
  30. * data: 'foo',
  31. * rangeStart: 2
  32. * },
  33. * {
  34. * name: 'b'
  35. * instanceOf: Element
  36. * priority: 1,
  37. * children: [
  38. * { instanceOf: Text, data: 'bar', rangeEnd: 2 }
  39. * ]
  40. * }
  41. * ]
  42. * }
  43. *
  44. *
  45. * @param {core.treeView.Writer} writer Writer instance. Used to test priority.
  46. * @param {core.treeView.Range|core.treeView.Position } location Range instance or Position instance.
  47. * Treated as Range when when `rangeStart`, `rangeEnd` is used, treated as Position when `position` is used.
  48. * @param {core.treeView.Node} node Element to check.
  49. * @param {Object} description Object describing expected element and its children.
  50. */
  51. function test( writer, location, node, description ) {
  52. // If no root node provided - iterate over node array.
  53. if ( description instanceof Array && node instanceof Array ) {
  54. node.forEach( ( n, i ) => {
  55. test( writer, location, n, description[ i ] );
  56. } );
  57. }
  58. if ( description.instanceOf ) {
  59. expect( node ).to.be.instanceof( description.instanceOf );
  60. }
  61. if ( description.name ) {
  62. expect( description.name ).to.equal( node.name );
  63. }
  64. if ( description.data ) {
  65. expect( description.data ).to.equal( node.data );
  66. }
  67. if ( description.priority !== undefined ) {
  68. expect( description.priority ).to.equal( writer.getPriority( node ) );
  69. }
  70. if ( description.rangeStart !== undefined ) {
  71. expect( node ).to.equal( location.start.parent );
  72. expect( description.rangeStart ).to.equal( location.start.offset );
  73. }
  74. if ( description.rangeEnd !== undefined ) {
  75. expect( node ).to.equal( location.end.parent );
  76. expect( description.rangeEnd ).to.equal( location.end.offset );
  77. }
  78. if ( description.attributes ) {
  79. Object.keys( description.attributes ).forEach( ( key ) => {
  80. expect( description.attributes[ key ] ).to.equal( node.getAttribute( key ) );
  81. } );
  82. }
  83. if ( description.position !== undefined ) {
  84. expect( node ).to.equal( location.parent );
  85. expect( description.position ).to.equal( location.offset );
  86. }
  87. if ( description.children ) {
  88. expect( description.children.length ).to.equal( node.getChildCount() );
  89. description.children.forEach( ( desc, index ) => {
  90. test( writer, location, node.getChild( index ), desc );
  91. } );
  92. }
  93. }
  94. /**
  95. * Helper function that is used to create treeView elements from description object.
  96. *
  97. * @param {core.treeView.Writer} writer Writer instance. Used to set priorities.
  98. * @param {Object} description Description object.
  99. * @param {core.treeView.Range} [range] Optional parameter, used in recurrent calls.
  100. * @param {core.treeView.Position} [position] Optional parameter, used in recurrent calls.
  101. * @returns {Object} Returns object with `node`, `range`, `position` fields, containing created node and, optionally
  102. * range and position if description object contain information about them.
  103. */
  104. function create( writer, description, range, position ) {
  105. const node = new description.instanceOf();
  106. if ( !range ) {
  107. range = Range.createFromParentsAndOffsets( node, 0, node, 0 );
  108. }
  109. if ( !position ) {
  110. position = new Position( node, 0 );
  111. }
  112. if ( description.name ) {
  113. node.name = description.name;
  114. }
  115. if ( description.data ) {
  116. node.data = description.data;
  117. }
  118. if ( description.attributes ) {
  119. Object.keys( description.attributes ).forEach( ( key ) => {
  120. node.setAttribute( key, description.attributes[ key ] );
  121. } );
  122. }
  123. if ( description.priority !== undefined ) {
  124. writer.setPriority( node, description.priority );
  125. }
  126. if ( description.rangeStart !== undefined ) {
  127. range.start.parent = node;
  128. range.start.offset = description.rangeStart;
  129. }
  130. if ( description.rangeEnd !== undefined ) {
  131. range.end.parent = node;
  132. range.end.offset = description.rangeEnd;
  133. }
  134. if ( description.position !== undefined ) {
  135. position.parent = node;
  136. position.offset = description.position;
  137. }
  138. if ( description.children ) {
  139. description.children.forEach( ( desc, index ) => {
  140. const created = create( writer, desc, range, position );
  141. node.insertChildren( index, created.node );
  142. } );
  143. }
  144. return { node, range, position };
  145. }
  146. describe( 'isContainer', () => {
  147. it( 'should return true for container elements', () => {
  148. const containerElement = new Element( 'p' );
  149. const attributeElement = new Element( 'b' );
  150. writer._priorities.set( attributeElement, 1 );
  151. expect( writer.isContainer( containerElement ) ).to.be.true;
  152. expect( writer.isContainer( attributeElement ) ).to.be.false;
  153. } );
  154. } );
  155. describe( 'isAttribute', () => {
  156. it( 'should return true for container elements', () => {
  157. const containerElement = new Element( 'p' );
  158. const attributeElement = new Element( 'b' );
  159. writer._priorities.set( attributeElement, 1 );
  160. expect( writer.isAttribute( containerElement ) ).to.be.false;
  161. expect( writer.isAttribute( attributeElement ) ).to.be.true;
  162. } );
  163. } );
  164. describe( 'setPriority', () => {
  165. it( 'sets node priority', () => {
  166. const nodeMock = {};
  167. writer.setPriority( nodeMock, 10 );
  168. expect( writer._priorities.get( nodeMock ) ).to.equal( 10 );
  169. } );
  170. } );
  171. describe( 'getPriority', () => {
  172. it( 'gets node priority', () => {
  173. const nodeMock = {};
  174. writer._priorities.set( nodeMock, 12 );
  175. expect( writer.getPriority( nodeMock ) ).to.equal( 12 );
  176. } );
  177. } );
  178. describe( 'getParentContainer', () => {
  179. it( 'should return parent container of the node', () => {
  180. const text = new Text( 'foobar' );
  181. const b = new Element( 'b', null, [ text ] );
  182. const parent = new Element( 'p', null, [ b ] );
  183. writer.setPriority( b, 1 );
  184. const container = writer.getParentContainer( new Position( text, 0 ) );
  185. expect( container ).to.equal( parent );
  186. } );
  187. it( 'should return null if no parent container', () => {
  188. const text = new Text( 'foobar' );
  189. const b = new Element( 'b', null, [ text ] );
  190. writer.setPriority( b, 1 );
  191. const container = writer.getParentContainer( new Position( text, 0 ) );
  192. expect( container ).to.equal( null );
  193. } );
  194. } );
  195. describe( 'breakAttributes', () => {
  196. // <p>{|foobar}</p> -> <p>|{foobar}</p>
  197. it( '<p>{|foobar}</p>', () => {
  198. const created = create( writer, {
  199. instanceOf: Element,
  200. name: 'p',
  201. children: [
  202. { instanceOf: Text, data: 'foobar', position: 0 }
  203. ]
  204. } );
  205. const newPosition = writer.breakAttributes( created.position );
  206. test( writer, newPosition, created.node, {
  207. instanceOf: Element,
  208. name: 'p',
  209. position: 0,
  210. children: [
  211. { instanceOf: Text, data: 'foobar' }
  212. ]
  213. } );
  214. } );
  215. it( '<p>foo|bar</p>', () => {
  216. // <p>{foo|bar}</p> -> <p>{foo}|{bar}</p>
  217. const created = create( writer, {
  218. instanceOf: Element,
  219. name: 'p',
  220. children: [
  221. { instanceOf: Text, data: 'foobar', position: 3 }
  222. ]
  223. } );
  224. const newPosition = writer.breakAttributes( created.position );
  225. test( writer, newPosition, created.node, {
  226. instanceOf: Element,
  227. name: 'p',
  228. position: 1,
  229. children: [
  230. { instanceOf: Text, data: 'foo' },
  231. { instanceOf: Text, data: 'bar' }
  232. ]
  233. } );
  234. } );
  235. it( '<p>{foobar|}</p>', () => {
  236. // <p>{foobar|}</p> -> <p>{foobar}|</p>
  237. const created = create( writer, {
  238. instanceOf: Element,
  239. name: 'p',
  240. children: [
  241. { instanceOf: Text, data: 'foobar', position: 6 }
  242. ]
  243. } );
  244. const newPosition = writer.breakAttributes( created.position );
  245. test( writer, newPosition, created.node, {
  246. instanceOf: Element,
  247. name: 'p',
  248. position: 1,
  249. children: [
  250. { instanceOf: Text, data: 'foobar' }
  251. ]
  252. } );
  253. } );
  254. it( '<p><b>{foo|bar}</b></p>', () => {
  255. // <p><b>{foo|bar}</b></p> -> <p><b>{foo}</b>|<b>{bar}</b></p>
  256. const created = create( writer, {
  257. instanceOf: Element,
  258. name: 'p',
  259. children: [
  260. {
  261. instanceOf: Element,
  262. name: 'b',
  263. priority: 1,
  264. children: [
  265. { instanceOf: Text, data: 'foobar', position: 3 }
  266. ]
  267. }
  268. ]
  269. } );
  270. const newPosition = writer.breakAttributes( created.position );
  271. test( writer, newPosition, created.node, {
  272. instanceOf: Element,
  273. name: 'p',
  274. position: 1,
  275. children: [
  276. {
  277. instanceOf: Element,
  278. name: 'b',
  279. priority: 1,
  280. children: [
  281. { instanceOf: Text, data: 'foo' }
  282. ]
  283. },
  284. {
  285. instanceOf: Element,
  286. name: 'b',
  287. priority: 1,
  288. children: [
  289. { instanceOf: Text, data: 'bar' }
  290. ]
  291. }
  292. ]
  293. } );
  294. } );
  295. it( '<p><b><u>{|foobar}</u></b></p>', () => {
  296. // <p><b><u>{|foobar}</u></b></p> -> <p>|<b><u>{foobar}</u></b></p>
  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 created = create( writer, {
  345. instanceOf: Element,
  346. name: 'p',
  347. children: [
  348. {
  349. instanceOf: Element,
  350. name: 'b',
  351. priority: 1,
  352. children: [
  353. {
  354. instanceOf: Element,
  355. name: 'u',
  356. priority: 1,
  357. children: [
  358. { instanceOf: Text, data: 'foobar', position: 3 }
  359. ]
  360. }
  361. ]
  362. }
  363. ]
  364. } );
  365. const newPosition = writer.breakAttributes( created.position );
  366. test( writer, newPosition, created.node, {
  367. instanceOf: Element,
  368. name: 'p',
  369. position: 1,
  370. children: [
  371. {
  372. instanceOf: Element,
  373. name: 'b',
  374. priority: 1,
  375. children: [
  376. {
  377. instanceOf: Element,
  378. name: 'u',
  379. priority: 1,
  380. children: [
  381. { instanceOf: Text, data: 'foo' }
  382. ]
  383. }
  384. ]
  385. },
  386. {
  387. instanceOf: Element,
  388. name: 'b',
  389. priority: 1,
  390. children: [
  391. {
  392. instanceOf: Element,
  393. name: 'u',
  394. priority: 1,
  395. children: [
  396. { instanceOf: Text, data: 'bar' }
  397. ]
  398. }
  399. ]
  400. }
  401. ]
  402. } );
  403. } );
  404. it( '<p><b><u>{foobar|}</u></b></p>', () => {
  405. // <p><b><u>{foobar|}</u></b></p> -> <p><b><u>{foobar}</u></b>|</p>
  406. const created = create( writer, {
  407. instanceOf: Element,
  408. name: 'p',
  409. children: [
  410. {
  411. instanceOf: Element,
  412. name: 'b',
  413. priority: 1,
  414. children: [
  415. {
  416. instanceOf: Element,
  417. name: 'u',
  418. priority: 1,
  419. children: [
  420. { instanceOf: Text, data: 'foobar', position: 6 }
  421. ]
  422. }
  423. ]
  424. }
  425. ]
  426. } );
  427. const newPosition = writer.breakAttributes( created.position );
  428. test( writer, newPosition, created.node, {
  429. instanceOf: Element,
  430. name: 'p',
  431. position: 1,
  432. children: [
  433. {
  434. instanceOf: Element,
  435. name: 'b',
  436. priority: 1,
  437. children: [
  438. {
  439. instanceOf: Element,
  440. name: 'u',
  441. priority: 1,
  442. children: [
  443. { instanceOf: Text, data: 'foobar' }
  444. ]
  445. }
  446. ]
  447. }
  448. ]
  449. } );
  450. } );
  451. } );
  452. describe( 'breakRange', () => {
  453. it( 'should throw when range placed in two containers', () => {
  454. const p1 = new Element( 'p' );
  455. const p2 = new Element( 'p' );
  456. expect( () => {
  457. writer.breakRange( Range.createFromParentsAndOffsets( p1, 0, p2, 0 ) );
  458. } ).to.throw( 'treeview-writer-invalid-range-container' );
  459. } );
  460. it( 'should break at collapsed range and return collapsed one', () => {
  461. // <p>{foo[]bar}</p> -> <p>{foo}[]{bar}</p>
  462. const created = create( writer, {
  463. instanceOf: Element,
  464. name: 'p',
  465. children: [
  466. { instanceOf: Text, data: 'foobar', rangeStart: 3, rangeEnd: 3 }
  467. ]
  468. } );
  469. const newRange = writer.breakRange( created.range );
  470. test( writer, newRange, created.node, {
  471. instanceOf: Element,
  472. name: 'p',
  473. rangeStart: 1,
  474. rangeEnd: 1,
  475. children: [
  476. { instanceOf: Text, data: 'foo' },
  477. { instanceOf: Text, data: 'bar' }
  478. ]
  479. } );
  480. } );
  481. it( 'should break inside text node #1', () => {
  482. // <p>{foo[bar]baz}</p> -> <p>{foo}[{bar}]{baz}</p>
  483. const created = create( writer, {
  484. instanceOf: Element,
  485. name: 'p',
  486. children: [
  487. { instanceOf: Text, data: 'foobarbaz', rangeStart: 3, rangeEnd: 6 }
  488. ]
  489. } );
  490. const newRange = writer.breakRange( created.range );
  491. test( writer, newRange, created.node, {
  492. instanceOf: Element,
  493. name: 'p',
  494. rangeStart: 1,
  495. rangeEnd: 2,
  496. children: [
  497. { instanceOf: Text, data: 'foo' },
  498. { instanceOf: Text, data: 'bar' },
  499. { instanceOf: Text, data: 'baz' }
  500. ]
  501. } );
  502. } );
  503. it( 'should break inside text node #2', () => {
  504. // <p>{foo[barbaz]}</p> -> <p>{foo}[{barbaz}]</p>
  505. const created = create( writer, {
  506. instanceOf: Element,
  507. name: 'p',
  508. children: [
  509. { instanceOf: Text, data: 'foobarbaz', rangeStart: 3, rangeEnd: 9 }
  510. ]
  511. } );
  512. const newRange = writer.breakRange( created.range );
  513. test( writer, newRange, created.node, {
  514. instanceOf: Element,
  515. name: 'p',
  516. rangeStart: 1,
  517. rangeEnd: 2,
  518. children: [
  519. { instanceOf: Text, data: 'foo' },
  520. { instanceOf: Text, data: 'barbaz' }
  521. ]
  522. } );
  523. } );
  524. it( 'should break inside text node #3', () => {
  525. // <p>{foo[barbaz}]</p> -> <p>{foo}[{barbaz}]</p>
  526. const created = create( writer, {
  527. instanceOf: Element,
  528. name: 'p',
  529. rangeEnd: 1,
  530. children: [
  531. { instanceOf: Text, data: 'foobarbaz', rangeStart: 3 }
  532. ]
  533. } );
  534. const newRange = writer.breakRange( created.range );
  535. test( writer, newRange, created.node, {
  536. instanceOf: Element,
  537. name: 'p',
  538. rangeStart: 1,
  539. rangeEnd: 2,
  540. children: [
  541. { instanceOf: Text, data: 'foo' },
  542. { instanceOf: Text, data: 'barbaz' }
  543. ]
  544. } );
  545. } );
  546. it( 'should break inside text node #4', () => {
  547. // <p>{[foo]barbaz}</p> -> <p>[{foo}]{barbaz]</p>
  548. const created = create( writer, {
  549. instanceOf: Element,
  550. name: 'p',
  551. children: [
  552. { instanceOf: Text, data: 'foobarbaz', rangeStart: 0, rangeEnd: 3 }
  553. ]
  554. } );
  555. const newRange = writer.breakRange( created.range );
  556. test( writer, newRange, created.node, {
  557. instanceOf: Element,
  558. name: 'p',
  559. rangeStart: 0,
  560. rangeEnd: 1,
  561. children: [
  562. { instanceOf: Text, data: 'foo' },
  563. { instanceOf: Text, data: 'barbaz' }
  564. ]
  565. } );
  566. } );
  567. it( 'should break inside text node #5', () => {
  568. // <p>[{foo]barbaz}</p> -> <p>[{foo}]{barbaz]</p>
  569. const created = create( writer, {
  570. instanceOf: Element,
  571. name: 'p',
  572. rangeStart: 0,
  573. children: [
  574. { instanceOf: Text, data: 'foobarbaz', rangeEnd: 3 }
  575. ]
  576. } );
  577. const newRange = writer.breakRange( created.range );
  578. test( writer, newRange, created.node, {
  579. instanceOf: Element,
  580. name: 'p',
  581. rangeStart: 0,
  582. rangeEnd: 1,
  583. children: [
  584. { instanceOf: Text, data: 'foo' },
  585. { instanceOf: Text, data: 'barbaz' }
  586. ]
  587. } );
  588. } );
  589. it( 'should break placed inside different nodes', () => {
  590. // <p>{foo[bar}<b>{baz]qux}</b></p>
  591. // <p>{foo}[{bar}<b>{baz}</b>]<b>qux</b></p>
  592. const created = create( writer, {
  593. instanceOf: Element,
  594. name: 'p',
  595. children: [
  596. { instanceOf: Text, data: 'foobar', rangeStart: 3 },
  597. {
  598. instanceOf: Element,
  599. name: 'b',
  600. priority: 1,
  601. children: [
  602. { instanceOf: Text, data: 'bazqux', rangeEnd: 3 }
  603. ]
  604. }
  605. ]
  606. } );
  607. const newRange = writer.breakRange( created.range );
  608. test( writer, newRange, created.node, {
  609. instanceOf: Element,
  610. name: 'p',
  611. rangeStart: 1,
  612. rangeEnd: 3,
  613. children: [
  614. { instanceOf: Text, data: 'foo' },
  615. { instanceOf: Text, data: 'bar' },
  616. {
  617. instanceOf: Element,
  618. name: 'b',
  619. priority: 1,
  620. children: [
  621. { instanceOf: Text, data: 'baz' }
  622. ]
  623. },
  624. {
  625. instanceOf: Element,
  626. name: 'b',
  627. priority: 1,
  628. children: [
  629. { instanceOf: Text, data: 'qux' }
  630. ]
  631. }
  632. ]
  633. } );
  634. } );
  635. } );
  636. describe( 'mergeAttributes', () => {
  637. it( 'should not merge if inside text node', () => {
  638. // <p>{fo|obar}</p>
  639. const description = {
  640. instanceOf: Element,
  641. name: 'p',
  642. children: [
  643. { instanceOf: Text, data: 'foobar', position: 2 }
  644. ]
  645. };
  646. const created = create( writer, description );
  647. const newPosition = writer.mergeAttributes( created.position );
  648. test( writer, newPosition, created.node, description );
  649. } );
  650. it( 'should not merge if between containers', () => {
  651. // <div><p>{foo}</p>|<p>{bar}</p></div>
  652. const description = {
  653. instanceOf: Element,
  654. name: 'div',
  655. position: 1,
  656. children: [
  657. {
  658. instanceOf: Element,
  659. name: 'p',
  660. children: [
  661. { instanceOf: Text, data: 'foo' }
  662. ]
  663. },
  664. {
  665. instanceOf: Element,
  666. name: 'p',
  667. children: [
  668. { instanceOf: Text, data: 'bar' }
  669. ]
  670. }
  671. ]
  672. };
  673. const created = create( writer, description );
  674. const newPosition = writer.mergeAttributes( created.position );
  675. test( writer, newPosition, created.node, description );
  676. } );
  677. it( 'should return same position when inside empty container', () => {
  678. // <p>|</p>
  679. const description = { instanceOf: Element, name: 'p', position: 0 };
  680. const created = create( writer, description );
  681. const newPosition = writer.mergeAttributes( created.position );
  682. test( writer, newPosition, created.node, description );
  683. } );
  684. it( 'should not merge when position is placed at the beginning of the container', () => {
  685. // <p>|<b></b></p>
  686. const description = {
  687. instanceOf: Element,
  688. name: 'p',
  689. position: 0,
  690. children: [
  691. {
  692. instanceOf: Element,
  693. name: 'b',
  694. priority: 1
  695. }
  696. ]
  697. };
  698. const created = create( writer, description );
  699. const newPosition = writer.mergeAttributes( created.position );
  700. test( writer, newPosition, created.node, description );
  701. } );
  702. it( 'should not merge when position is placed at the end of the container', () => {
  703. // <p><b></b>|</p>
  704. const description = {
  705. instanceOf: Element,
  706. name: 'p',
  707. position: 1,
  708. children: [
  709. {
  710. instanceOf: Element,
  711. name: 'b',
  712. priority: 1
  713. }
  714. ]
  715. };
  716. const created = create( writer, description );
  717. const newPosition = writer.mergeAttributes( created.position );
  718. test( writer, newPosition, created.node, description );
  719. } );
  720. it( 'should merge when placed between two text nodes', () => {
  721. // <p>{foo}|{bar}</p> -> <p>{foo|bar}</p>
  722. const created = create( writer, {
  723. instanceOf: Element,
  724. name: 'p',
  725. position: 1,
  726. children: [
  727. { instanceOf: Text, data: 'foo' },
  728. { instanceOf: Text, data: 'bar' }
  729. ]
  730. } );
  731. const newPosition = writer.mergeAttributes( created.position );
  732. test( writer, newPosition, created.node, {
  733. instanceOf: Element,
  734. name: 'p',
  735. children: [
  736. { instanceOf: Text, data: 'foobar', position: 3 }
  737. ]
  738. } );
  739. } );
  740. it( 'should merge when placed between similar attribute nodes', () => {
  741. // <p><b foo="bar"></b>|<b foo="bar"></b></p> -> <p><b foo="bar">|</b></p>
  742. const created = create( writer, {
  743. instanceOf: Element,
  744. name: 'p',
  745. position: 1,
  746. children: [
  747. {
  748. instanceOf: Element,
  749. name: 'b',
  750. priority: 1,
  751. attributes: { foo: 'bar' }
  752. },
  753. {
  754. instanceOf: Element,
  755. name: 'b',
  756. priority: 1,
  757. attributes: { foo: 'bar' }
  758. }
  759. ]
  760. } );
  761. const newPosition = writer.mergeAttributes( created.position );
  762. test( writer, newPosition, created.node, {
  763. instanceOf: Element,
  764. name: 'p',
  765. children: [
  766. {
  767. instanceOf: Element,
  768. name: 'b',
  769. priority: 1,
  770. position: 0,
  771. attributes: { foo: 'bar' }
  772. }
  773. ]
  774. } );
  775. } );
  776. it( 'should not merge when placed between non-similar attribute nodes', () => {
  777. // <p><b foo="bar"></b>|<b foo="baz"></b></p> ->
  778. // <p><b foo="bar"></b>|<b foo="baz"></b></p>
  779. const description = {
  780. instanceOf: Element,
  781. name: 'p',
  782. position: 1,
  783. children: [
  784. {
  785. instanceOf: Element,
  786. name: 'b',
  787. priority: 1,
  788. attributes: { foo: 'bar' }
  789. },
  790. {
  791. instanceOf: Element,
  792. name: 'b',
  793. priority: 1,
  794. attributes: { foo: 'baz' }
  795. }
  796. ]
  797. };
  798. const created = create( writer, description );
  799. const newPosition = writer.mergeAttributes( created.position );
  800. test( writer, newPosition, created.node, description );
  801. } );
  802. it( 'should not merge when placed between similar attribute nodes with different priority', () => {
  803. // <p><b foo="bar"></b>|<b foo="bar"></b></p> -> <p><b foo="bar"></b>|<b foo="bar"></b></p>
  804. const description = {
  805. instanceOf: Element,
  806. name: 'p',
  807. position: 1,
  808. children: [
  809. {
  810. instanceOf: Element,
  811. name: 'b',
  812. priority: 1,
  813. attributes: { foo: 'bar' }
  814. },
  815. {
  816. instanceOf: Element,
  817. name: 'b',
  818. priority: 2,
  819. attributes: { foo: 'bar' }
  820. }
  821. ]
  822. };
  823. const created = create( writer,description );
  824. const newPosition = writer.mergeAttributes( created.position );
  825. test( writer, newPosition, created.node, description );
  826. } );
  827. it( 'should merge attribute nodes and their contents if possible', () => {
  828. // <p><b foo="bar">{foo}</b>|<b foo="bar">{bar}</b></p>
  829. // <p><b foo="bar">{foo}|{bar}</b></p>
  830. // <p><b foo="bar">{foo|bar}</b></p>
  831. const created = create( writer, {
  832. instanceOf: Element,
  833. name: 'p',
  834. position: 1,
  835. children: [
  836. {
  837. instanceOf: Element,
  838. name: 'b',
  839. priority: 1,
  840. attributes: { foo: 'bar' },
  841. children: [
  842. { instanceOf: Text, data: 'foo' }
  843. ]
  844. },
  845. {
  846. instanceOf: Element,
  847. name: 'b',
  848. priority: 1,
  849. attributes: { foo: 'bar' },
  850. children: [
  851. { instanceOf: Text, data: 'bar' }
  852. ]
  853. }
  854. ]
  855. } );
  856. const newPosition = writer.mergeAttributes( created.position );
  857. test( writer, newPosition, created.node, {
  858. instanceOf: Element,
  859. name: 'p',
  860. children: [
  861. {
  862. instanceOf: Element,
  863. name: 'b',
  864. priority: 1,
  865. attributes: { foo: 'bar' },
  866. children: [
  867. { instanceOf: Text, data: 'foobar', position: 3 }
  868. ]
  869. }
  870. ]
  871. } );
  872. } );
  873. } );
  874. describe( 'insert', () => {
  875. it( 'should return collapsed range in insertion position when using empty array', () => {
  876. // <p>{foo|bar}</p> -> <p>{foo[]bar}</p>
  877. const created = create( writer, {
  878. instanceOf: Element,
  879. name: 'p',
  880. children: [
  881. { instanceOf: Text, data: 'foobar', position: 3 }
  882. ]
  883. } );
  884. const newRange = writer.insert( created.position, [] );
  885. test( writer, newRange, created.node, {
  886. instanceOf: Element,
  887. name: 'p',
  888. children: [
  889. { instanceOf: Text, data: 'foobar', rangeStart: 3, rangeEnd: 3 }
  890. ]
  891. } );
  892. } );
  893. it( 'should insert text into another text node', () => {
  894. // <p>{foo|bar}</p> insert {baz}
  895. // <p>{foo[baz]bar}</p>
  896. const created = create( writer, {
  897. instanceOf: Element,
  898. name: 'p',
  899. children: [
  900. { instanceOf: Text, data: 'foobar', position: 3 }
  901. ]
  902. } );
  903. const newRange = writer.insert( created.position, new Text( 'baz' ) );
  904. test( writer, newRange, created.node, {
  905. instanceOf: Element,
  906. name: 'p',
  907. children: [
  908. { instanceOf: Text, data: 'foobazbar' }
  909. ]
  910. } );
  911. } );
  912. it( 'should break attributes when inserting into text node', () => {
  913. // <p>{foo|bar}</p> insert <b>{baz}</b>
  914. // <p>{foo}[<b>baz</b>]{bar}</p>
  915. const created = create( writer, {
  916. instanceOf: Element,
  917. name: 'p',
  918. children: [
  919. { instanceOf: Text, data: 'foobar', position: 3 }
  920. ]
  921. } );
  922. const toInsert = create( writer, {
  923. instanceOf: Element,
  924. name: 'b',
  925. priority: 1,
  926. children: [
  927. { instanceOf: Text, data: 'baz' }
  928. ]
  929. } );
  930. const newRange = writer.insert( created.position, toInsert.node );
  931. test( writer, newRange, created.node, {
  932. instanceOf: Element,
  933. name: 'p',
  934. rangeStart: 1,
  935. rangeEnd: 2,
  936. children: [
  937. { instanceOf: Text, data: 'foo' },
  938. {
  939. instanceOf: Element,
  940. name: 'b',
  941. priority: 1,
  942. children: [
  943. { instanceOf: Text, data: 'baz' }
  944. ]
  945. },
  946. { instanceOf: Text, data: 'bar' }
  947. ]
  948. } );
  949. } );
  950. it( 'should merge same attribute nodes', () => {
  951. // <p><b>{foo|bar}</b></p> insert <b>{baz}</b>
  952. // <p><b>{foo[baz]bar}</b></p>
  953. const created = create( writer, {
  954. instanceOf: Element,
  955. name: 'p',
  956. children: [
  957. {
  958. instanceOf: Element,
  959. name: 'b',
  960. priority: 1,
  961. children: [
  962. { instanceOf: Text, data: 'foobar', position: 3 }
  963. ]
  964. }
  965. ]
  966. } );
  967. const toInsert = create( writer, {
  968. instanceOf: Element,
  969. name: 'b',
  970. priority: 1,
  971. children: [
  972. { instanceOf: Text, data: 'baz' }
  973. ]
  974. } );
  975. const newRange = writer.insert( created.position, toInsert.node );
  976. test( writer, newRange, created.node, {
  977. instanceOf: Element,
  978. name: 'p',
  979. children: [
  980. {
  981. instanceOf: Element,
  982. name: 'b',
  983. priority: 1,
  984. children: [
  985. { instanceOf: Text, data: 'foobazbar', rangeStart: 3, rangeEnd: 6 }
  986. ]
  987. }
  988. ]
  989. } );
  990. } );
  991. it( 'should not merge different attributes', () => {
  992. // <p><b>{foo|bar}</b></p> insert <b>{baz}</b> ( different priority )
  993. // <p><b>{foo}</b>[<b>{baz}</b>]<b>{bar}</b></p>
  994. const created = create( writer, {
  995. instanceOf: Element,
  996. name: 'p',
  997. children: [
  998. {
  999. instanceOf: Element,
  1000. name: 'b',
  1001. priority: 1,
  1002. children: [
  1003. { instanceOf: Text, data: 'foobar', position: 3 }
  1004. ]
  1005. }
  1006. ]
  1007. } );
  1008. const toInsert = create( writer, {
  1009. instanceOf: Element,
  1010. name: 'b',
  1011. priority: 2,
  1012. children: [
  1013. { instanceOf: Text, data: 'baz' }
  1014. ]
  1015. } );
  1016. const newRange = writer.insert( created.position, toInsert.node );
  1017. test( writer, newRange, created.node, {
  1018. instanceOf: Element,
  1019. name: 'p',
  1020. rangeStart: 1,
  1021. rangeEnd: 2,
  1022. children: [
  1023. {
  1024. instanceOf: Element,
  1025. name: 'b',
  1026. priority: 1,
  1027. children: [
  1028. { instanceOf: Text, data: 'foo' }
  1029. ]
  1030. },
  1031. {
  1032. instanceOf: Element,
  1033. name: 'b',
  1034. priority: 2,
  1035. children: [
  1036. { instanceOf: Text, data: 'baz' }
  1037. ]
  1038. },
  1039. {
  1040. instanceOf: Element,
  1041. name: 'b',
  1042. priority: 1,
  1043. children: [
  1044. { instanceOf: Text, data: 'bar' }
  1045. ]
  1046. }
  1047. ]
  1048. } );
  1049. } );
  1050. it( 'should allow to insert multiple nodes', () => {
  1051. // <p>|</p> insert <b>{foo}</b>{bar}
  1052. // <p>[<b>{foo}</b>{bar}]</p>
  1053. const root = new Element( 'p' );
  1054. const toInsert = create( writer, {
  1055. instanceOf: Element,
  1056. name: 'fake',
  1057. children: [
  1058. {
  1059. instanceOf: Element,
  1060. name: 'b',
  1061. priority: 1,
  1062. children: [
  1063. { instanceOf: Text, data: 'foo' }
  1064. ]
  1065. },
  1066. { instanceOf: Text, data: 'bar' }
  1067. ]
  1068. } ).node.getChildren();
  1069. const position = new Position( root, 0 );
  1070. const newRange = writer.insert( position, toInsert );
  1071. test( writer, newRange, root, {
  1072. instanceOf: Element,
  1073. name: 'p',
  1074. rangeStart: 0,
  1075. rangeEnd: 2,
  1076. children: [
  1077. {
  1078. instanceOf: Element,
  1079. name: 'b',
  1080. priority: 1,
  1081. children: [
  1082. { instanceOf: Text, data: 'foo' }
  1083. ]
  1084. },
  1085. { instanceOf: Text, data: 'bar' }
  1086. ]
  1087. } );
  1088. } );
  1089. it( 'should merge after inserting multiple nodes', () => {
  1090. // <p><b>{qux}</b>|{baz}</p> insert <b>{foo}</b>{bar}
  1091. // <p><b>{qux[foo}</b>{bar]baz}</p>
  1092. const created = create( writer, {
  1093. instanceOf: Element,
  1094. name: 'p',
  1095. position: 1,
  1096. children: [
  1097. {
  1098. instanceOf: Element,
  1099. name: 'b',
  1100. priority: 1,
  1101. children: [
  1102. { instanceOf: Text, data: 'qux' }
  1103. ]
  1104. },
  1105. { instanceOf: Text, data: 'baz' }
  1106. ]
  1107. } );
  1108. const toInsert = create( writer, {
  1109. instanceOf: Element,
  1110. name: 'fake',
  1111. children: [
  1112. {
  1113. instanceOf: Element,
  1114. name: 'b',
  1115. priority: 1,
  1116. children: [
  1117. { instanceOf: Text, data: 'foo' }
  1118. ]
  1119. },
  1120. { instanceOf: Text, data: 'bar' }
  1121. ]
  1122. } ).node.getChildren();
  1123. const newRange = writer.insert( created.position, toInsert );
  1124. test( writer, newRange, created.node, {
  1125. instanceOf: Element,
  1126. name: 'p',
  1127. children: [
  1128. {
  1129. instanceOf: Element,
  1130. name: 'b',
  1131. priority: 1,
  1132. children: [
  1133. { instanceOf: Text, data: 'quxfoo', rangeStart: 3 }
  1134. ]
  1135. },
  1136. { instanceOf: Text, data: 'barbaz', rangeEnd: 3 }
  1137. ]
  1138. } );
  1139. } );
  1140. } );
  1141. describe( 'remove', () => {
  1142. it( 'should throw when range placed in two containers', () => {
  1143. const p1 = new Element( 'p' );
  1144. const p2 = new Element( 'p' );
  1145. expect( () => {
  1146. writer.remove( Range.createFromParentsAndOffsets( p1, 0, p2, 0 ) );
  1147. } ).to.throw( 'treeview-writer-invalid-range-container' );
  1148. } );
  1149. it( 'should return empty array when range is collapsed', () => {
  1150. const p = new Element( 'p' );
  1151. const range = Range.createFromParentsAndOffsets( p, 0, p, 0 );
  1152. const nodes = writer.remove( range );
  1153. expect( nodes ).to.be.array;
  1154. expect( nodes.length ).to.equal( 0 );
  1155. expect( range.isCollapsed ).to.be.true;
  1156. } );
  1157. it( 'should remove single text node', () => {
  1158. // <p>[{foobar}]</p> -> <p>|</p>
  1159. const created = create( writer, {
  1160. instanceOf: Element,
  1161. name: 'p',
  1162. rangeStart: 0,
  1163. rangeEnd: 1,
  1164. children: [
  1165. { instanceOf: Text, data: 'foobar' }
  1166. ]
  1167. } );
  1168. const removed = writer.remove( created.range );
  1169. test( writer, created.range.start, created.node, {
  1170. instanceOf: Element,
  1171. name: 'p',
  1172. position: 0,
  1173. children: []
  1174. } );
  1175. // Test removed nodes.
  1176. test( writer, null, removed, [
  1177. { instanceOf: Text, data: 'foobar' }
  1178. ] );
  1179. } );
  1180. it( 'should not leave empty text nodes', () => {
  1181. // <p>{[foobar]}</p> -> <p>|</p>
  1182. const created = create( writer, {
  1183. instanceOf: Element,
  1184. name: 'p',
  1185. children: [
  1186. { instanceOf: Text, data: 'foobar', rangeStart: 0, rangeEnd: 6 }
  1187. ]
  1188. } );
  1189. const removed = writer.remove( created.range );
  1190. test( writer, created.range.start, created.node, {
  1191. instanceOf: Element,
  1192. name: 'p',
  1193. position: 0,
  1194. children: []
  1195. } );
  1196. // Test removed nodes.
  1197. test( writer, null, removed, [
  1198. { instanceOf: Text, data: 'foobar' }
  1199. ] );
  1200. } );
  1201. it( 'should remove part of the text node', () => {
  1202. // <p>{f[oob]ar}</p> -> <p>{f|ar}</p>
  1203. const created = create( writer, {
  1204. instanceOf: Element,
  1205. name: 'p',
  1206. children: [
  1207. { instanceOf: Text, data: 'foobar', rangeStart: 1, rangeEnd: 4 }
  1208. ]
  1209. } );
  1210. const removed = writer.remove( created.range );
  1211. test( writer, created.range.start, created.node, {
  1212. instanceOf: Element,
  1213. name: 'p',
  1214. children: [
  1215. { instanceOf: Text, data: 'far', position: 1 }
  1216. ]
  1217. } );
  1218. // Test removed nodes.
  1219. test( writer, null, removed, [
  1220. { instanceOf: Text, data: 'oob' }
  1221. ] );
  1222. } );
  1223. it( 'should remove parts of nodes', () => {
  1224. // <p>{f[oo}<b>{ba]r}</b></p> -> <p>{f}|<b>r</b></p>
  1225. const created = create( writer, {
  1226. instanceOf: Element,
  1227. name: 'p',
  1228. children: [
  1229. { instanceOf: Text, data: 'foo', rangeStart: 1 },
  1230. {
  1231. instanceOf: Element,
  1232. name: 'b',
  1233. priority: 1,
  1234. children: [
  1235. { instanceOf: Text, data: 'bar', rangeEnd: 2 }
  1236. ]
  1237. }
  1238. ]
  1239. } );
  1240. const removed = writer.remove( created.range );
  1241. test( writer, created.range.start, created.node, {
  1242. instanceOf: Element,
  1243. name: 'p',
  1244. position: 1,
  1245. children: [
  1246. { instanceOf: Text, data: 'f' },
  1247. {
  1248. instanceOf: Element,
  1249. name: 'b',
  1250. priority: 1,
  1251. children: [
  1252. { instanceOf: Text, data: 'r' }
  1253. ]
  1254. }
  1255. ]
  1256. } );
  1257. // Test removed nodes.
  1258. test( writer, null, removed, [
  1259. { instanceOf: Text, data: 'oo' },
  1260. {
  1261. instanceOf: Element,
  1262. priority: 1,
  1263. name: 'b',
  1264. children: [
  1265. { instanceOf: Text, data: 'ba' }
  1266. ]
  1267. }
  1268. ] );
  1269. } );
  1270. it( 'should merge after removing #1', () => {
  1271. // <p><b>foo</b>[{bar}]<b>bazqux</b></p> -> <p><b>foo|bazqux</b></p>
  1272. const created = create( writer, {
  1273. instanceOf: Element,
  1274. name: 'p',
  1275. rangeStart: 1,
  1276. rangeEnd: 2,
  1277. children: [
  1278. {
  1279. instanceOf: Element,
  1280. name: 'b',
  1281. priority: 1,
  1282. children: [
  1283. { instanceOf: Text, data: 'foo' }
  1284. ]
  1285. },
  1286. { instanceOf: Text, data: 'bar' },
  1287. {
  1288. instanceOf: Element,
  1289. name: 'b',
  1290. priority: 1,
  1291. children: [
  1292. { instanceOf: Text, data: 'bazqux' }
  1293. ]
  1294. }
  1295. ]
  1296. } );
  1297. const removed = writer.remove( created.range );
  1298. test( writer, created.range.start, created.node, {
  1299. instanceOf: Element,
  1300. name: 'p',
  1301. children: [
  1302. {
  1303. instanceOf: Element,
  1304. name: 'b',
  1305. priority: 1,
  1306. children: [
  1307. { instanceOf: Text, data: 'foobazqux', position: 3 }
  1308. ]
  1309. }
  1310. ]
  1311. } );
  1312. // Test removed nodes.
  1313. test( writer, null, removed, [
  1314. { instanceOf: Text, data: 'bar' }
  1315. ] );
  1316. } );
  1317. it( 'should merge after removing #2', () => {
  1318. // <p><b>fo[o</b>{bar}<b>ba]zqux</b></p> -> <p><b>fo|zqux</b></p>
  1319. const created = create( writer, {
  1320. instanceOf: Element,
  1321. name: 'p',
  1322. children: [
  1323. {
  1324. instanceOf: Element,
  1325. name: 'b',
  1326. priority: 1,
  1327. children: [
  1328. { instanceOf: Text, data: 'foo', rangeStart: 2 }
  1329. ]
  1330. },
  1331. { instanceOf: Text, data: 'bar' },
  1332. {
  1333. instanceOf: Element,
  1334. name: 'b',
  1335. priority: 1,
  1336. children: [
  1337. { instanceOf: Text, data: 'bazqux', rangeEnd: 2 }
  1338. ]
  1339. }
  1340. ]
  1341. } );
  1342. const removed = writer.remove( created.range );
  1343. test( writer, created.range.start, created.node, {
  1344. instanceOf: Element,
  1345. name: 'p',
  1346. children: [
  1347. {
  1348. instanceOf: Element,
  1349. name: 'b',
  1350. priority: 1,
  1351. children: [
  1352. { instanceOf: Text, data: 'fozqux', position: 2 }
  1353. ]
  1354. }
  1355. ]
  1356. } );
  1357. // Test removed nodes.
  1358. test( writer, null, removed, [
  1359. {
  1360. instanceOf: Element,
  1361. priority: 1,
  1362. name: 'b',
  1363. children: [
  1364. { instanceOf: Text, data: 'o' }
  1365. ]
  1366. },
  1367. { instanceOf: Text, data: 'bar' },
  1368. {
  1369. instanceOf: Element,
  1370. priority: 1,
  1371. name: 'b',
  1372. children: [
  1373. { instanceOf: Text, data: 'ba' }
  1374. ]
  1375. }
  1376. ] );
  1377. } );
  1378. } );
  1379. describe( 'move', () => {
  1380. it( 'should move nodes using remove and insert methods', () => {
  1381. // <p>[{foobar}]</p>
  1382. // Move to <div>|</div>
  1383. // <div>[{foobar}]</div>
  1384. const source = create( writer, {
  1385. instanceOf: Element,
  1386. name: 'p',
  1387. rangeStart: 0,
  1388. rangeEnd: 1,
  1389. children: [
  1390. { instanceOf: Text, data: 'foobar' }
  1391. ]
  1392. } );
  1393. const target = create( writer, {
  1394. instanceOf: Element,
  1395. name: 'div',
  1396. position: 0
  1397. } );
  1398. const removeSpy = sinon.spy( writer, 'remove' );
  1399. const insertSpy = sinon.spy( writer, 'insert' );
  1400. const newRange = writer.move( source.range, target.position );
  1401. sinon.assert.calledOnce( removeSpy );
  1402. sinon.assert.calledWithExactly( removeSpy, source.range );
  1403. sinon.assert.calledOnce( insertSpy );
  1404. sinon.assert.calledWithExactly( insertSpy, target.position, removeSpy.firstCall.returnValue );
  1405. expect( newRange ).to.equal( insertSpy.firstCall.returnValue );
  1406. } );
  1407. } );
  1408. describe( 'wrap', () => {
  1409. it( 'should do nothing on collapsed ranges', () => {
  1410. const description = {
  1411. instanceOf: Element,
  1412. name: 'p',
  1413. children: [
  1414. { instanceOf: Text, data: 'foo', rangeStart: 1, rangeEnd: 1 }
  1415. ]
  1416. };
  1417. const created = create( writer, description );
  1418. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  1419. test( writer, newRange, created.node, description );
  1420. } );
  1421. it( 'wraps single text node', () => {
  1422. // <p>[{foobar}]</p>
  1423. // wrap <b>
  1424. // <p>[<b>{foobar}<b>]</p>
  1425. const created = create( writer, {
  1426. instanceOf: Element,
  1427. name: 'p',
  1428. rangeStart: 0,
  1429. rangeEnd: 1,
  1430. children: [
  1431. { instanceOf: Text, data: 'foobar' }
  1432. ]
  1433. } );
  1434. const b = new Element( 'b' );
  1435. const newRange = writer.wrap( created.range, b, 1 );
  1436. test( writer, newRange, created.node, {
  1437. instanceOf: Element,
  1438. name: 'p',
  1439. rangeStart: 0,
  1440. rangeEnd: 1,
  1441. children: [
  1442. {
  1443. instanceOf: Element,
  1444. name: 'b',
  1445. priority: 1,
  1446. children: [
  1447. { instanceOf: Text, data: 'foobar' }
  1448. ]
  1449. }
  1450. ]
  1451. } );
  1452. } );
  1453. it( 'should throw error when range placed in two containers', () => {
  1454. const container1 = new Element( 'p' );
  1455. const container2 = new Element( 'p' );
  1456. const range = new Range(
  1457. new Position( container1, 0 ),
  1458. new Position( container2, 1 )
  1459. );
  1460. const b = new Element( 'b' );
  1461. expect( () => {
  1462. writer.wrap( range, b, 1 );
  1463. } ).to.throw( CKEditorError, 'treeview-writer-invalid-range-container' );
  1464. } );
  1465. it( 'wraps part of a single text node #1', () => {
  1466. // <p>[{foo]bar}</p>
  1467. // wrap with <b>
  1468. // <p>[<b>{foo}</b>]{bar}</p>
  1469. const created = create( writer, {
  1470. instanceOf: Element,
  1471. name: 'p',
  1472. rangeStart: 0,
  1473. children: [
  1474. { instanceOf: Text, data: 'foobar', rangeEnd: 3 }
  1475. ]
  1476. } );
  1477. const b = new Element( 'b' );
  1478. const newRange = writer.wrap( created.range, b, 2 );
  1479. test( writer, newRange, created.node, {
  1480. instanceOf: Element,
  1481. name: 'p',
  1482. rangeStart: 0,
  1483. rangeEnd: 1,
  1484. children: [
  1485. {
  1486. instanceOf: Element,
  1487. name: 'b',
  1488. priority: 2,
  1489. children: [
  1490. { instanceOf: Text, data: 'foo' }
  1491. ]
  1492. },
  1493. { instanceOf: Text, data: 'bar' }
  1494. ]
  1495. } );
  1496. } );
  1497. it( 'wraps part of a single text node #2', () => {
  1498. // <p>{[foo]bar}</p>
  1499. // wrap with <b>
  1500. // <p>[<b>{foo}</b>]{bar}</p>
  1501. const created = create( writer, {
  1502. instanceOf: Element,
  1503. name: 'p',
  1504. children: [
  1505. { instanceOf: Text, data: 'foobar', rangeStart: 0, rangeEnd: 3 }
  1506. ]
  1507. } );
  1508. const b = new Element( 'b' );
  1509. const newRange = writer.wrap( created.range, b, 2 );
  1510. test( writer, newRange, created.node, {
  1511. instanceOf: Element,
  1512. name: 'p',
  1513. rangeStart: 0,
  1514. rangeEnd: 1,
  1515. children: [
  1516. {
  1517. instanceOf: Element,
  1518. name: 'b',
  1519. priority: 2,
  1520. children: [
  1521. { instanceOf: Text, data: 'foo' }
  1522. ]
  1523. },
  1524. { instanceOf: Text, data: 'bar' }
  1525. ]
  1526. } );
  1527. } );
  1528. it( 'wraps part of a single text node #3', () => {
  1529. // <p>{foo[bar]}</p>
  1530. // wrap with <b>
  1531. // <p>{foo}[<b>{bar}</b>]</p>
  1532. const created = create( writer, {
  1533. instanceOf: Element,
  1534. name: 'p',
  1535. children: [
  1536. { instanceOf: Text, data: 'foobar', rangeStart: 3, rangeEnd: 6 }
  1537. ]
  1538. } );
  1539. const b = new Element( 'b' );
  1540. const newRange = writer.wrap( created.range, b, 2 );
  1541. test( writer, newRange, created.node, {
  1542. instanceOf: Element,
  1543. name: 'p',
  1544. rangeStart: 1,
  1545. rangeEnd: 2,
  1546. children: [
  1547. { instanceOf: Text, data: 'foo' },
  1548. {
  1549. instanceOf: Element,
  1550. name: 'b',
  1551. priority: 2,
  1552. children: [
  1553. { instanceOf: Text, data: 'bar' }
  1554. ]
  1555. }
  1556. ]
  1557. } );
  1558. } );
  1559. it( 'should not wrap inside nested containers', () => {
  1560. // <div>[{foobar}<p>{baz}</p>]</div>
  1561. // wrap with <b>
  1562. // <div>[<b>{foobar}</b><p>{baz}</p>]</div>
  1563. const created = create( writer, {
  1564. instanceOf: Element,
  1565. name: 'div',
  1566. rangeStart: 0,
  1567. rangeEnd: 2,
  1568. children: [
  1569. { instanceOf: Text, data: 'foobar' },
  1570. {
  1571. instanceOf: Element,
  1572. name: 'p',
  1573. children: [
  1574. { instanceOf: Text, data: 'baz' }
  1575. ]
  1576. }
  1577. ]
  1578. } );
  1579. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  1580. test( writer, newRange, created.node, {
  1581. instanceOf: Element,
  1582. name: 'div',
  1583. rangeStart: 0,
  1584. rangeEnd: 2,
  1585. children: [
  1586. {
  1587. instanceOf: Element,
  1588. name: 'b',
  1589. priority: 1,
  1590. children: [
  1591. { instanceOf: Text, data: 'foobar' }
  1592. ]
  1593. },
  1594. {
  1595. instanceOf: Element,
  1596. name: 'p',
  1597. children: [
  1598. { instanceOf: Text, data: 'baz' }
  1599. ]
  1600. }
  1601. ]
  1602. } );
  1603. } );
  1604. it( 'wraps according to priorities', () => {
  1605. // <p>[<u>{foobar}</u>]</p>
  1606. // wrap with <b> that has higher priority than <u>
  1607. // <p>[<u><b>{foobar}</b></u>]</p>
  1608. const created = create( writer, {
  1609. instanceOf: Element,
  1610. name: 'p',
  1611. rangeStart: 0,
  1612. rangeEnd: 1,
  1613. children: [
  1614. {
  1615. instanceOf: Element,
  1616. name: 'u',
  1617. priority: 1,
  1618. children: [
  1619. { instanceOf: Text, data: 'foobar' }
  1620. ]
  1621. }
  1622. ]
  1623. } );
  1624. const b = new Element( 'b' );
  1625. const newRange = writer.wrap( created.range, b, 2 );
  1626. test( writer, newRange, created.node, {
  1627. instanceOf: Element,
  1628. name: 'p',
  1629. rangeStart: 0,
  1630. rangeEnd: 1,
  1631. children: [
  1632. {
  1633. instanceOf: Element,
  1634. name: 'u',
  1635. priority: 1,
  1636. children: [
  1637. {
  1638. instanceOf: Element,
  1639. name: 'b',
  1640. priority: 2,
  1641. children: [
  1642. { instanceOf: Text, data: 'foobar' }
  1643. ]
  1644. }
  1645. ]
  1646. }
  1647. ]
  1648. } );
  1649. } );
  1650. it( 'merges wrapped nodes #1', () => {
  1651. // <p>[<b>{foo}</b>{bar}<b>{baz}</b>]</p>
  1652. // wrap with <b>
  1653. // <p>[<b>{foobarbaz}</b>]</p>
  1654. const created = create( writer, {
  1655. instanceOf: Element,
  1656. name: 'p',
  1657. rangeStart: 0,
  1658. rangeEnd: 3,
  1659. children: [
  1660. {
  1661. instanceOf: Element,
  1662. name: 'b',
  1663. priority: 1,
  1664. children: [
  1665. { instanceOf: Text, data: 'foo' }
  1666. ]
  1667. },
  1668. { instanceOf: Text, data: 'bar' },
  1669. {
  1670. instanceOf: Element,
  1671. name: 'b',
  1672. priority: 1,
  1673. children: [
  1674. { instanceOf: Text, data: 'baz' }
  1675. ]
  1676. }
  1677. ]
  1678. } );
  1679. const b = new Element( 'b' );
  1680. const newRange = writer.wrap( created.range, b, 1 );
  1681. test( writer, newRange, created.node, {
  1682. instanceOf: Element,
  1683. name: 'p',
  1684. rangeStart: 0,
  1685. rangeEnd: 1,
  1686. children: [
  1687. {
  1688. instanceOf: Element,
  1689. name: 'b',
  1690. priority: 1,
  1691. children: [
  1692. { instanceOf: Text, data: 'foobarbaz' }
  1693. ]
  1694. }
  1695. ]
  1696. } );
  1697. } );
  1698. it( 'merges wrapped nodes #2', () => {
  1699. // <p><b>{foo}</b>[{bar]baz}</p>
  1700. // wrap with <b>
  1701. // <p><b>{foo[bar}</b>]{baz}</p>
  1702. const created = create( writer, {
  1703. instanceOf: Element,
  1704. name: 'p',
  1705. rangeStart: 1,
  1706. children: [
  1707. {
  1708. instanceOf: Element,
  1709. name: 'b',
  1710. priority: 1,
  1711. children: [
  1712. { instanceOf: Text, data: 'foo' }
  1713. ]
  1714. },
  1715. { instanceOf: Text, data: 'barbaz', rangeEnd: 3 }
  1716. ]
  1717. } );
  1718. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  1719. test( writer, newRange, created.node, {
  1720. instanceOf: Element,
  1721. name: 'p',
  1722. rangeEnd: 1,
  1723. children: [
  1724. {
  1725. instanceOf: Element,
  1726. name: 'b',
  1727. priority: 1,
  1728. children: [
  1729. { instanceOf: Text, data: 'foobar', rangeStart: 3 }
  1730. ]
  1731. },
  1732. { instanceOf: Text, data: 'baz' }
  1733. ]
  1734. } );
  1735. } );
  1736. it( 'merges wrapped nodes #3', () => {
  1737. // <p><b>{foobar}</b>[{baz}]</p>
  1738. // wrap with <b>
  1739. // <p><b>{foobar[baz}</b>]</p>
  1740. const created = create( writer, {
  1741. instanceOf: Element,
  1742. name: 'p',
  1743. rangeStart: 1,
  1744. rangeEnd: 2,
  1745. children: [
  1746. {
  1747. instanceOf: Element,
  1748. name: 'b',
  1749. priority: 1,
  1750. children: [
  1751. { instanceOf: Text, data: 'foobar' }
  1752. ]
  1753. },
  1754. { instanceOf: Text, data: 'baz', rangeEnd: 3 }
  1755. ]
  1756. } );
  1757. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  1758. test( writer, newRange, created.node, {
  1759. instanceOf: Element,
  1760. name: 'p',
  1761. rangeEnd: 1,
  1762. children: [
  1763. {
  1764. instanceOf: Element,
  1765. name: 'b',
  1766. priority: 1,
  1767. children: [
  1768. { instanceOf: Text, data: 'foobarbaz', rangeStart: 6 }
  1769. ]
  1770. }
  1771. ]
  1772. } );
  1773. } );
  1774. it( 'merges wrapped nodes #4', () => {
  1775. // <p>[{foo}<i>{bar}</i>]{baz}</p>
  1776. // wrap with <b>
  1777. // <p>[<b>{foo}<i>{bar}</i></b>]{baz}</p>
  1778. const created = create( writer, {
  1779. instanceOf: Element,
  1780. name: 'p',
  1781. rangeStart: 0,
  1782. rangeEnd: 2,
  1783. children: [
  1784. { instanceOf: Text, data: 'foo' },
  1785. {
  1786. instanceOf: Element,
  1787. name: 'i',
  1788. priority: 1,
  1789. children: [
  1790. { instanceOf: Text, data: 'bar' }
  1791. ]
  1792. },
  1793. { instanceOf: Text, data: 'baz' }
  1794. ]
  1795. } );
  1796. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  1797. test( writer, newRange, created.node, {
  1798. instanceOf: Element,
  1799. name: 'p',
  1800. rangeStart: 0,
  1801. rangeEnd: 1,
  1802. children: [
  1803. {
  1804. instanceOf: Element,
  1805. name: 'b',
  1806. priority: 1,
  1807. children: [
  1808. { instanceOf: Text, data: 'foo' },
  1809. {
  1810. instanceOf: Element,
  1811. name: 'i',
  1812. priority: 1,
  1813. children: [
  1814. { instanceOf: Text, data: 'bar' }
  1815. ]
  1816. }
  1817. ]
  1818. },
  1819. { instanceOf: Text, data: 'baz' }
  1820. ]
  1821. } );
  1822. } );
  1823. it( 'merges wrapped nodes #5', () => {
  1824. // <p>[{foo}<i>{bar}</i>{baz}]</p>
  1825. // wrap with <b>, that has higher priority than <i>
  1826. // <p>[<b>{foo}</b><i><b>{bar}</b></i><b>{baz}</b>]</p>
  1827. const created = create( writer, {
  1828. instanceOf: Element,
  1829. name: 'p',
  1830. rangeStart: 0,
  1831. rangeEnd: 3,
  1832. children: [
  1833. { instanceOf: Text, data: 'foo' },
  1834. {
  1835. instanceOf: Element,
  1836. name: 'i',
  1837. priority: 1,
  1838. children: [
  1839. { instanceOf: Text, data: 'bar' }
  1840. ]
  1841. },
  1842. { instanceOf: Text, data: 'baz' }
  1843. ]
  1844. } );
  1845. const newRange = writer.wrap( created.range, new Element( 'b' ), 2 );
  1846. test( writer, newRange, created.node, {
  1847. instanceOf: Element,
  1848. name: 'p',
  1849. rangeStart: 0,
  1850. rangeEnd: 3,
  1851. children: [
  1852. {
  1853. instanceOf: Element,
  1854. name: 'b',
  1855. priority: 2,
  1856. children: [
  1857. { instanceOf: Text, data: 'foo' }
  1858. ]
  1859. },
  1860. {
  1861. instanceOf: Element,
  1862. name: 'i',
  1863. priority: 1,
  1864. children: [
  1865. {
  1866. instanceOf: Element,
  1867. name: 'b',
  1868. priority: 2,
  1869. children: [
  1870. { instanceOf: Text, data: 'bar' }
  1871. ]
  1872. }
  1873. ]
  1874. },
  1875. {
  1876. instanceOf: Element,
  1877. name: 'b',
  1878. priority: 2,
  1879. children: [
  1880. { instanceOf: Text, data: 'baz' }
  1881. ]
  1882. }
  1883. ]
  1884. } );
  1885. } );
  1886. } );
  1887. describe( 'unwrap', () => {
  1888. it( 'should do nothing on collapsed ranges', () => {
  1889. const description = {
  1890. instanceOf: Element,
  1891. name: 'p',
  1892. children: [
  1893. { instanceOf: Text, data: 'foo', rangeStart: 1, rangeEnd: 1 }
  1894. ]
  1895. };
  1896. const created = create( writer, description );
  1897. const newRange = writer.unwrap( created.range, new Element( 'b' ), 1 );
  1898. test( writer, newRange, created.node, description );
  1899. } );
  1900. it( 'should do nothing on single text node', () => {
  1901. // <p>[{foobar}]</p>
  1902. const description = {
  1903. instanceOf: Element,
  1904. name: 'p',
  1905. rangeStart: 0,
  1906. rangeEnd: 1,
  1907. children: [
  1908. { instanceOf: Text, data: 'foobar' }
  1909. ]
  1910. };
  1911. const created = create( writer, description );
  1912. const b = new Element( 'b' );
  1913. writer.setPriority( b, 1 );
  1914. const newRange = writer.unwrap( created.range, b );
  1915. test( writer, newRange, created.node, description );
  1916. } );
  1917. it( 'should throw error when range placed in two containers', () => {
  1918. const container1 = new Element( 'p' );
  1919. const container2 = new Element( 'p' );
  1920. const range = new Range(
  1921. new Position( container1, 0 ),
  1922. new Position( container2, 1 )
  1923. );
  1924. const b = new Element( 'b' );
  1925. expect( () => {
  1926. writer.unwrap( range, b, 1 );
  1927. } ).to.throw( CKEditorError, 'treeview-writer-invalid-range-container' );
  1928. } );
  1929. it( 'should unwrap single node', () => {
  1930. // <p>[<b>{foobar}</b>]<p> -> <p>[{foobar}]</p>
  1931. const created = create( writer, {
  1932. instanceOf: Element,
  1933. name: 'p',
  1934. rangeStart: 0,
  1935. rangeEnd: 1,
  1936. children: [
  1937. {
  1938. instanceOf: Element,
  1939. priority: 1,
  1940. name: 'b',
  1941. children: [
  1942. { instanceOf: Text, data: 'foobar' }
  1943. ]
  1944. }
  1945. ]
  1946. } );
  1947. const b = new Element( 'b' );
  1948. writer.setPriority( b, 1 );
  1949. const newRange = writer.unwrap( created.range, b );
  1950. test( writer, newRange, created.node, {
  1951. instanceOf: Element,
  1952. name: 'p',
  1953. rangeStart: 0,
  1954. rangeEnd: 1,
  1955. children: [
  1956. { instanceOf: Text, data: 'foobar' }
  1957. ]
  1958. } );
  1959. } );
  1960. it( 'should not unwrap attributes with different priorities #1', () => {
  1961. // <p>[<b>{foobar}</b>]<p> -> <p>[<b>{foobar}</b>]</p>
  1962. // Unwrapped with <b> but using different priority.
  1963. const description = {
  1964. instanceOf: Element,
  1965. name: 'p',
  1966. rangeStart: 0,
  1967. rangeEnd: 1,
  1968. children: [
  1969. {
  1970. instanceOf: Element,
  1971. priority: 1,
  1972. name: 'b',
  1973. children: [
  1974. { instanceOf: Text, data: 'foobar' }
  1975. ]
  1976. }
  1977. ]
  1978. };
  1979. const created = create( writer, description );
  1980. const b = new Element( 'b' );
  1981. writer.setPriority( b, 2 );
  1982. const newRange = writer.unwrap( created.range, b );
  1983. test( writer, newRange, created.node, description );
  1984. } );
  1985. it( 'should not unwrap attributes with different priorities #2', () => {
  1986. // <p>[<b>{foo}</b><b>{bar}</b><b>{baz}</b>]<p> -> <p>[{foo}<b>bar</b>{baz}]</p>
  1987. // <b> around `bar` has different priority than others.
  1988. const created = create( writer, {
  1989. instanceOf: Element,
  1990. name: 'p',
  1991. rangeStart: 0,
  1992. rangeEnd: 3,
  1993. children: [
  1994. {
  1995. instanceOf: Element,
  1996. priority: 2,
  1997. name: 'b',
  1998. children: [
  1999. { instanceOf: Text, data: 'foo' }
  2000. ]
  2001. },
  2002. {
  2003. instanceOf: Element,
  2004. priority: 1,
  2005. name: 'b',
  2006. children: [
  2007. { instanceOf: Text, data: 'bar' }
  2008. ]
  2009. },
  2010. {
  2011. instanceOf: Element,
  2012. priority: 2,
  2013. name: 'b',
  2014. children: [
  2015. { instanceOf: Text, data: 'baz' }
  2016. ]
  2017. }
  2018. ]
  2019. } );
  2020. const b = new Element( 'b' );
  2021. writer.setPriority( b, 2 );
  2022. const newRange = writer.unwrap( created.range, b );
  2023. test( writer, newRange, created.node, {
  2024. instanceOf: Element,
  2025. name: 'p',
  2026. rangeStart: 0,
  2027. rangeEnd: 3,
  2028. children: [
  2029. { instanceOf: Text, data: 'foo' },
  2030. {
  2031. instanceOf: Element,
  2032. priority: 1,
  2033. name: 'b',
  2034. children: [
  2035. { instanceOf: Text, data: 'bar' }
  2036. ]
  2037. },
  2038. { instanceOf: Text, data: 'baz' }
  2039. ]
  2040. } );
  2041. } );
  2042. it( 'should unwrap part of the node', () => {
  2043. // <p>[{baz}<b>{foo]bar}</b><p> -> <p>[{bazfoo}]<b>{bar}</b></p>
  2044. const created = create( writer, {
  2045. instanceOf: Element,
  2046. name: 'p',
  2047. rangeStart: 0,
  2048. children: [
  2049. { instanceOf: Text, data: 'baz' },
  2050. {
  2051. instanceOf: Element,
  2052. name: 'b',
  2053. priority: 1,
  2054. children: [
  2055. { instanceOf: Text, data: 'foobar', rangeEnd: 3 }
  2056. ]
  2057. }
  2058. ]
  2059. } );
  2060. const b = new Element( 'b' );
  2061. writer.setPriority( b, 1 );
  2062. const newRange = writer.unwrap( created.range, b );
  2063. test( writer, newRange, created.node, {
  2064. instanceOf: Element,
  2065. name: 'p',
  2066. rangeStart: 0,
  2067. rangeEnd: 1,
  2068. children: [
  2069. { instanceOf: Text, data: 'bazfoo' },
  2070. {
  2071. instanceOf: Element,
  2072. name: 'b',
  2073. priority: 1,
  2074. children: [
  2075. { instanceOf: Text, data: 'bar' }
  2076. ]
  2077. }
  2078. ]
  2079. } );
  2080. } );
  2081. it( 'should unwrap nested attributes', () => {
  2082. // <p>[<u><b>{foobar}</b></u>]</p> -> <p>[<u>{foobar}</u>]</p>
  2083. const created = create( writer, {
  2084. instanceOf: Element,
  2085. name: 'p',
  2086. rangeStart: 0,
  2087. rangeEnd: 1,
  2088. children: [
  2089. {
  2090. instanceOf: Element,
  2091. name: 'u',
  2092. priority: 1,
  2093. children: [
  2094. {
  2095. instanceOf: Element,
  2096. name: 'b',
  2097. priority: 1,
  2098. children: [
  2099. { instanceOf: Text, data: 'foobar' }
  2100. ]
  2101. }
  2102. ]
  2103. }
  2104. ]
  2105. } );
  2106. const b = new Element( 'b' );
  2107. writer.setPriority( b, 1 );
  2108. const newRange = writer.unwrap( created.range, b );
  2109. test( writer, newRange, created.node, {
  2110. instanceOf: Element,
  2111. name: 'p',
  2112. rangeStart: 0,
  2113. rangeEnd: 1,
  2114. children: [
  2115. {
  2116. instanceOf: Element,
  2117. name: 'u',
  2118. priority: 1,
  2119. children: [
  2120. { instanceOf: Text, data: 'foobar' }
  2121. ]
  2122. }
  2123. ]
  2124. } );
  2125. } );
  2126. it( 'should merge unwrapped nodes #1', () => {
  2127. // <p>{foo}[<b>{bar}</b>]{bom}</p> -> <p>{foo[bar]bom}</p>
  2128. const created = create( writer, {
  2129. instanceOf: Element,
  2130. name: 'p',
  2131. rangeStart: 1,
  2132. rangeEnd: 2,
  2133. children: [
  2134. { instanceOf: Text, data: 'foo' },
  2135. {
  2136. instanceOf: Element,
  2137. name: 'b',
  2138. priority: 1,
  2139. children: [
  2140. { instanceOf: Text, data: 'bar' }
  2141. ]
  2142. },
  2143. { instanceOf: Text, data: 'bom' }
  2144. ]
  2145. } );
  2146. const b = new Element( 'b' );
  2147. writer.setPriority( b, 1 );
  2148. const newRange = writer.unwrap( created.range, b );
  2149. test( writer, newRange, created.node, {
  2150. instanceOf: Element,
  2151. name: 'p',
  2152. children: [
  2153. {
  2154. instanceOf: Text,
  2155. data: 'foobarbom',
  2156. rangeStart: 3,
  2157. rangeEnd: 6
  2158. }
  2159. ]
  2160. } );
  2161. } );
  2162. it( 'should merge unwrapped nodes #2', () => {
  2163. // <p>{foo}<u>{bar}</u>[<b><u>{bazqux}</u></b>]</p> -> <p>{foo}<u>{bar[bazqux}</u>]</p>
  2164. const created = create( writer, {
  2165. instanceOf: Element,
  2166. name: 'p',
  2167. rangeStart: 2,
  2168. rangeEnd: 3,
  2169. children: [
  2170. { instanceOf: Text, data: 'foo' },
  2171. {
  2172. instanceOf: Element,
  2173. name: 'u',
  2174. priority: 1,
  2175. children: [
  2176. { instanceOf: Text, data: 'bar' }
  2177. ]
  2178. },
  2179. {
  2180. instanceOf: Element,
  2181. name: 'b',
  2182. priority: 1,
  2183. children: [
  2184. {
  2185. instanceOf: Element,
  2186. name: 'u',
  2187. priority: 1,
  2188. children: [
  2189. { instanceOf: Text, data: 'bazqux' }
  2190. ]
  2191. }
  2192. ]
  2193. }
  2194. ]
  2195. } );
  2196. const b = new Element( 'b' );
  2197. writer.setPriority( b, 1 );
  2198. const newRange = writer.unwrap( created.range, b );
  2199. test( writer, newRange, created.node, {
  2200. instanceOf: Element,
  2201. name: 'p',
  2202. rangeEnd: 2,
  2203. children: [
  2204. {
  2205. instanceOf: Text,
  2206. data: 'foo'
  2207. },
  2208. {
  2209. instanceOf: Element,
  2210. name: 'u',
  2211. priority: 1,
  2212. children: [
  2213. { instanceOf: Text, data: 'barbazqux', rangeStart: 3 }
  2214. ]
  2215. }
  2216. ]
  2217. } );
  2218. } );
  2219. it( 'should merge unwrapped nodes #3', () => {
  2220. // <p>{foo}<u>{bar}</u>[<b><u>{baz]qux}</u></b></p> -> <p>{foo}<u>{bar[baz}</u>]<b><u>{qux}</u></b></p>
  2221. const created = create( writer, {
  2222. instanceOf: Element,
  2223. name: 'p',
  2224. rangeStart: 2,
  2225. children: [
  2226. { instanceOf: Text, data: 'foo' },
  2227. {
  2228. instanceOf: Element,
  2229. name: 'u',
  2230. priority: 1,
  2231. children: [
  2232. { instanceOf: Text, data: 'bar' }
  2233. ]
  2234. },
  2235. {
  2236. instanceOf: Element,
  2237. name: 'b',
  2238. priority: 1,
  2239. children: [
  2240. {
  2241. instanceOf: Element,
  2242. name: 'u',
  2243. priority: 1,
  2244. children: [
  2245. { instanceOf: Text, data: 'bazqux', rangeEnd: 3 }
  2246. ]
  2247. }
  2248. ]
  2249. }
  2250. ]
  2251. } );
  2252. const b = new Element( 'b' );
  2253. writer.setPriority( b, 1 );
  2254. const newRange = writer.unwrap( created.range, b );
  2255. test( writer, newRange, created.node, {
  2256. instanceOf: Element,
  2257. name: 'p',
  2258. rangeEnd: 2,
  2259. children: [
  2260. {
  2261. instanceOf: Text,
  2262. data: 'foo'
  2263. },
  2264. {
  2265. instanceOf: Element,
  2266. name: 'u',
  2267. priority: 1,
  2268. children: [
  2269. { instanceOf: Text, data: 'barbaz', rangeStart: 3 }
  2270. ]
  2271. },
  2272. {
  2273. instanceOf: Element,
  2274. name: 'b',
  2275. priority: 1,
  2276. children: [
  2277. {
  2278. instanceOf: Element,
  2279. name: 'u',
  2280. priority: 1,
  2281. children: [
  2282. { instanceOf: Text, data: 'qux' }
  2283. ]
  2284. }
  2285. ]
  2286. }
  2287. ]
  2288. } );
  2289. } );
  2290. it( 'should merge unwrapped nodes #4', () => {
  2291. // <p>{foo}<u>{bar}</u>[<b><u>{baz}</u></b>]<u>qux</u></p> -> <p>{foo}<u>{bar[baz]qux}</u></p>
  2292. const created = create( writer, {
  2293. instanceOf: Element,
  2294. name: 'p',
  2295. rangeStart: 2,
  2296. rangeEnd: 3,
  2297. children: [
  2298. { instanceOf: Text, data: 'foo' },
  2299. {
  2300. instanceOf: Element,
  2301. name: 'u',
  2302. priority: 1,
  2303. children: [
  2304. { instanceOf: Text, data: 'bar' }
  2305. ]
  2306. },
  2307. {
  2308. instanceOf: Element,
  2309. name: 'b',
  2310. priority: 1,
  2311. children: [
  2312. {
  2313. instanceOf: Element,
  2314. name: 'u',
  2315. priority: 1,
  2316. children: [
  2317. { instanceOf: Text, data: 'baz' }
  2318. ]
  2319. }
  2320. ]
  2321. },
  2322. {
  2323. instanceOf: Element,
  2324. name: 'u',
  2325. priority: 1,
  2326. children: [
  2327. { instanceOf: Text, data: 'qux' }
  2328. ]
  2329. }
  2330. ]
  2331. } );
  2332. const b = new Element( 'b' );
  2333. writer.setPriority( b, 1 );
  2334. const newRange = writer.unwrap( created.range, b );
  2335. test( writer, newRange, created.node, {
  2336. instanceOf: Element,
  2337. name: 'p',
  2338. children: [
  2339. {
  2340. instanceOf: Text,
  2341. data: 'foo'
  2342. },
  2343. {
  2344. instanceOf: Element,
  2345. name: 'u',
  2346. priority: 1,
  2347. children: [
  2348. { instanceOf: Text, data: 'barbazqux', rangeStart: 3, rangeEnd: 6 }
  2349. ]
  2350. }
  2351. ]
  2352. } );
  2353. } );
  2354. it( 'should merge unwrapped nodes #5', () => {
  2355. // <p>[<b><u>{foo}</u></b><b><u>{bar}</u></b><b><u>{baz}</u></b>]</p> -> <p>[<u>{foobarbaz}</u>]</p>
  2356. const created = create( writer, {
  2357. instanceOf: Element,
  2358. name: 'p',
  2359. rangeStart: 0,
  2360. rangeEnd: 3,
  2361. children: [
  2362. {
  2363. instanceOf: Element,
  2364. name: 'b',
  2365. priority: 1,
  2366. children: [
  2367. {
  2368. instanceOf: Element,
  2369. name: 'u',
  2370. priority: 1,
  2371. children: [
  2372. { instanceOf: Text, data: 'foo' }
  2373. ]
  2374. }
  2375. ]
  2376. },
  2377. {
  2378. instanceOf: Element,
  2379. name: 'b',
  2380. priority: 1,
  2381. children: [
  2382. {
  2383. instanceOf: Element,
  2384. name: 'u',
  2385. priority: 1,
  2386. children: [
  2387. { instanceOf: Text, data: 'bar' }
  2388. ]
  2389. }
  2390. ]
  2391. },
  2392. {
  2393. instanceOf: Element,
  2394. name: 'b',
  2395. priority: 1,
  2396. children: [
  2397. {
  2398. instanceOf: Element,
  2399. name: 'u',
  2400. priority: 1,
  2401. children: [
  2402. { instanceOf: Text, data: 'baz' }
  2403. ]
  2404. }
  2405. ]
  2406. }
  2407. ]
  2408. } );
  2409. const b = new Element( 'b' );
  2410. writer.setPriority( b, 1 );
  2411. const newRange = writer.unwrap( created.range, b );
  2412. test( writer, newRange, created.node, {
  2413. instanceOf: Element,
  2414. name: 'p',
  2415. rangeStart: 0,
  2416. rangeEnd: 1,
  2417. children: [
  2418. {
  2419. instanceOf: Element,
  2420. name: 'u',
  2421. priority: 1,
  2422. children: [
  2423. { instanceOf: Text, data: 'foobarbaz' }
  2424. ]
  2425. }
  2426. ]
  2427. } );
  2428. } );
  2429. it( 'should unwrap mixed ranges #1', () => {
  2430. // <p>[<u><b>{foo}]</b></u></p> -> <p>[<u>{foo}</u>]</p
  2431. const created = create( writer, {
  2432. instanceOf: Element,
  2433. name: 'p',
  2434. rangeStart: 0,
  2435. children: [
  2436. {
  2437. instanceOf: Element,
  2438. name: 'u',
  2439. priority: 1,
  2440. children: [
  2441. {
  2442. instanceOf: Element,
  2443. name: 'b',
  2444. priority: 1,
  2445. rangeEnd: 1,
  2446. children: [
  2447. { instanceOf: Text, data: 'foo' }
  2448. ]
  2449. }
  2450. ]
  2451. }
  2452. ]
  2453. } );
  2454. const b = new Element( 'b' );
  2455. writer.setPriority( b, 1 );
  2456. const newRange = writer.unwrap( created.range, b );
  2457. test( writer, newRange, created.node, {
  2458. instanceOf: Element,
  2459. name: 'p',
  2460. rangeStart: 0,
  2461. rangeEnd: 1,
  2462. children: [
  2463. {
  2464. instanceOf: Element,
  2465. name: 'u',
  2466. priority: 1,
  2467. children: [
  2468. { instanceOf: Text, data: 'foo' }
  2469. ]
  2470. }
  2471. ]
  2472. } );
  2473. } );
  2474. it( 'should unwrap mixed ranges #2', () => {
  2475. // <p>[<u><b>{foo]}</b></u></p> -> <p>[<u>{foo}</u>]</p
  2476. const created = create( writer, {
  2477. instanceOf: Element,
  2478. name: 'p',
  2479. rangeStart: 0,
  2480. children: [
  2481. {
  2482. instanceOf: Element,
  2483. name: 'u',
  2484. priority: 1,
  2485. children: [
  2486. {
  2487. instanceOf: Element,
  2488. name: 'b',
  2489. priority: 1,
  2490. children: [
  2491. { instanceOf: Text, data: 'foo', rangeEnd: 3 }
  2492. ]
  2493. }
  2494. ]
  2495. }
  2496. ]
  2497. } );
  2498. const b = new Element( 'b' );
  2499. writer.setPriority( b, 1 );
  2500. const newRange = writer.unwrap( created.range, b );
  2501. test( writer, newRange, created.node, {
  2502. instanceOf: Element,
  2503. name: 'p',
  2504. rangeStart: 0,
  2505. rangeEnd: 1,
  2506. children: [
  2507. {
  2508. instanceOf: Element,
  2509. name: 'u',
  2510. priority: 1,
  2511. children: [
  2512. { instanceOf: Text, data: 'foo' }
  2513. ]
  2514. }
  2515. ]
  2516. } );
  2517. } );
  2518. } );
  2519. } );