|
/** Focus Starting Point @version 0.1.2 @license CC0-1.0 @author Jonathan Neal (https://github.com/jonathantneal) */ |
|
|
|
let weakTarget = new WeakMap |
|
let weakOffset = new WeakMap |
|
|
|
export class FocusStartingPoint extends EventTarget { |
|
constructor(window = globalThis) { |
|
super() |
|
|
|
let { document, history } = window |
|
|
|
// @ts-expect-error "Property 'caretPositionFromPoint' does not exist on type 'Document'." |
|
let { caretPositionFromPoint, body } = document |
|
|
|
let compareTarget = (/** @type {Node} */ seekingTarget, /** @type {number} */ seekingOffset) => { |
|
if ( |
|
currentTarget !== seekingTarget |
|
|| currentOffset !== seekingOffset |
|
) { |
|
weakTarget.set(this, seekingTarget) |
|
weakOffset.set(this, seekingOffset) |
|
|
|
this.dispatchEvent(Object.assign(new Event('change'), { |
|
oldNode: currentTarget, |
|
newNode: seekingTarget, |
|
oldOffset: currentOffset, |
|
newOffset: seekingOffset, |
|
})) |
|
|
|
currentTarget = seekingTarget |
|
currentOffset = seekingOffset |
|
} |
|
} |
|
|
|
let currentTarget = /** @type {Node} */ (document.activeElement) |
|
|
|
let currentOffset = 0 |
|
let historyLength = history.length |
|
/** @type {Element} */ |
|
let element |
|
|
|
weakTarget.set(this, currentTarget) |
|
weakOffset.set(this, currentOffset) |
|
|
|
if (!caretPositionFromPoint) { |
|
caretPositionFromPoint = (/** @type {number} */ clientX, /** @type {number} */ clientY) => { |
|
let range = /** @type {Range} */ (document.caretRangeFromPoint(clientX, clientY)) |
|
let offsetNode = range.startContainer |
|
let offset = range.startOffset |
|
let boundaryRect = range.selectNode(offsetNode) || range.getBoundingClientRect() |
|
|
|
if ( |
|
offsetNode.nodeType === 1 |
|
|| clientX < boundaryRect.left |
|
|| clientX > boundaryRect.right |
|
|| clientY < boundaryRect.top |
|
|| clientY > boundaryRect.bottom |
|
) { |
|
offsetNode = element |
|
offset = 0 |
|
} |
|
|
|
return { offsetNode, offset } |
|
} |
|
} |
|
|
|
window.addEventListener('focusin', () => { |
|
compareTarget(/** @type {Element} */(document.activeElement), 0) |
|
}, true) |
|
|
|
window.addEventListener('hashchange', () => { |
|
if (historyLength !== (historyLength = history.length)) { |
|
compareTarget(document.querySelector(':target') || body, 0) |
|
} |
|
}, true) |
|
|
|
window.addEventListener('pointerdown', event => { |
|
element = /** @type {Element} */ (document.elementFromPoint(event.clientX, event.clientY)) |
|
|
|
let { offsetNode, offset } = caretPositionFromPoint.call(document, event.clientX, event.clientY) |
|
|
|
offsetNode.contains(element) |
|
? compareTarget(element, 0) |
|
: compareTarget(offsetNode, offset) |
|
}, true) |
|
} |
|
|
|
get node() { |
|
return weakTarget.get(this) |
|
} |
|
|
|
get offset() { |
|
return weakOffset.get(this) |
|
} |
|
} |