utils.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. import Range from '../../../src/model/range';
  6. import TreeWalker from '../../../src/model/treewalker';
  7. import Text from '../../../src/model/text';
  8. import TextProxy from '../../../src/model/textproxy';
  9. import Delta from '../../../src/model/delta/delta';
  10. import Batch from '../../../src/model/batch';
  11. /**
  12. * Returns tree structure as a simplified string. Elements are uppercase and characters are lowercase.
  13. * Start and end of an element is marked the same way, by the element's name (in uppercase).
  14. *
  15. * let element = new Element( 'div', [], [ 'abc', new Element( 'p', [], 'foo' ), 'xyz' ] );
  16. * modelUtils.getNodesAndText( element ); // abcPfooPxyz
  17. *
  18. * @param {engine.model.Range} range Range to stringify.
  19. * @returns {String} String representing element inner structure.
  20. */
  21. export function getNodesAndText( range ) {
  22. let txt = '';
  23. const treeWalker = new TreeWalker( { boundaries: range } );
  24. for ( const value of treeWalker ) {
  25. const node = value.item;
  26. const nodeText = node.data;
  27. if ( nodeText ) {
  28. txt += nodeText.toLowerCase();
  29. } else {
  30. txt += node.name.toUpperCase();
  31. }
  32. }
  33. return txt;
  34. }
  35. /**
  36. * Returns object JSON representation. It pases an object by JSON.stringify and JSON.parse functions.
  37. *
  38. * @param {Object|Array} object
  39. */
  40. export function jsonParseStringify( object ) {
  41. return JSON.parse( JSON.stringify( object ) );
  42. }
  43. /**
  44. * Adds given {@link module:engine/model/operation/operation~Operation operation} to a newly created
  45. * {@link module:engine/model/delta/delta~Delta delta}
  46. * and returns it back. Every operation, when applied, have to be added to a delta. This helper function is useful in those
  47. * tests which focus on operations, not deltas.
  48. *
  49. * @param {module:engine/model/operation/operation~Operation} operation Operation to wrap
  50. * @returns {module:engine/model/operation/operation~Operation}
  51. */
  52. export function wrapInDelta( operation ) {
  53. const delta = new Delta();
  54. // Batch() requires the document but only a few lines of code needs batch in `document#changes`
  55. // so we may have an invalid batch instance for some tests.
  56. const batch = new Batch();
  57. delta.addOperation( operation );
  58. batch.addDelta( delta );
  59. return operation;
  60. }
  61. /**
  62. * Returns a {@link engine.model.Node} or if it starts at given offset, or {@link engine.model.TextProxy} with one
  63. * character, if given offset is occupied by a {@link engine.model.Text}.
  64. *
  65. * @param {engine.model.Element} parent Element from which item will be returned.
  66. * @param {Number} offset Item's offset.
  67. * @returns {engine.model.Node|engine.model.TextProxy}
  68. */
  69. export function itemAt( parent, offset ) {
  70. const index = parent.offsetToIndex( offset );
  71. const node = parent.getChild( index );
  72. if ( node instanceof Text ) {
  73. const offsetInText = offset - node.startOffset;
  74. return new TextProxy( node, offsetInText, 1 );
  75. }
  76. return node;
  77. }
  78. /**
  79. * Returns all text contents that are inside given element and all it's children.
  80. *
  81. * @param {engine.model.Element} element Element from which text will be returned.
  82. * @returns {String} Text contents of the element.
  83. */
  84. export function getText( element ) {
  85. let text = '';
  86. for ( const child of element.getChildren() ) {
  87. if ( child.data ) {
  88. text += child.data;
  89. } else if ( child.name ) {
  90. text += getText( child );
  91. }
  92. }
  93. return text;
  94. }
  95. /**
  96. * Creates a range on given {@link engine.model.Element element} only. The range starts directly before that element
  97. * and ends before the first child of that element.
  98. *
  99. * @param {engine.model.Element} element Element on which range should be created.
  100. * @returns {engine.model.Range}
  101. */
  102. export function createRangeOnElementOnly( element ) {
  103. return Range.createFromParentsAndOffsets( element.parent, element.startOffset, element, 0 );
  104. }