8
0

insert.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* bender-tags: treeview */
  6. 'use strict';
  7. import Writer from '/ckeditor5/engine/treeview/writer.js';
  8. import ContainerElement from '/ckeditor5/engine/treeview/containerelement.js';
  9. import AttributeElement from '/ckeditor5/engine/treeview/attributeelement.js';
  10. import Element from '/ckeditor5/engine/treeview/element.js';
  11. import Position from '/ckeditor5/engine/treeview/position.js';
  12. import Text from '/ckeditor5/engine/treeview/text.js';
  13. import utils from '/tests/engine/treeview/writer/_utils/utils.js';
  14. import CKEditorError from '/ckeditor5/utils/ckeditorerror.js';
  15. describe( 'Writer', () => {
  16. const create = utils.create;
  17. const test = utils.test;
  18. let writer;
  19. beforeEach( () => {
  20. writer = new Writer();
  21. } );
  22. describe( 'insert', () => {
  23. it( 'should return collapsed range in insertion position when using empty array', () => {
  24. // <p>{foo|bar}</p> -> <p>{foo[]bar}</p>
  25. const created = create( writer, {
  26. instanceOf: ContainerElement,
  27. name: 'p',
  28. children: [
  29. { instanceOf: Text, data: 'foobar', position: 3 }
  30. ]
  31. } );
  32. const newRange = writer.insert( created.position, [] );
  33. test( writer, newRange, created.node, {
  34. instanceOf: ContainerElement,
  35. name: 'p',
  36. children: [
  37. { instanceOf: Text, data: 'foobar', rangeStart: 3, rangeEnd: 3 }
  38. ]
  39. } );
  40. } );
  41. it( 'should insert text into another text node #1', () => {
  42. // <p>{foo|bar}</p> insert {baz}
  43. // <p>{foo[baz]bar}</p>
  44. const created = create( writer, {
  45. instanceOf: ContainerElement,
  46. name: 'p',
  47. children: [
  48. { instanceOf: Text, data: 'foobar', position: 3 }
  49. ]
  50. } );
  51. const newRange = writer.insert( created.position, new Text( 'baz' ) );
  52. test( writer, newRange, created.node, {
  53. instanceOf: ContainerElement,
  54. name: 'p',
  55. children: [
  56. { instanceOf: Text, data: 'foobazbar', rangeStart: 3, rangeEnd: 6 }
  57. ]
  58. } );
  59. } );
  60. it( 'should insert text into another text node #2', () => {
  61. // <p>{foobar|}</p> insert {baz}
  62. // <p>{foobar[baz}]</p>
  63. const created = create( writer, {
  64. instanceOf: ContainerElement,
  65. name: 'p',
  66. children: [
  67. { instanceOf: Text, data: 'foobar', position: 6 }
  68. ]
  69. } );
  70. const newRange = writer.insert( created.position, new Text( 'baz' ) );
  71. test( writer, newRange, created.node, {
  72. instanceOf: ContainerElement,
  73. name: 'p',
  74. rangeEnd: 1,
  75. children: [
  76. { instanceOf: Text, data: 'foobarbaz', rangeStart: 6 }
  77. ]
  78. } );
  79. } );
  80. it( 'should insert text into another text node #3', () => {
  81. // <p>{|foobar}</p> insert {baz}
  82. // <p>[{baz]foobar}</p>
  83. const created = create( writer, {
  84. instanceOf: ContainerElement,
  85. name: 'p',
  86. children: [
  87. { instanceOf: Text, data: 'foobar', position: 0 }
  88. ]
  89. } );
  90. const newRange = writer.insert( created.position, new Text( 'baz' ) );
  91. test( writer, newRange, created.node, {
  92. instanceOf: ContainerElement,
  93. name: 'p',
  94. rangeStart: 0,
  95. children: [
  96. { instanceOf: Text, data: 'bazfoobar', rangeEnd: 3 }
  97. ]
  98. } );
  99. } );
  100. it( 'should break attributes when inserting into text node', () => {
  101. // <p>{foo|bar}</p> insert <b>{baz}</b>
  102. // <p>{foo}[<b>baz</b>]{bar}</p>
  103. const created = create( writer, {
  104. instanceOf: ContainerElement,
  105. name: 'p',
  106. children: [
  107. { instanceOf: Text, data: 'foobar', position: 3 }
  108. ]
  109. } );
  110. const toInsert = create( writer, {
  111. instanceOf: ContainerElement,
  112. name: 'b',
  113. priority: 1,
  114. children: [
  115. { instanceOf: Text, data: 'baz' }
  116. ]
  117. } );
  118. const newRange = writer.insert( created.position, toInsert.node );
  119. test( writer, newRange, created.node, {
  120. instanceOf: ContainerElement,
  121. name: 'p',
  122. rangeStart: 1,
  123. rangeEnd: 2,
  124. children: [
  125. { instanceOf: Text, data: 'foo' },
  126. {
  127. instanceOf: ContainerElement,
  128. name: 'b',
  129. priority: 1,
  130. children: [
  131. { instanceOf: Text, data: 'baz' }
  132. ]
  133. },
  134. { instanceOf: Text, data: 'bar' }
  135. ]
  136. } );
  137. } );
  138. it( 'should merge text ndoes', () => {
  139. // <p>|{foobar}</p> insert {baz}
  140. // <p>[{baz]foobar}</p>
  141. const created = create( writer, {
  142. instanceOf: ContainerElement,
  143. name: 'p',
  144. position: 0,
  145. children: [
  146. { instanceOf: Text, data: 'foobar' }
  147. ]
  148. } );
  149. const newRange = writer.insert( created.position, new Text( 'baz' ) );
  150. test( writer, newRange, created.node, {
  151. instanceOf: ContainerElement,
  152. name: 'p',
  153. rangeStart: 0,
  154. children: [
  155. { instanceOf: Text, data: 'bazfoobar', rangeEnd: 3 }
  156. ]
  157. } );
  158. } );
  159. it( 'should merge same attribute nodes', () => {
  160. // <p><b>{foo|bar}</b></p> insert <b>{baz}</b>
  161. // <p><b>{foo[baz]bar}</b></p>
  162. const created = create( writer, {
  163. instanceOf: ContainerElement,
  164. name: 'p',
  165. children: [
  166. {
  167. instanceOf: AttributeElement,
  168. name: 'b',
  169. priority: 1,
  170. children: [
  171. { instanceOf: Text, data: 'foobar', position: 3 }
  172. ]
  173. }
  174. ]
  175. } );
  176. const toInsert = create( writer, {
  177. instanceOf: AttributeElement,
  178. name: 'b',
  179. priority: 1,
  180. children: [
  181. { instanceOf: Text, data: 'baz' }
  182. ]
  183. } );
  184. const newRange = writer.insert( created.position, toInsert.node );
  185. test( writer, newRange, created.node, {
  186. instanceOf: ContainerElement,
  187. name: 'p',
  188. children: [
  189. {
  190. instanceOf: AttributeElement,
  191. name: 'b',
  192. priority: 1,
  193. children: [
  194. { instanceOf: Text, data: 'foobazbar', rangeStart: 3, rangeEnd: 6 }
  195. ]
  196. }
  197. ]
  198. } );
  199. } );
  200. it( 'should not merge different attributes', () => {
  201. // <p><b>{foo|bar}</b></p> insert <b>{baz}</b> ( different priority )
  202. // <p><b>{foo}</b>[<b>{baz}</b>]<b>{bar}</b></p>
  203. const created = create( writer, {
  204. instanceOf: ContainerElement,
  205. name: 'p',
  206. children: [
  207. {
  208. instanceOf: AttributeElement,
  209. name: 'b',
  210. priority: 1,
  211. children: [
  212. { instanceOf: Text, data: 'foobar', position: 3 }
  213. ]
  214. }
  215. ]
  216. } );
  217. const toInsert = create( writer, {
  218. instanceOf: AttributeElement,
  219. name: 'b',
  220. priority: 2,
  221. children: [
  222. { instanceOf: Text, data: 'baz' }
  223. ]
  224. } );
  225. const newRange = writer.insert( created.position, toInsert.node );
  226. test( writer, newRange, created.node, {
  227. instanceOf: ContainerElement,
  228. name: 'p',
  229. rangeStart: 1,
  230. rangeEnd: 2,
  231. children: [
  232. {
  233. instanceOf: AttributeElement,
  234. name: 'b',
  235. priority: 1,
  236. children: [
  237. { instanceOf: Text, data: 'foo' }
  238. ]
  239. },
  240. {
  241. instanceOf: AttributeElement,
  242. name: 'b',
  243. priority: 2,
  244. children: [
  245. { instanceOf: Text, data: 'baz' }
  246. ]
  247. },
  248. {
  249. instanceOf: AttributeElement,
  250. name: 'b',
  251. priority: 1,
  252. children: [
  253. { instanceOf: Text, data: 'bar' }
  254. ]
  255. }
  256. ]
  257. } );
  258. } );
  259. it( 'should allow to insert multiple nodes', () => {
  260. // <p>|</p> insert <b>{foo}</b>{bar}
  261. // <p>[<b>{foo}</b>{bar}]</p>
  262. const root = new ContainerElement( 'p' );
  263. const toInsert = create( writer, {
  264. instanceOf: ContainerElement,
  265. name: 'fake',
  266. children: [
  267. {
  268. instanceOf: AttributeElement,
  269. name: 'b',
  270. priority: 1,
  271. children: [
  272. { instanceOf: Text, data: 'foo' }
  273. ]
  274. },
  275. { instanceOf: Text, data: 'bar' }
  276. ]
  277. } ).node.getChildren();
  278. const position = new Position( root, 0 );
  279. const newRange = writer.insert( position, toInsert );
  280. test( writer, newRange, root, {
  281. instanceOf: ContainerElement,
  282. name: 'p',
  283. rangeStart: 0,
  284. rangeEnd: 2,
  285. children: [
  286. {
  287. instanceOf: AttributeElement,
  288. name: 'b',
  289. priority: 1,
  290. children: [
  291. { instanceOf: Text, data: 'foo' }
  292. ]
  293. },
  294. { instanceOf: Text, data: 'bar' }
  295. ]
  296. } );
  297. } );
  298. it( 'should merge after inserting multiple nodes', () => {
  299. // <p><b>{qux}</b>|{baz}</p> insert <b>{foo}</b>{bar}
  300. // <p><b>{qux[foo}</b>{bar]baz}</p>
  301. const created = create( writer, {
  302. instanceOf: ContainerElement,
  303. name: 'p',
  304. position: 1,
  305. children: [
  306. {
  307. instanceOf: AttributeElement,
  308. name: 'b',
  309. priority: 1,
  310. children: [
  311. { instanceOf: Text, data: 'qux' }
  312. ]
  313. },
  314. { instanceOf: Text, data: 'baz' }
  315. ]
  316. } );
  317. const toInsert = create( writer, {
  318. instanceOf: ContainerElement,
  319. name: 'fake',
  320. children: [
  321. {
  322. instanceOf: AttributeElement,
  323. name: 'b',
  324. priority: 1,
  325. children: [
  326. { instanceOf: Text, data: 'foo' }
  327. ]
  328. },
  329. { instanceOf: Text, data: 'bar' }
  330. ]
  331. } ).node.getChildren();
  332. const newRange = writer.insert( created.position, toInsert );
  333. test( writer, newRange, created.node, {
  334. instanceOf: ContainerElement,
  335. name: 'p',
  336. children: [
  337. {
  338. instanceOf: AttributeElement,
  339. name: 'b',
  340. priority: 1,
  341. children: [
  342. { instanceOf: Text, data: 'quxfoo', rangeStart: 3 }
  343. ]
  344. },
  345. { instanceOf: Text, data: 'barbaz', rangeEnd: 3 }
  346. ]
  347. } );
  348. } );
  349. it( 'should throw when inserting Element', () => {
  350. const element = new Element( 'b' );
  351. const container = new ContainerElement( 'p' );
  352. const position = new Position( container, 0 );
  353. expect( () => {
  354. writer.insert( position, element );
  355. } ).to.throw( CKEditorError, 'treeview-writer-insert-invalid-node' );
  356. } );
  357. it( 'should throw when Element is inserted as child node', () => {
  358. const element = new Element( 'b' );
  359. const root = new ContainerElement( 'p', null, element );
  360. const container = new ContainerElement( 'p' );
  361. const position = new Position( container, 0 );
  362. expect( () => {
  363. writer.insert( position, root );
  364. } ).to.throw( CKEditorError, 'treeview-writer-insert-invalid-node' );
  365. } );
  366. } );
  367. } );