writer.js 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797
  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 attribute 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 #1', () => {
  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', rangeStart: 3, rangeEnd: 6 }
  909. ]
  910. } );
  911. } );
  912. it( 'should insert text into another text node #2', () => {
  913. // <p>{foobar|}</p> insert {baz}
  914. // <p>{foobar[baz}]</p>
  915. const created = create( writer, {
  916. instanceOf: Element,
  917. name: 'p',
  918. children: [
  919. { instanceOf: Text, data: 'foobar', position: 6 }
  920. ]
  921. } );
  922. const newRange = writer.insert( created.position, new Text( 'baz' ) );
  923. test( writer, newRange, created.node, {
  924. instanceOf: Element,
  925. name: 'p',
  926. rangeEnd: 1,
  927. children: [
  928. { instanceOf: Text, data: 'foobarbaz', rangeStart: 6 }
  929. ]
  930. } );
  931. } );
  932. it( 'should insert text into another text node #3', () => {
  933. // <p>{|foobar}</p> insert {baz}
  934. // <p>[{baz]foobar}</p>
  935. const created = create( writer, {
  936. instanceOf: Element,
  937. name: 'p',
  938. children: [
  939. { instanceOf: Text, data: 'foobar', position: 0 }
  940. ]
  941. } );
  942. const newRange = writer.insert( created.position, new Text( 'baz' ) );
  943. test( writer, newRange, created.node, {
  944. instanceOf: Element,
  945. name: 'p',
  946. rangeStart: 0,
  947. children: [
  948. { instanceOf: Text, data: 'bazfoobar', rangeEnd: 3 }
  949. ]
  950. } );
  951. } );
  952. it( 'should break attributes when inserting into text node', () => {
  953. // <p>{foo|bar}</p> insert <b>{baz}</b>
  954. // <p>{foo}[<b>baz</b>]{bar}</p>
  955. const created = create( writer, {
  956. instanceOf: Element,
  957. name: 'p',
  958. children: [
  959. { instanceOf: Text, data: 'foobar', position: 3 }
  960. ]
  961. } );
  962. const toInsert = create( writer, {
  963. instanceOf: Element,
  964. name: 'b',
  965. priority: 1,
  966. children: [
  967. { instanceOf: Text, data: 'baz' }
  968. ]
  969. } );
  970. const newRange = writer.insert( created.position, toInsert.node );
  971. test( writer, newRange, created.node, {
  972. instanceOf: Element,
  973. name: 'p',
  974. rangeStart: 1,
  975. rangeEnd: 2,
  976. children: [
  977. { instanceOf: Text, data: 'foo' },
  978. {
  979. instanceOf: Element,
  980. name: 'b',
  981. priority: 1,
  982. children: [
  983. { instanceOf: Text, data: 'baz' }
  984. ]
  985. },
  986. { instanceOf: Text, data: 'bar' }
  987. ]
  988. } );
  989. } );
  990. it( 'should merge text ndoes', () => {
  991. // <p>|{foobar}</p> insert {baz}
  992. // <p>[{baz]foobar}</p>
  993. const created = create( writer, {
  994. instanceOf: Element,
  995. name: 'p',
  996. position: 0,
  997. children: [
  998. { instanceOf: Text, data: 'foobar' }
  999. ]
  1000. } );
  1001. const newRange = writer.insert( created.position, new Text( 'baz' ) );
  1002. test( writer, newRange, created.node, {
  1003. instanceOf: Element,
  1004. name: 'p',
  1005. rangeStart: 0,
  1006. children: [
  1007. { instanceOf: Text, data: 'bazfoobar', rangeEnd: 3 }
  1008. ]
  1009. } );
  1010. } );
  1011. it( 'should merge same attribute nodes', () => {
  1012. // <p><b>{foo|bar}</b></p> insert <b>{baz}</b>
  1013. // <p><b>{foo[baz]bar}</b></p>
  1014. const created = create( writer, {
  1015. instanceOf: Element,
  1016. name: 'p',
  1017. children: [
  1018. {
  1019. instanceOf: Element,
  1020. name: 'b',
  1021. priority: 1,
  1022. children: [
  1023. { instanceOf: Text, data: 'foobar', position: 3 }
  1024. ]
  1025. }
  1026. ]
  1027. } );
  1028. const toInsert = create( writer, {
  1029. instanceOf: Element,
  1030. name: 'b',
  1031. priority: 1,
  1032. children: [
  1033. { instanceOf: Text, data: 'baz' }
  1034. ]
  1035. } );
  1036. const newRange = writer.insert( created.position, toInsert.node );
  1037. test( writer, newRange, created.node, {
  1038. instanceOf: Element,
  1039. name: 'p',
  1040. children: [
  1041. {
  1042. instanceOf: Element,
  1043. name: 'b',
  1044. priority: 1,
  1045. children: [
  1046. { instanceOf: Text, data: 'foobazbar', rangeStart: 3, rangeEnd: 6 }
  1047. ]
  1048. }
  1049. ]
  1050. } );
  1051. } );
  1052. it( 'should not merge different attributes', () => {
  1053. // <p><b>{foo|bar}</b></p> insert <b>{baz}</b> ( different priority )
  1054. // <p><b>{foo}</b>[<b>{baz}</b>]<b>{bar}</b></p>
  1055. const created = create( writer, {
  1056. instanceOf: Element,
  1057. name: 'p',
  1058. children: [
  1059. {
  1060. instanceOf: Element,
  1061. name: 'b',
  1062. priority: 1,
  1063. children: [
  1064. { instanceOf: Text, data: 'foobar', position: 3 }
  1065. ]
  1066. }
  1067. ]
  1068. } );
  1069. const toInsert = create( writer, {
  1070. instanceOf: Element,
  1071. name: 'b',
  1072. priority: 2,
  1073. children: [
  1074. { instanceOf: Text, data: 'baz' }
  1075. ]
  1076. } );
  1077. const newRange = writer.insert( created.position, toInsert.node );
  1078. test( writer, newRange, created.node, {
  1079. instanceOf: Element,
  1080. name: 'p',
  1081. rangeStart: 1,
  1082. rangeEnd: 2,
  1083. children: [
  1084. {
  1085. instanceOf: Element,
  1086. name: 'b',
  1087. priority: 1,
  1088. children: [
  1089. { instanceOf: Text, data: 'foo' }
  1090. ]
  1091. },
  1092. {
  1093. instanceOf: Element,
  1094. name: 'b',
  1095. priority: 2,
  1096. children: [
  1097. { instanceOf: Text, data: 'baz' }
  1098. ]
  1099. },
  1100. {
  1101. instanceOf: Element,
  1102. name: 'b',
  1103. priority: 1,
  1104. children: [
  1105. { instanceOf: Text, data: 'bar' }
  1106. ]
  1107. }
  1108. ]
  1109. } );
  1110. } );
  1111. it( 'should allow to insert multiple nodes', () => {
  1112. // <p>|</p> insert <b>{foo}</b>{bar}
  1113. // <p>[<b>{foo}</b>{bar}]</p>
  1114. const root = new Element( 'p' );
  1115. const toInsert = create( writer, {
  1116. instanceOf: Element,
  1117. name: 'fake',
  1118. children: [
  1119. {
  1120. instanceOf: Element,
  1121. name: 'b',
  1122. priority: 1,
  1123. children: [
  1124. { instanceOf: Text, data: 'foo' }
  1125. ]
  1126. },
  1127. { instanceOf: Text, data: 'bar' }
  1128. ]
  1129. } ).node.getChildren();
  1130. const position = new Position( root, 0 );
  1131. const newRange = writer.insert( position, toInsert );
  1132. test( writer, newRange, root, {
  1133. instanceOf: Element,
  1134. name: 'p',
  1135. rangeStart: 0,
  1136. rangeEnd: 2,
  1137. children: [
  1138. {
  1139. instanceOf: Element,
  1140. name: 'b',
  1141. priority: 1,
  1142. children: [
  1143. { instanceOf: Text, data: 'foo' }
  1144. ]
  1145. },
  1146. { instanceOf: Text, data: 'bar' }
  1147. ]
  1148. } );
  1149. } );
  1150. it( 'should merge after inserting multiple nodes', () => {
  1151. // <p><b>{qux}</b>|{baz}</p> insert <b>{foo}</b>{bar}
  1152. // <p><b>{qux[foo}</b>{bar]baz}</p>
  1153. const created = create( writer, {
  1154. instanceOf: Element,
  1155. name: 'p',
  1156. position: 1,
  1157. children: [
  1158. {
  1159. instanceOf: Element,
  1160. name: 'b',
  1161. priority: 1,
  1162. children: [
  1163. { instanceOf: Text, data: 'qux' }
  1164. ]
  1165. },
  1166. { instanceOf: Text, data: 'baz' }
  1167. ]
  1168. } );
  1169. const toInsert = create( writer, {
  1170. instanceOf: Element,
  1171. name: 'fake',
  1172. children: [
  1173. {
  1174. instanceOf: Element,
  1175. name: 'b',
  1176. priority: 1,
  1177. children: [
  1178. { instanceOf: Text, data: 'foo' }
  1179. ]
  1180. },
  1181. { instanceOf: Text, data: 'bar' }
  1182. ]
  1183. } ).node.getChildren();
  1184. const newRange = writer.insert( created.position, toInsert );
  1185. test( writer, newRange, created.node, {
  1186. instanceOf: Element,
  1187. name: 'p',
  1188. children: [
  1189. {
  1190. instanceOf: Element,
  1191. name: 'b',
  1192. priority: 1,
  1193. children: [
  1194. { instanceOf: Text, data: 'quxfoo', rangeStart: 3 }
  1195. ]
  1196. },
  1197. { instanceOf: Text, data: 'barbaz', rangeEnd: 3 }
  1198. ]
  1199. } );
  1200. } );
  1201. } );
  1202. describe( 'remove', () => {
  1203. it( 'should throw when range placed in two containers', () => {
  1204. const p1 = new Element( 'p' );
  1205. const p2 = new Element( 'p' );
  1206. expect( () => {
  1207. writer.remove( Range.createFromParentsAndOffsets( p1, 0, p2, 0 ) );
  1208. } ).to.throw( 'treeview-writer-invalid-range-container' );
  1209. } );
  1210. it( 'should return empty array when range is collapsed', () => {
  1211. const p = new Element( 'p' );
  1212. const range = Range.createFromParentsAndOffsets( p, 0, p, 0 );
  1213. const nodes = writer.remove( range );
  1214. expect( nodes ).to.be.array;
  1215. expect( nodes.length ).to.equal( 0 );
  1216. expect( range.isCollapsed ).to.be.true;
  1217. } );
  1218. it( 'should remove single text node', () => {
  1219. // <p>[{foobar}]</p> -> <p>|</p>
  1220. const created = create( writer, {
  1221. instanceOf: Element,
  1222. name: 'p',
  1223. rangeStart: 0,
  1224. rangeEnd: 1,
  1225. children: [
  1226. { instanceOf: Text, data: 'foobar' }
  1227. ]
  1228. } );
  1229. const removed = writer.remove( created.range );
  1230. test( writer, created.range.start, created.node, {
  1231. instanceOf: Element,
  1232. name: 'p',
  1233. position: 0,
  1234. children: []
  1235. } );
  1236. // Test removed nodes.
  1237. test( writer, null, removed, [
  1238. { instanceOf: Text, data: 'foobar' }
  1239. ] );
  1240. } );
  1241. it( 'should not leave empty text nodes', () => {
  1242. // <p>{[foobar]}</p> -> <p>|</p>
  1243. const created = create( writer, {
  1244. instanceOf: Element,
  1245. name: 'p',
  1246. children: [
  1247. { instanceOf: Text, data: 'foobar', rangeStart: 0, rangeEnd: 6 }
  1248. ]
  1249. } );
  1250. const removed = writer.remove( created.range );
  1251. test( writer, created.range.start, created.node, {
  1252. instanceOf: Element,
  1253. name: 'p',
  1254. position: 0,
  1255. children: []
  1256. } );
  1257. // Test removed nodes.
  1258. test( writer, null, removed, [
  1259. { instanceOf: Text, data: 'foobar' }
  1260. ] );
  1261. } );
  1262. it( 'should remove part of the text node', () => {
  1263. // <p>{f[oob]ar}</p> -> <p>{f|ar}</p>
  1264. const created = create( writer, {
  1265. instanceOf: Element,
  1266. name: 'p',
  1267. children: [
  1268. { instanceOf: Text, data: 'foobar', rangeStart: 1, rangeEnd: 4 }
  1269. ]
  1270. } );
  1271. const removed = writer.remove( created.range );
  1272. test( writer, created.range.start, created.node, {
  1273. instanceOf: Element,
  1274. name: 'p',
  1275. children: [
  1276. { instanceOf: Text, data: 'far', position: 1 }
  1277. ]
  1278. } );
  1279. // Test removed nodes.
  1280. test( writer, null, removed, [
  1281. { instanceOf: Text, data: 'oob' }
  1282. ] );
  1283. } );
  1284. it( 'should remove parts of nodes', () => {
  1285. // <p>{f[oo}<b>{ba]r}</b></p> -> <p>{f}|<b>r</b></p>
  1286. const created = create( writer, {
  1287. instanceOf: Element,
  1288. name: 'p',
  1289. children: [
  1290. { instanceOf: Text, data: 'foo', rangeStart: 1 },
  1291. {
  1292. instanceOf: Element,
  1293. name: 'b',
  1294. priority: 1,
  1295. children: [
  1296. { instanceOf: Text, data: 'bar', rangeEnd: 2 }
  1297. ]
  1298. }
  1299. ]
  1300. } );
  1301. const removed = writer.remove( created.range );
  1302. test( writer, created.range.start, created.node, {
  1303. instanceOf: Element,
  1304. name: 'p',
  1305. position: 1,
  1306. children: [
  1307. { instanceOf: Text, data: 'f' },
  1308. {
  1309. instanceOf: Element,
  1310. name: 'b',
  1311. priority: 1,
  1312. children: [
  1313. { instanceOf: Text, data: 'r' }
  1314. ]
  1315. }
  1316. ]
  1317. } );
  1318. // Test removed nodes.
  1319. test( writer, null, removed, [
  1320. { instanceOf: Text, data: 'oo' },
  1321. {
  1322. instanceOf: Element,
  1323. priority: 1,
  1324. name: 'b',
  1325. children: [
  1326. { instanceOf: Text, data: 'ba' }
  1327. ]
  1328. }
  1329. ] );
  1330. } );
  1331. it( 'should merge after removing #1', () => {
  1332. // <p><b>foo</b>[{bar}]<b>bazqux</b></p> -> <p><b>foo|bazqux</b></p>
  1333. const created = create( writer, {
  1334. instanceOf: Element,
  1335. name: 'p',
  1336. rangeStart: 1,
  1337. rangeEnd: 2,
  1338. children: [
  1339. {
  1340. instanceOf: Element,
  1341. name: 'b',
  1342. priority: 1,
  1343. children: [
  1344. { instanceOf: Text, data: 'foo' }
  1345. ]
  1346. },
  1347. { instanceOf: Text, data: 'bar' },
  1348. {
  1349. instanceOf: Element,
  1350. name: 'b',
  1351. priority: 1,
  1352. children: [
  1353. { instanceOf: Text, data: 'bazqux' }
  1354. ]
  1355. }
  1356. ]
  1357. } );
  1358. const removed = writer.remove( created.range );
  1359. test( writer, created.range.start, created.node, {
  1360. instanceOf: Element,
  1361. name: 'p',
  1362. children: [
  1363. {
  1364. instanceOf: Element,
  1365. name: 'b',
  1366. priority: 1,
  1367. children: [
  1368. { instanceOf: Text, data: 'foobazqux', position: 3 }
  1369. ]
  1370. }
  1371. ]
  1372. } );
  1373. // Test removed nodes.
  1374. test( writer, null, removed, [
  1375. { instanceOf: Text, data: 'bar' }
  1376. ] );
  1377. } );
  1378. it( 'should merge after removing #2', () => {
  1379. // <p><b>fo[o</b>{bar}<b>ba]zqux</b></p> -> <p><b>fo|zqux</b></p>
  1380. const created = create( writer, {
  1381. instanceOf: Element,
  1382. name: 'p',
  1383. children: [
  1384. {
  1385. instanceOf: Element,
  1386. name: 'b',
  1387. priority: 1,
  1388. children: [
  1389. { instanceOf: Text, data: 'foo', rangeStart: 2 }
  1390. ]
  1391. },
  1392. { instanceOf: Text, data: 'bar' },
  1393. {
  1394. instanceOf: Element,
  1395. name: 'b',
  1396. priority: 1,
  1397. children: [
  1398. { instanceOf: Text, data: 'bazqux', rangeEnd: 2 }
  1399. ]
  1400. }
  1401. ]
  1402. } );
  1403. const removed = writer.remove( created.range );
  1404. test( writer, created.range.start, created.node, {
  1405. instanceOf: Element,
  1406. name: 'p',
  1407. children: [
  1408. {
  1409. instanceOf: Element,
  1410. name: 'b',
  1411. priority: 1,
  1412. children: [
  1413. { instanceOf: Text, data: 'fozqux', position: 2 }
  1414. ]
  1415. }
  1416. ]
  1417. } );
  1418. // Test removed nodes.
  1419. test( writer, null, removed, [
  1420. {
  1421. instanceOf: Element,
  1422. priority: 1,
  1423. name: 'b',
  1424. children: [
  1425. { instanceOf: Text, data: 'o' }
  1426. ]
  1427. },
  1428. { instanceOf: Text, data: 'bar' },
  1429. {
  1430. instanceOf: Element,
  1431. priority: 1,
  1432. name: 'b',
  1433. children: [
  1434. { instanceOf: Text, data: 'ba' }
  1435. ]
  1436. }
  1437. ] );
  1438. } );
  1439. } );
  1440. describe( 'move', () => {
  1441. it( 'should move nodes using remove and insert methods', () => {
  1442. // <p>[{foobar}]</p>
  1443. // Move to <div>|</div>
  1444. // <div>[{foobar}]</div>
  1445. const source = create( writer, {
  1446. instanceOf: Element,
  1447. name: 'p',
  1448. rangeStart: 0,
  1449. rangeEnd: 1,
  1450. children: [
  1451. { instanceOf: Text, data: 'foobar' }
  1452. ]
  1453. } );
  1454. const target = create( writer, {
  1455. instanceOf: Element,
  1456. name: 'div',
  1457. position: 0
  1458. } );
  1459. const removeSpy = sinon.spy( writer, 'remove' );
  1460. const insertSpy = sinon.spy( writer, 'insert' );
  1461. const newRange = writer.move( source.range, target.position );
  1462. sinon.assert.calledOnce( removeSpy );
  1463. sinon.assert.calledWithExactly( removeSpy, source.range );
  1464. sinon.assert.calledOnce( insertSpy );
  1465. sinon.assert.calledWithExactly( insertSpy, target.position, removeSpy.firstCall.returnValue );
  1466. expect( newRange ).to.equal( insertSpy.firstCall.returnValue );
  1467. } );
  1468. } );
  1469. describe( 'wrap', () => {
  1470. it( 'should do nothing on collapsed ranges', () => {
  1471. const description = {
  1472. instanceOf: Element,
  1473. name: 'p',
  1474. children: [
  1475. { instanceOf: Text, data: 'foo', rangeStart: 1, rangeEnd: 1 }
  1476. ]
  1477. };
  1478. const created = create( writer, description );
  1479. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  1480. test( writer, newRange, created.node, description );
  1481. } );
  1482. it( 'wraps single text node', () => {
  1483. // <p>[{foobar}]</p>
  1484. // wrap <b>
  1485. // <p>[<b>{foobar}<b>]</p>
  1486. const created = create( writer, {
  1487. instanceOf: Element,
  1488. name: 'p',
  1489. rangeStart: 0,
  1490. rangeEnd: 1,
  1491. children: [
  1492. { instanceOf: Text, data: 'foobar' }
  1493. ]
  1494. } );
  1495. const b = new Element( 'b' );
  1496. const newRange = writer.wrap( created.range, b, 1 );
  1497. test( writer, newRange, created.node, {
  1498. instanceOf: Element,
  1499. name: 'p',
  1500. rangeStart: 0,
  1501. rangeEnd: 1,
  1502. children: [
  1503. {
  1504. instanceOf: Element,
  1505. name: 'b',
  1506. priority: 1,
  1507. children: [
  1508. { instanceOf: Text, data: 'foobar' }
  1509. ]
  1510. }
  1511. ]
  1512. } );
  1513. } );
  1514. it( 'should throw error when range placed in two containers', () => {
  1515. const container1 = new Element( 'p' );
  1516. const container2 = new Element( 'p' );
  1517. const range = new Range(
  1518. new Position( container1, 0 ),
  1519. new Position( container2, 1 )
  1520. );
  1521. const b = new Element( 'b' );
  1522. expect( () => {
  1523. writer.wrap( range, b, 1 );
  1524. } ).to.throw( CKEditorError, 'treeview-writer-invalid-range-container' );
  1525. } );
  1526. it( 'wraps part of a single text node #1', () => {
  1527. // <p>[{foo]bar}</p>
  1528. // wrap with <b>
  1529. // <p>[<b>{foo}</b>]{bar}</p>
  1530. const created = create( writer, {
  1531. instanceOf: Element,
  1532. name: 'p',
  1533. rangeStart: 0,
  1534. children: [
  1535. { instanceOf: Text, data: 'foobar', rangeEnd: 3 }
  1536. ]
  1537. } );
  1538. const b = new Element( 'b' );
  1539. const newRange = writer.wrap( created.range, b, 2 );
  1540. test( writer, newRange, created.node, {
  1541. instanceOf: Element,
  1542. name: 'p',
  1543. rangeStart: 0,
  1544. rangeEnd: 1,
  1545. children: [
  1546. {
  1547. instanceOf: Element,
  1548. name: 'b',
  1549. priority: 2,
  1550. children: [
  1551. { instanceOf: Text, data: 'foo' }
  1552. ]
  1553. },
  1554. { instanceOf: Text, data: 'bar' }
  1555. ]
  1556. } );
  1557. } );
  1558. it( 'wraps part of a single text node #2', () => {
  1559. // <p>{[foo]bar}</p>
  1560. // wrap with <b>
  1561. // <p>[<b>{foo}</b>]{bar}</p>
  1562. const created = create( writer, {
  1563. instanceOf: Element,
  1564. name: 'p',
  1565. children: [
  1566. { instanceOf: Text, data: 'foobar', rangeStart: 0, rangeEnd: 3 }
  1567. ]
  1568. } );
  1569. const b = new Element( 'b' );
  1570. const newRange = writer.wrap( created.range, b, 2 );
  1571. test( writer, newRange, created.node, {
  1572. instanceOf: Element,
  1573. name: 'p',
  1574. rangeStart: 0,
  1575. rangeEnd: 1,
  1576. children: [
  1577. {
  1578. instanceOf: Element,
  1579. name: 'b',
  1580. priority: 2,
  1581. children: [
  1582. { instanceOf: Text, data: 'foo' }
  1583. ]
  1584. },
  1585. { instanceOf: Text, data: 'bar' }
  1586. ]
  1587. } );
  1588. } );
  1589. it( 'wraps part of a single text node #3', () => {
  1590. // <p>{foo[bar]}</p>
  1591. // wrap with <b>
  1592. // <p>{foo}[<b>{bar}</b>]</p>
  1593. const created = create( writer, {
  1594. instanceOf: Element,
  1595. name: 'p',
  1596. children: [
  1597. { instanceOf: Text, data: 'foobar', rangeStart: 3, rangeEnd: 6 }
  1598. ]
  1599. } );
  1600. const b = new Element( 'b' );
  1601. const newRange = writer.wrap( created.range, b, 2 );
  1602. test( writer, newRange, created.node, {
  1603. instanceOf: Element,
  1604. name: 'p',
  1605. rangeStart: 1,
  1606. rangeEnd: 2,
  1607. children: [
  1608. { instanceOf: Text, data: 'foo' },
  1609. {
  1610. instanceOf: Element,
  1611. name: 'b',
  1612. priority: 2,
  1613. children: [
  1614. { instanceOf: Text, data: 'bar' }
  1615. ]
  1616. }
  1617. ]
  1618. } );
  1619. } );
  1620. it( 'should not wrap inside nested containers', () => {
  1621. // <div>[{foobar}<p>{baz}</p>]</div>
  1622. // wrap with <b>
  1623. // <div>[<b>{foobar}</b><p>{baz}</p>]</div>
  1624. const created = create( writer, {
  1625. instanceOf: Element,
  1626. name: 'div',
  1627. rangeStart: 0,
  1628. rangeEnd: 2,
  1629. children: [
  1630. { instanceOf: Text, data: 'foobar' },
  1631. {
  1632. instanceOf: Element,
  1633. name: 'p',
  1634. children: [
  1635. { instanceOf: Text, data: 'baz' }
  1636. ]
  1637. }
  1638. ]
  1639. } );
  1640. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  1641. test( writer, newRange, created.node, {
  1642. instanceOf: Element,
  1643. name: 'div',
  1644. rangeStart: 0,
  1645. rangeEnd: 2,
  1646. children: [
  1647. {
  1648. instanceOf: Element,
  1649. name: 'b',
  1650. priority: 1,
  1651. children: [
  1652. { instanceOf: Text, data: 'foobar' }
  1653. ]
  1654. },
  1655. {
  1656. instanceOf: Element,
  1657. name: 'p',
  1658. children: [
  1659. { instanceOf: Text, data: 'baz' }
  1660. ]
  1661. }
  1662. ]
  1663. } );
  1664. } );
  1665. it( 'wraps according to priorities', () => {
  1666. // <p>[<u>{foobar}</u>]</p>
  1667. // wrap with <b> that has higher priority than <u>
  1668. // <p>[<u><b>{foobar}</b></u>]</p>
  1669. const created = create( writer, {
  1670. instanceOf: Element,
  1671. name: 'p',
  1672. rangeStart: 0,
  1673. rangeEnd: 1,
  1674. children: [
  1675. {
  1676. instanceOf: Element,
  1677. name: 'u',
  1678. priority: 1,
  1679. children: [
  1680. { instanceOf: Text, data: 'foobar' }
  1681. ]
  1682. }
  1683. ]
  1684. } );
  1685. const b = new Element( 'b' );
  1686. const newRange = writer.wrap( created.range, b, 2 );
  1687. test( writer, newRange, created.node, {
  1688. instanceOf: Element,
  1689. name: 'p',
  1690. rangeStart: 0,
  1691. rangeEnd: 1,
  1692. children: [
  1693. {
  1694. instanceOf: Element,
  1695. name: 'u',
  1696. priority: 1,
  1697. children: [
  1698. {
  1699. instanceOf: Element,
  1700. name: 'b',
  1701. priority: 2,
  1702. children: [
  1703. { instanceOf: Text, data: 'foobar' }
  1704. ]
  1705. }
  1706. ]
  1707. }
  1708. ]
  1709. } );
  1710. } );
  1711. it( 'merges wrapped nodes #1', () => {
  1712. // <p>[<b>{foo}</b>{bar}<b>{baz}</b>]</p>
  1713. // wrap with <b>
  1714. // <p>[<b>{foobarbaz}</b>]</p>
  1715. const created = create( writer, {
  1716. instanceOf: Element,
  1717. name: 'p',
  1718. rangeStart: 0,
  1719. rangeEnd: 3,
  1720. children: [
  1721. {
  1722. instanceOf: Element,
  1723. name: 'b',
  1724. priority: 1,
  1725. children: [
  1726. { instanceOf: Text, data: 'foo' }
  1727. ]
  1728. },
  1729. { instanceOf: Text, data: 'bar' },
  1730. {
  1731. instanceOf: Element,
  1732. name: 'b',
  1733. priority: 1,
  1734. children: [
  1735. { instanceOf: Text, data: 'baz' }
  1736. ]
  1737. }
  1738. ]
  1739. } );
  1740. const b = new Element( 'b' );
  1741. const newRange = writer.wrap( created.range, b, 1 );
  1742. test( writer, newRange, created.node, {
  1743. instanceOf: Element,
  1744. name: 'p',
  1745. rangeStart: 0,
  1746. rangeEnd: 1,
  1747. children: [
  1748. {
  1749. instanceOf: Element,
  1750. name: 'b',
  1751. priority: 1,
  1752. children: [
  1753. { instanceOf: Text, data: 'foobarbaz' }
  1754. ]
  1755. }
  1756. ]
  1757. } );
  1758. } );
  1759. it( 'merges wrapped nodes #2', () => {
  1760. // <p><b>{foo}</b>[{bar]baz}</p>
  1761. // wrap with <b>
  1762. // <p><b>{foo[bar}</b>]{baz}</p>
  1763. const created = create( writer, {
  1764. instanceOf: Element,
  1765. name: 'p',
  1766. rangeStart: 1,
  1767. children: [
  1768. {
  1769. instanceOf: Element,
  1770. name: 'b',
  1771. priority: 1,
  1772. children: [
  1773. { instanceOf: Text, data: 'foo' }
  1774. ]
  1775. },
  1776. { instanceOf: Text, data: 'barbaz', rangeEnd: 3 }
  1777. ]
  1778. } );
  1779. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  1780. test( writer, newRange, created.node, {
  1781. instanceOf: Element,
  1782. name: 'p',
  1783. rangeEnd: 1,
  1784. children: [
  1785. {
  1786. instanceOf: Element,
  1787. name: 'b',
  1788. priority: 1,
  1789. children: [
  1790. { instanceOf: Text, data: 'foobar', rangeStart: 3 }
  1791. ]
  1792. },
  1793. { instanceOf: Text, data: 'baz' }
  1794. ]
  1795. } );
  1796. } );
  1797. it( 'merges wrapped nodes #3', () => {
  1798. // <p><b>{foobar}</b>[{baz}]</p>
  1799. // wrap with <b>
  1800. // <p><b>{foobar[baz}</b>]</p>
  1801. const created = create( writer, {
  1802. instanceOf: Element,
  1803. name: 'p',
  1804. rangeStart: 1,
  1805. rangeEnd: 2,
  1806. children: [
  1807. {
  1808. instanceOf: Element,
  1809. name: 'b',
  1810. priority: 1,
  1811. children: [
  1812. { instanceOf: Text, data: 'foobar' }
  1813. ]
  1814. },
  1815. { instanceOf: Text, data: 'baz', rangeEnd: 3 }
  1816. ]
  1817. } );
  1818. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  1819. test( writer, newRange, created.node, {
  1820. instanceOf: Element,
  1821. name: 'p',
  1822. rangeEnd: 1,
  1823. children: [
  1824. {
  1825. instanceOf: Element,
  1826. name: 'b',
  1827. priority: 1,
  1828. children: [
  1829. { instanceOf: Text, data: 'foobarbaz', rangeStart: 6 }
  1830. ]
  1831. }
  1832. ]
  1833. } );
  1834. } );
  1835. it( 'merges wrapped nodes #4', () => {
  1836. // <p>[{foo}<i>{bar}</i>]{baz}</p>
  1837. // wrap with <b>
  1838. // <p>[<b>{foo}<i>{bar}</i></b>]{baz}</p>
  1839. const created = create( writer, {
  1840. instanceOf: Element,
  1841. name: 'p',
  1842. rangeStart: 0,
  1843. rangeEnd: 2,
  1844. children: [
  1845. { instanceOf: Text, data: 'foo' },
  1846. {
  1847. instanceOf: Element,
  1848. name: 'i',
  1849. priority: 1,
  1850. children: [
  1851. { instanceOf: Text, data: 'bar' }
  1852. ]
  1853. },
  1854. { instanceOf: Text, data: 'baz' }
  1855. ]
  1856. } );
  1857. const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
  1858. test( writer, newRange, created.node, {
  1859. instanceOf: Element,
  1860. name: 'p',
  1861. rangeStart: 0,
  1862. rangeEnd: 1,
  1863. children: [
  1864. {
  1865. instanceOf: Element,
  1866. name: 'b',
  1867. priority: 1,
  1868. children: [
  1869. { instanceOf: Text, data: 'foo' },
  1870. {
  1871. instanceOf: Element,
  1872. name: 'i',
  1873. priority: 1,
  1874. children: [
  1875. { instanceOf: Text, data: 'bar' }
  1876. ]
  1877. }
  1878. ]
  1879. },
  1880. { instanceOf: Text, data: 'baz' }
  1881. ]
  1882. } );
  1883. } );
  1884. it( 'merges wrapped nodes #5', () => {
  1885. // <p>[{foo}<i>{bar}</i>{baz}]</p>
  1886. // wrap with <b>, that has higher priority than <i>
  1887. // <p>[<b>{foo}</b><i><b>{bar}</b></i><b>{baz}</b>]</p>
  1888. const created = create( writer, {
  1889. instanceOf: Element,
  1890. name: 'p',
  1891. rangeStart: 0,
  1892. rangeEnd: 3,
  1893. children: [
  1894. { instanceOf: Text, data: 'foo' },
  1895. {
  1896. instanceOf: Element,
  1897. name: 'i',
  1898. priority: 1,
  1899. children: [
  1900. { instanceOf: Text, data: 'bar' }
  1901. ]
  1902. },
  1903. { instanceOf: Text, data: 'baz' }
  1904. ]
  1905. } );
  1906. const newRange = writer.wrap( created.range, new Element( 'b' ), 2 );
  1907. test( writer, newRange, created.node, {
  1908. instanceOf: Element,
  1909. name: 'p',
  1910. rangeStart: 0,
  1911. rangeEnd: 3,
  1912. children: [
  1913. {
  1914. instanceOf: Element,
  1915. name: 'b',
  1916. priority: 2,
  1917. children: [
  1918. { instanceOf: Text, data: 'foo' }
  1919. ]
  1920. },
  1921. {
  1922. instanceOf: Element,
  1923. name: 'i',
  1924. priority: 1,
  1925. children: [
  1926. {
  1927. instanceOf: Element,
  1928. name: 'b',
  1929. priority: 2,
  1930. children: [
  1931. { instanceOf: Text, data: 'bar' }
  1932. ]
  1933. }
  1934. ]
  1935. },
  1936. {
  1937. instanceOf: Element,
  1938. name: 'b',
  1939. priority: 2,
  1940. children: [
  1941. { instanceOf: Text, data: 'baz' }
  1942. ]
  1943. }
  1944. ]
  1945. } );
  1946. } );
  1947. } );
  1948. describe( 'unwrap', () => {
  1949. it( 'should do nothing on collapsed ranges', () => {
  1950. const description = {
  1951. instanceOf: Element,
  1952. name: 'p',
  1953. children: [
  1954. { instanceOf: Text, data: 'foo', rangeStart: 1, rangeEnd: 1 }
  1955. ]
  1956. };
  1957. const created = create( writer, description );
  1958. const newRange = writer.unwrap( created.range, new Element( 'b' ), 1 );
  1959. test( writer, newRange, created.node, description );
  1960. } );
  1961. it( 'should do nothing on single text node', () => {
  1962. // <p>[{foobar}]</p>
  1963. const description = {
  1964. instanceOf: Element,
  1965. name: 'p',
  1966. rangeStart: 0,
  1967. rangeEnd: 1,
  1968. children: [
  1969. { instanceOf: Text, data: 'foobar' }
  1970. ]
  1971. };
  1972. const created = create( writer, description );
  1973. const b = new Element( 'b' );
  1974. writer.setPriority( b, 1 );
  1975. const newRange = writer.unwrap( created.range, b );
  1976. test( writer, newRange, created.node, description );
  1977. } );
  1978. it( 'should throw error when range placed in two containers', () => {
  1979. const container1 = new Element( 'p' );
  1980. const container2 = new Element( 'p' );
  1981. const range = new Range(
  1982. new Position( container1, 0 ),
  1983. new Position( container2, 1 )
  1984. );
  1985. const b = new Element( 'b' );
  1986. expect( () => {
  1987. writer.unwrap( range, b, 1 );
  1988. } ).to.throw( CKEditorError, 'treeview-writer-invalid-range-container' );
  1989. } );
  1990. it( 'should unwrap single node', () => {
  1991. // <p>[<b>{foobar}</b>]<p> -> <p>[{foobar}]</p>
  1992. const created = create( writer, {
  1993. instanceOf: Element,
  1994. name: 'p',
  1995. rangeStart: 0,
  1996. rangeEnd: 1,
  1997. children: [
  1998. {
  1999. instanceOf: Element,
  2000. priority: 1,
  2001. name: 'b',
  2002. children: [
  2003. { instanceOf: Text, data: 'foobar' }
  2004. ]
  2005. }
  2006. ]
  2007. } );
  2008. const b = new Element( 'b' );
  2009. writer.setPriority( b, 1 );
  2010. const newRange = writer.unwrap( created.range, b );
  2011. test( writer, newRange, created.node, {
  2012. instanceOf: Element,
  2013. name: 'p',
  2014. rangeStart: 0,
  2015. rangeEnd: 1,
  2016. children: [
  2017. { instanceOf: Text, data: 'foobar' }
  2018. ]
  2019. } );
  2020. } );
  2021. it( 'should not unwrap attributes with different priorities #1', () => {
  2022. // <p>[<b>{foobar}</b>]<p> -> <p>[<b>{foobar}</b>]</p>
  2023. // Unwrapped with <b> but using different priority.
  2024. const description = {
  2025. instanceOf: Element,
  2026. name: 'p',
  2027. rangeStart: 0,
  2028. rangeEnd: 1,
  2029. children: [
  2030. {
  2031. instanceOf: Element,
  2032. priority: 1,
  2033. name: 'b',
  2034. children: [
  2035. { instanceOf: Text, data: 'foobar' }
  2036. ]
  2037. }
  2038. ]
  2039. };
  2040. const created = create( writer, description );
  2041. const b = new Element( 'b' );
  2042. writer.setPriority( b, 2 );
  2043. const newRange = writer.unwrap( created.range, b );
  2044. test( writer, newRange, created.node, description );
  2045. } );
  2046. it( 'should not unwrap attributes with different priorities #2', () => {
  2047. // <p>[<b>{foo}</b><b>{bar}</b><b>{baz}</b>]<p> -> <p>[{foo}<b>bar</b>{baz}]</p>
  2048. // <b> around `bar` has different priority than others.
  2049. const created = create( writer, {
  2050. instanceOf: Element,
  2051. name: 'p',
  2052. rangeStart: 0,
  2053. rangeEnd: 3,
  2054. children: [
  2055. {
  2056. instanceOf: Element,
  2057. priority: 2,
  2058. name: 'b',
  2059. children: [
  2060. { instanceOf: Text, data: 'foo' }
  2061. ]
  2062. },
  2063. {
  2064. instanceOf: Element,
  2065. priority: 1,
  2066. name: 'b',
  2067. children: [
  2068. { instanceOf: Text, data: 'bar' }
  2069. ]
  2070. },
  2071. {
  2072. instanceOf: Element,
  2073. priority: 2,
  2074. name: 'b',
  2075. children: [
  2076. { instanceOf: Text, data: 'baz' }
  2077. ]
  2078. }
  2079. ]
  2080. } );
  2081. const b = new Element( 'b' );
  2082. writer.setPriority( b, 2 );
  2083. const newRange = writer.unwrap( created.range, b );
  2084. test( writer, newRange, created.node, {
  2085. instanceOf: Element,
  2086. name: 'p',
  2087. rangeStart: 0,
  2088. rangeEnd: 3,
  2089. children: [
  2090. { instanceOf: Text, data: 'foo' },
  2091. {
  2092. instanceOf: Element,
  2093. priority: 1,
  2094. name: 'b',
  2095. children: [
  2096. { instanceOf: Text, data: 'bar' }
  2097. ]
  2098. },
  2099. { instanceOf: Text, data: 'baz' }
  2100. ]
  2101. } );
  2102. } );
  2103. it( 'should unwrap part of the node', () => {
  2104. // <p>[{baz}<b>{foo]bar}</b><p> -> <p>[{bazfoo}]<b>{bar}</b></p>
  2105. const created = create( writer, {
  2106. instanceOf: Element,
  2107. name: 'p',
  2108. rangeStart: 0,
  2109. children: [
  2110. { instanceOf: Text, data: 'baz' },
  2111. {
  2112. instanceOf: Element,
  2113. name: 'b',
  2114. priority: 1,
  2115. children: [
  2116. { instanceOf: Text, data: 'foobar', rangeEnd: 3 }
  2117. ]
  2118. }
  2119. ]
  2120. } );
  2121. const b = new Element( 'b' );
  2122. writer.setPriority( b, 1 );
  2123. const newRange = writer.unwrap( created.range, b );
  2124. test( writer, newRange, created.node, {
  2125. instanceOf: Element,
  2126. name: 'p',
  2127. rangeStart: 0,
  2128. rangeEnd: 1,
  2129. children: [
  2130. { instanceOf: Text, data: 'bazfoo' },
  2131. {
  2132. instanceOf: Element,
  2133. name: 'b',
  2134. priority: 1,
  2135. children: [
  2136. { instanceOf: Text, data: 'bar' }
  2137. ]
  2138. }
  2139. ]
  2140. } );
  2141. } );
  2142. it( 'should unwrap nested attributes', () => {
  2143. // <p>[<u><b>{foobar}</b></u>]</p> -> <p>[<u>{foobar}</u>]</p>
  2144. const created = create( writer, {
  2145. instanceOf: Element,
  2146. name: 'p',
  2147. rangeStart: 0,
  2148. rangeEnd: 1,
  2149. children: [
  2150. {
  2151. instanceOf: Element,
  2152. name: 'u',
  2153. priority: 1,
  2154. children: [
  2155. {
  2156. instanceOf: Element,
  2157. name: 'b',
  2158. priority: 1,
  2159. children: [
  2160. { instanceOf: Text, data: 'foobar' }
  2161. ]
  2162. }
  2163. ]
  2164. }
  2165. ]
  2166. } );
  2167. const b = new Element( 'b' );
  2168. writer.setPriority( b, 1 );
  2169. const newRange = writer.unwrap( created.range, b );
  2170. test( writer, newRange, created.node, {
  2171. instanceOf: Element,
  2172. name: 'p',
  2173. rangeStart: 0,
  2174. rangeEnd: 1,
  2175. children: [
  2176. {
  2177. instanceOf: Element,
  2178. name: 'u',
  2179. priority: 1,
  2180. children: [
  2181. { instanceOf: Text, data: 'foobar' }
  2182. ]
  2183. }
  2184. ]
  2185. } );
  2186. } );
  2187. it( 'should merge unwrapped nodes #1', () => {
  2188. // <p>{foo}[<b>{bar}</b>]{bom}</p> -> <p>{foo[bar]bom}</p>
  2189. const created = create( writer, {
  2190. instanceOf: Element,
  2191. name: 'p',
  2192. rangeStart: 1,
  2193. rangeEnd: 2,
  2194. children: [
  2195. { instanceOf: Text, data: 'foo' },
  2196. {
  2197. instanceOf: Element,
  2198. name: 'b',
  2199. priority: 1,
  2200. children: [
  2201. { instanceOf: Text, data: 'bar' }
  2202. ]
  2203. },
  2204. { instanceOf: Text, data: 'bom' }
  2205. ]
  2206. } );
  2207. const b = new Element( 'b' );
  2208. writer.setPriority( b, 1 );
  2209. const newRange = writer.unwrap( created.range, b );
  2210. test( writer, newRange, created.node, {
  2211. instanceOf: Element,
  2212. name: 'p',
  2213. children: [
  2214. {
  2215. instanceOf: Text,
  2216. data: 'foobarbom',
  2217. rangeStart: 3,
  2218. rangeEnd: 6
  2219. }
  2220. ]
  2221. } );
  2222. } );
  2223. it( 'should merge unwrapped nodes #2', () => {
  2224. // <p>{foo}<u>{bar}</u>[<b><u>{bazqux}</u></b>]</p> -> <p>{foo}<u>{bar[bazqux}</u>]</p>
  2225. const created = create( writer, {
  2226. instanceOf: Element,
  2227. name: 'p',
  2228. rangeStart: 2,
  2229. rangeEnd: 3,
  2230. children: [
  2231. { instanceOf: Text, data: 'foo' },
  2232. {
  2233. instanceOf: Element,
  2234. name: 'u',
  2235. priority: 1,
  2236. children: [
  2237. { instanceOf: Text, data: 'bar' }
  2238. ]
  2239. },
  2240. {
  2241. instanceOf: Element,
  2242. name: 'b',
  2243. priority: 1,
  2244. children: [
  2245. {
  2246. instanceOf: Element,
  2247. name: 'u',
  2248. priority: 1,
  2249. children: [
  2250. { instanceOf: Text, data: 'bazqux' }
  2251. ]
  2252. }
  2253. ]
  2254. }
  2255. ]
  2256. } );
  2257. const b = new Element( 'b' );
  2258. writer.setPriority( b, 1 );
  2259. const newRange = writer.unwrap( created.range, b );
  2260. test( writer, newRange, created.node, {
  2261. instanceOf: Element,
  2262. name: 'p',
  2263. rangeEnd: 2,
  2264. children: [
  2265. {
  2266. instanceOf: Text,
  2267. data: 'foo'
  2268. },
  2269. {
  2270. instanceOf: Element,
  2271. name: 'u',
  2272. priority: 1,
  2273. children: [
  2274. { instanceOf: Text, data: 'barbazqux', rangeStart: 3 }
  2275. ]
  2276. }
  2277. ]
  2278. } );
  2279. } );
  2280. it( 'should merge unwrapped nodes #3', () => {
  2281. // <p>{foo}<u>{bar}</u>[<b><u>{baz]qux}</u></b></p> -> <p>{foo}<u>{bar[baz}</u>]<b><u>{qux}</u></b></p>
  2282. const created = create( writer, {
  2283. instanceOf: Element,
  2284. name: 'p',
  2285. rangeStart: 2,
  2286. children: [
  2287. { instanceOf: Text, data: 'foo' },
  2288. {
  2289. instanceOf: Element,
  2290. name: 'u',
  2291. priority: 1,
  2292. children: [
  2293. { instanceOf: Text, data: 'bar' }
  2294. ]
  2295. },
  2296. {
  2297. instanceOf: Element,
  2298. name: 'b',
  2299. priority: 1,
  2300. children: [
  2301. {
  2302. instanceOf: Element,
  2303. name: 'u',
  2304. priority: 1,
  2305. children: [
  2306. { instanceOf: Text, data: 'bazqux', rangeEnd: 3 }
  2307. ]
  2308. }
  2309. ]
  2310. }
  2311. ]
  2312. } );
  2313. const b = new Element( 'b' );
  2314. writer.setPriority( b, 1 );
  2315. const newRange = writer.unwrap( created.range, b );
  2316. test( writer, newRange, created.node, {
  2317. instanceOf: Element,
  2318. name: 'p',
  2319. rangeEnd: 2,
  2320. children: [
  2321. {
  2322. instanceOf: Text,
  2323. data: 'foo'
  2324. },
  2325. {
  2326. instanceOf: Element,
  2327. name: 'u',
  2328. priority: 1,
  2329. children: [
  2330. { instanceOf: Text, data: 'barbaz', rangeStart: 3 }
  2331. ]
  2332. },
  2333. {
  2334. instanceOf: Element,
  2335. name: 'b',
  2336. priority: 1,
  2337. children: [
  2338. {
  2339. instanceOf: Element,
  2340. name: 'u',
  2341. priority: 1,
  2342. children: [
  2343. { instanceOf: Text, data: 'qux' }
  2344. ]
  2345. }
  2346. ]
  2347. }
  2348. ]
  2349. } );
  2350. } );
  2351. it( 'should merge unwrapped nodes #4', () => {
  2352. // <p>{foo}<u>{bar}</u>[<b><u>{baz}</u></b>]<u>qux</u></p> -> <p>{foo}<u>{bar[baz]qux}</u></p>
  2353. const created = create( writer, {
  2354. instanceOf: Element,
  2355. name: 'p',
  2356. rangeStart: 2,
  2357. rangeEnd: 3,
  2358. children: [
  2359. { instanceOf: Text, data: 'foo' },
  2360. {
  2361. instanceOf: Element,
  2362. name: 'u',
  2363. priority: 1,
  2364. children: [
  2365. { instanceOf: Text, data: 'bar' }
  2366. ]
  2367. },
  2368. {
  2369. instanceOf: Element,
  2370. name: 'b',
  2371. priority: 1,
  2372. children: [
  2373. {
  2374. instanceOf: Element,
  2375. name: 'u',
  2376. priority: 1,
  2377. children: [
  2378. { instanceOf: Text, data: 'baz' }
  2379. ]
  2380. }
  2381. ]
  2382. },
  2383. {
  2384. instanceOf: Element,
  2385. name: 'u',
  2386. priority: 1,
  2387. children: [
  2388. { instanceOf: Text, data: 'qux' }
  2389. ]
  2390. }
  2391. ]
  2392. } );
  2393. const b = new Element( 'b' );
  2394. writer.setPriority( b, 1 );
  2395. const newRange = writer.unwrap( created.range, b );
  2396. test( writer, newRange, created.node, {
  2397. instanceOf: Element,
  2398. name: 'p',
  2399. children: [
  2400. {
  2401. instanceOf: Text,
  2402. data: 'foo'
  2403. },
  2404. {
  2405. instanceOf: Element,
  2406. name: 'u',
  2407. priority: 1,
  2408. children: [
  2409. { instanceOf: Text, data: 'barbazqux', rangeStart: 3, rangeEnd: 6 }
  2410. ]
  2411. }
  2412. ]
  2413. } );
  2414. } );
  2415. it( 'should merge unwrapped nodes #5', () => {
  2416. // <p>[<b><u>{foo}</u></b><b><u>{bar}</u></b><b><u>{baz}</u></b>]</p> -> <p>[<u>{foobarbaz}</u>]</p>
  2417. const created = create( writer, {
  2418. instanceOf: Element,
  2419. name: 'p',
  2420. rangeStart: 0,
  2421. rangeEnd: 3,
  2422. children: [
  2423. {
  2424. instanceOf: Element,
  2425. name: 'b',
  2426. priority: 1,
  2427. children: [
  2428. {
  2429. instanceOf: Element,
  2430. name: 'u',
  2431. priority: 1,
  2432. children: [
  2433. { instanceOf: Text, data: 'foo' }
  2434. ]
  2435. }
  2436. ]
  2437. },
  2438. {
  2439. instanceOf: Element,
  2440. name: 'b',
  2441. priority: 1,
  2442. children: [
  2443. {
  2444. instanceOf: Element,
  2445. name: 'u',
  2446. priority: 1,
  2447. children: [
  2448. { instanceOf: Text, data: 'bar' }
  2449. ]
  2450. }
  2451. ]
  2452. },
  2453. {
  2454. instanceOf: Element,
  2455. name: 'b',
  2456. priority: 1,
  2457. children: [
  2458. {
  2459. instanceOf: Element,
  2460. name: 'u',
  2461. priority: 1,
  2462. children: [
  2463. { instanceOf: Text, data: 'baz' }
  2464. ]
  2465. }
  2466. ]
  2467. }
  2468. ]
  2469. } );
  2470. const b = new Element( 'b' );
  2471. writer.setPriority( b, 1 );
  2472. const newRange = writer.unwrap( created.range, b );
  2473. test( writer, newRange, created.node, {
  2474. instanceOf: Element,
  2475. name: 'p',
  2476. rangeStart: 0,
  2477. rangeEnd: 1,
  2478. children: [
  2479. {
  2480. instanceOf: Element,
  2481. name: 'u',
  2482. priority: 1,
  2483. children: [
  2484. { instanceOf: Text, data: 'foobarbaz' }
  2485. ]
  2486. }
  2487. ]
  2488. } );
  2489. } );
  2490. it( 'should unwrap mixed ranges #1', () => {
  2491. // <p>[<u><b>{foo}]</b></u></p> -> <p>[<u>{foo}</u>]</p
  2492. const created = create( writer, {
  2493. instanceOf: Element,
  2494. name: 'p',
  2495. rangeStart: 0,
  2496. children: [
  2497. {
  2498. instanceOf: Element,
  2499. name: 'u',
  2500. priority: 1,
  2501. children: [
  2502. {
  2503. instanceOf: Element,
  2504. name: 'b',
  2505. priority: 1,
  2506. rangeEnd: 1,
  2507. children: [
  2508. { instanceOf: Text, data: 'foo' }
  2509. ]
  2510. }
  2511. ]
  2512. }
  2513. ]
  2514. } );
  2515. const b = new Element( 'b' );
  2516. writer.setPriority( b, 1 );
  2517. const newRange = writer.unwrap( created.range, b );
  2518. test( writer, newRange, created.node, {
  2519. instanceOf: Element,
  2520. name: 'p',
  2521. rangeStart: 0,
  2522. rangeEnd: 1,
  2523. children: [
  2524. {
  2525. instanceOf: Element,
  2526. name: 'u',
  2527. priority: 1,
  2528. children: [
  2529. { instanceOf: Text, data: 'foo' }
  2530. ]
  2531. }
  2532. ]
  2533. } );
  2534. } );
  2535. it( 'should unwrap mixed ranges #2', () => {
  2536. // <p>[<u><b>{foo]}</b></u></p> -> <p>[<u>{foo}</u>]</p
  2537. const created = create( writer, {
  2538. instanceOf: Element,
  2539. name: 'p',
  2540. rangeStart: 0,
  2541. children: [
  2542. {
  2543. instanceOf: Element,
  2544. name: 'u',
  2545. priority: 1,
  2546. children: [
  2547. {
  2548. instanceOf: Element,
  2549. name: 'b',
  2550. priority: 1,
  2551. children: [
  2552. { instanceOf: Text, data: 'foo', rangeEnd: 3 }
  2553. ]
  2554. }
  2555. ]
  2556. }
  2557. ]
  2558. } );
  2559. const b = new Element( 'b' );
  2560. writer.setPriority( b, 1 );
  2561. const newRange = writer.unwrap( created.range, b );
  2562. test( writer, newRange, created.node, {
  2563. instanceOf: Element,
  2564. name: 'p',
  2565. rangeStart: 0,
  2566. rangeEnd: 1,
  2567. children: [
  2568. {
  2569. instanceOf: Element,
  2570. name: 'u',
  2571. priority: 1,
  2572. children: [
  2573. { instanceOf: Text, data: 'foo' }
  2574. ]
  2575. }
  2576. ]
  2577. } );
  2578. } );
  2579. } );
  2580. } );