getselectedcontent.js 13 KB

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