getselectedcontent.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import Model from '../../../src/model/model';
  6. import DocumentFragment from '../../../src/model/documentfragment';
  7. import getSelectedContent from '../../../src/model/utils/getselectedcontent';
  8. import { setData, stringify } from '../../../src/dev-utils/model';
  9. describe( 'DataController utils', () => {
  10. let model, doc;
  11. describe( 'getSelectedContent', () => {
  12. it( 'should use parent batch', () => {
  13. model = new Model();
  14. doc = model.document;
  15. doc.createRoot();
  16. model.schema.extend( '$text', { allowIn: '$root' } );
  17. setData( model, 'x[abc]x' );
  18. model.change( writer => {
  19. getSelectedContent( model, doc.selection );
  20. expect( writer.batch.deltas ).to.length( 1 );
  21. } );
  22. } );
  23. describe( 'in simple scenarios', () => {
  24. beforeEach( () => {
  25. model = new Model();
  26. doc = model.document;
  27. doc.createRoot();
  28. const schema = model.schema;
  29. schema.register( 'image', { inheritAllFrom: '$inline' } );
  30. schema.extend( '$text', { allowIn: '$root' } );
  31. schema.extend( 'image', { allowIn: '$root' } );
  32. schema.allow( { name: '$inline', attributes: [ 'bold' ] } );
  33. schema.allow( { name: '$inline', attributes: [ 'italic' ] } );
  34. } );
  35. it( 'returns empty fragment for no selection', () => {
  36. setData( model, 'abc' );
  37. const frag = getSelectedContent( model, doc.selection );
  38. expect( frag ).instanceOf( DocumentFragment );
  39. expect( frag.isEmpty ).to.be.true;
  40. } );
  41. it( 'returns empty fragment for empty selection', () => {
  42. setData( model, 'a[]bc' );
  43. const frag = getSelectedContent( model, doc.selection );
  44. expect( frag ).instanceOf( DocumentFragment );
  45. expect( frag.isEmpty ).to.be.true;
  46. } );
  47. it( 'gets one character', () => {
  48. setData( model, 'a[b]c' );
  49. const frag = getSelectedContent( model, doc.selection );
  50. const content = stringify( frag );
  51. expect( frag ).instanceOf( DocumentFragment );
  52. expect( content ).to.equal( 'b' );
  53. } );
  54. it( 'gets full text', () => {
  55. setData( model, '[abc]' );
  56. const content = stringify( getSelectedContent( model, doc.selection ) );
  57. expect( content ).to.equal( 'abc' );
  58. } );
  59. it( 'gets text with an attribute', () => {
  60. setData( model, 'xxx<$text bold="true">a[b]c</$text>' );
  61. const content = stringify( getSelectedContent( model, doc.selection ) );
  62. expect( content ).to.equal( '<$text bold="true">b</$text>' );
  63. } );
  64. it( 'gets text with attributes', () => {
  65. setData( model, 'x<$text bold="true">a[b</$text><$text italic="true">c]d</$text>x' );
  66. const content = stringify( getSelectedContent( model, doc.selection ) );
  67. expect( content ).to.equal( '<$text bold="true">b</$text><$text italic="true">c</$text>' );
  68. } );
  69. it( 'gets text with and without attribute', () => {
  70. setData( model, '<$text bold="true">a[b</$text>c]d' );
  71. const content = stringify( getSelectedContent( model, doc.selection ) );
  72. expect( content ).to.equal( '<$text bold="true">b</$text>c' );
  73. } );
  74. it( 'gets text and element', () => {
  75. setData( model, '[ab<image></image>c]' );
  76. const content = stringify( getSelectedContent( model, doc.selection ) );
  77. expect( content ).to.equal( 'ab<image></image>c' );
  78. } );
  79. it( 'gets one element', () => {
  80. setData( model, 'a[<image></image>]b' );
  81. const content = stringify( getSelectedContent( model, doc.selection ) );
  82. expect( content ).to.equal( '<image></image>' );
  83. } );
  84. it( 'gets multiple elements', () => {
  85. setData( model, '[<image></image><image></image>]' );
  86. const content = stringify( getSelectedContent( model, doc.selection ) );
  87. expect( content ).to.equal( '<image></image><image></image>' );
  88. } );
  89. } );
  90. describe( 'in blocks', () => {
  91. beforeEach( () => {
  92. model = new Model();
  93. doc = model.document;
  94. doc.createRoot();
  95. const schema = model.schema;
  96. schema.register( 'paragraph', { inheritAllFrom: '$block' } );
  97. schema.register( 'heading1', { inheritAllFrom: '$block' } );
  98. schema.register( 'blockImage' );
  99. schema.register( 'caption' );
  100. schema.register( 'image', { inheritAllFrom: '$inline' } );
  101. schema.extend( 'blockImage', { allowIn: '$root' } );
  102. schema.extend( 'caption', { allowIn: 'blockImage' } );
  103. schema.extend( '$inline', { allowIn: 'caption' } );
  104. schema.allow( { name: '$inline', attributes: [ 'bold' ] } );
  105. } );
  106. it( 'gets one character', () => {
  107. setData( model, '<paragraph>a[b]c</paragraph>' );
  108. const content = stringify( getSelectedContent( model, doc.selection ) );
  109. expect( content ).to.equal( 'b' );
  110. } );
  111. it( 'gets entire paragraph content', () => {
  112. setData( model, '<paragraph>[a<image></image>b]</paragraph>' );
  113. const content = stringify( getSelectedContent( model, doc.selection ) );
  114. expect( content ).to.equal( 'a<image></image>b' );
  115. } );
  116. it( 'gets two blocks - partial, partial', () => {
  117. setData( model, '<heading1>a[bc</heading1><paragraph>de]f</paragraph>' );
  118. const content = stringify( getSelectedContent( model, doc.selection ) );
  119. expect( content ).to.equal( '<heading1>bc</heading1><paragraph>de</paragraph>' );
  120. } );
  121. it( 'gets two blocks - full, partial', () => {
  122. setData( model, '<heading1>[abc</heading1><paragraph>de]f</paragraph>' );
  123. const content = stringify( getSelectedContent( model, doc.selection ) );
  124. expect( content ).to.equal( '<heading1>abc</heading1><paragraph>de</paragraph>' );
  125. } );
  126. it( 'gets two blocks - full, partial 2', () => {
  127. setData( model, '<heading1>[abc</heading1><paragraph>de<image></image>]f</paragraph>' );
  128. const content = stringify( getSelectedContent( model, doc.selection ) );
  129. expect( content ).to.equal( '<heading1>abc</heading1><paragraph>de<image></image></paragraph>' );
  130. } );
  131. it( 'gets two blocks - full, partial 3', () => {
  132. setData( model, '<heading1>x</heading1><heading1>[abc</heading1><paragraph><image></image>de]f</paragraph>' );
  133. const content = stringify( getSelectedContent( model, doc.selection ) );
  134. expect( content ).to.equal( '<heading1>abc</heading1><paragraph><image></image>de</paragraph>' );
  135. } );
  136. it( 'gets two blocks - full, partial 4', () => {
  137. setData( model, '<heading1>[abc</heading1><paragraph>de]f<image></image></paragraph>' );
  138. const content = stringify( getSelectedContent( model, doc.selection ) );
  139. expect( content ).to.equal( '<heading1>abc</heading1><paragraph>de</paragraph>' );
  140. } );
  141. it( 'gets two blocks - partial, full', () => {
  142. setData( model, '<heading1>a[bc</heading1><paragraph>def]</paragraph>' );
  143. const content = stringify( getSelectedContent( model, doc.selection ) );
  144. expect( content ).to.equal( '<heading1>bc</heading1><paragraph>def</paragraph>' );
  145. } );
  146. it( 'gets two blocks - partial, full 2', () => {
  147. setData( model, '<heading1>a[<image></image>bc</heading1><paragraph>def]</paragraph>' );
  148. const content = stringify( getSelectedContent( model, doc.selection ) );
  149. expect( content ).to.equal( '<heading1><image></image>bc</heading1><paragraph>def</paragraph>' );
  150. } );
  151. // See https://github.com/ckeditor/ckeditor5-engine/issues/652#issuecomment-261358484
  152. it( 'gets two blocks - empty, full', () => {
  153. setData( model, '<heading1>abc[</heading1><paragraph>def]</paragraph>' );
  154. const content = stringify( getSelectedContent( model, doc.selection ) );
  155. expect( content ).to.equal( '<paragraph>def</paragraph>' );
  156. } );
  157. // See https://github.com/ckeditor/ckeditor5-engine/issues/652#issuecomment-261358484
  158. it( 'gets two blocks - partial, empty', () => {
  159. setData( model, '<heading1>a[bc</heading1><paragraph>]def</paragraph>' );
  160. const content = stringify( getSelectedContent( model, doc.selection ) );
  161. expect( content ).to.equal( '<heading1>bc</heading1>' );
  162. } );
  163. it( 'gets three blocks', () => {
  164. setData( model, '<heading1>a[bc</heading1><paragraph>x</paragraph><paragraph>de]f</paragraph>' );
  165. const content = stringify( getSelectedContent( model, doc.selection ) );
  166. expect( content ).to.equal( '<heading1>bc</heading1><paragraph>x</paragraph><paragraph>de</paragraph>' );
  167. } );
  168. it( 'gets block image', () => {
  169. setData( model, '<paragraph>a</paragraph>[<blockImage><caption>Foo</caption></blockImage>]<paragraph>b</paragraph>' );
  170. const content = stringify( getSelectedContent( model, doc.selection ) );
  171. expect( content ).to.equal( '<blockImage><caption>Foo</caption></blockImage>' );
  172. } );
  173. it( 'gets two blocks', () => {
  174. setData( model, '<paragraph>a</paragraph>[<blockImage></blockImage><blockImage></blockImage>]<paragraph>b</paragraph>' );
  175. const content = stringify( getSelectedContent( model, doc.selection ) );
  176. expect( content ).to.equal( '<blockImage></blockImage><blockImage></blockImage>' );
  177. } );
  178. // Purely related to the current implementation.
  179. it( 'gets content when multiple text items needs to be removed from the right excess', () => {
  180. setData( model, '<paragraph>a[b</paragraph><paragraph>c]d<$text bold="true">e</$text>f</paragraph>' );
  181. const content = stringify( getSelectedContent( model, doc.selection ) );
  182. expect( content )
  183. .to.equal( '<paragraph>b</paragraph><paragraph>c</paragraph>' );
  184. } );
  185. // Purely related to the current implementation.
  186. it( 'gets content when multiple text items needs to be removed from the left excess', () => {
  187. setData( model, '<paragraph>a<$text bold="true">b</$text>c[d</paragraph><paragraph>e]f</paragraph>' );
  188. const content = stringify( getSelectedContent( model, doc.selection ) );
  189. expect( content )
  190. .to.equal( '<paragraph>d</paragraph><paragraph>e</paragraph>' );
  191. } );
  192. } );
  193. describe( 'in blocks (deeply nested)', () => {
  194. beforeEach( () => {
  195. model = new Model();
  196. doc = model.document;
  197. doc.createRoot();
  198. const schema = model.schema;
  199. schema.register( 'paragraph', { inheritAllFrom: '$block' } );
  200. schema.register( 'heading1', { inheritAllFrom: '$block' } );
  201. schema.register( 'quote' );
  202. schema.extend( '$block', { allowIn: 'quote' } );
  203. schema.extend( 'quote', { allowIn: '$root' } );
  204. } );
  205. it( 'gets content when ends are equally deeply nested', () => {
  206. setData( model, '<heading1>x</heading1><quote><paragraph>a[bc</paragraph><paragraph>de]f</paragraph></quote>' );
  207. const content = stringify( getSelectedContent( model, doc.selection ) );
  208. expect( content ).to.equal( '<paragraph>bc</paragraph><paragraph>de</paragraph>' );
  209. } );
  210. it( 'gets content when left end nested deeper', () => {
  211. setData( model, '<quote><paragraph>a[bc</paragraph></quote><paragraph>de]f</paragraph>' );
  212. const content = stringify( getSelectedContent( model, doc.selection ) );
  213. expect( content ).to.equal( '<quote><paragraph>bc</paragraph></quote><paragraph>de</paragraph>' );
  214. } );
  215. it( 'gets content when left end nested deeper 2', () => {
  216. setData( model, '<quote><paragraph>a[bc</paragraph><heading1>x</heading1></quote><paragraph>de]f</paragraph>' );
  217. const content = stringify( getSelectedContent( model, doc.selection ) );
  218. expect( content ).to.equal( '<quote><paragraph>bc</paragraph><heading1>x</heading1></quote><paragraph>de</paragraph>' );
  219. } );
  220. it( 'gets content when left end nested deeper 3', () => {
  221. setData( model, '<quote><heading1>x</heading1><paragraph>a[bc</paragraph></quote><paragraph>de]f</paragraph>' );
  222. const content = stringify( getSelectedContent( model, doc.selection ) );
  223. expect( content ).to.equal( '<quote><paragraph>bc</paragraph></quote><paragraph>de</paragraph>' );
  224. } );
  225. // See https://github.com/ckeditor/ckeditor5-engine/issues/652#issuecomment-261358484
  226. it( 'gets content when left end nested deeper 4', () => {
  227. setData( model, '<quote><heading1>x[</heading1><paragraph>abc</paragraph></quote><paragraph>de]f</paragraph>' );
  228. const content = stringify( getSelectedContent( model, doc.selection ) );
  229. expect( content ).to.equal( '<quote><paragraph>abc</paragraph></quote><paragraph>de</paragraph>' );
  230. } );
  231. it( 'gets content when right end nested deeper', () => {
  232. setData( model, '<paragraph>a[bc</paragraph><quote><paragraph>de]f</paragraph></quote>' );
  233. const content = stringify( getSelectedContent( model, doc.selection ) );
  234. expect( content ).to.equal( '<paragraph>bc</paragraph><quote><paragraph>de</paragraph></quote>' );
  235. } );
  236. it( 'gets content when both ends nested deeper than the middle element', () => {
  237. setData( model, '<quote><heading1>a[bc</heading1></quote><heading1>x</heading1><quote><heading1>de]f</heading1></quote>' );
  238. const content = stringify( getSelectedContent( model, doc.selection ) );
  239. expect( content )
  240. .to.equal( '<quote><heading1>bc</heading1></quote><heading1>x</heading1><quote><heading1>de</heading1></quote>' );
  241. } );
  242. // See: https://github.com/ckeditor/ckeditor5-engine/pull/1043#issuecomment-318012286
  243. it( 'ensures that elements are retrieved by indexes instead of offsets', () => {
  244. model.schema.extend( '$text', { allowIn: '$root' } );
  245. model.schema.extend( '$text', { allowIn: 'quote' } );
  246. setData( model,
  247. 'foo' +
  248. '<quote>' +
  249. '<paragraph>' +
  250. 'b[ar' +
  251. '</paragraph>' +
  252. 'bo]m' +
  253. '</quote>'
  254. );
  255. const content = stringify( getSelectedContent( model, doc.selection ) );
  256. expect( content )
  257. .to.equal( '<paragraph>ar</paragraph>bo' );
  258. } );
  259. } );
  260. } );
  261. } );