setSelectionPerMouse.js 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import { getUIValue, setUISelection } from '../../document/UI.js';
  2. import '../../utils/dataTransfer/Clipboard.js';
  3. import { hasNoSelection, hasOwnSelection } from '../../utils/focus/selection.js';
  4. import { resolveCaretPosition } from './resolveCaretPosition.js';
  5. function setSelectionPerMouseDown({ document, target, clickCount, node, offset }) {
  6. if (hasNoSelection(target)) {
  7. return;
  8. }
  9. const targetHasOwnSelection = hasOwnSelection(target);
  10. // On non-input elements the text selection per multiple click
  11. // can extend beyond the target boundaries.
  12. // The exact mechanism what is considered in the same line is unclear.
  13. // Looks it might be every inline element.
  14. // TODO: Check what might be considered part of the same line of text.
  15. const text = String(targetHasOwnSelection ? getUIValue(target) : target.textContent);
  16. const [start, end] = node ? // which elements might be considered in the same line of text.
  17. // TODO: support expanding initial range on multiple clicks if node is given
  18. [
  19. offset,
  20. offset
  21. ] : getTextRange(text, offset, clickCount);
  22. // TODO: implement modifying selection per shift/ctrl+mouse
  23. if (targetHasOwnSelection) {
  24. setUISelection(target, {
  25. anchorOffset: start !== null && start !== undefined ? start : text.length,
  26. focusOffset: end !== null && end !== undefined ? end : text.length
  27. });
  28. return {
  29. node: target,
  30. start: start !== null && start !== undefined ? start : 0,
  31. end: end !== null && end !== undefined ? end : text.length
  32. };
  33. } else {
  34. const { node: startNode, offset: startOffset } = resolveCaretPosition({
  35. target,
  36. node,
  37. offset: start
  38. });
  39. const { node: endNode, offset: endOffset } = resolveCaretPosition({
  40. target,
  41. node,
  42. offset: end
  43. });
  44. const range = target.ownerDocument.createRange();
  45. try {
  46. range.setStart(startNode, startOffset);
  47. range.setEnd(endNode, endOffset);
  48. } catch (e) {
  49. throw new Error('The given offset is out of bounds.');
  50. }
  51. const selection = document.getSelection();
  52. selection === null || selection === undefined ? undefined : selection.removeAllRanges();
  53. selection === null || selection === undefined ? undefined : selection.addRange(range.cloneRange());
  54. return range;
  55. }
  56. }
  57. function getTextRange(text, pos, clickCount) {
  58. if (clickCount % 3 === 1 || text.length === 0) {
  59. return [
  60. pos,
  61. pos
  62. ];
  63. }
  64. const textPos = pos !== null && pos !== undefined ? pos : text.length;
  65. if (clickCount % 3 === 2) {
  66. return [
  67. textPos - text.substr(0, pos).match(/(\w+|\s+|\W)?$/)[0].length,
  68. pos === undefined ? pos : pos + text.substr(pos).match(/^(\w+|\s+|\W)?/)[0].length
  69. ];
  70. }
  71. // triple click
  72. return [
  73. textPos - text.substr(0, pos).match(/[^\r\n]*$/)[0].length,
  74. pos === undefined ? pos : pos + text.substr(pos).match(/^[^\r\n]*/)[0].length
  75. ];
  76. }
  77. export { setSelectionPerMouseDown };