import { FolkShape } from '../folk-shape.ts'; import { ClientRectObserverManager, ClientRectObserverEntry } from './client-rect-observer.ts'; import { TransformEvent } from './TransformEvent.ts'; const clientRectObserver = new ClientRectObserverManager(); interface ObservedElementEntry { selector: string; element: Element; count: number; } class ObservedElements { #elements: ObservedElementEntry[] = []; observe(selector: string) { let entry = this.#elements.find((e) => e.selector === selector); if (entry === undefined) { entry = { selector, element: document.querySelector(selector)!, count: 0 }; this.#elements.push(entry); } entry.count += 1; return entry.element; } unobserve(selector: string) { const entryIndex = this.#elements.findIndex((e) => e.selector === selector); const entry = this.#elements[entryIndex]; if (entry === undefined) return; entry.count -= 1; if (entry.count === 0) { this.#elements.splice(entryIndex, 1); } } getElement(selector: string) { return this.#elements.find((e) => e.selector === selector)?.element; } getSelector(element: Element) { return this.#elements.find((e) => e.element === element)?.selector; } } // If this page is framed in then mock inject the following post message script if (window.parent !== window) { // keep track of count of elements being observed const observedElements = new Map(); const observedSelectors = new Map(); function boundingBoxCallback(entry: ClientRectObserverEntry) { window.parent.postMessage({ type: 'folk-element-change', selector: observedSelectors.get(entry.target), boundingBox: entry.contentRect, }); } function onGeometryChange(event: TransformEvent) { window.parent.postMessage({ type: 'folk-element-change', selector: observedSelectors.get(event.target), boundingBox: (event.target as FolkShape)?.getTransformDOMRect(), }); } window.addEventListener('message', (event) => { switch (event.data.type) { case 'folk-observe-element': { const selector = event.data.selector; const element = document.querySelector(selector); if (element === null) return; observedElements.set(selector, element); observedSelectors.set(element, selector); if (element instanceof FolkShape) { element.addEventListener('transform', onGeometryChange); window.parent.postMessage({ type: 'folk-element-change', selector: selector, boundingBox: element.getTransformDOMRect(), }); } else { clientRectObserver.observe(element, boundingBoxCallback); } return; } case 'folk-unobserve-element': { const selector = event.data.selector; const element = observedElements.get(selector); if (element === undefined) return; if (element instanceof FolkShape) { element.removeEventListener('transform', onGeometryChange); observedElements.delete(selector); observedSelectors.delete(element); } else { clientRectObserver.unobserve(element, boundingBoxCallback); } return; } } }); window.parent.postMessage({ type: 'folk-iframe-ready', }); }