resolveCaretPosition.js 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. 'use strict';
  2. var UI = require('../../document/UI.js');
  3. require('../../utils/dataTransfer/Clipboard.js');
  4. var selection = require('../../utils/focus/selection.js');
  5. function resolveCaretPosition({ target, node, offset }) {
  6. if (selection.hasOwnSelection(target)) {
  7. return {
  8. node: target,
  9. offset: offset !== null && offset !== undefined ? offset : UI.getUIValue(target).length
  10. };
  11. } else if (node) {
  12. return {
  13. node,
  14. offset: offset !== null && offset !== undefined ? offset : node.nodeType === 3 ? node.nodeValue.length : node.childNodes.length
  15. };
  16. }
  17. return findNodeAtTextOffset(target, offset);
  18. }
  19. function findNodeAtTextOffset(node, offset, isRoot = true) {
  20. // When clicking after the content the browser behavior can be complicated:
  21. // 1. If there is textContent after the last element child,
  22. // the cursor is moved there.
  23. // 2. If there is textContent in the last element child,
  24. // the browser moves the cursor to the last non-empty text node inside this element.
  25. // 3. Otherwise the cursor is moved to the end of the target.
  26. let i = offset === undefined ? node.childNodes.length - 1 : 0;
  27. const step = offset === undefined ? -1 : 1;
  28. while(offset === undefined ? i >= (isRoot ? Math.max(node.childNodes.length - 1, 0) : 0) : i <= node.childNodes.length){
  29. if (offset && i === node.childNodes.length) {
  30. throw new Error('The given offset is out of bounds.');
  31. }
  32. const c = node.childNodes.item(i);
  33. const text = String(c.textContent);
  34. if (text.length) {
  35. if (offset !== undefined && text.length < offset) {
  36. offset -= text.length;
  37. } else if (c.nodeType === 1) {
  38. return findNodeAtTextOffset(c, offset, false);
  39. } else {
  40. // The pre-commit hooks keeps changing this
  41. // See https://github.com/kentcdodds/kcd-scripts/issues/218
  42. /* istanbul ignore else */ // eslint-disable-next-line no-lonely-if
  43. if (c.nodeType === 3) {
  44. return {
  45. node: c,
  46. offset: offset !== null && offset !== undefined ? offset : c.nodeValue.length
  47. };
  48. }
  49. }
  50. }
  51. i += step;
  52. }
  53. return {
  54. node,
  55. offset: node.childNodes.length
  56. };
  57. }
  58. exports.resolveCaretPosition = resolveCaretPosition;