cursor.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import Parchment from 'parchment';
  2. import TextBlot from './text';
  3. class Cursor extends Parchment.Embed {
  4. static value() {
  5. return undefined;
  6. }
  7. constructor(domNode, selection) {
  8. super(domNode);
  9. this.selection = selection;
  10. this.textNode = document.createTextNode(Cursor.CONTENTS);
  11. this.domNode.appendChild(this.textNode);
  12. this._length = 0;
  13. }
  14. detach() {
  15. // super.detach() will also clear domNode.__blot
  16. if (this.parent != null) this.parent.removeChild(this);
  17. }
  18. format(name, value) {
  19. if (this._length !== 0) {
  20. return super.format(name, value);
  21. }
  22. let target = this, index = 0;
  23. while (target != null && target.statics.scope !== Parchment.Scope.BLOCK_BLOT) {
  24. index += target.offset(target.parent);
  25. target = target.parent;
  26. }
  27. if (target != null) {
  28. this._length = Cursor.CONTENTS.length;
  29. target.optimize();
  30. target.formatAt(index, Cursor.CONTENTS.length, name, value);
  31. this._length = 0;
  32. }
  33. }
  34. index(node, offset) {
  35. if (node === this.textNode) return 0;
  36. return super.index(node, offset);
  37. }
  38. length() {
  39. return this._length;
  40. }
  41. position() {
  42. return [this.textNode, this.textNode.data.length];
  43. }
  44. remove() {
  45. super.remove();
  46. this.parent = null;
  47. }
  48. restore() {
  49. if (this.selection.composing || this.parent == null) return;
  50. let textNode = this.textNode;
  51. let range = this.selection.getNativeRange();
  52. let restoreText, start, end;
  53. if (range != null && range.start.node === textNode && range.end.node === textNode) {
  54. [restoreText, start, end] = [textNode, range.start.offset, range.end.offset];
  55. }
  56. // Link format will insert text outside of anchor tag
  57. while (this.domNode.lastChild != null && this.domNode.lastChild !== this.textNode) {
  58. this.domNode.parentNode.insertBefore(this.domNode.lastChild, this.domNode);
  59. }
  60. if (this.textNode.data !== Cursor.CONTENTS) {
  61. let text = this.textNode.data.split(Cursor.CONTENTS).join('');
  62. if (this.next instanceof TextBlot) {
  63. restoreText = this.next.domNode;
  64. this.next.insertAt(0, text);
  65. this.textNode.data = Cursor.CONTENTS;
  66. } else {
  67. this.textNode.data = text;
  68. this.parent.insertBefore(Parchment.create(this.textNode), this);
  69. this.textNode = document.createTextNode(Cursor.CONTENTS);
  70. this.domNode.appendChild(this.textNode);
  71. }
  72. }
  73. this.remove();
  74. if (start != null) {
  75. [start, end] = [start, end].map(function(offset) {
  76. return Math.max(0, Math.min(restoreText.data.length, offset - 1));
  77. });
  78. return {
  79. startNode: restoreText,
  80. startOffset: start,
  81. endNode: restoreText,
  82. endOffset: end
  83. };
  84. }
  85. }
  86. update(mutations, context) {
  87. if (mutations.some((mutation) => {
  88. return mutation.type === 'characterData' && mutation.target === this.textNode;
  89. })) {
  90. let range = this.restore();
  91. if (range) context.range = range;
  92. }
  93. }
  94. value() {
  95. return '';
  96. }
  97. }
  98. Cursor.blotName = 'cursor';
  99. Cursor.className = 'ql-cursor';
  100. Cursor.tagName = 'span';
  101. Cursor.CONTENTS = "\uFEFF"; // Zero width no break space
  102. export default Cursor;