dom.ts 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. export function getCharacterOffsetInLine(lineElement: Element, targetNode: Node, offset: number): number {
  2. const r = document.createRange()
  3. r.selectNodeContents(lineElement)
  4. r.setEnd(targetNode, offset)
  5. return r.toString().length
  6. }
  7. export function getNodeOffsetInLine(lineElement: Element, charIndex: number): { node: Node; offset: number } | null {
  8. const walker = document.createTreeWalker(lineElement, NodeFilter.SHOW_TEXT, null)
  9. let remaining = Math.max(0, charIndex)
  10. let lastText: Node | null = null
  11. let lastLen = 0
  12. let node: Node | null
  13. while ((node = walker.nextNode())) {
  14. const len = node.textContent?.length || 0
  15. lastText = node
  16. lastLen = len
  17. if (remaining <= len) return { node, offset: remaining }
  18. remaining -= len
  19. }
  20. if (lastText) return { node: lastText, offset: lastLen }
  21. if (lineElement.firstChild) return { node: lineElement.firstChild, offset: 0 }
  22. return null
  23. }
  24. export function getSelectionInContainer(
  25. container: HTMLElement,
  26. ): { sl: number; sch: number; el: number; ech: number } | null {
  27. const s = window.getSelection()
  28. if (!s || s.rangeCount === 0) return null
  29. const r = s.getRangeAt(0)
  30. const sc = r.startContainer
  31. const ec = r.endContainer
  32. const getLineElement = (n: Node) =>
  33. (n.nodeType === Node.TEXT_NODE ? (n.parentElement as Element) : (n as Element))?.closest(".line")
  34. const sle = getLineElement(sc)
  35. const ele = getLineElement(ec)
  36. if (!sle || !ele) return null
  37. if (!container.contains(sle as Node) || !container.contains(ele as Node)) return null
  38. const cc = container.querySelector("code") as HTMLElement | null
  39. if (!cc) return null
  40. const lines = Array.from(cc.querySelectorAll(".line"))
  41. const sli = lines.indexOf(sle as Element)
  42. const eli = lines.indexOf(ele as Element)
  43. if (sli === -1 || eli === -1) return null
  44. const sl = sli + 1
  45. const el = eli + 1
  46. const sch = getCharacterOffsetInLine(sle as Element, sc, r.startOffset)
  47. const ech = getCharacterOffsetInLine(ele as Element, ec, r.endOffset)
  48. return { sl, sch, el, ech }
  49. }