@livetemplate/client 0.7.9 → 0.7.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../livetemplate-client.ts", "../node_modules/morphdom/dist/morphdom-esm.js", "../constants.ts", "../dom/focus-manager.ts", "../dom/directives.ts", "../utils/rate-limit.ts", "../utils/confirm.ts", "../dom/event-delegation.ts", "../dom/observer-manager.ts", "../dom/modal-manager.ts", "../dom/loading-indicator.ts", "../dom/form-disabler.ts", "../dom/reactive-attributes.ts", "../state/tree-renderer.ts", "../state/form-lifecycle-manager.ts", "../transport/websocket.ts", "../upload/s3-uploader.ts", "../upload/upload-handler.ts", "../utils/logger.ts", "../utils/testing.ts"],
4
- "sourcesContent": ["/**\n * LiveTemplate TypeScript Client\n *\n * Reconstructs HTML from tree-based updates using cached static structure,\n * following the Phoenix LiveView optimization approach.\n */\n\nimport morphdom from \"morphdom\";\nimport { FocusManager } from \"./dom/focus-manager\";\nimport {\n handleAnimateDirectives,\n handleHighlightDirectives,\n handleScrollDirectives,\n} from \"./dom/directives\";\nimport { EventDelegator } from \"./dom/event-delegation\";\nimport { ObserverManager } from \"./dom/observer-manager\";\nimport { ModalManager } from \"./dom/modal-manager\";\nimport { LoadingIndicator } from \"./dom/loading-indicator\";\nimport { FormDisabler } from \"./dom/form-disabler\";\nimport { setupReactiveAttributeListeners } from \"./dom/reactive-attributes\";\nimport { TreeRenderer } from \"./state/tree-renderer\";\nimport { FormLifecycleManager } from \"./state/form-lifecycle-manager\";\nimport { WebSocketManager } from \"./transport/websocket\";\nimport { UploadHandler } from \"./upload/upload-handler\";\nimport type {\n UploadProgressMessage,\n UploadStartResponse,\n} from \"./upload/types\";\nimport type {\n LiveTemplateClientOptions,\n ResponseMetadata,\n TreeNode,\n UpdateResponse,\n UpdateResult,\n} from \"./types\";\nimport { createLogger, Logger } from \"./utils/logger\";\nexport { loadAndApplyUpdate, compareHTML } from \"./utils/testing\";\nexport { setupReactiveAttributeListeners } from \"./dom/reactive-attributes\";\nexport { checkLvtConfirm, extractLvtData } from \"./utils/confirm\";\n\nexport class LiveTemplateClient {\n private readonly treeRenderer: TreeRenderer;\n private readonly focusManager: FocusManager;\n private readonly logger: Logger;\n private lvtId: string | null = null;\n\n // Transport properties\n private webSocketManager: WebSocketManager;\n public ws: WebSocket | null = null;\n private wrapperElement: Element | null = null;\n private options: LiveTemplateClientOptions;\n private useHTTP: boolean = false; // True when WebSocket is unavailable\n private sessionCookie: string | null = null; // For HTTP mode session tracking\n\n // Rate limiting: cache of debounced/throttled handlers per element+eventType\n private rateLimitedHandlers: WeakMap<Element, Map<string, Function>> =\n new WeakMap();\n\n private eventDelegator: EventDelegator;\n private observerManager: ObserverManager;\n private modalManager: ModalManager;\n private formLifecycleManager: FormLifecycleManager;\n private loadingIndicator: LoadingIndicator;\n private formDisabler: FormDisabler;\n private uploadHandler: UploadHandler;\n\n // Initialization tracking for loading indicator\n private isInitialized: boolean = false;\n\n // Message tracking for deterministic E2E testing\n private messageCount: number = 0;\n\n constructor(options: LiveTemplateClientOptions = {}) {\n const { logger: providedLogger, logLevel, debug, ...restOptions } = options;\n const resolvedLevel = logLevel ?? (debug ? \"debug\" : \"info\");\n const baseLogger = providedLogger ?? createLogger({ level: resolvedLevel });\n\n if (providedLogger) {\n if (logLevel) {\n providedLogger.setLevel(logLevel);\n } else if (debug) {\n providedLogger.setLevel(\"debug\");\n }\n } else {\n baseLogger.setLevel(resolvedLevel);\n }\n\n this.logger = baseLogger.child(\"Client\");\n\n this.options = {\n autoReconnect: false, // Disable autoReconnect by default to avoid connection loops\n reconnectDelay: 1000,\n liveUrl: window.location.pathname + window.location.search, // Connect to current page (including query params)\n ...restOptions,\n };\n\n this.treeRenderer = new TreeRenderer(this.logger.child(\"TreeRenderer\"));\n this.focusManager = new FocusManager(this.logger.child(\"FocusManager\"));\n\n this.modalManager = new ModalManager(this.logger.child(\"ModalManager\"));\n this.formLifecycleManager = new FormLifecycleManager(this.modalManager);\n this.loadingIndicator = new LoadingIndicator();\n this.formDisabler = new FormDisabler();\n\n // Initialize upload handler\n this.uploadHandler = new UploadHandler(\n (message) => this.send(message),\n {\n chunkSize: 256 * 1024, // 256KB chunks\n onProgress: (entry) => {\n // Trigger DOM update to refresh upload progress\n if (this.wrapperElement) {\n this.wrapperElement.dispatchEvent(\n new CustomEvent(\"lvt:upload:progress\", {\n detail: { entry },\n })\n );\n }\n },\n onComplete: (uploadName, entries) => {\n this.logger.info(`Upload complete: ${uploadName}`, entries);\n if (this.wrapperElement) {\n this.wrapperElement.dispatchEvent(\n new CustomEvent(\"lvt:upload:complete\", {\n detail: { uploadName, entries },\n })\n );\n }\n },\n onError: (entry, error) => {\n this.logger.error(`Upload error for ${entry.id}:`, error);\n if (this.wrapperElement) {\n this.wrapperElement.dispatchEvent(\n new CustomEvent(\"lvt:upload:error\", {\n detail: { entry, error },\n })\n );\n }\n },\n }\n );\n\n this.eventDelegator = new EventDelegator(\n {\n getWrapperElement: () => this.wrapperElement,\n getRateLimitedHandlers: () => this.rateLimitedHandlers,\n parseValue: (value: string) => this.parseValue(value),\n send: (message: any) => this.send(message),\n setActiveSubmission: (\n form: HTMLFormElement | null,\n button: HTMLButtonElement | null,\n originalButtonText: string | null\n ) =>\n this.formLifecycleManager.setActiveSubmission(\n form,\n button,\n originalButtonText\n ),\n openModal: (modalId: string) => this.modalManager.open(modalId),\n closeModal: (modalId: string) => this.modalManager.close(modalId),\n getWebSocketReadyState: () => this.webSocketManager.getReadyState(),\n triggerPendingUploads: (uploadName: string) =>\n this.uploadHandler.triggerPendingUploads(uploadName),\n },\n this.logger.child(\"EventDelegator\")\n );\n\n this.observerManager = new ObserverManager(\n {\n getWrapperElement: () => this.wrapperElement,\n send: (message: any) => this.send(message),\n },\n this.logger.child(\"ObserverManager\")\n );\n\n this.webSocketManager = new WebSocketManager({\n options: this.options,\n logger: this.logger.child(\"Transport\"),\n onConnected: () => {\n this.ws = this.webSocketManager.getSocket();\n this.logger.info(\"WebSocket connected\");\n\n // Clear flash-related query params from URL to prevent stale flash on reload\n // This handles the redirect pattern: /auth?error=invalid_credentials\n this.clearFlashQueryParams();\n\n this.options.onConnect?.();\n this.wrapperElement?.dispatchEvent(new Event(\"lvt:connected\"));\n },\n onDisconnected: () => {\n this.ws = null;\n this.logger.info(\"WebSocket disconnected\");\n this.options.onDisconnect?.();\n this.wrapperElement?.dispatchEvent(new Event(\"lvt:disconnected\"));\n },\n onMessage: (response, event) => {\n this.handleWebSocketPayload(response, event);\n },\n onReconnectAttempt: () => {\n this.logger.info(\"Attempting to reconnect...\");\n },\n onError: (error) => {\n this.logger.error(\"WebSocket error:\", error);\n this.options.onError?.(error);\n },\n });\n }\n\n /**\n * Auto-initialize when DOM is ready\n * Called automatically when script loads\n */\n static autoInit(): void {\n const autoInitLogger = createLogger({ scope: \"Client:autoInit\" });\n const init = () => {\n const wrapper = document.querySelector(\"[data-lvt-id]\");\n if (wrapper) {\n const client = new LiveTemplateClient();\n client.wrapperElement = wrapper;\n\n // Check if loading indicator should be shown\n const shouldShowLoading =\n wrapper.getAttribute(\"data-lvt-loading\") === \"true\";\n if (shouldShowLoading) {\n client.loadingIndicator.show();\n client.formDisabler.disable(client.wrapperElement);\n }\n\n client.connect().catch((error) => {\n autoInitLogger.error(\"Auto-initialization connect failed:\", error);\n });\n\n // Expose as global for programmatic access\n (window as any).liveTemplateClient = client;\n }\n };\n\n // Initialize when DOM is ready\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", init);\n } else {\n init();\n }\n }\n\n /**\n * Handle server-sent updates delivered via WebSocket or HTTP fallback.\n */\n private handleWebSocketPayload(\n response: UpdateResponse,\n event?: MessageEvent<string>\n ): void {\n if (event) {\n (window as any).__lastWSMessage = event.data;\n\n if (!(window as any).__wsMessages) {\n (window as any).__wsMessages = [];\n }\n (window as any).__wsMessages.push(response);\n }\n\n // Check if this is an upload-specific message\n const uploadMessage = response as any;\n if (uploadMessage.type === \"upload_progress\") {\n this.uploadHandler.handleProgressMessage(\n uploadMessage as UploadProgressMessage\n );\n return;\n }\n\n // Check if this is an upload_start response\n if (uploadMessage.upload_name && uploadMessage.entries) {\n const startResponse = uploadMessage as UploadStartResponse;\n // Handle upload start response with error handling\n try {\n this.handleUploadStartResponse(startResponse);\n } catch (error) {\n this.logger.error(\"Error handling upload start response:\", error);\n }\n return;\n }\n\n // Check if this is an upload_complete response\n if (uploadMessage.upload_name && uploadMessage.hasOwnProperty('success')) {\n // UploadCompleteResponse - just log it, no tree update needed\n if (uploadMessage.success) {\n this.logger.info(`Upload complete: ${uploadMessage.upload_name}`);\n } else {\n this.logger.error(`Upload failed: ${uploadMessage.upload_name}`, uploadMessage.error);\n }\n return;\n }\n\n if (!this.isInitialized) {\n this.loadingIndicator.hide();\n this.formDisabler.enable(this.wrapperElement);\n if (\n this.wrapperElement &&\n this.wrapperElement.hasAttribute(\"data-lvt-loading\")\n ) {\n this.wrapperElement.removeAttribute(\"data-lvt-loading\");\n }\n this.isInitialized = true;\n }\n\n if (this.wrapperElement) {\n this.updateDOM(this.wrapperElement, response.tree, response.meta);\n this.messageCount++;\n (window as any).__wsMessageCount = this.messageCount;\n\n this.wrapperElement.dispatchEvent(\n new CustomEvent(\"lvt:updated\", {\n detail: {\n messageCount: this.messageCount,\n action: response.meta?.action,\n success: response.meta?.success,\n },\n })\n );\n }\n }\n\n /**\n * Connect to WebSocket and start receiving updates\n * @param wrapperSelector - CSS selector for the LiveTemplate wrapper (defaults to '[data-lvt-id]')\n */\n async connect(wrapperSelector: string = \"[data-lvt-id]\"): Promise<void> {\n // Find the wrapper element\n this.wrapperElement = document.querySelector(wrapperSelector);\n if (!this.wrapperElement) {\n throw new Error(\n `LiveTemplate wrapper not found with selector: ${wrapperSelector}`\n );\n }\n\n this.webSocketManager.disconnect();\n\n const connectionResult = await this.webSocketManager.connect();\n this.useHTTP = !connectionResult.usingWebSocket;\n\n if (this.useHTTP) {\n this.ws = null;\n this.logger.info(\"WebSocket not available, using HTTP mode\");\n this.options.onConnect?.();\n if (connectionResult.initialState && this.wrapperElement) {\n this.handleWebSocketPayload(connectionResult.initialState);\n }\n }\n // Set up event delegation for lvt-* attributes\n this.eventDelegator.setupEventDelegation();\n\n // Set up window-* event delegation\n this.eventDelegator.setupWindowEventDelegation();\n\n // Set up click-away delegation\n this.eventDelegator.setupClickAwayDelegation();\n\n // Set up modal delegation\n this.eventDelegator.setupModalDelegation();\n\n // Set up focus trap delegation for lvt-focus-trap attribute\n this.eventDelegator.setupFocusTrapDelegation();\n\n // Set up autofocus delegation for lvt-autofocus attribute\n this.eventDelegator.setupAutofocusDelegation();\n\n // Set up reactive attribute listeners for lvt-{action}-on:{event} attributes\n setupReactiveAttributeListeners();\n\n // Initialize focus tracking\n this.focusManager.attach(this.wrapperElement);\n\n // Set up infinite scroll observers\n this.observerManager.setupInfiniteScrollObserver();\n this.observerManager.setupInfiniteScrollMutationObserver();\n }\n\n /**\n * Disconnect from WebSocket\n */\n disconnect(): void {\n this.webSocketManager.disconnect();\n this.ws = null;\n this.useHTTP = false;\n this.observerManager.teardown();\n this.formLifecycleManager.reset();\n this.loadingIndicator.hide();\n this.formDisabler.enable(this.wrapperElement);\n }\n\n /**\n * Clear flash-related query parameters (error, success) from the URL.\n * This prevents stale flash messages from reappearing on page reload.\n * Uses history.replaceState to update URL without triggering navigation.\n */\n private clearFlashQueryParams(): void {\n const url = new URL(window.location.href);\n const flashParams = [\"error\", \"success\"];\n let hasFlashParams = false;\n\n for (const param of flashParams) {\n if (url.searchParams.has(param)) {\n url.searchParams.delete(param);\n hasFlashParams = true;\n }\n }\n\n if (hasFlashParams) {\n this.logger.debug(\"Clearing flash query params from URL\");\n window.history.replaceState(null, \"\", url.toString());\n }\n }\n\n /**\n * Determine whether the client finished its initial load and has an active transport.\n */\n isReady(): boolean {\n const wrapper = this.wrapperElement;\n\n if (!wrapper || wrapper.hasAttribute(\"data-lvt-loading\")) {\n return false;\n }\n\n if (this.useHTTP) {\n return true;\n }\n\n const readyState = this.webSocketManager.getReadyState();\n return readyState === 1; // WebSocket.OPEN = 1\n }\n\n /**\n * Send a message to the server via WebSocket or HTTP\n * @param message - Message to send (will be JSON stringified)\n */\n send(message: any): void {\n // Debug flag for testing\n (window as any).__lvtSendCalled = true;\n (window as any).__lvtMessageAction = message?.action;\n\n const readyState = this.webSocketManager.getReadyState();\n\n if (this.logger.isDebugEnabled()) {\n this.logger.debug(\"send() invoked\", {\n message,\n useHTTP: this.useHTTP,\n hasWebSocket: readyState !== undefined,\n readyState,\n });\n }\n\n if (this.useHTTP) {\n // HTTP mode: send via POST and handle response\n this.logger.debug(\"Using HTTP mode for send\");\n (window as any).__lvtSendPath = \"http\";\n this.sendHTTP(message);\n } else if (readyState === 1) { // WebSocket.OPEN = 1\n // WebSocket mode\n this.logger.debug(\"Sending via WebSocket\");\n (window as any).__lvtSendPath = \"websocket\";\n (window as any).__lvtWSMessage = JSON.stringify(message);\n this.webSocketManager.send(JSON.stringify(message));\n this.logger.debug(\"WebSocket send complete\");\n (window as any).__lvtWSSendComplete = true;\n } else if (readyState !== undefined) {\n // WebSocket is connecting or closing, fall back to HTTP temporarily\n this.logger.warn(\n `WebSocket not ready (state: ${readyState}), using HTTP fallback`\n );\n (window as any).__lvtSendPath = \"http-fallback\";\n this.sendHTTP(message);\n } else {\n this.logger.error(\"No transport available\");\n (window as any).__lvtSendPath = \"no-transport\";\n }\n }\n\n /**\n * Send action via HTTP POST\n */\n private async sendHTTP(message: any): Promise<void> {\n try {\n const liveUrl = this.options.liveUrl || \"/live\";\n const response = await fetch(liveUrl, {\n method: \"POST\",\n credentials: \"include\", // Include cookies for session\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify(message),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP request failed: ${response.status}`);\n }\n\n // Handle the update response\n const updateResponse: UpdateResponse = await response.json();\n if (this.wrapperElement) {\n this.updateDOM(\n this.wrapperElement,\n updateResponse.tree,\n updateResponse.meta\n );\n }\n } catch (error) {\n this.logger.error(\"Failed to send HTTP request:\", error);\n }\n }\n\n /**\n * Parse a string value into appropriate type (number, boolean, or string)\n * @param value - String value to parse\n * @returns Parsed value with correct type\n */\n private parseValue(value: string): any {\n // Try to parse as number\n const num = parseFloat(value);\n if (!isNaN(num) && value.trim() === num.toString()) {\n return num;\n }\n\n // Try to parse as boolean\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n\n // Return as string\n return value;\n }\n\n /**\n * Apply an update to the current state and reconstruct HTML\n * @param update - Tree update object from LiveTemplate server\n * @returns Reconstructed HTML and whether anything changed\n */\n applyUpdate(update: TreeNode): UpdateResult {\n return this.treeRenderer.applyUpdate(update);\n }\n\n /**\n * Apply updates to existing HTML using morphdom for efficient DOM updates\n * @param existingHTML - Current full HTML document\n * @param update - Tree update object from LiveTemplate server\n * @returns Updated HTML content\n */\n applyUpdateToHTML(existingHTML: string, update: TreeNode): string {\n // Apply the update to our internal state\n const result = this.applyUpdate(update);\n\n // Extract lvt-id from existing HTML if we don't have it\n if (!this.lvtId) {\n const match = existingHTML.match(/data-lvt-id=\"([^\"]+)\"/);\n if (match) {\n this.lvtId = match[1];\n }\n }\n\n // The new tree includes complete HTML structure, so we can reconstruct properly\n const innerContent = result.html;\n\n // Find where to insert the reconstructed content\n const bodyMatch = existingHTML.match(/<body>([\\s\\S]*?)<\\/body>/);\n if (!bodyMatch) {\n return existingHTML;\n }\n\n // Replace the body content with our reconstructed HTML\n // We need to preserve the wrapper div with data-lvt-id\n const wrapperStart = `<div data-lvt-id=\"${this.lvtId || \"lvt-unknown\"}\">`;\n const wrapperEnd = \"</div>\";\n const newBodyContent = wrapperStart + innerContent + wrapperEnd;\n\n return existingHTML.replace(\n /<body>[\\s\\S]*?<\\/body>/,\n `<body>${newBodyContent}</body>`\n );\n }\n\n /**\n * Update a live DOM element with new tree data\n * @param element - DOM element containing the LiveTemplate content (the wrapper div)\n * @param update - Tree update object from LiveTemplate server\n * @param meta - Optional metadata about the update (action, success, errors)\n */\n updateDOM(element: Element, update: TreeNode, meta?: ResponseMetadata): void {\n // Apply update to internal state and get reconstructed HTML\n const result = this.applyUpdate(update);\n\n // Helper to recursively check if there are any statics in the tree\n const hasStaticsInTree = (node: any): boolean => {\n if (!node || typeof node !== \"object\") return false;\n if (node.s && Array.isArray(node.s)) return true;\n return Object.values(node).some((v) => hasStaticsInTree(v));\n };\n\n if (!result.changed && !hasStaticsInTree(update)) {\n // No changes detected and no statics in update, skip morphdom\n return;\n }\n\n // Create a temporary wrapper to hold the new content\n // We need to create a DOM element of the same type as 'element' to avoid browser HTML corrections\n // For example, if we put <tr> elements in a <div>, the browser strips them out\n const tempWrapper = document.createElement(element.tagName);\n\n if (this.logger.isDebugEnabled()) {\n this.logger.debug(\"[updateDOM] element.tagName:\", element.tagName);\n this.logger.debug(\n \"[updateDOM] result.html (first 500 chars):\",\n result.html.substring(0, 500)\n );\n this.logger.debug(\n \"[updateDOM] Has <table> tag:\",\n result.html.includes(\"<table>\")\n );\n this.logger.debug(\n \"[updateDOM] Has <tbody> tag:\",\n result.html.includes(\"<tbody>\")\n );\n this.logger.debug(\n \"[updateDOM] Has <tr> tag:\",\n result.html.includes(\"<tr\")\n );\n }\n\n tempWrapper.innerHTML = result.html;\n\n if (this.logger.isDebugEnabled()) {\n this.logger.debug(\n \"[updateDOM] tempWrapper.innerHTML (first 500 chars):\",\n tempWrapper.innerHTML.substring(0, 500)\n );\n this.logger.debug(\n \"[updateDOM] tempWrapper has <table>:\",\n tempWrapper.innerHTML.includes(\"<table>\")\n );\n this.logger.debug(\n \"[updateDOM] tempWrapper has <tbody>:\",\n tempWrapper.innerHTML.includes(\"<tbody>\")\n );\n this.logger.debug(\n \"[updateDOM] tempWrapper has <tr>:\",\n tempWrapper.innerHTML.includes(\"<tr\")\n );\n }\n\n // Use morphdom to efficiently update the element\n morphdom(element, tempWrapper, {\n childrenOnly: true, // Only update children, preserve the wrapper element itself\n getNodeKey: (node: any) => {\n // Use data-key or data-lvt-key for efficient reconciliation\n if (node.nodeType === 1) {\n return (\n node.getAttribute(\"data-key\") ||\n node.getAttribute(\"data-lvt-key\") ||\n undefined\n );\n }\n },\n onBeforeElUpdated: (fromEl, toEl) => {\n // Preserve value for the last focused textual input\n const lastFocused = this.focusManager.getLastFocusedElement();\n if (lastFocused && this.focusManager.isTextualInput(fromEl)) {\n if (fromEl === lastFocused) {\n // Preserve the current value being typed\n (toEl as any).value = (fromEl as any).value;\n }\n }\n\n // Only update if content actually changed\n if (fromEl.isEqualNode(toEl)) {\n return false;\n }\n // Execute lvt-updated lifecycle hook\n this.executeLifecycleHook(fromEl, \"lvt-updated\");\n return true;\n },\n onNodeAdded: (node) => {\n // Execute lvt-mounted lifecycle hook\n if (node.nodeType === Node.ELEMENT_NODE) {\n this.executeLifecycleHook(node as Element, \"lvt-mounted\");\n }\n },\n onBeforeNodeDiscarded: (node) => {\n // Execute lvt-destroyed lifecycle hook\n if (node.nodeType === Node.ELEMENT_NODE) {\n this.executeLifecycleHook(node as Element, \"lvt-destroyed\");\n }\n return true;\n },\n });\n\n // Restore focus to previously focused element\n this.focusManager.restoreFocusedElement();\n\n // Handle scroll directives\n handleScrollDirectives(element);\n\n // Handle highlight directives\n handleHighlightDirectives(element);\n\n // Handle animate directives\n handleAnimateDirectives(element);\n\n // Initialize upload file inputs\n this.uploadHandler.initializeFileInputs(element);\n\n // Handle form lifecycle if metadata is present\n if (meta) {\n this.formLifecycleManager.handleResponse(meta);\n }\n }\n\n /**\n * Handle upload_start response from server\n */\n private handleUploadStartResponse(response: UploadStartResponse): void {\n this.uploadHandler.handleUploadStartResponse(response);\n }\n\n /**\n * Execute lifecycle hook on an element\n * @param element - Element with lifecycle hook attribute\n * @param hookName - Name of the lifecycle hook attribute (e.g., 'lvt-mounted')\n */\n private executeLifecycleHook(element: Element, hookName: string): void {\n const hookValue = element.getAttribute(hookName);\n if (!hookValue) {\n return;\n }\n\n try {\n // Create a function from the hook value and execute it\n // The function has access to 'this' (the element) and 'event'\n const hookFunction = new Function(\"element\", hookValue);\n hookFunction.call(element, element);\n } catch (error) {\n this.logger.error(`Error executing ${hookName} hook:`, error);\n }\n }\n\n /**\n * Reset client state (useful for testing)\n */\n reset(): void {\n this.treeRenderer.reset();\n this.focusManager.reset();\n this.observerManager.teardown();\n this.formLifecycleManager.reset();\n this.loadingIndicator.hide();\n this.formDisabler.enable(this.wrapperElement);\n this.lvtId = null;\n }\n\n /**\n * Get current tree state (for debugging)\n */\n getTreeState(): TreeNode {\n return this.treeRenderer.getTreeState();\n }\n\n /**\n * Get the static structure if available\n */\n getStaticStructure(): string[] | null {\n return this.treeRenderer.getStaticStructure();\n }\n}\n\n// Auto-initialize when script loads (for browser use)\nif (typeof window !== \"undefined\") {\n LiveTemplateClient.autoInit();\n}\n", "var DOCUMENT_FRAGMENT_NODE = 11;\n\nfunction morphAttrs(fromNode, toNode) {\n var toNodeAttrs = toNode.attributes;\n var attr;\n var attrName;\n var attrNamespaceURI;\n var attrValue;\n var fromValue;\n\n // document-fragments dont have attributes so lets not do anything\n if (toNode.nodeType === DOCUMENT_FRAGMENT_NODE || fromNode.nodeType === DOCUMENT_FRAGMENT_NODE) {\n return;\n }\n\n // update attributes on original DOM element\n for (var i = toNodeAttrs.length - 1; i >= 0; i--) {\n attr = toNodeAttrs[i];\n attrName = attr.name;\n attrNamespaceURI = attr.namespaceURI;\n attrValue = attr.value;\n\n if (attrNamespaceURI) {\n attrName = attr.localName || attrName;\n fromValue = fromNode.getAttributeNS(attrNamespaceURI, attrName);\n\n if (fromValue !== attrValue) {\n if (attr.prefix === 'xmlns'){\n attrName = attr.name; // It's not allowed to set an attribute with the XMLNS namespace without specifying the `xmlns` prefix\n }\n fromNode.setAttributeNS(attrNamespaceURI, attrName, attrValue);\n }\n } else {\n fromValue = fromNode.getAttribute(attrName);\n\n if (fromValue !== attrValue) {\n fromNode.setAttribute(attrName, attrValue);\n }\n }\n }\n\n // Remove any extra attributes found on the original DOM element that\n // weren't found on the target element.\n var fromNodeAttrs = fromNode.attributes;\n\n for (var d = fromNodeAttrs.length - 1; d >= 0; d--) {\n attr = fromNodeAttrs[d];\n attrName = attr.name;\n attrNamespaceURI = attr.namespaceURI;\n\n if (attrNamespaceURI) {\n attrName = attr.localName || attrName;\n\n if (!toNode.hasAttributeNS(attrNamespaceURI, attrName)) {\n fromNode.removeAttributeNS(attrNamespaceURI, attrName);\n }\n } else {\n if (!toNode.hasAttribute(attrName)) {\n fromNode.removeAttribute(attrName);\n }\n }\n }\n}\n\nvar range; // Create a range object for efficently rendering strings to elements.\nvar NS_XHTML = 'http://www.w3.org/1999/xhtml';\n\nvar doc = typeof document === 'undefined' ? undefined : document;\nvar HAS_TEMPLATE_SUPPORT = !!doc && 'content' in doc.createElement('template');\nvar HAS_RANGE_SUPPORT = !!doc && doc.createRange && 'createContextualFragment' in doc.createRange();\n\nfunction createFragmentFromTemplate(str) {\n var template = doc.createElement('template');\n template.innerHTML = str;\n return template.content.childNodes[0];\n}\n\nfunction createFragmentFromRange(str) {\n if (!range) {\n range = doc.createRange();\n range.selectNode(doc.body);\n }\n\n var fragment = range.createContextualFragment(str);\n return fragment.childNodes[0];\n}\n\nfunction createFragmentFromWrap(str) {\n var fragment = doc.createElement('body');\n fragment.innerHTML = str;\n return fragment.childNodes[0];\n}\n\n/**\n * This is about the same\n * var html = new DOMParser().parseFromString(str, 'text/html');\n * return html.body.firstChild;\n *\n * @method toElement\n * @param {String} str\n */\nfunction toElement(str) {\n str = str.trim();\n if (HAS_TEMPLATE_SUPPORT) {\n // avoid restrictions on content for things like `<tr><th>Hi</th></tr>` which\n // createContextualFragment doesn't support\n // <template> support not available in IE\n return createFragmentFromTemplate(str);\n } else if (HAS_RANGE_SUPPORT) {\n return createFragmentFromRange(str);\n }\n\n return createFragmentFromWrap(str);\n}\n\n/**\n * Returns true if two node's names are the same.\n *\n * NOTE: We don't bother checking `namespaceURI` because you will never find two HTML elements with the same\n * nodeName and different namespace URIs.\n *\n * @param {Element} a\n * @param {Element} b The target element\n * @return {boolean}\n */\nfunction compareNodeNames(fromEl, toEl) {\n var fromNodeName = fromEl.nodeName;\n var toNodeName = toEl.nodeName;\n var fromCodeStart, toCodeStart;\n\n if (fromNodeName === toNodeName) {\n return true;\n }\n\n fromCodeStart = fromNodeName.charCodeAt(0);\n toCodeStart = toNodeName.charCodeAt(0);\n\n // If the target element is a virtual DOM node or SVG node then we may\n // need to normalize the tag name before comparing. Normal HTML elements that are\n // in the \"http://www.w3.org/1999/xhtml\"\n // are converted to upper case\n if (fromCodeStart <= 90 && toCodeStart >= 97) { // from is upper and to is lower\n return fromNodeName === toNodeName.toUpperCase();\n } else if (toCodeStart <= 90 && fromCodeStart >= 97) { // to is upper and from is lower\n return toNodeName === fromNodeName.toUpperCase();\n } else {\n return false;\n }\n}\n\n/**\n * Create an element, optionally with a known namespace URI.\n *\n * @param {string} name the element name, e.g. 'div' or 'svg'\n * @param {string} [namespaceURI] the element's namespace URI, i.e. the value of\n * its `xmlns` attribute or its inferred namespace.\n *\n * @return {Element}\n */\nfunction createElementNS(name, namespaceURI) {\n return !namespaceURI || namespaceURI === NS_XHTML ?\n doc.createElement(name) :\n doc.createElementNS(namespaceURI, name);\n}\n\n/**\n * Copies the children of one DOM element to another DOM element\n */\nfunction moveChildren(fromEl, toEl) {\n var curChild = fromEl.firstChild;\n while (curChild) {\n var nextChild = curChild.nextSibling;\n toEl.appendChild(curChild);\n curChild = nextChild;\n }\n return toEl;\n}\n\nfunction syncBooleanAttrProp(fromEl, toEl, name) {\n if (fromEl[name] !== toEl[name]) {\n fromEl[name] = toEl[name];\n if (fromEl[name]) {\n fromEl.setAttribute(name, '');\n } else {\n fromEl.removeAttribute(name);\n }\n }\n}\n\nvar specialElHandlers = {\n OPTION: function(fromEl, toEl) {\n var parentNode = fromEl.parentNode;\n if (parentNode) {\n var parentName = parentNode.nodeName.toUpperCase();\n if (parentName === 'OPTGROUP') {\n parentNode = parentNode.parentNode;\n parentName = parentNode && parentNode.nodeName.toUpperCase();\n }\n if (parentName === 'SELECT' && !parentNode.hasAttribute('multiple')) {\n if (fromEl.hasAttribute('selected') && !toEl.selected) {\n // Workaround for MS Edge bug where the 'selected' attribute can only be\n // removed if set to a non-empty value:\n // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12087679/\n fromEl.setAttribute('selected', 'selected');\n fromEl.removeAttribute('selected');\n }\n // We have to reset select element's selectedIndex to -1, otherwise setting\n // fromEl.selected using the syncBooleanAttrProp below has no effect.\n // The correct selectedIndex will be set in the SELECT special handler below.\n parentNode.selectedIndex = -1;\n }\n }\n syncBooleanAttrProp(fromEl, toEl, 'selected');\n },\n /**\n * The \"value\" attribute is special for the <input> element since it sets\n * the initial value. Changing the \"value\" attribute without changing the\n * \"value\" property will have no effect since it is only used to the set the\n * initial value. Similar for the \"checked\" attribute, and \"disabled\".\n */\n INPUT: function(fromEl, toEl) {\n syncBooleanAttrProp(fromEl, toEl, 'checked');\n syncBooleanAttrProp(fromEl, toEl, 'disabled');\n\n if (fromEl.value !== toEl.value) {\n fromEl.value = toEl.value;\n }\n\n if (!toEl.hasAttribute('value')) {\n fromEl.removeAttribute('value');\n }\n },\n\n TEXTAREA: function(fromEl, toEl) {\n var newValue = toEl.value;\n if (fromEl.value !== newValue) {\n fromEl.value = newValue;\n }\n\n var firstChild = fromEl.firstChild;\n if (firstChild) {\n // Needed for IE. Apparently IE sets the placeholder as the\n // node value and vise versa. This ignores an empty update.\n var oldValue = firstChild.nodeValue;\n\n if (oldValue == newValue || (!newValue && oldValue == fromEl.placeholder)) {\n return;\n }\n\n firstChild.nodeValue = newValue;\n }\n },\n SELECT: function(fromEl, toEl) {\n if (!toEl.hasAttribute('multiple')) {\n var selectedIndex = -1;\n var i = 0;\n // We have to loop through children of fromEl, not toEl since nodes can be moved\n // from toEl to fromEl directly when morphing.\n // At the time this special handler is invoked, all children have already been morphed\n // and appended to / removed from fromEl, so using fromEl here is safe and correct.\n var curChild = fromEl.firstChild;\n var optgroup;\n var nodeName;\n while(curChild) {\n nodeName = curChild.nodeName && curChild.nodeName.toUpperCase();\n if (nodeName === 'OPTGROUP') {\n optgroup = curChild;\n curChild = optgroup.firstChild;\n // handle empty optgroups\n if (!curChild) {\n curChild = optgroup.nextSibling;\n optgroup = null;\n }\n } else {\n if (nodeName === 'OPTION') {\n if (curChild.hasAttribute('selected')) {\n selectedIndex = i;\n break;\n }\n i++;\n }\n curChild = curChild.nextSibling;\n if (!curChild && optgroup) {\n curChild = optgroup.nextSibling;\n optgroup = null;\n }\n }\n }\n\n fromEl.selectedIndex = selectedIndex;\n }\n }\n};\n\nvar ELEMENT_NODE = 1;\nvar DOCUMENT_FRAGMENT_NODE$1 = 11;\nvar TEXT_NODE = 3;\nvar COMMENT_NODE = 8;\n\nfunction noop() {}\n\nfunction defaultGetNodeKey(node) {\n if (node) {\n return (node.getAttribute && node.getAttribute('id')) || node.id;\n }\n}\n\nfunction morphdomFactory(morphAttrs) {\n\n return function morphdom(fromNode, toNode, options) {\n if (!options) {\n options = {};\n }\n\n if (typeof toNode === 'string') {\n if (fromNode.nodeName === '#document' || fromNode.nodeName === 'HTML' || fromNode.nodeName === 'BODY') {\n var toNodeHtml = toNode;\n toNode = doc.createElement('html');\n toNode.innerHTML = toNodeHtml;\n } else {\n toNode = toElement(toNode);\n }\n } else if (toNode.nodeType === DOCUMENT_FRAGMENT_NODE$1) {\n toNode = toNode.firstElementChild;\n }\n\n var getNodeKey = options.getNodeKey || defaultGetNodeKey;\n var onBeforeNodeAdded = options.onBeforeNodeAdded || noop;\n var onNodeAdded = options.onNodeAdded || noop;\n var onBeforeElUpdated = options.onBeforeElUpdated || noop;\n var onElUpdated = options.onElUpdated || noop;\n var onBeforeNodeDiscarded = options.onBeforeNodeDiscarded || noop;\n var onNodeDiscarded = options.onNodeDiscarded || noop;\n var onBeforeElChildrenUpdated = options.onBeforeElChildrenUpdated || noop;\n var skipFromChildren = options.skipFromChildren || noop;\n var addChild = options.addChild || function(parent, child){ return parent.appendChild(child); };\n var childrenOnly = options.childrenOnly === true;\n\n // This object is used as a lookup to quickly find all keyed elements in the original DOM tree.\n var fromNodesLookup = Object.create(null);\n var keyedRemovalList = [];\n\n function addKeyedRemoval(key) {\n keyedRemovalList.push(key);\n }\n\n function walkDiscardedChildNodes(node, skipKeyedNodes) {\n if (node.nodeType === ELEMENT_NODE) {\n var curChild = node.firstChild;\n while (curChild) {\n\n var key = undefined;\n\n if (skipKeyedNodes && (key = getNodeKey(curChild))) {\n // If we are skipping keyed nodes then we add the key\n // to a list so that it can be handled at the very end.\n addKeyedRemoval(key);\n } else {\n // Only report the node as discarded if it is not keyed. We do this because\n // at the end we loop through all keyed elements that were unmatched\n // and then discard them in one final pass.\n onNodeDiscarded(curChild);\n if (curChild.firstChild) {\n walkDiscardedChildNodes(curChild, skipKeyedNodes);\n }\n }\n\n curChild = curChild.nextSibling;\n }\n }\n }\n\n /**\n * Removes a DOM node out of the original DOM\n *\n * @param {Node} node The node to remove\n * @param {Node} parentNode The nodes parent\n * @param {Boolean} skipKeyedNodes If true then elements with keys will be skipped and not discarded.\n * @return {undefined}\n */\n function removeNode(node, parentNode, skipKeyedNodes) {\n if (onBeforeNodeDiscarded(node) === false) {\n return;\n }\n\n if (parentNode) {\n parentNode.removeChild(node);\n }\n\n onNodeDiscarded(node);\n walkDiscardedChildNodes(node, skipKeyedNodes);\n }\n\n // // TreeWalker implementation is no faster, but keeping this around in case this changes in the future\n // function indexTree(root) {\n // var treeWalker = document.createTreeWalker(\n // root,\n // NodeFilter.SHOW_ELEMENT);\n //\n // var el;\n // while((el = treeWalker.nextNode())) {\n // var key = getNodeKey(el);\n // if (key) {\n // fromNodesLookup[key] = el;\n // }\n // }\n // }\n\n // // NodeIterator implementation is no faster, but keeping this around in case this changes in the future\n //\n // function indexTree(node) {\n // var nodeIterator = document.createNodeIterator(node, NodeFilter.SHOW_ELEMENT);\n // var el;\n // while((el = nodeIterator.nextNode())) {\n // var key = getNodeKey(el);\n // if (key) {\n // fromNodesLookup[key] = el;\n // }\n // }\n // }\n\n function indexTree(node) {\n if (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE$1) {\n var curChild = node.firstChild;\n while (curChild) {\n var key = getNodeKey(curChild);\n if (key) {\n fromNodesLookup[key] = curChild;\n }\n\n // Walk recursively\n indexTree(curChild);\n\n curChild = curChild.nextSibling;\n }\n }\n }\n\n indexTree(fromNode);\n\n function handleNodeAdded(el) {\n onNodeAdded(el);\n\n var curChild = el.firstChild;\n while (curChild) {\n var nextSibling = curChild.nextSibling;\n\n var key = getNodeKey(curChild);\n if (key) {\n var unmatchedFromEl = fromNodesLookup[key];\n // if we find a duplicate #id node in cache, replace `el` with cache value\n // and morph it to the child node.\n if (unmatchedFromEl && compareNodeNames(curChild, unmatchedFromEl)) {\n curChild.parentNode.replaceChild(unmatchedFromEl, curChild);\n morphEl(unmatchedFromEl, curChild);\n } else {\n handleNodeAdded(curChild);\n }\n } else {\n // recursively call for curChild and it's children to see if we find something in\n // fromNodesLookup\n handleNodeAdded(curChild);\n }\n\n curChild = nextSibling;\n }\n }\n\n function cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey) {\n // We have processed all of the \"to nodes\". If curFromNodeChild is\n // non-null then we still have some from nodes left over that need\n // to be removed\n while (curFromNodeChild) {\n var fromNextSibling = curFromNodeChild.nextSibling;\n if ((curFromNodeKey = getNodeKey(curFromNodeChild))) {\n // Since the node is keyed it might be matched up later so we defer\n // the actual removal to later\n addKeyedRemoval(curFromNodeKey);\n } else {\n // NOTE: we skip nested keyed nodes from being removed since there is\n // still a chance they will be matched up later\n removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);\n }\n curFromNodeChild = fromNextSibling;\n }\n }\n\n function morphEl(fromEl, toEl, childrenOnly) {\n var toElKey = getNodeKey(toEl);\n\n if (toElKey) {\n // If an element with an ID is being morphed then it will be in the final\n // DOM so clear it out of the saved elements collection\n delete fromNodesLookup[toElKey];\n }\n\n if (!childrenOnly) {\n // optional\n var beforeUpdateResult = onBeforeElUpdated(fromEl, toEl);\n if (beforeUpdateResult === false) {\n return;\n } else if (beforeUpdateResult instanceof HTMLElement) {\n fromEl = beforeUpdateResult;\n // reindex the new fromEl in case it's not in the same\n // tree as the original fromEl\n // (Phoenix LiveView sometimes returns a cloned tree,\n // but keyed lookups would still point to the original tree)\n indexTree(fromEl);\n }\n\n // update attributes on original DOM element first\n morphAttrs(fromEl, toEl);\n // optional\n onElUpdated(fromEl);\n\n if (onBeforeElChildrenUpdated(fromEl, toEl) === false) {\n return;\n }\n }\n\n if (fromEl.nodeName !== 'TEXTAREA') {\n morphChildren(fromEl, toEl);\n } else {\n specialElHandlers.TEXTAREA(fromEl, toEl);\n }\n }\n\n function morphChildren(fromEl, toEl) {\n var skipFrom = skipFromChildren(fromEl, toEl);\n var curToNodeChild = toEl.firstChild;\n var curFromNodeChild = fromEl.firstChild;\n var curToNodeKey;\n var curFromNodeKey;\n\n var fromNextSibling;\n var toNextSibling;\n var matchingFromEl;\n\n // walk the children\n outer: while (curToNodeChild) {\n toNextSibling = curToNodeChild.nextSibling;\n curToNodeKey = getNodeKey(curToNodeChild);\n\n // walk the fromNode children all the way through\n while (!skipFrom && curFromNodeChild) {\n fromNextSibling = curFromNodeChild.nextSibling;\n\n if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {\n curToNodeChild = toNextSibling;\n curFromNodeChild = fromNextSibling;\n continue outer;\n }\n\n curFromNodeKey = getNodeKey(curFromNodeChild);\n\n var curFromNodeType = curFromNodeChild.nodeType;\n\n // this means if the curFromNodeChild doesnt have a match with the curToNodeChild\n var isCompatible = undefined;\n\n if (curFromNodeType === curToNodeChild.nodeType) {\n if (curFromNodeType === ELEMENT_NODE) {\n // Both nodes being compared are Element nodes\n\n if (curToNodeKey) {\n // The target node has a key so we want to match it up with the correct element\n // in the original DOM tree\n if (curToNodeKey !== curFromNodeKey) {\n // The current element in the original DOM tree does not have a matching key so\n // let's check our lookup to see if there is a matching element in the original\n // DOM tree\n if ((matchingFromEl = fromNodesLookup[curToNodeKey])) {\n if (fromNextSibling === matchingFromEl) {\n // Special case for single element removals. To avoid removing the original\n // DOM node out of the tree (since that can break CSS transitions, etc.),\n // we will instead discard the current node and wait until the next\n // iteration to properly match up the keyed target element with its matching\n // element in the original tree\n isCompatible = false;\n } else {\n // We found a matching keyed element somewhere in the original DOM tree.\n // Let's move the original DOM node into the current position and morph\n // it.\n\n // NOTE: We use insertBefore instead of replaceChild because we want to go through\n // the `removeNode()` function for the node that is being discarded so that\n // all lifecycle hooks are correctly invoked\n fromEl.insertBefore(matchingFromEl, curFromNodeChild);\n\n // fromNextSibling = curFromNodeChild.nextSibling;\n\n if (curFromNodeKey) {\n // Since the node is keyed it might be matched up later so we defer\n // the actual removal to later\n addKeyedRemoval(curFromNodeKey);\n } else {\n // NOTE: we skip nested keyed nodes from being removed since there is\n // still a chance they will be matched up later\n removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);\n }\n\n curFromNodeChild = matchingFromEl;\n curFromNodeKey = getNodeKey(curFromNodeChild);\n }\n } else {\n // The nodes are not compatible since the \"to\" node has a key and there\n // is no matching keyed node in the source tree\n isCompatible = false;\n }\n }\n } else if (curFromNodeKey) {\n // The original has a key\n isCompatible = false;\n }\n\n isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);\n if (isCompatible) {\n // We found compatible DOM elements so transform\n // the current \"from\" node to match the current\n // target DOM node.\n // MORPH\n morphEl(curFromNodeChild, curToNodeChild);\n }\n\n } else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {\n // Both nodes being compared are Text or Comment nodes\n isCompatible = true;\n // Simply update nodeValue on the original node to\n // change the text value\n if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {\n curFromNodeChild.nodeValue = curToNodeChild.nodeValue;\n }\n\n }\n }\n\n if (isCompatible) {\n // Advance both the \"to\" child and the \"from\" child since we found a match\n // Nothing else to do as we already recursively called morphChildren above\n curToNodeChild = toNextSibling;\n curFromNodeChild = fromNextSibling;\n continue outer;\n }\n\n // No compatible match so remove the old node from the DOM and continue trying to find a\n // match in the original DOM. However, we only do this if the from node is not keyed\n // since it is possible that a keyed node might match up with a node somewhere else in the\n // target tree and we don't want to discard it just yet since it still might find a\n // home in the final DOM tree. After everything is done we will remove any keyed nodes\n // that didn't find a home\n if (curFromNodeKey) {\n // Since the node is keyed it might be matched up later so we defer\n // the actual removal to later\n addKeyedRemoval(curFromNodeKey);\n } else {\n // NOTE: we skip nested keyed nodes from being removed since there is\n // still a chance they will be matched up later\n removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);\n }\n\n curFromNodeChild = fromNextSibling;\n } // END: while(curFromNodeChild) {}\n\n // If we got this far then we did not find a candidate match for\n // our \"to node\" and we exhausted all of the children \"from\"\n // nodes. Therefore, we will just append the current \"to\" node\n // to the end\n if (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {\n // MORPH\n if(!skipFrom){ addChild(fromEl, matchingFromEl); }\n morphEl(matchingFromEl, curToNodeChild);\n } else {\n var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);\n if (onBeforeNodeAddedResult !== false) {\n if (onBeforeNodeAddedResult) {\n curToNodeChild = onBeforeNodeAddedResult;\n }\n\n if (curToNodeChild.actualize) {\n curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);\n }\n addChild(fromEl, curToNodeChild);\n handleNodeAdded(curToNodeChild);\n }\n }\n\n curToNodeChild = toNextSibling;\n curFromNodeChild = fromNextSibling;\n }\n\n cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey);\n\n var specialElHandler = specialElHandlers[fromEl.nodeName];\n if (specialElHandler) {\n specialElHandler(fromEl, toEl);\n }\n } // END: morphChildren(...)\n\n var morphedNode = fromNode;\n var morphedNodeType = morphedNode.nodeType;\n var toNodeType = toNode.nodeType;\n\n if (!childrenOnly) {\n // Handle the case where we are given two DOM nodes that are not\n // compatible (e.g. <div> --> <span> or <div> --> TEXT)\n if (morphedNodeType === ELEMENT_NODE) {\n if (toNodeType === ELEMENT_NODE) {\n if (!compareNodeNames(fromNode, toNode)) {\n onNodeDiscarded(fromNode);\n morphedNode = moveChildren(fromNode, createElementNS(toNode.nodeName, toNode.namespaceURI));\n }\n } else {\n // Going from an element node to a text node\n morphedNode = toNode;\n }\n } else if (morphedNodeType === TEXT_NODE || morphedNodeType === COMMENT_NODE) { // Text or comment node\n if (toNodeType === morphedNodeType) {\n if (morphedNode.nodeValue !== toNode.nodeValue) {\n morphedNode.nodeValue = toNode.nodeValue;\n }\n\n return morphedNode;\n } else {\n // Text node to something else\n morphedNode = toNode;\n }\n }\n }\n\n if (morphedNode === toNode) {\n // The \"to node\" was not compatible with the \"from node\" so we had to\n // toss out the \"from node\" and use the \"to node\"\n onNodeDiscarded(fromNode);\n } else {\n if (toNode.isSameNode && toNode.isSameNode(morphedNode)) {\n return;\n }\n\n morphEl(morphedNode, toNode, childrenOnly);\n\n // We now need to loop over any keyed nodes that might need to be\n // removed. We only do the removal if we know that the keyed node\n // never found a match. When a keyed node is matched up we remove\n // it out of fromNodesLookup and we use fromNodesLookup to determine\n // if a keyed node has been matched up or not\n if (keyedRemovalList) {\n for (var i=0, len=keyedRemovalList.length; i<len; i++) {\n var elToRemove = fromNodesLookup[keyedRemovalList[i]];\n if (elToRemove) {\n removeNode(elToRemove, elToRemove.parentNode, false);\n }\n }\n }\n }\n\n if (!childrenOnly && morphedNode !== fromNode && fromNode.parentNode) {\n if (morphedNode.actualize) {\n morphedNode = morphedNode.actualize(fromNode.ownerDocument || doc);\n }\n // If we had to swap out the from node with a new node because the old\n // node was not compatible with the target node then we need to\n // replace the old DOM node in the original DOM tree. This is only\n // possible if the original DOM node was part of a DOM tree which\n // we know is the case if it has a parent node.\n fromNode.parentNode.replaceChild(morphedNode, fromNode);\n }\n\n return morphedNode;\n };\n}\n\nvar morphdom = morphdomFactory(morphAttrs);\n\nexport default morphdom;\n", "export const FOCUSABLE_INPUTS = [\n \"text\",\n \"textarea\",\n \"number\",\n \"email\",\n \"password\",\n \"search\",\n \"tel\",\n \"url\",\n \"date\",\n \"time\",\n \"datetime-local\",\n \"color\",\n \"range\",\n];\n", "import { FOCUSABLE_INPUTS } from \"../constants\";\nimport type { Logger } from \"../utils/logger\";\n\nexport class FocusManager {\n private wrapperElement: Element | null = null;\n private focusableElements: HTMLElement[] = [];\n private lastFocusedElement: HTMLElement | null = null;\n private lastFocusedSelectionStart: number | null = null;\n private lastFocusedSelectionEnd: number | null = null;\n\n constructor(private readonly logger: Logger) {}\n\n attach(wrapper: Element | null): void {\n this.wrapperElement = wrapper;\n\n if (!wrapper) {\n return;\n }\n\n this.updateFocusableElements();\n this.setupFocusTracking();\n }\n\n reset(): void {\n this.wrapperElement = null;\n this.focusableElements = [];\n this.lastFocusedElement = null;\n this.lastFocusedSelectionStart = null;\n this.lastFocusedSelectionEnd = null;\n }\n\n updateFocusableElements(): void {\n if (!this.wrapperElement) return;\n\n const inputSelectors = FOCUSABLE_INPUTS.map((type) =>\n type === \"textarea\"\n ? \"textarea:not([disabled])\"\n : `input[type=\"${type}\"]:not([disabled])`\n ).join(\", \");\n\n const otherFocusable =\n 'select:not([disabled]), button:not([disabled]), [contenteditable=\"true\"], [tabindex]:not([tabindex=\"-1\"])';\n const selector = `${inputSelectors}, ${otherFocusable}`;\n\n this.focusableElements = Array.from(\n this.wrapperElement.querySelectorAll(selector)\n );\n }\n\n setupFocusTracking(): void {\n if (!this.wrapperElement) return;\n\n const wrapperId = this.wrapperElement.getAttribute(\"data-lvt-id\");\n const focusKey = `__lvt_focus_tracker_${wrapperId}`;\n const blurKey = `__lvt_blur_tracker_${wrapperId}`;\n\n const focusListener = (event: Event) => {\n const target = event.target as HTMLElement;\n if (!target || !this.wrapperElement?.contains(target)) return;\n\n if (this.isTextualInput(target) || target instanceof HTMLSelectElement) {\n this.lastFocusedElement = target;\n this.logger.debug(\n \"[Focus] Tracked focus on:\",\n target.tagName,\n target.id || target.getAttribute(\"name\")\n );\n\n if (this.isTextualInput(target)) {\n this.lastFocusedSelectionStart = target.selectionStart;\n this.lastFocusedSelectionEnd = target.selectionEnd;\n }\n }\n };\n\n const blurListener = (event: Event) => {\n const target = event.target as HTMLElement;\n if (!target || !this.wrapperElement?.contains(target)) return;\n\n if (this.isTextualInput(target) && target === this.lastFocusedElement) {\n this.lastFocusedSelectionStart = target.selectionStart;\n this.lastFocusedSelectionEnd = target.selectionEnd;\n this.logger.debug(\n \"[Focus] Saved cursor on blur:\",\n this.lastFocusedSelectionStart,\n \"-\",\n this.lastFocusedSelectionEnd\n );\n }\n };\n\n if ((document as any)[focusKey]) {\n document.removeEventListener(\"focus\", (document as any)[focusKey], true);\n }\n if ((document as any)[blurKey]) {\n document.removeEventListener(\"blur\", (document as any)[blurKey], true);\n }\n\n (document as any)[focusKey] = focusListener;\n (document as any)[blurKey] = blurListener;\n\n document.addEventListener(\"focus\", focusListener, true);\n document.addEventListener(\"blur\", blurListener, true);\n\n this.logger.debug(\"[Focus] Focus tracking set up\");\n }\n\n restoreFocusedElement(): void {\n this.logger.debug(\n \"[Focus] restoreFocusedElement - lastFocusedElement:\",\n this.lastFocusedElement?.tagName,\n this.lastFocusedElement?.id ||\n this.lastFocusedElement?.getAttribute(\"name\")\n );\n\n if (!this.lastFocusedElement || !this.wrapperElement) {\n this.logger.debug(\"[Focus] No element to restore\");\n return;\n }\n\n const selector = this.getElementSelector(this.lastFocusedElement);\n this.logger.debug(\"[Focus] Selector for last focused:\", selector);\n\n if (!selector) {\n this.logger.debug(\"[Focus] Could not generate selector\");\n return;\n }\n\n let element: HTMLElement | null = null;\n\n if (selector.startsWith(\"data-focus-index-\")) {\n this.updateFocusableElements();\n const index = parseInt(selector.replace(\"data-focus-index-\", \"\"), 10);\n element = this.focusableElements[index] || null;\n this.logger.debug(\"[Focus] Found by index:\", index, element?.tagName);\n } else {\n element = this.wrapperElement.querySelector(selector);\n this.logger.debug(\n \"[Focus] Found by selector:\",\n selector,\n element?.tagName\n );\n }\n\n if (!element) {\n this.logger.debug(\"[Focus] Element not found in updated DOM\");\n return;\n }\n\n const wasFocused = element.matches(\":focus\");\n this.logger.debug(\"[Focus] Already focused:\", wasFocused);\n\n if (!wasFocused) {\n element.focus();\n this.logger.debug(\"[Focus] Restored focus\");\n }\n\n if (\n this.isTextualInput(element) &&\n this.lastFocusedSelectionStart !== null &&\n this.lastFocusedSelectionEnd !== null\n ) {\n element.setSelectionRange(\n this.lastFocusedSelectionStart,\n this.lastFocusedSelectionEnd\n );\n this.logger.debug(\n \"[Focus] Restored cursor:\",\n this.lastFocusedSelectionStart,\n \"-\",\n this.lastFocusedSelectionEnd\n );\n }\n }\n\n isTextualInput(el: Element): el is HTMLInputElement | HTMLTextAreaElement {\n if (el instanceof HTMLTextAreaElement) return true;\n if (el instanceof HTMLInputElement) {\n return FOCUSABLE_INPUTS.indexOf(el.type) >= 0;\n }\n return false;\n }\n\n getLastFocusedElement(): HTMLElement | null {\n return this.lastFocusedElement;\n }\n\n private getElementSelector(el: HTMLElement): string | null {\n if (el.id) return `#${el.id}`;\n if ((el as any).name) return `[name=\"${(el as any).name}\"]`;\n if (el.getAttribute(\"data-key\"))\n return `[data-key=\"${el.getAttribute(\"data-key\")}\"]`;\n\n const index = this.focusableElements.indexOf(el);\n return index >= 0 ? `data-focus-index-${index}` : null;\n }\n}\n", "/**\n * Apply scroll directives on elements with lvt-scroll attributes.\n */\nexport function handleScrollDirectives(rootElement: Element): void {\n const scrollElements = rootElement.querySelectorAll(\"[lvt-scroll]\");\n\n scrollElements.forEach((element) => {\n const htmlElement = element as HTMLElement;\n const mode = htmlElement.getAttribute(\"lvt-scroll\");\n const behavior =\n (htmlElement.getAttribute(\"lvt-scroll-behavior\") as ScrollBehavior) ||\n \"auto\";\n const threshold = parseInt(\n htmlElement.getAttribute(\"lvt-scroll-threshold\") || \"100\",\n 10\n );\n\n if (!mode) return;\n\n switch (mode) {\n case \"bottom\":\n htmlElement.scrollTo({\n top: htmlElement.scrollHeight,\n behavior,\n });\n break;\n\n case \"bottom-sticky\": {\n const isNearBottom =\n htmlElement.scrollHeight -\n htmlElement.scrollTop -\n htmlElement.clientHeight <=\n threshold;\n if (isNearBottom) {\n htmlElement.scrollTo({\n top: htmlElement.scrollHeight,\n behavior,\n });\n }\n break;\n }\n\n case \"top\":\n htmlElement.scrollTo({\n top: 0,\n behavior,\n });\n break;\n\n case \"preserve\":\n break;\n\n default:\n console.warn(`Unknown lvt-scroll mode: ${mode}`);\n }\n });\n}\n\n/**\n * Apply highlight directives to elements with lvt-highlight attributes.\n */\nexport function handleHighlightDirectives(rootElement: Element): void {\n const highlightElements = rootElement.querySelectorAll(\"[lvt-highlight]\");\n\n highlightElements.forEach((element) => {\n const mode = element.getAttribute(\"lvt-highlight\");\n const duration = parseInt(\n element.getAttribute(\"lvt-highlight-duration\") || \"500\",\n 10\n );\n const color = element.getAttribute(\"lvt-highlight-color\") || \"#ffc107\";\n\n if (!mode) return;\n\n const htmlElement = element as HTMLElement;\n const originalBackground = htmlElement.style.backgroundColor;\n const originalTransition = htmlElement.style.transition;\n\n htmlElement.style.transition = `background-color ${duration}ms ease-out`;\n htmlElement.style.backgroundColor = color;\n\n setTimeout(() => {\n htmlElement.style.backgroundColor = originalBackground;\n\n setTimeout(() => {\n htmlElement.style.transition = originalTransition;\n }, duration);\n }, 50);\n });\n}\n\n/**\n * Apply animation directives to elements with lvt-animate attributes.\n */\nexport function handleAnimateDirectives(rootElement: Element): void {\n const animateElements = rootElement.querySelectorAll(\"[lvt-animate]\");\n\n animateElements.forEach((element) => {\n const animation = element.getAttribute(\"lvt-animate\");\n const duration = parseInt(\n element.getAttribute(\"lvt-animate-duration\") || \"300\",\n 10\n );\n\n if (!animation) return;\n\n const htmlElement = element as HTMLElement;\n\n htmlElement.style.setProperty(\"--lvt-animate-duration\", `${duration}ms`);\n\n switch (animation) {\n case \"fade\":\n htmlElement.style.animation = `lvt-fade-in var(--lvt-animate-duration) ease-out`;\n break;\n\n case \"slide\":\n htmlElement.style.animation = `lvt-slide-in var(--lvt-animate-duration) ease-out`;\n break;\n\n case \"scale\":\n htmlElement.style.animation = `lvt-scale-in var(--lvt-animate-duration) ease-out`;\n break;\n\n default:\n console.warn(`Unknown lvt-animate mode: ${animation}`);\n }\n\n htmlElement.addEventListener(\n \"animationend\",\n () => {\n htmlElement.style.animation = \"\";\n },\n { once: true }\n );\n });\n\n if (!document.getElementById(\"lvt-animate-styles\")) {\n const style = document.createElement(\"style\");\n style.id = \"lvt-animate-styles\";\n style.textContent = `\n @keyframes lvt-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes lvt-slide-in {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n @keyframes lvt-scale-in {\n from {\n opacity: 0;\n transform: scale(0.95);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n }\n `;\n document.head.appendChild(style);\n }\n}\n", "/**\n * Debounce function: delays execution until after a pause in calls\n */\nexport function debounce<T extends (...args: any[]) => any>(\n func: T,\n wait: number\n): (...args: Parameters<T>) => void {\n let timeout: number | null = null;\n\n return function debounceWrapper(this: unknown, ...args: Parameters<T>) {\n const context = this;\n\n if (timeout !== null) {\n clearTimeout(timeout);\n }\n\n timeout = window.setTimeout(() => {\n func.apply(context, args);\n }, wait);\n };\n}\n\n/**\n * Throttle function: limits execution to at most once per time period\n * First call executes immediately, subsequent calls are delayed\n */\nexport function throttle<T extends (...args: any[]) => any>(\n func: T,\n limit: number\n): (...args: Parameters<T>) => void {\n let inThrottle = false;\n\n return function throttleWrapper(this: unknown, ...args: Parameters<T>) {\n const context = this;\n\n if (!inThrottle) {\n func.apply(context, args);\n inThrottle = true;\n\n setTimeout(() => {\n inThrottle = false;\n }, limit);\n }\n };\n}\n", "/**\n * Check if an element has lvt-confirm attribute and prompt user if needed.\n * Returns true if action should proceed, false if cancelled.\n */\nexport function checkLvtConfirm(element: HTMLElement): boolean {\n if (element.hasAttribute(\"lvt-confirm\")) {\n const confirmMessage = element.getAttribute(\"lvt-confirm\");\n if (confirmMessage && !confirm(confirmMessage)) {\n return false; // User cancelled\n }\n }\n return true; // Proceed\n}\n\n/**\n * Extract lvt-data-* attributes from an element.\n * lvt-data-id=\"123\" becomes { id: \"123\" }\n * lvt-data-user-name=\"john\" becomes { \"user-name\": \"john\" }\n */\nexport function extractLvtData(element: HTMLElement): Record<string, string> {\n const data: Record<string, string> = {};\n const attributes = element.attributes;\n\n for (let i = 0; i < attributes.length; i++) {\n const attr = attributes[i];\n if (attr.name.startsWith(\"lvt-data-\")) {\n const key = attr.name.substring(9); // Remove \"lvt-data-\" prefix\n data[key] = attr.value;\n }\n }\n\n return data;\n}\n", "import { debounce, throttle } from \"../utils/rate-limit\";\nimport { checkLvtConfirm } from \"../utils/confirm\";\nimport type { Logger } from \"../utils/logger\";\n\nexport interface EventDelegationContext {\n getWrapperElement(): Element | null;\n getRateLimitedHandlers(): WeakMap<Element, Map<string, Function>>;\n parseValue(value: string): any;\n send(message: any): void;\n setActiveSubmission(\n form: HTMLFormElement | null,\n button: HTMLButtonElement | null,\n originalButtonText: string | null\n ): void;\n openModal(modalId: string): void;\n closeModal(modalId: string): void;\n getWebSocketReadyState(): number | undefined;\n triggerPendingUploads(uploadName: string): void;\n}\n\n/**\n * Handles all DOM event delegation concerns for LiveTemplateClient.\n */\nexport class EventDelegator {\n constructor(\n private readonly context: EventDelegationContext,\n private readonly logger: Logger\n ) {}\n\n setupEventDelegation(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n const eventTypes = [\n \"click\",\n \"submit\",\n \"change\",\n \"input\",\n \"keydown\",\n \"keyup\",\n \"focus\",\n \"blur\",\n \"mouseenter\",\n \"mouseleave\",\n ];\n const wrapperId = wrapperElement.getAttribute(\"data-lvt-id\");\n const rateLimitedHandlers = this.context.getRateLimitedHandlers();\n\n eventTypes.forEach((eventType) => {\n const listenerKey = `__lvt_delegated_${eventType}_${wrapperId}`;\n const existingListener = (document as any)[listenerKey];\n if (existingListener) {\n document.removeEventListener(eventType, existingListener, false);\n }\n\n const listener = (e: Event) => {\n const currentWrapper = this.context.getWrapperElement();\n if (!currentWrapper) return;\n\n if (eventType === \"submit\") {\n (window as any).__lvtSubmitListenerTriggered = true;\n (window as any).__lvtSubmitEventTarget = (\n e.target as Element\n )?.tagName;\n }\n\n this.logger.debug(\"Event listener triggered:\", eventType, e.target);\n\n const target = e.target as Element;\n if (!target) return;\n\n let element: Element | null = target;\n let inWrapper = false;\n\n while (element) {\n if (element === currentWrapper) {\n inWrapper = true;\n break;\n }\n element = element.parentElement;\n }\n\n if (eventType === \"submit\") {\n (window as any).__lvtInWrapper = inWrapper;\n (window as any).__lvtWrapperElement =\n currentWrapper.getAttribute(\"data-lvt-id\");\n }\n\n if (!inWrapper) return;\n\n const attrName = `lvt-${eventType}`;\n element = target;\n\n while (element && element !== currentWrapper.parentElement) {\n let action = element.getAttribute(attrName);\n let actionElement = element;\n\n // Check for lvt-persist on form submit (auto-persist to database)\n if (!action && eventType === \"submit\" && element instanceof HTMLFormElement) {\n const persistTable = element.getAttribute(\"lvt-persist\");\n if (persistTable) {\n action = `persist:${persistTable}`;\n actionElement = element;\n }\n }\n\n if (!action && (eventType === \"change\" || eventType === \"input\")) {\n const formElement: HTMLFormElement | null = element.closest(\"form\");\n if (formElement && formElement.hasAttribute(\"lvt-change\")) {\n action = formElement.getAttribute(\"lvt-change\");\n actionElement = formElement;\n }\n }\n\n if (action && actionElement) {\n if (eventType === \"submit\") {\n (window as any).__lvtActionFound = action;\n (window as any).__lvtActionElement = actionElement.tagName;\n }\n\n if (eventType === \"submit\") {\n e.preventDefault();\n }\n\n if (\n (eventType === \"keydown\" || eventType === \"keyup\") &&\n actionElement.hasAttribute(\"lvt-key\")\n ) {\n const keyFilter = actionElement.getAttribute(\"lvt-key\");\n const keyboardEvent = e as KeyboardEvent;\n if (keyFilter && keyboardEvent.key !== keyFilter) {\n element = element.parentElement;\n continue;\n }\n }\n\n const targetElement = actionElement;\n\n const handleAction = () => {\n this.logger.debug(\"handleAction called\", {\n action,\n eventType,\n targetElement,\n });\n\n // Handle lvt-confirm for any action\n if (!checkLvtConfirm(targetElement as HTMLElement)) {\n this.logger.debug(\"Action cancelled by user:\", action);\n return;\n }\n\n const message: any = { action, data: {} };\n\n if (targetElement instanceof HTMLFormElement) {\n this.logger.debug(\"Processing form element\");\n const formData = new FormData(targetElement);\n\n const checkboxes = Array.from(\n targetElement.querySelectorAll('input[type=\"checkbox\"][name]')\n ) as HTMLInputElement[];\n const checkboxNames = new Set(checkboxes.map((cb) => cb.name));\n\n checkboxNames.forEach((name) => {\n message.data[name] = false;\n });\n\n // Get password field names to skip parseValue for them\n const passwordFields = new Set(\n Array.from(\n targetElement.querySelectorAll('input[type=\"password\"][name]')\n ).map((el) => (el as HTMLInputElement).name)\n );\n\n formData.forEach((value, key) => {\n if (checkboxNames.has(key)) {\n message.data[key] = true;\n this.logger.debug(\"Converted checkbox\", key, \"to true\");\n } else if (passwordFields.has(key)) {\n // Never parse password values - always keep as string\n message.data[key] = value as string;\n } else {\n message.data[key] = this.context.parseValue(\n value as string\n );\n }\n });\n this.logger.debug(\"Form data collected:\", message.data);\n } else if (eventType === \"change\" || eventType === \"input\") {\n if (targetElement instanceof HTMLInputElement) {\n const key = targetElement.name || \"value\";\n message.data[key] = this.context.parseValue(\n targetElement.value\n );\n } else if (targetElement instanceof HTMLSelectElement) {\n const key = targetElement.name || \"value\";\n message.data[key] = this.context.parseValue(\n targetElement.value\n );\n } else if (targetElement instanceof HTMLTextAreaElement) {\n const key = targetElement.name || \"value\";\n message.data[key] = this.context.parseValue(\n targetElement.value\n );\n }\n }\n\n Array.from(targetElement.attributes).forEach((attr) => {\n if (attr.name.startsWith(\"lvt-data-\")) {\n const key = attr.name.replace(\"lvt-data-\", \"\");\n message.data[key] = this.context.parseValue(attr.value);\n }\n });\n\n Array.from(targetElement.attributes).forEach((attr) => {\n if (attr.name.startsWith(\"lvt-value-\")) {\n const key = attr.name.replace(\"lvt-value-\", \"\");\n message.data[key] = this.context.parseValue(attr.value);\n }\n });\n\n if (\n eventType === \"submit\" &&\n targetElement instanceof HTMLFormElement\n ) {\n const submitEvent = e as SubmitEvent;\n const submitButton =\n submitEvent.submitter as HTMLButtonElement | null;\n let originalButtonText: string | null = null;\n\n if (\n submitButton &&\n submitButton.hasAttribute(\"lvt-disable-with\")\n ) {\n originalButtonText = submitButton.textContent;\n submitButton.disabled = true;\n submitButton.textContent =\n submitButton.getAttribute(\"lvt-disable-with\");\n this.logger.debug(\"Disabled submit button\");\n }\n\n this.context.setActiveSubmission(\n targetElement,\n submitButton || null,\n originalButtonText\n );\n\n // Trigger pending uploads for any file inputs in the form\n const fileInputs = targetElement.querySelectorAll<HTMLInputElement>(\n 'input[type=\"file\"][lvt-upload]'\n );\n fileInputs.forEach((input) => {\n const uploadName = input.getAttribute(\"lvt-upload\");\n if (uploadName) {\n this.logger.debug(\"Triggering pending uploads for:\", uploadName);\n this.context.triggerPendingUploads(uploadName);\n }\n });\n\n targetElement.dispatchEvent(\n new CustomEvent(\"lvt:pending\", { detail: message })\n );\n this.logger.debug(\"Emitted lvt:pending event\");\n }\n\n this.logger.debug(\"About to send message:\", message);\n this.logger.debug(\n \"WebSocket state:\",\n this.context.getWebSocketReadyState()\n );\n\n this.context.send(message);\n this.logger.debug(\"send() called\");\n };\n\n const throttleValue = actionElement.getAttribute(\"lvt-throttle\");\n const debounceValue = actionElement.getAttribute(\"lvt-debounce\");\n\n if (throttleValue || debounceValue) {\n if (!rateLimitedHandlers.has(actionElement)) {\n rateLimitedHandlers.set(actionElement, new Map());\n }\n const handlerCache = rateLimitedHandlers.get(actionElement)!;\n const cacheKey = `${eventType}:${action}`;\n\n let rateLimitedHandler = handlerCache.get(cacheKey);\n if (!rateLimitedHandler) {\n if (throttleValue) {\n const limit = parseInt(throttleValue, 10);\n rateLimitedHandler = throttle(handleAction, limit);\n } else if (debounceValue) {\n const wait = parseInt(debounceValue, 10);\n rateLimitedHandler = debounce(handleAction, wait);\n }\n if (rateLimitedHandler) {\n handlerCache.set(cacheKey, rateLimitedHandler);\n }\n }\n\n if (rateLimitedHandler) {\n rateLimitedHandler();\n }\n } else {\n if (eventType === \"submit\") {\n (window as any).__lvtBeforeHandleAction = true;\n }\n handleAction();\n if (eventType === \"submit\") {\n (window as any).__lvtAfterHandleAction = true;\n }\n }\n\n return;\n }\n element = element.parentElement;\n }\n };\n\n (document as any)[listenerKey] = listener;\n document.addEventListener(eventType, listener, false);\n this.logger.debug(\n \"Registered event listener:\",\n eventType,\n \"with key:\",\n listenerKey\n );\n });\n }\n\n setupWindowEventDelegation(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n const windowEvents = [\n \"keydown\",\n \"keyup\",\n \"scroll\",\n \"resize\",\n \"focus\",\n \"blur\",\n ];\n const wrapperId = wrapperElement.getAttribute(\"data-lvt-id\");\n const rateLimitedHandlers = this.context.getRateLimitedHandlers();\n\n windowEvents.forEach((eventType) => {\n const listenerKey = `__lvt_window_${eventType}_${wrapperId}`;\n const existingListener = (window as any)[listenerKey];\n if (existingListener) {\n window.removeEventListener(eventType, existingListener);\n }\n\n const listener = (e: Event) => {\n const currentWrapper = this.context.getWrapperElement();\n if (!currentWrapper) return;\n\n const attrName = `lvt-window-${eventType}`;\n const elements = currentWrapper.querySelectorAll(`[${attrName}]`);\n\n elements.forEach((element) => {\n const action = element.getAttribute(attrName);\n if (!action) return;\n\n if (\n (eventType === \"keydown\" || eventType === \"keyup\") &&\n element.hasAttribute(\"lvt-key\")\n ) {\n const keyFilter = element.getAttribute(\"lvt-key\");\n const keyboardEvent = e as KeyboardEvent;\n if (keyFilter && keyboardEvent.key !== keyFilter) {\n return;\n }\n }\n\n const message: any = { action, data: {} };\n\n Array.from(element.attributes).forEach((attr) => {\n if (attr.name.startsWith(\"lvt-data-\")) {\n const key = attr.name.replace(\"lvt-data-\", \"\");\n message.data[key] = this.context.parseValue(attr.value);\n }\n });\n\n Array.from(element.attributes).forEach((attr) => {\n if (attr.name.startsWith(\"lvt-value-\")) {\n const key = attr.name.replace(\"lvt-value-\", \"\");\n message.data[key] = this.context.parseValue(attr.value);\n }\n });\n\n const throttleValue = element.getAttribute(\"lvt-throttle\");\n const debounceValue = element.getAttribute(\"lvt-debounce\");\n\n const handleAction = () => this.context.send(message);\n\n if (throttleValue || debounceValue) {\n if (!rateLimitedHandlers.has(element)) {\n rateLimitedHandlers.set(element, new Map());\n }\n const handlerCache = rateLimitedHandlers.get(element)!;\n const cacheKey = `window-${eventType}:${action}`;\n\n let rateLimitedHandler = handlerCache.get(cacheKey);\n if (!rateLimitedHandler) {\n if (throttleValue) {\n const limit = parseInt(throttleValue, 10);\n rateLimitedHandler = throttle(handleAction, limit);\n } else if (debounceValue) {\n const wait = parseInt(debounceValue, 10);\n rateLimitedHandler = debounce(handleAction, wait);\n }\n if (rateLimitedHandler) {\n handlerCache.set(cacheKey, rateLimitedHandler);\n }\n }\n\n if (rateLimitedHandler) {\n rateLimitedHandler();\n }\n } else {\n handleAction();\n }\n });\n };\n\n (window as any)[listenerKey] = listener;\n window.addEventListener(eventType, listener);\n });\n }\n\n setupClickAwayDelegation(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n const wrapperId = wrapperElement.getAttribute(\"data-lvt-id\");\n const listenerKey = `__lvt_click_away_${wrapperId}`;\n const existingListener = (document as any)[listenerKey];\n if (existingListener) {\n document.removeEventListener(\"click\", existingListener);\n }\n\n const listener = (e: Event) => {\n const currentWrapper = this.context.getWrapperElement();\n if (!currentWrapper) return;\n\n const target = e.target as Element;\n const elements = currentWrapper.querySelectorAll(\"[lvt-click-away]\");\n\n elements.forEach((element) => {\n if (!element.contains(target)) {\n const action = element.getAttribute(\"lvt-click-away\");\n if (!action) return;\n\n const message: any = { action, data: {} };\n\n Array.from(element.attributes).forEach((attr) => {\n if (attr.name.startsWith(\"lvt-data-\")) {\n const key = attr.name.replace(\"lvt-data-\", \"\");\n message.data[key] = this.context.parseValue(attr.value);\n }\n });\n\n Array.from(element.attributes).forEach((attr) => {\n if (attr.name.startsWith(\"lvt-value-\")) {\n const key = attr.name.replace(\"lvt-value-\", \"\");\n message.data[key] = this.context.parseValue(attr.value);\n }\n });\n\n this.context.send(message);\n }\n });\n };\n\n (document as any)[listenerKey] = listener;\n document.addEventListener(\"click\", listener);\n }\n\n setupModalDelegation(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n const wrapperId = wrapperElement.getAttribute(\"data-lvt-id\");\n\n const openListenerKey = `__lvt_modal_open_${wrapperId}`;\n const existingOpenListener = (document as any)[openListenerKey];\n if (existingOpenListener) {\n document.removeEventListener(\"click\", existingOpenListener);\n }\n\n const openListener = (e: Event) => {\n const currentWrapper = this.context.getWrapperElement();\n if (!currentWrapper) return;\n\n const target = (e.target as Element)?.closest(\"[lvt-modal-open]\");\n if (!target || !currentWrapper.contains(target)) return;\n\n const modalId = target.getAttribute(\"lvt-modal-open\");\n if (!modalId) return;\n\n e.preventDefault();\n this.context.openModal(modalId);\n };\n\n (document as any)[openListenerKey] = openListener;\n document.addEventListener(\"click\", openListener);\n\n const closeListenerKey = `__lvt_modal_close_${wrapperId}`;\n const existingCloseListener = (document as any)[closeListenerKey];\n if (existingCloseListener) {\n document.removeEventListener(\"click\", existingCloseListener);\n }\n\n // Close listener is intentionally NOT scoped to wrapper (unlike openListener).\n // Close buttons may be inside modals rendered in portals outside the wrapper.\n // Instead, we verify the target modal exists by ID.\n const closeListener = (e: Event) => {\n const target = (e.target as Element)?.closest(\"[lvt-modal-close]\");\n if (!target) return;\n\n const modalId = target.getAttribute(\"lvt-modal-close\");\n if (!modalId) return;\n\n // Verify the modal exists before attempting to close\n const modal = document.getElementById(modalId);\n if (!modal) return;\n\n e.preventDefault();\n this.context.closeModal(modalId);\n };\n\n (document as any)[closeListenerKey] = closeListener;\n document.addEventListener(\"click\", closeListener);\n\n const backdropListenerKey = `__lvt_modal_backdrop_${wrapperId}`;\n const existingBackdropListener = (document as any)[backdropListenerKey];\n if (existingBackdropListener) {\n document.removeEventListener(\"click\", existingBackdropListener);\n }\n\n const backdropListener = (e: Event) => {\n const target = e.target as Element;\n if (!target.hasAttribute(\"data-modal-backdrop\")) return;\n\n const modalId = target.getAttribute(\"data-modal-id\");\n if (modalId) {\n this.context.closeModal(modalId);\n }\n };\n\n (document as any)[backdropListenerKey] = backdropListener;\n document.addEventListener(\"click\", backdropListener);\n\n const escapeListenerKey = `__lvt_modal_escape_${wrapperId}`;\n const existingEscapeListener = (document as any)[escapeListenerKey];\n if (existingEscapeListener) {\n document.removeEventListener(\"keydown\", existingEscapeListener);\n }\n\n const escapeListener = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") return;\n const currentWrapper = this.context.getWrapperElement();\n if (!currentWrapper) return;\n\n const openModals = currentWrapper.querySelectorAll(\n '[role=\"dialog\"]:not([hidden])'\n );\n if (openModals.length > 0) {\n const lastModal = openModals[openModals.length - 1];\n if (lastModal.id) {\n this.context.closeModal(lastModal.id);\n }\n }\n };\n\n (document as any)[escapeListenerKey] = escapeListener;\n document.addEventListener(\"keydown\", escapeListener);\n }\n\n /**\n * Sets up focus trapping for elements with lvt-focus-trap attribute.\n * Focus is trapped within the element, cycling through focusable elements\n * when Tab/Shift+Tab is pressed.\n */\n setupFocusTrapDelegation(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n const wrapperId = wrapperElement.getAttribute(\"data-lvt-id\");\n const listenerKey = `__lvt_focus_trap_${wrapperId}`;\n const existingListener = (document as any)[listenerKey];\n if (existingListener) {\n document.removeEventListener(\"keydown\", existingListener);\n }\n\n const getFocusableElements = (container: Element): HTMLElement[] => {\n const selector = [\n 'a[href]:not([disabled])',\n 'button:not([disabled])',\n 'textarea:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"]):not([disabled])',\n '[contenteditable=\"true\"]'\n ].join(', ');\n\n return Array.from(container.querySelectorAll(selector)).filter(\n (el) => {\n const htmlEl = el as HTMLElement;\n // Check if element is visible\n const style = window.getComputedStyle(htmlEl);\n const isNotDisplayNone = style.display !== 'none';\n const isNotVisibilityHidden = style.visibility !== 'hidden';\n // offsetParent can be null in JSDOM or for fixed/absolute positioned elements\n const hasLayoutContext = htmlEl.offsetParent !== null ||\n style.position === 'fixed' ||\n style.position === 'absolute' ||\n // In test environments, offsetParent may always be null\n (typeof process !== 'undefined' && (process as any).env?.NODE_ENV === 'test');\n return isNotDisplayNone && isNotVisibilityHidden && hasLayoutContext;\n }\n ) as HTMLElement[];\n };\n\n const listener = (e: KeyboardEvent) => {\n if (e.key !== \"Tab\") return;\n\n const currentWrapper = this.context.getWrapperElement();\n if (!currentWrapper) return;\n\n // Find the active focus trap container (innermost one containing the focused element)\n const focusTrapElements = currentWrapper.querySelectorAll(\"[lvt-focus-trap]\");\n let activeTrap: Element | null = null;\n\n focusTrapElements.forEach((trap) => {\n if (trap.contains(document.activeElement)) {\n // Check if this is the innermost trap containing the focused element\n if (!activeTrap || trap.contains(activeTrap)) {\n activeTrap = trap;\n }\n }\n });\n\n // If there's a visible focus trap that doesn't contain the active element,\n // and is visible, trap focus there (for newly opened modals/dropdowns)\n if (!activeTrap) {\n focusTrapElements.forEach((trap) => {\n const htmlTrap = trap as HTMLElement;\n const style = window.getComputedStyle(htmlTrap);\n if (style.display !== 'none' && style.visibility !== 'hidden') {\n activeTrap = trap;\n }\n });\n }\n\n if (!activeTrap) return;\n\n const focusableElements = getFocusableElements(activeTrap);\n if (focusableElements.length === 0) return;\n\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (e.shiftKey) {\n // Shift+Tab: moving backwards\n if (document.activeElement === firstElement) {\n e.preventDefault();\n lastElement.focus();\n }\n } else {\n // Tab: moving forwards\n if (document.activeElement === lastElement) {\n e.preventDefault();\n firstElement.focus();\n }\n }\n };\n\n (document as any)[listenerKey] = listener;\n document.addEventListener(\"keydown\", listener);\n this.logger.debug(\"Focus trap delegation set up\");\n }\n\n /**\n * Sets up autofocus for elements with lvt-autofocus attribute.\n * Automatically focuses the first element with lvt-autofocus when it becomes visible.\n * Uses MutationObserver to detect when elements are added or become visible.\n */\n setupAutofocusDelegation(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n const wrapperId = wrapperElement.getAttribute(\"data-lvt-id\");\n const observerKey = `__lvt_autofocus_observer_${wrapperId}`;\n\n // Disconnect existing observer if any\n const existingObserver = (wrapperElement as any)[observerKey];\n if (existingObserver) {\n existingObserver.disconnect();\n }\n\n const processAutofocus = () => {\n const currentWrapper = this.context.getWrapperElement();\n if (!currentWrapper) return;\n\n // Find all elements with lvt-autofocus that are visible\n const autofocusElements = currentWrapper.querySelectorAll(\"[lvt-autofocus]\");\n\n autofocusElements.forEach((element) => {\n const htmlElement = element as HTMLElement;\n const style = window.getComputedStyle(htmlElement);\n\n // Check if element is visible and hasn't been focused yet in this visibility state\n // Note: offsetParent can be null in JSDOM or for fixed/absolute positioned elements,\n // so we only use it as a secondary check when it's available\n const isNotDisplayNone = style.display !== 'none';\n const isNotVisibilityHidden = style.visibility !== 'hidden';\n const hasLayoutContext = htmlElement.offsetParent !== null ||\n style.position === 'fixed' ||\n style.position === 'absolute' ||\n htmlElement.tagName === 'BODY' ||\n // In test environments, offsetParent may always be null\n (typeof process !== 'undefined' && (process as any).env?.NODE_ENV === 'test');\n const isVisible = isNotDisplayNone && isNotVisibilityHidden && hasLayoutContext;\n\n const wasFocused = htmlElement.getAttribute(\"data-lvt-autofocused\") === \"true\";\n\n if (isVisible && !wasFocused) {\n // Mark as focused to prevent re-focusing on every mutation\n htmlElement.setAttribute(\"data-lvt-autofocused\", \"true\");\n\n // Use requestAnimationFrame to ensure DOM is ready\n requestAnimationFrame(() => {\n htmlElement.focus();\n this.logger.debug(\"Autofocused element:\", htmlElement.tagName, htmlElement.id || htmlElement.getAttribute(\"name\"));\n });\n } else if (!isVisible && wasFocused) {\n // Reset the flag when element becomes hidden so it can be refocused when shown again\n htmlElement.removeAttribute(\"data-lvt-autofocused\");\n }\n });\n };\n\n // Process autofocus immediately for any existing elements\n processAutofocus();\n\n // Set up MutationObserver to watch for new autofocus elements or visibility changes\n const observer = new MutationObserver((mutations) => {\n let shouldProcess = false;\n\n mutations.forEach((mutation) => {\n // Check for added nodes\n if (mutation.type === \"childList\" && mutation.addedNodes.length > 0) {\n mutation.addedNodes.forEach((node) => {\n if (node instanceof Element) {\n if (node.hasAttribute(\"lvt-autofocus\") || node.querySelector(\"[lvt-autofocus]\")) {\n shouldProcess = true;\n }\n }\n });\n }\n\n // Check for attribute changes that might affect visibility\n // We intentionally check \"class\" changes to handle CSS-based visibility\n // (e.g., Tailwind's \"hidden\" class, Bootstrap's \"d-none\", etc.)\n // This may cause some extra processing but ensures visibility changes\n // via class toggles are detected.\n if (mutation.type === \"attributes\") {\n const target = mutation.target as Element;\n if (target.hasAttribute(\"lvt-autofocus\") ||\n mutation.attributeName === \"hidden\" ||\n mutation.attributeName === \"style\" ||\n mutation.attributeName === \"class\") {\n shouldProcess = true;\n }\n }\n });\n\n if (shouldProcess) {\n processAutofocus();\n }\n });\n\n observer.observe(wrapperElement, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"hidden\", \"style\", \"class\", \"lvt-autofocus\"]\n });\n\n (wrapperElement as any)[observerKey] = observer;\n this.logger.debug(\"Autofocus delegation set up\");\n }\n}\n", "import type { Logger } from \"../utils/logger\";\n\nexport interface ObserverContext {\n getWrapperElement(): Element | null;\n send(message: any): void;\n}\n\n/**\n * Manages LiveTemplate observers such as infinite scroll and DOM mutations.\n */\nexport class ObserverManager {\n private infiniteScrollObserver: IntersectionObserver | null = null;\n private mutationObserver: MutationObserver | null = null;\n\n constructor(\n private readonly context: ObserverContext,\n private readonly logger: Logger\n ) {}\n\n setupInfiniteScrollObserver(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n const sentinel = document.getElementById(\"scroll-sentinel\");\n if (!sentinel) {\n return;\n }\n\n if (this.infiniteScrollObserver) {\n this.infiniteScrollObserver.disconnect();\n }\n\n this.infiniteScrollObserver = new IntersectionObserver(\n (entries) => {\n if (entries[0].isIntersecting) {\n this.logger.debug(\"Sentinel visible, sending load_more action\");\n this.context.send({ action: \"load_more\" });\n }\n },\n {\n rootMargin: \"200px\",\n }\n );\n\n this.infiniteScrollObserver.observe(sentinel);\n this.logger.debug(\"Observer set up successfully\");\n }\n\n setupInfiniteScrollMutationObserver(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n if (this.mutationObserver) {\n this.mutationObserver.disconnect();\n }\n\n this.mutationObserver = new MutationObserver(() => {\n this.setupInfiniteScrollObserver();\n });\n\n this.mutationObserver.observe(wrapperElement, {\n childList: true,\n subtree: true,\n });\n\n this.logger.debug(\"MutationObserver set up successfully\");\n }\n\n teardown(): void {\n if (this.infiniteScrollObserver) {\n this.infiniteScrollObserver.disconnect();\n this.infiniteScrollObserver = null;\n }\n if (this.mutationObserver) {\n this.mutationObserver.disconnect();\n this.mutationObserver = null;\n }\n }\n}\n", "import type { Logger } from \"../utils/logger\";\n\n/**\n * Manages client-side modal interactions for LiveTemplate.\n */\nexport class ModalManager {\n constructor(private readonly logger: Logger) {}\n\n open(modalId: string): void {\n const modal = document.getElementById(modalId);\n if (!modal) {\n this.logger.warn(`Modal with id=\"${modalId}\" not found`);\n return;\n }\n\n modal.removeAttribute(\"hidden\");\n modal.style.display = \"flex\";\n modal.setAttribute(\"aria-hidden\", \"false\");\n modal.dispatchEvent(new CustomEvent(\"lvt:modal-opened\", { bubbles: true }));\n\n this.logger.info(`Opened modal: ${modalId}`);\n\n const firstInput = modal.querySelector(\n \"input, textarea, select\"\n ) as HTMLElement | null;\n if (firstInput) {\n setTimeout(() => {\n const activeElement = document.activeElement as HTMLElement | null;\n const isVisible = (element: HTMLElement | null): boolean => {\n if (!element) {\n return false;\n }\n\n if (element === document.body) {\n return true;\n }\n\n // Use offsetParent and bounding rects to determine visibility without hitting layout too hard.\n if (element.offsetParent !== null) {\n return true;\n }\n\n return element.getClientRects().length > 0;\n };\n\n const shouldMoveFocus =\n !activeElement ||\n !modal.contains(activeElement) ||\n !isVisible(activeElement);\n\n if (shouldMoveFocus) {\n firstInput.focus();\n }\n }, 100);\n }\n }\n\n close(modalId: string): void {\n const modal = document.getElementById(modalId);\n if (!modal) {\n this.logger.warn(`Modal with id=\"${modalId}\" not found`);\n return;\n }\n\n modal.setAttribute(\"hidden\", \"\");\n modal.style.display = \"none\";\n modal.setAttribute(\"aria-hidden\", \"true\");\n modal.dispatchEvent(new CustomEvent(\"lvt:modal-closed\", { bubbles: true }));\n\n this.logger.info(`Closed modal: ${modalId}`);\n }\n}\n", "/**\n * Handles showing and hiding the global LiveTemplate loading indicator.\n */\nexport class LoadingIndicator {\n private bar: HTMLElement | null = null;\n\n show(): void {\n if (this.bar) return;\n\n const bar = document.createElement(\"div\");\n bar.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: linear-gradient(90deg, #3b82f6 0%, #60a5fa 50%, #3b82f6 100%);\n background-size: 200% 100%;\n z-index: 9999;\n animation: lvt-loading-shimmer 1.5s ease-in-out infinite;\n `;\n\n if (!document.getElementById(\"lvt-loading-styles\")) {\n const style = document.createElement(\"style\");\n style.id = \"lvt-loading-styles\";\n style.textContent = `\n @keyframes lvt-loading-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n `;\n document.head.appendChild(style);\n }\n\n document.body.insertBefore(bar, document.body.firstChild);\n this.bar = bar;\n }\n\n hide(): void {\n if (!this.bar) return;\n\n if (this.bar.parentNode) {\n this.bar.parentNode.removeChild(this.bar);\n }\n this.bar = null;\n }\n}\n", "/**\n * Enables and disables all form controls inside the LiveTemplate wrapper.\n */\nexport class FormDisabler {\n disable(wrapper: Element | null): void {\n if (!wrapper) return;\n\n const forms = wrapper.querySelectorAll(\"form\");\n forms.forEach((form) => {\n const inputs = form.querySelectorAll(\"input, textarea, select, button\");\n inputs.forEach((input) => {\n (input as HTMLInputElement).disabled = true;\n });\n });\n }\n\n enable(wrapper: Element | null): void {\n if (!wrapper) return;\n\n const forms = wrapper.querySelectorAll(\"form\");\n forms.forEach((form) => {\n const inputs = form.querySelectorAll(\"input, textarea, select, button\");\n inputs.forEach((input) => {\n (input as HTMLInputElement).disabled = false;\n });\n });\n }\n}\n", "/**\n * Reactive Attributes - Declarative DOM actions triggered by LiveTemplate lifecycle events.\n *\n * Attribute Pattern: lvt-{action}-on:{event}=\"param\"\n *\n * Events:\n * - pending: Action started, waiting for server response\n * - success: Action completed successfully\n * - error: Action completed with validation errors\n * - done: Action completed (regardless of success/error)\n *\n * Event Scope:\n * - Global: lvt-reset-on:success (any action)\n * - Action-specific: lvt-reset-on:create-todo:success (specific action only)\n *\n * Actions:\n * - reset: Calls form.reset()\n * - disable: Sets element.disabled = true\n * - enable: Sets element.disabled = false\n * - addClass: Adds CSS class(es)\n * - removeClass: Removes CSS class(es)\n * - toggleClass: Toggles CSS class(es)\n * - setAttr: Sets an attribute (name:value format)\n * - toggleAttr: Toggles a boolean attribute\n */\n\nexport type ReactiveAction =\n | \"reset\"\n | \"disable\"\n | \"enable\"\n | \"addClass\"\n | \"removeClass\"\n | \"toggleClass\"\n | \"setAttr\"\n | \"toggleAttr\";\n\nexport type LifecycleEvent = \"pending\" | \"success\" | \"error\" | \"done\";\n\nexport interface ReactiveBinding {\n action: ReactiveAction;\n lifecycle: LifecycleEvent;\n actionName?: string;\n param?: string;\n}\n\nconst LIFECYCLE_EVENTS: LifecycleEvent[] = [\"pending\", \"success\", \"error\", \"done\"];\n\nconst REACTIVE_ACTIONS: ReactiveAction[] = [\n \"reset\",\n \"disable\",\n \"enable\",\n \"addClass\",\n \"removeClass\",\n \"toggleClass\",\n \"setAttr\",\n \"toggleAttr\",\n];\n\n// Lowercase versions for case-insensitive matching (HTML attributes are lowercased)\nconst ACTION_MAP: Record<string, ReactiveAction> = {\n reset: \"reset\",\n disable: \"disable\",\n enable: \"enable\",\n addclass: \"addClass\",\n removeclass: \"removeClass\",\n toggleclass: \"toggleClass\",\n setattr: \"setAttr\",\n toggleattr: \"toggleAttr\",\n};\n\n/**\n * Parse a reactive attribute name and value into a binding.\n *\n * Examples:\n * parseReactiveAttribute(\"lvt-reset-on:success\", \"\") => { action: \"reset\", lifecycle: \"success\" }\n * parseReactiveAttribute(\"lvt-addClass-on:pending\", \"loading\") => { action: \"addClass\", lifecycle: \"pending\", param: \"loading\" }\n * parseReactiveAttribute(\"lvt-reset-on:create-todo:success\", \"\") => { action: \"reset\", lifecycle: \"success\", actionName: \"create-todo\" }\n */\nexport function parseReactiveAttribute(\n attrName: string,\n attrValue: string\n): ReactiveBinding | null {\n // Pattern: lvt-{action}-on:{actionName?}:{lifecycle}\n // The lifecycle must be at the end, action name is optional in the middle\n // Note: HTML attributes are lowercased by browsers, so we match case-insensitively\n const match = attrName.toLowerCase().match(/^lvt-(\\w+)-on:(.+)$/);\n if (!match) return null;\n\n const actionKey = match[1];\n const action = ACTION_MAP[actionKey];\n if (!action) return null;\n\n const eventPart = match[2];\n\n // Check if the last segment is a lifecycle event\n // Format: \"{actionName}:{lifecycle}\" or just \"{lifecycle}\"\n const segments = eventPart.split(\":\");\n const lastSegment = segments[segments.length - 1] as LifecycleEvent;\n\n if (!LIFECYCLE_EVENTS.includes(lastSegment)) return null;\n\n const lifecycle = lastSegment;\n const actionName = segments.length > 1 ? segments.slice(0, -1).join(\":\") : undefined;\n\n return {\n action,\n lifecycle,\n actionName: actionName || undefined,\n param: attrValue || undefined,\n };\n}\n\n/**\n * Execute a reactive action on an element.\n */\nexport function executeAction(\n element: Element,\n action: ReactiveAction,\n param?: string\n): void {\n switch (action) {\n case \"reset\":\n if (element instanceof HTMLFormElement) {\n element.reset();\n }\n break;\n\n case \"disable\":\n if (\"disabled\" in element) {\n (element as HTMLButtonElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement).disabled = true;\n }\n break;\n\n case \"enable\":\n if (\"disabled\" in element) {\n (element as HTMLButtonElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement).disabled = false;\n }\n break;\n\n case \"addClass\":\n if (param) {\n const classes = param.split(/\\s+/).filter(Boolean);\n element.classList.add(...classes);\n }\n break;\n\n case \"removeClass\":\n if (param) {\n const classes = param.split(/\\s+/).filter(Boolean);\n element.classList.remove(...classes);\n }\n break;\n\n case \"toggleClass\":\n if (param) {\n const classes = param.split(/\\s+/).filter(Boolean);\n classes.forEach((c) => element.classList.toggle(c));\n }\n break;\n\n case \"setAttr\":\n if (param) {\n const colonIndex = param.indexOf(\":\");\n if (colonIndex > 0) {\n const name = param.substring(0, colonIndex);\n const value = param.substring(colonIndex + 1);\n element.setAttribute(name, value);\n }\n }\n break;\n\n case \"toggleAttr\":\n if (param) {\n element.toggleAttribute(param);\n }\n break;\n }\n}\n\n/**\n * Check if an event matches a binding.\n *\n * @param binding The reactive binding to check\n * @param lifecycle The lifecycle event that fired\n * @param actionName The action name that triggered the event (optional)\n */\nexport function matchesEvent(\n binding: ReactiveBinding,\n lifecycle: LifecycleEvent,\n actionName?: string\n): boolean {\n // Lifecycle must match\n if (binding.lifecycle !== lifecycle) return false;\n\n // If binding has no actionName, it's global (matches any action)\n if (!binding.actionName) return true;\n\n // If binding has actionName, it must match the fired action\n return binding.actionName === actionName;\n}\n\n/**\n * Process all reactive attributes for a lifecycle event.\n *\n * Instead of building complex selectors, we iterate all elements with any lvt-*-on:* attribute\n * and check each one against the fired event.\n */\nexport function processReactiveAttributes(\n lifecycle: LifecycleEvent,\n actionName?: string\n): void {\n // Find all elements that might have reactive attributes\n // This is a broad selector but avoids escaping issues with attribute names containing colons\n const allElements = document.querySelectorAll(\"*\");\n\n allElements.forEach((element) => {\n // Check all attributes on this element for reactive bindings\n Array.from(element.attributes).forEach((attr) => {\n // Quick filter: only process lvt-*-on: attributes\n if (!attr.name.startsWith(\"lvt-\") || !attr.name.includes(\"-on:\")) {\n return;\n }\n\n const binding = parseReactiveAttribute(attr.name, attr.value);\n if (binding && matchesEvent(binding, lifecycle, actionName)) {\n executeAction(element, binding.action, binding.param);\n }\n });\n });\n}\n\n/**\n * Set up document-level event listeners for reactive attributes.\n * This should be called once during client initialization.\n */\nexport function setupReactiveAttributeListeners(): void {\n LIFECYCLE_EVENTS.forEach((lifecycle) => {\n // Listen in capture phase to process before event bubbles\n document.addEventListener(\n `lvt:${lifecycle}`,\n (e: Event) => {\n const customEvent = e as CustomEvent;\n const actionName = customEvent.detail?.action;\n processReactiveAttributes(lifecycle, actionName);\n },\n true // capture phase\n );\n });\n}\n", "import type { TreeNode, UpdateResult } from \"../types\";\nimport type { Logger } from \"../utils/logger\";\n\ninterface RangeStateEntry {\n items: any[];\n statics: any[];\n staticsMap?: Record<string, string[]>;\n}\n\n/**\n * Deep clone an object. Uses structuredClone if available (Node 17+, modern browsers),\n * falls back to JSON.parse/stringify for older environments.\n */\nfunction deepClone<T>(obj: T): T {\n if (typeof structuredClone === \"function\") {\n return structuredClone(obj);\n }\n return JSON.parse(JSON.stringify(obj));\n}\n\n/**\n * Handles tree state management and HTML reconstruction logic for LiveTemplate.\n */\nexport class TreeRenderer {\n private treeState: TreeNode = {};\n private rangeState: Record<string, RangeStateEntry> = {};\n private rangeIdKeys: Record<string, string> = {};\n\n constructor(private readonly logger: Logger) {}\n\n applyUpdate(update: TreeNode): UpdateResult {\n let changed = false;\n\n for (const [key, value] of Object.entries(update)) {\n const isDifferentialOps =\n Array.isArray(value) &&\n value.length > 0 &&\n Array.isArray(value[0]) &&\n typeof value[0][0] === \"string\";\n\n if (isDifferentialOps) {\n // Check if there's an existing range structure to apply operations to\n const existing = this.treeState[key];\n const existingIsRange =\n existing &&\n typeof existing === \"object\" &&\n !Array.isArray(existing) &&\n Array.isArray(existing.d) &&\n Array.isArray(existing.s);\n\n if (existingIsRange) {\n // Apply differential operations to existing range structure\n this.treeState[key] = deepClone(existing);\n this.applyDifferentialOpsToRange(this.treeState[key], value, key);\n } else {\n // No existing range, store operations directly (will use rangeState later)\n this.treeState[key] = value;\n }\n changed = true;\n } else {\n const oldValue = this.treeState[key];\n const newValue =\n typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? this.deepMergeTreeNodes(oldValue, value, key)\n : value;\n\n if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) {\n this.treeState[key] = newValue;\n changed = true;\n }\n }\n }\n\n const html = this.reconstructFromTree(this.treeState, \"\");\n return { html, changed };\n }\n\n reset(): void {\n this.treeState = {};\n this.rangeState = {};\n this.rangeIdKeys = {};\n }\n\n getTreeState(): TreeNode {\n return { ...this.treeState };\n }\n\n getStaticStructure(): string[] | null {\n return this.treeState.s || null;\n }\n\n private deepMergeTreeNodes(\n existing: any,\n update: any,\n currentPath: string = \"\"\n ): any {\n if (\n typeof update !== \"object\" ||\n update === null ||\n Array.isArray(update)\n ) {\n return update;\n }\n\n if (\n typeof existing !== \"object\" ||\n existing === null ||\n Array.isArray(existing)\n ) {\n return update;\n }\n\n const merged: any = { ...existing };\n\n for (const [key, value] of Object.entries(update)) {\n const fieldPath = currentPath ? `${currentPath}.${key}` : key;\n\n // Check if value is a differential operations array\n const isDifferentialOps =\n Array.isArray(value) &&\n value.length > 0 &&\n Array.isArray(value[0]) &&\n typeof value[0][0] === \"string\";\n\n // Check if existing value is a range structure\n const existingIsRange =\n merged[key] &&\n typeof merged[key] === \"object\" &&\n !Array.isArray(merged[key]) &&\n Array.isArray(merged[key].d) &&\n Array.isArray(merged[key].s);\n\n if (isDifferentialOps && existingIsRange) {\n // Deep clone the range structure before modifying to avoid mutating the original\n // (shallow copy {...existing} keeps shared references to nested objects)\n merged[key] = deepClone(merged[key]);\n // Apply differential operations to the cloned range\n this.logger.debug(\n `[deepMerge] Applying diff ops at path ${fieldPath}`,\n { ops: value, rangeItems: merged[key].d?.length }\n );\n this.applyDifferentialOpsToRange(merged[key], value, fieldPath);\n } else if (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n typeof merged[key] === \"object\" &&\n merged[key] !== null &&\n !Array.isArray(merged[key])\n ) {\n merged[key] = this.deepMergeTreeNodes(merged[key], value, fieldPath);\n } else {\n merged[key] = value;\n }\n }\n\n return merged;\n }\n\n /**\n * Applies differential operations to the provided range structure in-place.\n * The caller is responsible for passing the object to be mutated (typically a clone).\n * This is called when merging nested updates that contain range operations.\n */\n private applyDifferentialOpsToRange(\n rangeStructure: any,\n operations: any[],\n statePath: string\n ): void {\n // Validate rangeStructure before proceeding\n if (\n !rangeStructure ||\n typeof rangeStructure !== \"object\" ||\n !Array.isArray(rangeStructure.d) ||\n !Array.isArray(rangeStructure.s)\n ) {\n this.logger.error(\n `[applyDiffOpsToRange] Invalid rangeStructure at path ${statePath}`,\n { rangeStructure }\n );\n return;\n }\n\n const currentItems = rangeStructure.d;\n\n // Ensure rangeState is synchronized\n if (!this.rangeState[statePath]) {\n this.rangeState[statePath] = {\n items: currentItems,\n statics: rangeStructure.s,\n staticsMap: rangeStructure.sm,\n };\n }\n // Also check for idKey metadata\n if (\n rangeStructure.m &&\n typeof rangeStructure.m === \"object\" &&\n typeof rangeStructure.m.idKey === \"string\"\n ) {\n this.rangeIdKeys[statePath] = rangeStructure.m.idKey;\n }\n\n this.logger.debug(\n `[applyDiffOpsToRange] path=${statePath}, idKey=${this.rangeIdKeys[statePath]}, items=${currentItems.length}, ops=${operations.length}`\n );\n\n for (const operation of operations) {\n if (!Array.isArray(operation) || operation.length < 2) {\n continue;\n }\n\n const opType = operation[0];\n\n switch (opType) {\n case \"r\": {\n const key = operation[1];\n const removeIndex = this.findItemIndexByKey(\n currentItems,\n key,\n rangeStructure.s,\n statePath\n );\n this.logger.debug(\n `[applyDiffOpsToRange] Remove: key=${key}, index=${removeIndex}, total=${currentItems.length}`\n );\n if (removeIndex >= 0) {\n currentItems.splice(removeIndex, 1);\n this.logger.debug(`[applyDiffOpsToRange] After removal: ${currentItems.length} items`);\n } else {\n this.logger.debug(`[applyDiffOpsToRange] Remove failed: key ${key} not found`);\n }\n break;\n }\n case \"u\": {\n const updateIndex = this.findItemIndexByKey(\n currentItems,\n operation[1],\n rangeStructure.s,\n statePath\n );\n const changes = operation[2];\n if (updateIndex >= 0 && changes) {\n currentItems[updateIndex] = {\n ...currentItems[updateIndex],\n ...changes,\n };\n }\n break;\n }\n case \"a\": {\n const itemsToAdd = Array.isArray(operation[1])\n ? operation[1]\n : [operation[1]];\n if (operation[2]) {\n rangeStructure.s = operation[2];\n }\n currentItems.push(...itemsToAdd);\n if (\n operation[3] &&\n typeof operation[3] === \"object\" &&\n operation[3].idKey\n ) {\n this.rangeIdKeys[statePath] = operation[3].idKey;\n }\n break;\n }\n case \"p\": {\n const itemsToPrepend = Array.isArray(operation[1])\n ? operation[1]\n : [operation[1]];\n if (operation[2]) {\n rangeStructure.s = operation[2];\n }\n currentItems.unshift(...itemsToPrepend);\n break;\n }\n case \"i\": {\n const targetIndex = this.findItemIndexByKey(\n currentItems,\n operation[1],\n rangeStructure.s,\n statePath\n );\n if (targetIndex >= 0) {\n const itemsToInsert = Array.isArray(operation[2])\n ? operation[2]\n : [operation[2]];\n currentItems.splice(targetIndex + 1, 0, ...itemsToInsert);\n }\n break;\n }\n case \"o\": {\n const newOrder = operation[1] as string[];\n const reorderedItems: any[] = [];\n const itemsByKey = new Map<string, any>();\n\n for (const item of currentItems) {\n const itemKey = this.getItemKey(item, rangeStructure.s, statePath);\n if (itemKey) {\n itemsByKey.set(itemKey, item);\n }\n }\n\n for (const orderedKey of newOrder) {\n const item = itemsByKey.get(orderedKey);\n if (item) {\n reorderedItems.push(item);\n }\n }\n\n currentItems.length = 0;\n currentItems.push(...reorderedItems);\n break;\n }\n default:\n break;\n }\n }\n\n // Update rangeState to reflect the changes\n this.rangeState[statePath] = {\n items: currentItems,\n statics: rangeStructure.s,\n staticsMap: rangeStructure.sm,\n };\n }\n\n private reconstructFromTree(node: TreeNode, statePath: string): string {\n if (node.s && Array.isArray(node.s)) {\n let html = \"\";\n\n for (let i = 0; i < node.s.length; i++) {\n const staticSegment = node.s[i];\n html += staticSegment;\n\n if (i < node.s.length - 1) {\n const dynamicKey = i.toString();\n if (node[dynamicKey] !== undefined) {\n const newStatePath = statePath\n ? `${statePath}.${dynamicKey}`\n : dynamicKey;\n html += this.renderValue(\n node[dynamicKey],\n dynamicKey,\n newStatePath\n );\n }\n }\n }\n\n html = html.replace(/<root>/g, \"\").replace(/<\\/root>/g, \"\");\n return html;\n }\n\n return this.renderValue(node, \"\", statePath);\n }\n\n private renderValue(\n value: any,\n fieldKey?: string,\n statePath?: string\n ): string {\n if (value === null || value === undefined) {\n return \"\";\n }\n\n if (\n typeof value === \"string\" &&\n value.startsWith(\"{{\") &&\n value.endsWith(\"}}\")\n ) {\n return \"\";\n }\n\n if (typeof value === \"object\" && !Array.isArray(value)) {\n if (\n value.d &&\n Array.isArray(value.d) &&\n value.s &&\n Array.isArray(value.s)\n ) {\n const stateKey = statePath || fieldKey || \"\";\n if (stateKey) {\n this.rangeState[stateKey] = {\n items: value.d,\n statics: value.s,\n staticsMap: value.sm,\n };\n if (\n value.m &&\n typeof value.m === \"object\" &&\n typeof value.m.idKey === \"string\"\n ) {\n this.rangeIdKeys[stateKey] = value.m.idKey;\n }\n }\n return this.renderRangeStructure(value, fieldKey, statePath);\n }\n\n if (\"s\" in value && Array.isArray((value as TreeNode).s)) {\n return this.reconstructFromTree(value as TreeNode, statePath || \"\");\n }\n\n // Handle objects with only numeric keys (dynamics without statics)\n // This occurs when server sends partial updates for nested TreeNodes\n const keys = Object.keys(value);\n const numericKeys = keys.filter((k) => /^\\d+$/.test(k)).sort((a, b) => parseInt(a) - parseInt(b));\n if (numericKeys.length > 0 && numericKeys.length === keys.length) {\n // All keys are numeric - render each dynamic value in order\n return numericKeys\n .map((k) => {\n const itemStatePath = statePath ? `${statePath}.${k}` : k;\n return this.renderValue((value as Record<string, unknown>)[k], k, itemStatePath);\n })\n .join(\"\");\n }\n }\n\n if (Array.isArray(value)) {\n if (\n value.length > 0 &&\n Array.isArray(value[0]) &&\n typeof value[0][0] === \"string\"\n ) {\n return this.applyDifferentialOperations(value, statePath);\n }\n\n return value\n .map((item, idx) => {\n const itemKey = idx.toString();\n const itemStatePath = statePath ? `${statePath}.${itemKey}` : itemKey;\n if (typeof item === \"object\" && item && (item as TreeNode).s) {\n return this.reconstructFromTree(item as TreeNode, itemStatePath);\n }\n return this.renderValue(item, itemKey, itemStatePath);\n })\n .join(\"\");\n }\n\n if (typeof value === \"object\") {\n // Plain data objects (without tree structure) are state values that shouldn't be rendered.\n // This happens when state contains objects like EditingItem that are used by server-side\n // templates but aren't meant to be rendered directly in the DOM.\n // Skip them silently instead of converting to \"[object Object]\".\n this.logger.debug(\n \"Skipping plain object value (not a tree node) - this is normal for state-only data\"\n );\n return \"\";\n }\n\n return String(value);\n }\n\n private renderRangeStructure(\n rangeNode: any,\n fieldKey?: string,\n statePath?: string\n ): string {\n const { d: dynamics, s: statics, sm: staticsMap } = rangeNode;\n\n if (!dynamics || !Array.isArray(dynamics)) {\n return \"\";\n }\n\n if (dynamics.length === 0) {\n if (rangeNode[\"else\"]) {\n const elseKey = \"else\";\n const elseStatePath = statePath ? `${statePath}.else` : \"else\";\n return this.renderValue(rangeNode[\"else\"], elseKey, elseStatePath);\n }\n return \"\";\n }\n\n // Check if we have per-item statics via StaticsMap\n const hasStaticsMap = staticsMap && typeof staticsMap === \"object\";\n\n if (statics && Array.isArray(statics)) {\n return dynamics\n .map((item: any, itemIdx: number) => {\n // Get per-item statics from StaticsMap if available, otherwise use shared statics\n let itemStatics = statics;\n if (hasStaticsMap && item._sk && staticsMap[item._sk]) {\n itemStatics = staticsMap[item._sk];\n }\n\n let html = \"\";\n\n for (let i = 0; i < itemStatics.length; i++) {\n html += itemStatics[i];\n\n if (i < itemStatics.length - 1) {\n const localKey = i.toString();\n if (item[localKey] !== undefined) {\n const itemStatePath = statePath\n ? `${statePath}.${itemIdx}.${localKey}`\n : `${itemIdx}.${localKey}`;\n html += this.renderValue(\n item[localKey],\n localKey,\n itemStatePath\n );\n }\n }\n }\n\n return html;\n })\n .join(\"\");\n }\n\n return dynamics\n .map((item: any, idx: number) => {\n const itemKey = idx.toString();\n const itemStatePath = statePath ? `${statePath}.${itemKey}` : itemKey;\n return this.renderValue(item, itemKey, itemStatePath);\n })\n .join(\"\");\n }\n\n private applyDifferentialOperations(\n operations: any[],\n statePath?: string\n ): string {\n if (!statePath || !this.rangeState[statePath]) {\n return \"\";\n }\n\n const rangeData = this.rangeState[statePath];\n const currentItems = [...rangeData.items];\n const statics = rangeData.statics;\n\n for (const operation of operations) {\n if (!Array.isArray(operation) || operation.length < 2) {\n continue;\n }\n\n const opType = operation[0];\n\n switch (opType) {\n case \"r\": {\n const removeIndex = this.findItemIndexByKey(\n currentItems,\n operation[1],\n statics,\n statePath\n );\n if (removeIndex >= 0) {\n currentItems.splice(removeIndex, 1);\n }\n break;\n }\n case \"u\": {\n const updateIndex = this.findItemIndexByKey(\n currentItems,\n operation[1],\n statics,\n statePath\n );\n const changes = operation[2];\n if (updateIndex >= 0 && changes) {\n currentItems[updateIndex] = {\n ...currentItems[updateIndex],\n ...changes,\n };\n }\n break;\n }\n case \"a\": {\n this.addItemsToRange(\n currentItems,\n operation[1],\n operation[2],\n rangeData,\n false\n );\n if (\n operation[3] &&\n typeof operation[3] === \"object\" &&\n operation[3].idKey\n ) {\n this.rangeIdKeys[statePath || \"\"] = operation[3].idKey;\n }\n break;\n }\n case \"p\": {\n this.addItemsToRange(\n currentItems,\n operation[1],\n operation[2],\n rangeData,\n true\n );\n break;\n }\n case \"i\": {\n const targetIndex = this.findItemIndexByKey(\n currentItems,\n operation[1],\n statics,\n statePath\n );\n if (targetIndex >= 0) {\n const itemsToInsert = Array.isArray(operation[2])\n ? operation[2]\n : [operation[2]];\n currentItems.splice(targetIndex + 1, 0, ...itemsToInsert);\n }\n break;\n }\n case \"o\": {\n const newOrder = operation[1] as string[];\n const reorderedItems: any[] = [];\n const itemsByKey = new Map<string, any>();\n\n for (const item of currentItems) {\n const itemKey = this.getItemKey(item, statics, statePath);\n if (itemKey) {\n itemsByKey.set(itemKey, item);\n }\n }\n\n for (const orderedKey of newOrder) {\n const item = itemsByKey.get(orderedKey);\n if (item) {\n reorderedItems.push(item);\n }\n }\n\n currentItems.length = 0;\n currentItems.push(...reorderedItems);\n break;\n }\n default:\n break;\n }\n }\n\n this.rangeState[statePath] = {\n items: currentItems,\n statics: rangeData.statics,\n staticsMap: rangeData.staticsMap,\n };\n\n this.treeState[statePath] = {\n d: currentItems,\n s: rangeData.statics,\n sm: rangeData.staticsMap,\n };\n\n const rangeStructure = this.getCurrentRangeStructure(statePath);\n if (rangeStructure && rangeStructure.s) {\n return this.renderItemsWithStatics(\n currentItems,\n rangeStructure.s,\n rangeStructure.sm,\n statePath\n );\n }\n\n return currentItems.map((item) => this.renderValue(item)).join(\"\");\n }\n\n private getCurrentRangeStructure(stateKey: string): any {\n if (this.rangeState[stateKey]) {\n return {\n d: this.rangeState[stateKey].items,\n s: this.rangeState[stateKey].statics,\n sm: this.rangeState[stateKey].staticsMap,\n };\n }\n\n const fieldValue = this.treeState[stateKey];\n if (\n fieldValue &&\n typeof fieldValue === \"object\" &&\n (fieldValue as TreeNode).s\n ) {\n return fieldValue;\n }\n\n return null;\n }\n\n private renderItemsWithStatics(\n items: any[],\n statics: string[],\n staticsMap?: Record<string, string[]>,\n statePath?: string\n ): string {\n const result = items\n .map((item: any, itemIdx: number) => {\n // Get per-item statics from StaticsMap if available, otherwise use shared statics\n let itemStatics = statics;\n if (\n staticsMap &&\n typeof staticsMap === \"object\" &&\n item._sk &&\n staticsMap[item._sk]\n ) {\n itemStatics = staticsMap[item._sk];\n }\n\n let html = \"\";\n\n for (let i = 0; i < itemStatics.length; i++) {\n html += itemStatics[i];\n\n if (i < itemStatics.length - 1) {\n const fieldKey = i.toString();\n if (item[fieldKey] !== undefined) {\n const itemStatePath = statePath\n ? `${statePath}.${itemIdx}.${fieldKey}`\n : `${itemIdx}.${fieldKey}`;\n html += this.renderValue(item[fieldKey], fieldKey, itemStatePath);\n }\n }\n }\n\n return html;\n })\n .join(\"\");\n\n if (this.logger.isDebugEnabled()) {\n this.logger.debug(\"[renderItemsWithStatics] statics:\", statics);\n this.logger.debug(\"[renderItemsWithStatics] items count:\", items.length);\n this.logger.debug(\n \"[renderItemsWithStatics] result snippet:\",\n result.substring(0, 200)\n );\n }\n\n return result;\n }\n\n private addItemsToRange(\n currentItems: any[],\n items: any,\n statics: any[] | undefined,\n rangeData: RangeStateEntry,\n prepend: boolean\n ): void {\n if (statics) {\n rangeData.statics = statics;\n }\n\n if (!items) return;\n\n const itemsArray = Array.isArray(items) ? items : [items];\n if (prepend) {\n currentItems.unshift(...itemsArray);\n } else {\n currentItems.push(...itemsArray);\n }\n }\n\n private getItemKey(\n item: any,\n statics: any[],\n statePath?: string\n ): string | null {\n if (!statePath || !this.rangeIdKeys[statePath]) {\n return null;\n }\n\n const keyPosStr = this.rangeIdKeys[statePath];\n return item[keyPosStr] || null;\n }\n\n private findItemIndexByKey(\n items: any[],\n key: string,\n statics: any[],\n statePath?: string\n ): number {\n return items.findIndex(\n (item: any) => this.getItemKey(item, statics, statePath) === key\n );\n }\n}\n", "import type { ResponseMetadata } from \"../types\";\nimport { ModalManager } from \"../dom/modal-manager\";\n\n/**\n * Tracks form submission lifecycle for LiveTemplate actions.\n */\nexport class FormLifecycleManager {\n private activeForm: HTMLFormElement | null = null;\n private activeButton: HTMLButtonElement | null = null;\n private originalButtonText: string | null = null;\n\n constructor(private readonly modalManager: ModalManager) {}\n\n setActiveSubmission(\n form: HTMLFormElement | null,\n button: HTMLButtonElement | null,\n originalButtonText: string | null\n ): void {\n this.activeForm = form;\n this.activeButton = button;\n this.originalButtonText = originalButtonText;\n }\n\n handleResponse(meta: ResponseMetadata): void {\n if (this.activeForm) {\n this.activeForm.dispatchEvent(\n new CustomEvent(\"lvt:done\", { detail: meta })\n );\n }\n\n if (meta.success) {\n this.handleSuccess(meta);\n } else {\n this.handleError(meta);\n }\n\n this.restoreFormState();\n }\n\n reset(): void {\n this.restoreFormState();\n }\n\n private handleSuccess(meta: ResponseMetadata): void {\n if (!this.activeForm) {\n return;\n }\n\n this.activeForm.dispatchEvent(\n new CustomEvent(\"lvt:success\", { detail: meta })\n );\n\n const modalParent = this.activeForm.closest('[role=\"dialog\"]');\n if (modalParent && modalParent.id) {\n this.modalManager.close(modalParent.id);\n }\n\n if (!this.activeForm.hasAttribute(\"lvt-preserve\")) {\n this.activeForm.reset();\n }\n }\n\n private handleError(meta: ResponseMetadata): void {\n if (!this.activeForm) {\n return;\n }\n\n this.activeForm.dispatchEvent(\n new CustomEvent(\"lvt:error\", { detail: meta })\n );\n }\n\n private restoreFormState(): void {\n if (this.activeButton && this.originalButtonText !== null) {\n this.activeButton.disabled = false;\n this.activeButton.textContent = this.originalButtonText;\n }\n\n this.activeForm = null;\n this.activeButton = null;\n this.originalButtonText = null;\n }\n}\n", "import type { LiveTemplateClientOptions, UpdateResponse } from \"../types\";\nimport type { Logger } from \"../utils/logger\";\n\nexport interface WebSocketTransportOptions {\n url: string;\n autoReconnect?: boolean;\n reconnectDelay?: number;\n maxReconnectDelay?: number; // Maximum delay between reconnect attempts (default: 16000ms)\n maxReconnectAttempts?: number; // Maximum number of reconnect attempts (default: 10, 0 = unlimited)\n onOpen?: (socket: WebSocket) => void;\n onMessage?: (event: MessageEvent<string>) => void;\n onClose?: (event: CloseEvent) => void;\n onReconnectAttempt?: (attempt: number, delay: number) => void;\n onReconnectFailed?: () => void; // Called when max reconnect attempts reached\n onError?: (event: Event) => void;\n}\n\n/**\n * Lightweight wrapper around browser WebSocket with optional auto-reconnect support.\n * Implements exponential backoff with jitter to prevent thundering herd.\n */\nexport class WebSocketTransport {\n private socket: WebSocket | null = null;\n private reconnectTimer: number | null = null;\n private manuallyClosed = false;\n private reconnectAttempts = 0;\n\n constructor(private readonly options: WebSocketTransportOptions) {}\n\n connect(): void {\n this.manuallyClosed = false;\n this.clearReconnectTimer();\n\n this.socket = new WebSocket(this.options.url);\n const socket = this.socket;\n\n socket.onopen = () => {\n // Reset reconnect attempts on successful connection\n this.reconnectAttempts = 0;\n this.options.onOpen?.(socket);\n };\n\n socket.onmessage = (event: MessageEvent<string>) => {\n this.options.onMessage?.(event);\n };\n\n socket.onclose = (event: CloseEvent) => {\n this.options.onClose?.(event);\n if (!this.manuallyClosed && this.options.autoReconnect) {\n this.scheduleReconnect();\n }\n };\n\n socket.onerror = (event: Event) => {\n this.options.onError?.(event);\n };\n }\n\n send(data: string): void {\n if (this.socket && this.socket.readyState === 1) { // WebSocket.OPEN = 1\n this.socket.send(data);\n }\n }\n\n disconnect(): void {\n this.manuallyClosed = true;\n this.clearReconnectTimer();\n if (this.socket) {\n this.socket.close();\n this.socket = null;\n }\n }\n\n getSocket(): WebSocket | null {\n return this.socket;\n }\n\n private scheduleReconnect(): void {\n this.clearReconnectTimer();\n\n // Check if max reconnect attempts reached\n const maxAttempts = this.options.maxReconnectAttempts ?? 10;\n if (maxAttempts > 0 && this.reconnectAttempts >= maxAttempts) {\n this.options.onReconnectFailed?.();\n return;\n }\n\n this.reconnectAttempts++;\n\n // Calculate exponential backoff: baseDelay * 2^attempt\n const baseDelay = this.options.reconnectDelay ?? 1000;\n const maxDelay = this.options.maxReconnectDelay ?? 16000;\n const exponentialDelay = baseDelay * Math.pow(2, this.reconnectAttempts - 1);\n\n // Add jitter: random value between 0 and 1000ms to prevent thundering herd\n const jitter = Math.random() * 1000;\n\n // Calculate final delay with maximum cap\n const delay = Math.min(exponentialDelay + jitter, maxDelay);\n\n this.reconnectTimer = window.setTimeout(() => {\n this.options.onReconnectAttempt?.(this.reconnectAttempts, delay);\n this.connect();\n }, delay);\n }\n\n private clearReconnectTimer(): void {\n if (this.reconnectTimer !== null) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n}\n\nexport interface WebSocketManagerConfig {\n options: LiveTemplateClientOptions;\n onConnected: () => void;\n onDisconnected: () => void;\n onMessage: (response: UpdateResponse, event: MessageEvent<string>) => void;\n onReconnectAttempt?: (attempt: number, delay: number) => void;\n onReconnectFailed?: () => void;\n onError?: (event: Event) => void;\n logger: Logger;\n}\n\nexport interface WebSocketConnectResult {\n usingWebSocket: boolean;\n initialState?: UpdateResponse | null;\n}\n\nexport class WebSocketManager {\n private transport: WebSocketTransport | null = null;\n\n constructor(private readonly config: WebSocketManagerConfig) {}\n\n async connect(): Promise<WebSocketConnectResult> {\n const liveUrl = this.getLiveUrl();\n\n const wsAvailable = await checkWebSocketAvailability(\n liveUrl,\n this.config.logger\n );\n if (!wsAvailable) {\n const initialState = await fetchInitialState(liveUrl, this.config.logger);\n return { usingWebSocket: false, initialState };\n }\n\n this.transport = new WebSocketTransport({\n url: this.getWebSocketUrl(),\n autoReconnect: this.config.options.autoReconnect,\n reconnectDelay: this.config.options.reconnectDelay,\n maxReconnectDelay: 16000, // 16 seconds maximum\n maxReconnectAttempts: 10, // 10 attempts before giving up\n onOpen: () => {\n this.config.onConnected();\n },\n onMessage: (event) => {\n try {\n const payload: UpdateResponse = JSON.parse(event.data);\n this.config.onMessage(payload, event);\n } catch (error) {\n this.config.logger.error(\"Failed to parse WebSocket message:\", error);\n }\n },\n onClose: () => {\n this.config.onDisconnected();\n },\n onReconnectAttempt: (attempt, delay) => {\n this.config.onReconnectAttempt?.(attempt, delay);\n },\n onReconnectFailed: () => {\n this.config.onReconnectFailed?.();\n },\n onError: (event) => {\n this.config.onError?.(event);\n },\n });\n\n this.transport.connect();\n return { usingWebSocket: true };\n }\n\n disconnect(): void {\n this.transport?.disconnect();\n this.transport = null;\n }\n\n send(data: string): void {\n this.transport?.send(data);\n }\n\n getReadyState(): number | undefined {\n return this.transport?.getSocket()?.readyState;\n }\n\n getSocket(): WebSocket | null {\n return this.transport?.getSocket() ?? null;\n }\n\n private getWebSocketUrl(): string {\n const liveUrl = this.config.options.liveUrl || \"/live\";\n const baseUrl = this.config.options.wsUrl;\n if (baseUrl) {\n return baseUrl;\n }\n return `ws://${window.location.host}${liveUrl}`;\n }\n\n private getLiveUrl(): string {\n return this.config.options.liveUrl || window.location.pathname + window.location.search;\n }\n}\n\nexport async function checkWebSocketAvailability(\n liveUrl: string,\n logger?: Logger\n): Promise<boolean> {\n try {\n const response = await fetch(liveUrl, {\n method: \"HEAD\",\n });\n\n const wsHeader = response.headers.get(\"X-LiveTemplate-WebSocket\");\n if (wsHeader) {\n return wsHeader === \"enabled\";\n }\n\n return true;\n } catch (error) {\n logger?.warn(\"Failed to check WebSocket availability:\", error);\n return true;\n }\n}\n\nexport async function fetchInitialState(\n liveUrl: string,\n logger?: Logger\n): Promise<UpdateResponse | null> {\n try {\n const response = await fetch(liveUrl, {\n method: \"GET\",\n credentials: \"include\",\n headers: {\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch initial state: ${response.status}`);\n }\n\n return (await response.json()) as UpdateResponse;\n } catch (error) {\n logger?.warn(\"Failed to fetch initial state:\", error);\n return null;\n }\n}\n", "/**\n * S3Uploader - Handles direct uploads to S3 using presigned URLs\n */\n\nimport type {\n ExternalUploadMeta,\n UploadEntry,\n UploadProgressCallback,\n Uploader,\n} from \"./types\";\n\nexport class S3Uploader implements Uploader {\n /**\n * Upload a file directly to S3 using presigned PUT URL\n */\n async upload(\n entry: UploadEntry,\n meta: ExternalUploadMeta,\n onProgress?: UploadProgressCallback\n ): Promise<void> {\n const { file } = entry;\n\n // Create abort controller for cancellation\n entry.abortController = new AbortController();\n\n try {\n // Create XMLHttpRequest for progress tracking\n const xhr = new XMLHttpRequest();\n\n // Track upload progress and notify handler\n xhr.upload.addEventListener(\"progress\", (e) => {\n if (e.lengthComputable) {\n entry.bytesUploaded = e.loaded;\n entry.progress = Math.round((e.loaded / e.total) * 100);\n // Notify progress callback\n if (onProgress) {\n onProgress(entry);\n }\n }\n });\n\n // Handle abort\n entry.abortController.signal.addEventListener(\"abort\", () => {\n xhr.abort();\n });\n\n // Create promise for upload completion\n const uploadPromise = new Promise<void>((resolve, reject) => {\n xhr.addEventListener(\"load\", () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n entry.done = true;\n entry.progress = 100;\n resolve();\n } else {\n reject(new Error(`S3 upload failed with status ${xhr.status}: ${xhr.statusText}`));\n }\n });\n\n xhr.addEventListener(\"error\", () => {\n reject(new Error(\"S3 upload failed: Network error\"));\n });\n\n xhr.addEventListener(\"abort\", () => {\n reject(new Error(\"S3 upload cancelled\"));\n });\n });\n\n // Open connection\n xhr.open(\"PUT\", meta.url);\n\n // Set headers\n if (meta.headers) {\n for (const [key, value] of Object.entries(meta.headers)) {\n xhr.setRequestHeader(key, value);\n }\n }\n\n // Send file\n xhr.send(file);\n\n await uploadPromise;\n } catch (error) {\n entry.error = error instanceof Error ? error.message : String(error);\n throw error;\n }\n }\n}\n", "/**\n * UploadHandler - Manages file uploads for LiveTemplate\n *\n * Handles:\n * - File input detection and change events\n * - Chunked uploads to server via WebSocket\n * - External uploads (S3) via presigned URLs\n * - Progress tracking and callbacks\n * - Upload cancellation\n */\n\nimport { S3Uploader } from \"./s3-uploader\";\nimport type {\n ExternalUploadMeta,\n FileMetadata,\n UploadChunkMessage,\n UploadCompleteMessage,\n UploadEntry,\n UploadHandlerOptions,\n UploadProgressMessage,\n UploadStartMessage,\n UploadStartResponse,\n Uploader,\n} from \"./types\";\n\nexport class UploadHandler {\n private entries: Map<string, UploadEntry> = new Map();\n private pendingFiles: Map<string, File[]> = new Map(); // uploadName -> files\n private autoUploadConfig: Map<string, boolean> = new Map(); // uploadName -> autoUpload\n private chunkSize: number;\n private uploaders: Map<string, Uploader> = new Map();\n private onProgress?: (entry: UploadEntry) => void;\n private onComplete?: (uploadName: string, entries: UploadEntry[]) => void;\n private onError?: (entry: UploadEntry, error: string) => void;\n private inputHandlers: WeakMap<HTMLInputElement, EventListener> = new WeakMap();\n\n constructor(\n private sendMessage: (message: any) => void,\n options: UploadHandlerOptions = {}\n ) {\n this.chunkSize = options.chunkSize || 256 * 1024; // 256KB default\n this.onProgress = options.onProgress;\n this.onComplete = options.onComplete;\n this.onError = options.onError;\n\n // Register default uploaders\n this.uploaders.set(\"s3\", new S3Uploader());\n }\n\n /**\n * Initialize upload detection on file inputs with lvt-upload attribute\n */\n initializeFileInputs(container: Element) {\n const inputs = container.querySelectorAll<HTMLInputElement>(\n 'input[type=\"file\"][lvt-upload]'\n );\n\n inputs.forEach((input) => {\n const uploadName = input.getAttribute(\"lvt-upload\");\n if (!uploadName) return;\n\n // Remove existing listener if any\n const existingHandler = this.inputHandlers.get(input);\n if (existingHandler) {\n input.removeEventListener(\"change\", existingHandler);\n }\n\n // Create new handler\n const handler = (e: Event) => {\n const files = (e.target as HTMLInputElement).files;\n if (!files || files.length === 0) return;\n\n // Always send upload_start to get validation and config\n // But only proceed with chunks if autoUpload is true\n this.startUpload(uploadName, Array.from(files));\n };\n\n input.addEventListener(\"change\", handler);\n // Store handler in WeakMap to prevent memory leaks\n this.inputHandlers.set(input, handler);\n });\n }\n\n /**\n * Start upload process for selected files\n */\n async startUpload(uploadName: string, files: File[]): Promise<void> {\n // Store files temporarily for when server response arrives\n this.pendingFiles.set(uploadName, files);\n\n // Create file metadata\n const fileMetadata: FileMetadata[] = files.map((file) => ({\n name: file.name,\n type: file.type || \"application/octet-stream\",\n size: file.size,\n }));\n\n // Send upload_start message to server\n const startMessage: UploadStartMessage = {\n action: \"upload_start\",\n upload_name: uploadName,\n files: fileMetadata,\n };\n\n this.sendMessage(startMessage);\n }\n\n /**\n * Handle upload_start response from server\n */\n async handleUploadStartResponse(\n response: UploadStartResponse\n ): Promise<void> {\n const { upload_name, entries: entryInfos } = response;\n\n // Store autoUpload configuration from first entry\n if (entryInfos.length > 0) {\n this.autoUploadConfig.set(upload_name, entryInfos[0].auto_upload);\n }\n\n // Get pending files for this upload\n const files = this.pendingFiles.get(upload_name);\n if (!files) {\n console.error(`No pending files found for upload: ${upload_name}`);\n return;\n }\n\n // Clear pending files\n this.pendingFiles.delete(upload_name);\n\n // Build a map from file name to file object for lookup\n const fileMap = new Map<string, File>();\n for (const file of files) {\n fileMap.set(file.name, file);\n }\n\n // Create upload entries\n const entries: UploadEntry[] = [];\n\n for (const info of entryInfos) {\n const file = fileMap.get(info.client_name);\n\n if (!file) {\n console.warn(\n `No file found for entry ${info.entry_id} (client_name: ${info.client_name})`\n );\n continue;\n }\n\n const entry: UploadEntry = {\n id: info.entry_id,\n file,\n uploadName: upload_name,\n progress: 0,\n bytesUploaded: 0,\n valid: info.valid,\n done: false,\n error: info.error,\n external: info.external,\n };\n\n this.entries.set(entry.id, entry);\n entries.push(entry);\n\n // Handle invalid entries\n if (!info.valid) {\n if (this.onError && info.error) {\n this.onError(entry, info.error);\n }\n continue;\n }\n\n // Only start upload immediately if autoUpload is true\n // Otherwise, entries are stored and will be uploaded on form submit\n if (info.auto_upload) {\n // Start upload (external or chunked)\n if (info.external) {\n this.uploadExternal(entry, info.external);\n } else {\n this.uploadChunked(entry);\n }\n }\n }\n }\n\n /**\n * Upload file using external uploader (S3, etc.)\n */\n private async uploadExternal(\n entry: UploadEntry,\n meta: ExternalUploadMeta\n ): Promise<void> {\n try {\n const uploader = this.uploaders.get(meta.uploader);\n if (!uploader) {\n throw new Error(`Unknown uploader: ${meta.uploader}`);\n }\n\n // Start external upload with progress callback\n await uploader.upload(entry, meta, this.onProgress);\n\n // Notify server of completion\n const completeMessage: UploadCompleteMessage = {\n action: \"upload_complete\",\n upload_name: entry.uploadName,\n entry_ids: [entry.id],\n };\n\n this.sendMessage(completeMessage);\n\n if (this.onComplete) {\n this.onComplete(entry.uploadName, [entry]);\n }\n\n // Clear the file input to prevent re-upload of the same file\n this.clearFileInput(entry.uploadName);\n\n // Schedule cleanup after completion\n this.cleanupEntries(entry.uploadName);\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n entry.error = errorMsg;\n if (this.onError) {\n this.onError(entry, errorMsg);\n }\n // Schedule cleanup after error\n this.cleanupEntries(entry.uploadName);\n }\n }\n\n /**\n * Upload file in chunks via WebSocket\n */\n private async uploadChunked(entry: UploadEntry): Promise<void> {\n const { file, id } = entry;\n let offset = 0;\n\n // Create abort controller for cancellation\n entry.abortController = new AbortController();\n\n try {\n while (offset < file.size) {\n // Check if upload was cancelled\n if (entry.abortController.signal.aborted) {\n throw new Error(\"Upload cancelled\");\n }\n // Read chunk\n const end = Math.min(offset + this.chunkSize, file.size);\n const chunk = file.slice(offset, end);\n\n // Convert to base64\n const base64 = await this.fileToBase64(chunk);\n\n // Send chunk\n const chunkMessage: UploadChunkMessage = {\n action: \"upload_chunk\",\n entry_id: id,\n chunk_base64: base64,\n offset,\n total: file.size,\n };\n\n this.sendMessage(chunkMessage);\n\n // Update progress\n offset = end;\n entry.bytesUploaded = offset;\n entry.progress = Math.round((offset / file.size) * 100);\n\n if (this.onProgress) {\n this.onProgress(entry);\n }\n }\n\n // Send complete message\n entry.done = true;\n const completeMessage: UploadCompleteMessage = {\n action: \"upload_complete\",\n upload_name: entry.uploadName,\n entry_ids: [id],\n };\n\n this.sendMessage(completeMessage);\n\n if (this.onComplete) {\n this.onComplete(entry.uploadName, [entry]);\n }\n\n // Clear the file input to prevent re-upload of the same file\n this.clearFileInput(entry.uploadName);\n\n // Schedule cleanup after completion\n this.cleanupEntries(entry.uploadName);\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n entry.error = errorMsg;\n if (this.onError) {\n this.onError(entry, errorMsg);\n }\n // Schedule cleanup after error\n this.cleanupEntries(entry.uploadName);\n }\n }\n\n /**\n * Handle progress message from server (for chunked uploads)\n */\n handleProgressMessage(message: UploadProgressMessage): void {\n const entry = this.entries.get(message.entry_id);\n if (!entry) return;\n\n entry.progress = message.progress;\n entry.bytesUploaded = message.bytes_recv;\n\n if (this.onProgress) {\n this.onProgress(entry);\n }\n }\n\n /**\n * Cancel upload\n */\n cancelUpload(entryId: string): void {\n const entry = this.entries.get(entryId);\n if (!entry) return;\n\n // Abort external upload if in progress\n if (entry.abortController) {\n entry.abortController.abort();\n }\n\n // Send cancel message to server\n this.sendMessage({\n action: \"cancel_upload\",\n entry_id: entryId,\n });\n\n // Remove entry\n this.entries.delete(entryId);\n }\n\n /**\n * Get all entries for an upload name\n */\n getEntries(uploadName: string): UploadEntry[] {\n const entries: UploadEntry[] = [];\n for (const entry of this.entries.values()) {\n if (entry.uploadName === uploadName) {\n entries.push(entry);\n }\n }\n return entries;\n }\n\n /**\n * Trigger upload for all pending entries (used when autoUpload is false)\n * Called by LiveTemplate client on form submit\n */\n triggerPendingUploads(uploadName: string): void {\n // Get all entries for this upload that haven't started yet\n const pendingEntries: UploadEntry[] = [];\n for (const entry of this.entries.values()) {\n if (\n entry.uploadName === uploadName &&\n entry.progress === 0 &&\n !entry.done &&\n !entry.error\n ) {\n pendingEntries.push(entry);\n }\n }\n\n // Start uploads\n for (const entry of pendingEntries) {\n if (entry.external) {\n this.uploadExternal(entry, entry.external);\n } else {\n this.uploadChunked(entry);\n }\n }\n }\n\n /**\n * Register custom uploader\n */\n registerUploader(name: string, uploader: Uploader): void {\n this.uploaders.set(name, uploader);\n }\n\n /**\n * Clear file input to prevent re-upload of the same file\n * Called after successful upload completion\n */\n private clearFileInput(uploadName: string): void {\n // Find all file inputs with this upload name\n const inputs = document.querySelectorAll<HTMLInputElement>(\n `input[type=\"file\"][lvt-upload=\"${uploadName}\"]`\n );\n\n inputs.forEach((input) => {\n // Clear the file input value\n input.value = '';\n });\n }\n\n /**\n * Clean up completed or errored upload entries to prevent memory leaks\n * Automatically called after completion/error, but can be called manually\n * @param uploadName - Optional upload name to clean specific uploads\n * @param delay - Optional delay in ms before cleanup (default: 5000ms)\n */\n cleanupEntries(uploadName?: string, delay: number = 5000): void {\n setTimeout(() => {\n const entriesToRemove: string[] = [];\n\n for (const [id, entry] of this.entries) {\n // Skip if uploadName is specified and doesn't match\n if (uploadName && entry.uploadName !== uploadName) continue;\n\n // Remove completed or errored entries\n if (entry.done || entry.error) {\n entriesToRemove.push(id);\n }\n }\n\n for (const id of entriesToRemove) {\n this.entries.delete(id);\n }\n }, delay);\n }\n\n /**\n * Convert File/Blob to base64 string\n */\n private fileToBase64(blob: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = reader.result as string;\n // Remove data URL prefix\n const base64 = result.split(\",\")[1];\n resolve(base64);\n };\n reader.onerror = reject;\n reader.readAsDataURL(blob);\n });\n }\n}\n", "export type LogLevel = \"silent\" | \"error\" | \"warn\" | \"info\" | \"debug\";\n\nconst levelPriority: Record<LogLevel, number> = {\n silent: 0,\n error: 1,\n warn: 2,\n info: 3,\n debug: 4,\n};\n\ninterface LogState {\n level: LogLevel;\n}\n\ntype ConsoleMethod = \"error\" | \"warn\" | \"info\" | \"debug\";\n\nconst DEFAULT_SCOPE = \"LiveTemplate\";\n\n/**\n * Lightweight console logger with support for log levels and scoped prefixes.\n */\nexport class Logger {\n constructor(\n private readonly state: LogState,\n private readonly scope: string[] = [],\n private readonly sink: Console = console\n ) {}\n\n setLevel(level: LogLevel): void {\n this.state.level = level;\n }\n\n getLevel(): LogLevel {\n return this.state.level;\n }\n\n child(scope: string): Logger {\n return new Logger(this.state, [...this.scope, scope], this.sink);\n }\n\n isDebugEnabled(): boolean {\n return this.shouldLog(\"debug\");\n }\n\n error(...args: unknown[]): void {\n this.log(\"error\", \"error\", args);\n }\n\n warn(...args: unknown[]): void {\n this.log(\"warn\", \"warn\", args);\n }\n\n info(...args: unknown[]): void {\n this.log(\"info\", \"info\", args);\n }\n\n debug(...args: unknown[]): void {\n this.log(\"debug\", \"debug\", args);\n }\n\n private log(level: LogLevel, method: ConsoleMethod, args: unknown[]): void {\n if (!this.shouldLog(level)) {\n return;\n }\n\n const target =\n (this.sink[method] as (...args: unknown[]) => void) ||\n (console[method] as (...args: unknown[]) => void) ||\n console.log;\n target.apply(this.sink, [this.formatPrefix(), ...args]);\n }\n\n private shouldLog(level: LogLevel): boolean {\n return levelPriority[level] <= levelPriority[this.state.level];\n }\n\n private formatPrefix(): string {\n if (this.scope.length === 0) {\n return `[${DEFAULT_SCOPE}]`;\n }\n\n return `[${DEFAULT_SCOPE}:${this.scope.join(\":\")}]`;\n }\n}\n\nexport interface LoggerOptions {\n level?: LogLevel;\n scope?: string | string[];\n sink?: Console;\n}\n\nexport function createLogger(options: LoggerOptions = {}): Logger {\n const state: LogState = {\n level: options.level ?? \"info\",\n };\n\n const scope = Array.isArray(options.scope)\n ? options.scope\n : options.scope\n ? [options.scope]\n : [];\n\n return new Logger(state, scope, options.sink ?? console);\n}\n", "import type { LiveTemplateClient } from \"../livetemplate-client\";\nimport type { UpdateResult } from \"../types\";\n\n/**\n * Utility function to load and apply updates from JSON files.\n */\nexport async function loadAndApplyUpdate(\n client: LiveTemplateClient,\n updatePath: string\n): Promise<UpdateResult> {\n try {\n const nodeRequire = (globalThis as any)?.require;\n if (typeof nodeRequire === \"function\") {\n const fs = nodeRequire(\"fs\");\n const updateData = JSON.parse(fs.readFileSync(updatePath, \"utf8\"));\n return client.applyUpdate(updateData);\n }\n\n const response = await fetch(updatePath);\n const updateData = await response.json();\n return client.applyUpdate(updateData);\n } catch (error) {\n throw new Error(`Failed to load update from ${updatePath}: ${error}`);\n }\n}\n\n/**\n * Compare two HTML strings, ignoring whitespace differences.\n */\nexport function compareHTML(\n expected: string,\n actual: string\n): {\n match: boolean;\n differences: string[];\n} {\n const differences: string[] = [];\n\n const normalizeHTML = (html: string) => {\n return html.replace(/\\s+/g, \" \").replace(/>\\s+</g, \"><\").trim();\n };\n\n const normalizedExpected = normalizeHTML(expected);\n const normalizedActual = normalizeHTML(actual);\n\n if (normalizedExpected === normalizedActual) {\n return { match: true, differences: [] };\n }\n\n const expectedLines = normalizedExpected.split(\"\\n\");\n const actualLines = normalizedActual.split(\"\\n\");\n const maxLines = Math.max(expectedLines.length, actualLines.length);\n\n for (let i = 0; i < maxLines; i++) {\n const expectedLine = expectedLines[i] || \"\";\n const actualLine = actualLines[i] || \"\";\n\n if (expectedLine !== actualLine) {\n differences.push(`Line ${i + 1}:`);\n differences.push(` Expected: ${expectedLine}`);\n differences.push(` Actual: ${actualLine}`);\n }\n }\n\n return { match: false, differences };\n}\n"],
5
- "mappings": "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",
6
- "names": ["livetemplate_client_exports", "__export", "LiveTemplateClient", "checkLvtConfirm", "compareHTML", "extractLvtData", "loadAndApplyUpdate", "setupReactiveAttributeListeners", "DOCUMENT_FRAGMENT_NODE", "morphAttrs", "fromNode", "toNode", "toNodeAttrs", "attr", "attrName", "attrNamespaceURI", "attrValue", "fromValue", "i", "fromNodeAttrs", "range", "NS_XHTML", "doc", "HAS_TEMPLATE_SUPPORT", "HAS_RANGE_SUPPORT", "createFragmentFromTemplate", "str", "template", "createFragmentFromRange", "fragment", "createFragmentFromWrap", "toElement", "compareNodeNames", "fromEl", "toEl", "fromNodeName", "toNodeName", "fromCodeStart", "toCodeStart", "createElementNS", "name", "namespaceURI", "moveChildren", "curChild", "nextChild", "syncBooleanAttrProp", "specialElHandlers", "parentNode", "parentName", "newValue", "firstChild", "oldValue", "selectedIndex", "optgroup", "nodeName", "ELEMENT_NODE", "DOCUMENT_FRAGMENT_NODE$1", "TEXT_NODE", "COMMENT_NODE", "noop", "defaultGetNodeKey", "node", "morphdomFactory", "options", "toNodeHtml", "getNodeKey", "onBeforeNodeAdded", "onNodeAdded", "onBeforeElUpdated", "onElUpdated", "onBeforeNodeDiscarded", "onNodeDiscarded", "onBeforeElChildrenUpdated", "skipFromChildren", "addChild", "parent", "child", "childrenOnly", "fromNodesLookup", "keyedRemovalList", "addKeyedRemoval", "key", "walkDiscardedChildNodes", "skipKeyedNodes", "removeNode", "indexTree", "handleNodeAdded", "el", "nextSibling", "unmatchedFromEl", "morphEl", "cleanupFromEl", "curFromNodeChild", "curFromNodeKey", "fromNextSibling", "toElKey", "beforeUpdateResult", "morphChildren", "skipFrom", "curToNodeChild", "curToNodeKey", "toNextSibling", "matchingFromEl", "outer", "curFromNodeType", "isCompatible", "onBeforeNodeAddedResult", "specialElHandler", "morphedNode", "morphedNodeType", "toNodeType", "len", "elToRemove", "morphdom", "morphdom_esm_default", "FOCUSABLE_INPUTS", "FocusManager", "logger", "wrapper", "selector", "FOCUSABLE_INPUTS", "type", "wrapperId", "focusKey", "blurKey", "focusListener", "event", "_a", "target", "blurListener", "_b", "_c", "element", "index", "wasFocused", "el", "handleScrollDirectives", "rootElement", "element", "htmlElement", "mode", "behavior", "threshold", "handleHighlightDirectives", "duration", "color", "originalBackground", "originalTransition", "handleAnimateDirectives", "animation", "style", "debounce", "func", "wait", "timeout", "args", "context", "throttle", "limit", "inThrottle", "checkLvtConfirm", "element", "confirmMessage", "extractLvtData", "data", "attributes", "i", "attr", "key", "EventDelegator", "context", "logger", "wrapperElement", "eventTypes", "wrapperId", "rateLimitedHandlers", "eventType", "listenerKey", "existingListener", "listener", "e", "_a", "currentWrapper", "target", "element", "inWrapper", "attrName", "action", "actionElement", "persistTable", "formElement", "keyFilter", "targetElement", "handleAction", "checkLvtConfirm", "message", "formData", "checkboxes", "checkboxNames", "cb", "name", "passwordFields", "el", "value", "key", "attr", "submitButton", "originalButtonText", "input", "uploadName", "throttleValue", "debounceValue", "handlerCache", "cacheKey", "rateLimitedHandler", "limit", "throttle", "wait", "debounce", "windowEvents", "openListenerKey", "existingOpenListener", "openListener", "modalId", "closeListenerKey", "existingCloseListener", "closeListener", "backdropListenerKey", "existingBackdropListener", "backdropListener", "escapeListenerKey", "existingEscapeListener", "escapeListener", "openModals", "lastModal", "getFocusableElements", "container", "selector", "htmlEl", "style", "isNotDisplayNone", "isNotVisibilityHidden", "hasLayoutContext", "focusTrapElements", "activeTrap", "trap", "htmlTrap", "focusableElements", "firstElement", "lastElement", "observerKey", "existingObserver", "processAutofocus", "htmlElement", "isVisible", "wasFocused", "observer", "mutations", "shouldProcess", "mutation", "node", "ObserverManager", "context", "logger", "sentinel", "entries", "wrapperElement", "ModalManager", "logger", "modalId", "modal", "firstInput", "activeElement", "isVisible", "element", "LoadingIndicator", "bar", "style", "FormDisabler", "wrapper", "form", "input", "LIFECYCLE_EVENTS", "ACTION_MAP", "parseReactiveAttribute", "attrName", "attrValue", "match", "actionKey", "action", "segments", "lastSegment", "LIFECYCLE_EVENTS", "lifecycle", "actionName", "executeAction", "element", "param", "classes", "c", "colonIndex", "name", "value", "matchesEvent", "binding", "processReactiveAttributes", "attr", "setupReactiveAttributeListeners", "_a", "deepClone", "obj", "TreeRenderer", "logger", "update", "changed", "key", "value", "existing", "oldValue", "newValue", "currentPath", "_a", "merged", "fieldPath", "isDifferentialOps", "existingIsRange", "rangeStructure", "operations", "statePath", "currentItems", "operation", "removeIndex", "updateIndex", "changes", "itemsToAdd", "itemsToPrepend", "targetIndex", "itemsToInsert", "newOrder", "reorderedItems", "itemsByKey", "item", "itemKey", "orderedKey", "node", "html", "i", "staticSegment", "dynamicKey", "newStatePath", "fieldKey", "stateKey", "keys", "numericKeys", "k", "a", "b", "itemStatePath", "idx", "rangeNode", "dynamics", "statics", "staticsMap", "elseKey", "elseStatePath", "hasStaticsMap", "itemIdx", "itemStatics", "localKey", "rangeData", "fieldValue", "items", "result", "prepend", "itemsArray", "keyPosStr", "FormLifecycleManager", "modalManager", "form", "button", "originalButtonText", "meta", "modalParent", "WebSocketTransport", "options", "socket", "_a", "_b", "event", "data", "_c", "_d", "_e", "maxAttempts", "baseDelay", "maxDelay", "exponentialDelay", "jitter", "delay", "WebSocketManager", "config", "liveUrl", "checkWebSocketAvailability", "payload", "error", "attempt", "fetchInitialState", "baseUrl", "logger", "wsHeader", "response", "S3Uploader", "entry", "meta", "onProgress", "file", "xhr", "e", "uploadPromise", "resolve", "reject", "key", "value", "error", "UploadHandler", "sendMessage", "options", "S3Uploader", "container", "input", "uploadName", "existingHandler", "handler", "e", "files", "fileMetadata", "file", "startMessage", "response", "upload_name", "entryInfos", "fileMap", "entries", "info", "entry", "meta", "uploader", "completeMessage", "error", "errorMsg", "id", "offset", "end", "chunk", "base64", "chunkMessage", "message", "entryId", "pendingEntries", "name", "delay", "entriesToRemove", "blob", "resolve", "reject", "reader", "levelPriority", "DEFAULT_SCOPE", "Logger", "_Logger", "state", "scope", "sink", "level", "args", "method", "createLogger", "options", "_a", "_b", "loadAndApplyUpdate", "client", "updatePath", "nodeRequire", "fs", "updateData", "error", "compareHTML", "expected", "actual", "differences", "normalizeHTML", "html", "normalizedExpected", "normalizedActual", "expectedLines", "actualLines", "maxLines", "i", "expectedLine", "actualLine", "LiveTemplateClient", "_LiveTemplateClient", "options", "providedLogger", "logLevel", "debug", "restOptions", "resolvedLevel", "baseLogger", "createLogger", "TreeRenderer", "FocusManager", "ModalManager", "FormLifecycleManager", "LoadingIndicator", "FormDisabler", "UploadHandler", "message", "entry", "uploadName", "entries", "error", "EventDelegator", "value", "form", "button", "originalButtonText", "modalId", "ObserverManager", "WebSocketManager", "_a", "_b", "_c", "response", "event", "autoInitLogger", "init", "wrapper", "client", "uploadMessage", "startResponse", "wrapperSelector", "connectionResult", "setupReactiveAttributeListeners", "url", "flashParams", "hasFlashParams", "param", "readyState", "liveUrl", "updateResponse", "num", "update", "existingHTML", "result", "match", "innerContent", "newBodyContent", "element", "meta", "hasStaticsInTree", "node", "v", "tempWrapper", "morphdom_esm_default", "fromEl", "toEl", "lastFocused", "handleScrollDirectives", "handleHighlightDirectives", "handleAnimateDirectives", "hookName", "hookValue"]
4
+ "sourcesContent": ["/**\n * LiveTemplate TypeScript Client\n *\n * Reconstructs HTML from tree-based updates using cached static structure,\n * following the Phoenix LiveView optimization approach.\n */\n\nimport morphdom from \"morphdom\";\nimport { FocusManager } from \"./dom/focus-manager\";\nimport {\n handleAnimateDirectives,\n handleHighlightDirectives,\n handleScrollDirectives,\n} from \"./dom/directives\";\nimport { EventDelegator } from \"./dom/event-delegation\";\nimport { ObserverManager } from \"./dom/observer-manager\";\nimport { ModalManager } from \"./dom/modal-manager\";\nimport { LoadingIndicator } from \"./dom/loading-indicator\";\nimport { FormDisabler } from \"./dom/form-disabler\";\nimport { setupReactiveAttributeListeners } from \"./dom/reactive-attributes\";\nimport { TreeRenderer } from \"./state/tree-renderer\";\nimport { FormLifecycleManager } from \"./state/form-lifecycle-manager\";\nimport { WebSocketManager } from \"./transport/websocket\";\nimport { UploadHandler } from \"./upload/upload-handler\";\nimport type {\n UploadProgressMessage,\n UploadStartResponse,\n} from \"./upload/types\";\nimport type {\n LiveTemplateClientOptions,\n ResponseMetadata,\n TreeNode,\n UpdateResponse,\n UpdateResult,\n} from \"./types\";\nimport { createLogger, Logger } from \"./utils/logger\";\nexport { loadAndApplyUpdate, compareHTML } from \"./utils/testing\";\nexport { setupReactiveAttributeListeners } from \"./dom/reactive-attributes\";\nexport { checkLvtConfirm, extractLvtData } from \"./utils/confirm\";\n\nexport class LiveTemplateClient {\n private readonly treeRenderer: TreeRenderer;\n private readonly focusManager: FocusManager;\n private readonly logger: Logger;\n private lvtId: string | null = null;\n\n // Transport properties\n private webSocketManager: WebSocketManager;\n public ws: WebSocket | null = null;\n private wrapperElement: Element | null = null;\n private options: LiveTemplateClientOptions;\n private useHTTP: boolean = false; // True when WebSocket is unavailable\n private sessionCookie: string | null = null; // For HTTP mode session tracking\n\n // Rate limiting: cache of debounced/throttled handlers per element+eventType\n private rateLimitedHandlers: WeakMap<Element, Map<string, Function>> =\n new WeakMap();\n\n private eventDelegator: EventDelegator;\n private observerManager: ObserverManager;\n private modalManager: ModalManager;\n private formLifecycleManager: FormLifecycleManager;\n private loadingIndicator: LoadingIndicator;\n private formDisabler: FormDisabler;\n private uploadHandler: UploadHandler;\n\n // Initialization tracking for loading indicator\n private isInitialized: boolean = false;\n\n // Message tracking for deterministic E2E testing\n private messageCount: number = 0;\n\n constructor(options: LiveTemplateClientOptions = {}) {\n const { logger: providedLogger, logLevel, debug, ...restOptions } = options;\n const resolvedLevel = logLevel ?? (debug ? \"debug\" : \"info\");\n const baseLogger = providedLogger ?? createLogger({ level: resolvedLevel });\n\n if (providedLogger) {\n if (logLevel) {\n providedLogger.setLevel(logLevel);\n } else if (debug) {\n providedLogger.setLevel(\"debug\");\n }\n } else {\n baseLogger.setLevel(resolvedLevel);\n }\n\n this.logger = baseLogger.child(\"Client\");\n\n this.options = {\n autoReconnect: false, // Disable autoReconnect by default to avoid connection loops\n reconnectDelay: 1000,\n liveUrl: window.location.pathname + window.location.search, // Connect to current page (including query params)\n ...restOptions,\n };\n\n this.treeRenderer = new TreeRenderer(this.logger.child(\"TreeRenderer\"));\n this.focusManager = new FocusManager(this.logger.child(\"FocusManager\"));\n\n this.modalManager = new ModalManager(this.logger.child(\"ModalManager\"));\n this.formLifecycleManager = new FormLifecycleManager(this.modalManager);\n this.loadingIndicator = new LoadingIndicator();\n this.formDisabler = new FormDisabler();\n\n // Initialize upload handler\n this.uploadHandler = new UploadHandler(\n (message) => this.send(message),\n {\n chunkSize: 256 * 1024, // 256KB chunks\n onProgress: (entry) => {\n // Trigger DOM update to refresh upload progress\n if (this.wrapperElement) {\n this.wrapperElement.dispatchEvent(\n new CustomEvent(\"lvt:upload:progress\", {\n detail: { entry },\n })\n );\n }\n },\n onComplete: (uploadName, entries) => {\n this.logger.info(`Upload complete: ${uploadName}`, entries);\n if (this.wrapperElement) {\n this.wrapperElement.dispatchEvent(\n new CustomEvent(\"lvt:upload:complete\", {\n detail: { uploadName, entries },\n })\n );\n }\n },\n onError: (entry, error) => {\n this.logger.error(`Upload error for ${entry.id}:`, error);\n if (this.wrapperElement) {\n this.wrapperElement.dispatchEvent(\n new CustomEvent(\"lvt:upload:error\", {\n detail: { entry, error },\n })\n );\n }\n },\n }\n );\n\n this.eventDelegator = new EventDelegator(\n {\n getWrapperElement: () => this.wrapperElement,\n getRateLimitedHandlers: () => this.rateLimitedHandlers,\n parseValue: (value: string) => this.parseValue(value),\n send: (message: any) => this.send(message),\n setActiveSubmission: (\n form: HTMLFormElement | null,\n button: HTMLButtonElement | null,\n originalButtonText: string | null\n ) =>\n this.formLifecycleManager.setActiveSubmission(\n form,\n button,\n originalButtonText\n ),\n openModal: (modalId: string) => this.modalManager.open(modalId),\n closeModal: (modalId: string) => this.modalManager.close(modalId),\n getWebSocketReadyState: () => this.webSocketManager.getReadyState(),\n triggerPendingUploads: (uploadName: string) =>\n this.uploadHandler.triggerPendingUploads(uploadName),\n },\n this.logger.child(\"EventDelegator\")\n );\n\n this.observerManager = new ObserverManager(\n {\n getWrapperElement: () => this.wrapperElement,\n send: (message: any) => this.send(message),\n },\n this.logger.child(\"ObserverManager\")\n );\n\n this.webSocketManager = new WebSocketManager({\n options: this.options,\n logger: this.logger.child(\"Transport\"),\n onConnected: () => {\n this.ws = this.webSocketManager.getSocket();\n this.logger.info(\"WebSocket connected\");\n\n // Clear flash-related query params from URL to prevent stale flash on reload\n // This handles the redirect pattern: /auth?error=invalid_credentials\n this.clearFlashQueryParams();\n\n this.options.onConnect?.();\n this.wrapperElement?.dispatchEvent(new Event(\"lvt:connected\"));\n },\n onDisconnected: () => {\n this.ws = null;\n this.logger.info(\"WebSocket disconnected\");\n this.options.onDisconnect?.();\n this.wrapperElement?.dispatchEvent(new Event(\"lvt:disconnected\"));\n },\n onMessage: (response, event) => {\n this.handleWebSocketPayload(response, event);\n },\n onReconnectAttempt: () => {\n this.logger.info(\"Attempting to reconnect...\");\n },\n onError: (error) => {\n this.logger.error(\"WebSocket error:\", error);\n this.options.onError?.(error);\n },\n });\n }\n\n /**\n * Auto-initialize when DOM is ready\n * Called automatically when script loads\n */\n static autoInit(): void {\n const autoInitLogger = createLogger({ scope: \"Client:autoInit\" });\n const init = () => {\n const wrapper = document.querySelector(\"[data-lvt-id]\");\n if (wrapper) {\n const client = new LiveTemplateClient();\n client.wrapperElement = wrapper;\n\n // Check if loading indicator should be shown\n const shouldShowLoading =\n wrapper.getAttribute(\"data-lvt-loading\") === \"true\";\n if (shouldShowLoading) {\n client.loadingIndicator.show();\n client.formDisabler.disable(client.wrapperElement);\n }\n\n client.connect().catch((error) => {\n autoInitLogger.error(\"Auto-initialization connect failed:\", error);\n });\n\n // Expose as global for programmatic access\n (window as any).liveTemplateClient = client;\n }\n };\n\n // Initialize when DOM is ready\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", init);\n } else {\n init();\n }\n }\n\n /**\n * Handle server-sent updates delivered via WebSocket or HTTP fallback.\n */\n private handleWebSocketPayload(\n response: UpdateResponse,\n event?: MessageEvent<string>\n ): void {\n if (event) {\n (window as any).__lastWSMessage = event.data;\n\n if (!(window as any).__wsMessages) {\n (window as any).__wsMessages = [];\n }\n (window as any).__wsMessages.push(response);\n }\n\n // Check if this is an upload-specific message\n const uploadMessage = response as any;\n if (uploadMessage.type === \"upload_progress\") {\n this.uploadHandler.handleProgressMessage(\n uploadMessage as UploadProgressMessage\n );\n return;\n }\n\n // Check if this is an upload_start response\n if (uploadMessage.upload_name && uploadMessage.entries) {\n const startResponse = uploadMessage as UploadStartResponse;\n // Handle upload start response with error handling\n try {\n this.handleUploadStartResponse(startResponse);\n } catch (error) {\n this.logger.error(\"Error handling upload start response:\", error);\n }\n return;\n }\n\n // Check if this is an upload_complete response\n if (uploadMessage.upload_name && uploadMessage.hasOwnProperty('success')) {\n // UploadCompleteResponse - just log it, no tree update needed\n if (uploadMessage.success) {\n this.logger.info(`Upload complete: ${uploadMessage.upload_name}`);\n } else {\n this.logger.error(`Upload failed: ${uploadMessage.upload_name}`, uploadMessage.error);\n }\n return;\n }\n\n if (!this.isInitialized) {\n this.loadingIndicator.hide();\n this.formDisabler.enable(this.wrapperElement);\n if (\n this.wrapperElement &&\n this.wrapperElement.hasAttribute(\"data-lvt-loading\")\n ) {\n this.wrapperElement.removeAttribute(\"data-lvt-loading\");\n }\n this.isInitialized = true;\n }\n\n if (this.wrapperElement) {\n this.updateDOM(this.wrapperElement, response.tree, response.meta);\n this.messageCount++;\n (window as any).__wsMessageCount = this.messageCount;\n\n this.wrapperElement.dispatchEvent(\n new CustomEvent(\"lvt:updated\", {\n detail: {\n messageCount: this.messageCount,\n action: response.meta?.action,\n success: response.meta?.success,\n },\n })\n );\n }\n }\n\n /**\n * Connect to WebSocket and start receiving updates\n * @param wrapperSelector - CSS selector for the LiveTemplate wrapper (defaults to '[data-lvt-id]')\n */\n async connect(wrapperSelector: string = \"[data-lvt-id]\"): Promise<void> {\n // Find the wrapper element\n this.wrapperElement = document.querySelector(wrapperSelector);\n if (!this.wrapperElement) {\n throw new Error(\n `LiveTemplate wrapper not found with selector: ${wrapperSelector}`\n );\n }\n\n this.webSocketManager.disconnect();\n\n const connectionResult = await this.webSocketManager.connect();\n this.useHTTP = !connectionResult.usingWebSocket;\n\n if (this.useHTTP) {\n this.ws = null;\n this.logger.info(\"WebSocket not available, using HTTP mode\");\n this.options.onConnect?.();\n if (connectionResult.initialState && this.wrapperElement) {\n this.handleWebSocketPayload(connectionResult.initialState);\n }\n }\n // Set up event delegation for lvt-* attributes\n this.eventDelegator.setupEventDelegation();\n\n // Set up window-* event delegation\n this.eventDelegator.setupWindowEventDelegation();\n\n // Set up click-away delegation\n this.eventDelegator.setupClickAwayDelegation();\n\n // Set up modal delegation\n this.eventDelegator.setupModalDelegation();\n\n // Set up focus trap delegation for lvt-focus-trap attribute\n this.eventDelegator.setupFocusTrapDelegation();\n\n // Set up autofocus delegation for lvt-autofocus attribute\n this.eventDelegator.setupAutofocusDelegation();\n\n // Set up reactive attribute listeners for lvt-{action}-on:{event} attributes\n setupReactiveAttributeListeners();\n\n // Initialize focus tracking\n this.focusManager.attach(this.wrapperElement);\n\n // Set up infinite scroll observers\n this.observerManager.setupInfiniteScrollObserver();\n this.observerManager.setupInfiniteScrollMutationObserver();\n }\n\n /**\n * Disconnect from WebSocket\n */\n disconnect(): void {\n this.webSocketManager.disconnect();\n this.ws = null;\n this.useHTTP = false;\n this.observerManager.teardown();\n this.formLifecycleManager.reset();\n this.loadingIndicator.hide();\n this.formDisabler.enable(this.wrapperElement);\n }\n\n /**\n * Clear flash-related query parameters (error, success) from the URL.\n * This prevents stale flash messages from reappearing on page reload.\n * Uses history.replaceState to update URL without triggering navigation.\n */\n private clearFlashQueryParams(): void {\n const url = new URL(window.location.href);\n const flashParams = [\"error\", \"success\"];\n let hasFlashParams = false;\n\n for (const param of flashParams) {\n if (url.searchParams.has(param)) {\n url.searchParams.delete(param);\n hasFlashParams = true;\n }\n }\n\n if (hasFlashParams) {\n this.logger.debug(\"Clearing flash query params from URL\");\n window.history.replaceState(null, \"\", url.toString());\n }\n }\n\n /**\n * Determine whether the client finished its initial load and has an active transport.\n */\n isReady(): boolean {\n const wrapper = this.wrapperElement;\n\n if (!wrapper || wrapper.hasAttribute(\"data-lvt-loading\")) {\n return false;\n }\n\n if (this.useHTTP) {\n return true;\n }\n\n const readyState = this.webSocketManager.getReadyState();\n return readyState === 1; // WebSocket.OPEN = 1\n }\n\n /**\n * Send a message to the server via WebSocket or HTTP\n * @param message - Message to send (will be JSON stringified)\n */\n send(message: any): void {\n // Debug flag for testing\n (window as any).__lvtSendCalled = true;\n (window as any).__lvtMessageAction = message?.action;\n\n const readyState = this.webSocketManager.getReadyState();\n\n if (this.logger.isDebugEnabled()) {\n this.logger.debug(\"send() invoked\", {\n message,\n useHTTP: this.useHTTP,\n hasWebSocket: readyState !== undefined,\n readyState,\n });\n }\n\n if (this.useHTTP) {\n // HTTP mode: send via POST and handle response\n this.logger.debug(\"Using HTTP mode for send\");\n (window as any).__lvtSendPath = \"http\";\n this.sendHTTP(message);\n } else if (readyState === 1) { // WebSocket.OPEN = 1\n // WebSocket mode\n this.logger.debug(\"Sending via WebSocket\");\n (window as any).__lvtSendPath = \"websocket\";\n (window as any).__lvtWSMessage = JSON.stringify(message);\n this.webSocketManager.send(JSON.stringify(message));\n this.logger.debug(\"WebSocket send complete\");\n (window as any).__lvtWSSendComplete = true;\n } else if (readyState !== undefined) {\n // WebSocket is connecting or closing, fall back to HTTP temporarily\n this.logger.warn(\n `WebSocket not ready (state: ${readyState}), using HTTP fallback`\n );\n (window as any).__lvtSendPath = \"http-fallback\";\n this.sendHTTP(message);\n } else {\n this.logger.error(\"No transport available\");\n (window as any).__lvtSendPath = \"no-transport\";\n }\n }\n\n /**\n * Send action via HTTP POST\n */\n private async sendHTTP(message: any): Promise<void> {\n try {\n const liveUrl = this.options.liveUrl || \"/live\";\n const response = await fetch(liveUrl, {\n method: \"POST\",\n credentials: \"include\", // Include cookies for session\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n body: JSON.stringify(message),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP request failed: ${response.status}`);\n }\n\n // Handle the update response\n const updateResponse: UpdateResponse = await response.json();\n if (this.wrapperElement) {\n this.updateDOM(\n this.wrapperElement,\n updateResponse.tree,\n updateResponse.meta\n );\n }\n } catch (error) {\n this.logger.error(\"Failed to send HTTP request:\", error);\n }\n }\n\n /**\n * Parse a string value into appropriate type (number, boolean, or string)\n * @param value - String value to parse\n * @returns Parsed value with correct type\n */\n private parseValue(value: string): any {\n // Try to parse as number\n const num = parseFloat(value);\n if (!isNaN(num) && value.trim() === num.toString()) {\n return num;\n }\n\n // Try to parse as boolean\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n\n // Return as string\n return value;\n }\n\n /**\n * Apply an update to the current state and reconstruct HTML\n * @param update - Tree update object from LiveTemplate server\n * @returns Reconstructed HTML and whether anything changed\n */\n applyUpdate(update: TreeNode): UpdateResult {\n return this.treeRenderer.applyUpdate(update);\n }\n\n /**\n * Apply updates to existing HTML using morphdom for efficient DOM updates\n * @param existingHTML - Current full HTML document\n * @param update - Tree update object from LiveTemplate server\n * @returns Updated HTML content\n */\n applyUpdateToHTML(existingHTML: string, update: TreeNode): string {\n // Apply the update to our internal state\n const result = this.applyUpdate(update);\n\n // Extract lvt-id from existing HTML if we don't have it\n if (!this.lvtId) {\n const match = existingHTML.match(/data-lvt-id=\"([^\"]+)\"/);\n if (match) {\n this.lvtId = match[1];\n }\n }\n\n // The new tree includes complete HTML structure, so we can reconstruct properly\n const innerContent = result.html;\n\n // Find where to insert the reconstructed content\n const bodyMatch = existingHTML.match(/<body>([\\s\\S]*?)<\\/body>/);\n if (!bodyMatch) {\n return existingHTML;\n }\n\n // Replace the body content with our reconstructed HTML\n // We need to preserve the wrapper div with data-lvt-id\n const wrapperStart = `<div data-lvt-id=\"${this.lvtId || \"lvt-unknown\"}\">`;\n const wrapperEnd = \"</div>\";\n const newBodyContent = wrapperStart + innerContent + wrapperEnd;\n\n return existingHTML.replace(\n /<body>[\\s\\S]*?<\\/body>/,\n `<body>${newBodyContent}</body>`\n );\n }\n\n /**\n * Update a live DOM element with new tree data\n * @param element - DOM element containing the LiveTemplate content (the wrapper div)\n * @param update - Tree update object from LiveTemplate server\n * @param meta - Optional metadata about the update (action, success, errors)\n */\n updateDOM(element: Element, update: TreeNode, meta?: ResponseMetadata): void {\n // Apply update to internal state and get reconstructed HTML\n const result = this.applyUpdate(update);\n\n // Helper to recursively check if there are any statics in the tree\n const hasStaticsInTree = (node: any): boolean => {\n if (!node || typeof node !== \"object\") return false;\n if (node.s && Array.isArray(node.s)) return true;\n return Object.values(node).some((v) => hasStaticsInTree(v));\n };\n\n if (!result.changed && !hasStaticsInTree(update)) {\n // No changes detected and no statics in update, skip morphdom\n return;\n }\n\n // Create a temporary wrapper to hold the new content\n // We need to create a DOM element of the same type as 'element' to avoid browser HTML corrections\n // For example, if we put <tr> elements in a <div>, the browser strips them out\n const tempWrapper = document.createElement(element.tagName);\n\n if (this.logger.isDebugEnabled()) {\n this.logger.debug(\"[updateDOM] element.tagName:\", element.tagName);\n this.logger.debug(\n \"[updateDOM] result.html (first 500 chars):\",\n result.html.substring(0, 500)\n );\n this.logger.debug(\n \"[updateDOM] Has <table> tag:\",\n result.html.includes(\"<table>\")\n );\n this.logger.debug(\n \"[updateDOM] Has <tbody> tag:\",\n result.html.includes(\"<tbody>\")\n );\n this.logger.debug(\n \"[updateDOM] Has <tr> tag:\",\n result.html.includes(\"<tr\")\n );\n }\n\n tempWrapper.innerHTML = result.html;\n\n if (this.logger.isDebugEnabled()) {\n this.logger.debug(\n \"[updateDOM] tempWrapper.innerHTML (first 500 chars):\",\n tempWrapper.innerHTML.substring(0, 500)\n );\n this.logger.debug(\n \"[updateDOM] tempWrapper has <table>:\",\n tempWrapper.innerHTML.includes(\"<table>\")\n );\n this.logger.debug(\n \"[updateDOM] tempWrapper has <tbody>:\",\n tempWrapper.innerHTML.includes(\"<tbody>\")\n );\n this.logger.debug(\n \"[updateDOM] tempWrapper has <tr>:\",\n tempWrapper.innerHTML.includes(\"<tr\")\n );\n }\n\n // Use morphdom to efficiently update the element\n morphdom(element, tempWrapper, {\n childrenOnly: true, // Only update children, preserve the wrapper element itself\n getNodeKey: (node: any) => {\n // Use data-key or data-lvt-key for efficient reconciliation\n if (node.nodeType === 1) {\n return (\n node.getAttribute(\"data-key\") ||\n node.getAttribute(\"data-lvt-key\") ||\n undefined\n );\n }\n },\n onBeforeElUpdated: (fromEl, toEl) => {\n // Preserve value for the last focused textual input\n const lastFocused = this.focusManager.getLastFocusedElement();\n if (lastFocused && this.focusManager.isTextualInput(fromEl)) {\n if (fromEl === lastFocused) {\n // Preserve the current value being typed\n (toEl as any).value = (fromEl as any).value;\n }\n }\n\n // Only update if content actually changed\n if (fromEl.isEqualNode(toEl)) {\n return false;\n }\n // Execute lvt-updated lifecycle hook\n this.executeLifecycleHook(fromEl, \"lvt-updated\");\n return true;\n },\n onNodeAdded: (node) => {\n // Execute lvt-mounted lifecycle hook\n if (node.nodeType === Node.ELEMENT_NODE) {\n this.executeLifecycleHook(node as Element, \"lvt-mounted\");\n }\n },\n onBeforeNodeDiscarded: (node) => {\n // Execute lvt-destroyed lifecycle hook\n if (node.nodeType === Node.ELEMENT_NODE) {\n this.executeLifecycleHook(node as Element, \"lvt-destroyed\");\n }\n return true;\n },\n });\n\n // Restore focus to previously focused element\n this.focusManager.restoreFocusedElement();\n\n // Handle scroll directives\n handleScrollDirectives(element);\n\n // Handle highlight directives\n handleHighlightDirectives(element);\n\n // Handle animate directives\n handleAnimateDirectives(element);\n\n // Initialize upload file inputs\n this.uploadHandler.initializeFileInputs(element);\n\n // Handle form lifecycle if metadata is present\n if (meta) {\n this.formLifecycleManager.handleResponse(meta);\n }\n }\n\n /**\n * Handle upload_start response from server\n */\n private handleUploadStartResponse(response: UploadStartResponse): void {\n this.uploadHandler.handleUploadStartResponse(response);\n }\n\n /**\n * Execute lifecycle hook on an element\n * @param element - Element with lifecycle hook attribute\n * @param hookName - Name of the lifecycle hook attribute (e.g., 'lvt-mounted')\n */\n private executeLifecycleHook(element: Element, hookName: string): void {\n const hookValue = element.getAttribute(hookName);\n if (!hookValue) {\n return;\n }\n\n try {\n // Create a function from the hook value and execute it\n // The function has access to 'this' (the element) and 'event'\n const hookFunction = new Function(\"element\", hookValue);\n hookFunction.call(element, element);\n } catch (error) {\n this.logger.error(`Error executing ${hookName} hook:`, error);\n }\n }\n\n /**\n * Reset client state (useful for testing)\n */\n reset(): void {\n this.treeRenderer.reset();\n this.focusManager.reset();\n this.observerManager.teardown();\n this.formLifecycleManager.reset();\n this.loadingIndicator.hide();\n this.formDisabler.enable(this.wrapperElement);\n this.lvtId = null;\n }\n\n /**\n * Get current tree state (for debugging)\n */\n getTreeState(): TreeNode {\n return this.treeRenderer.getTreeState();\n }\n\n /**\n * Get the static structure if available\n */\n getStaticStructure(): string[] | null {\n return this.treeRenderer.getStaticStructure();\n }\n}\n\n// Auto-initialize when script loads (for browser use)\nif (typeof window !== \"undefined\") {\n LiveTemplateClient.autoInit();\n}\n", "var DOCUMENT_FRAGMENT_NODE = 11;\n\nfunction morphAttrs(fromNode, toNode) {\n var toNodeAttrs = toNode.attributes;\n var attr;\n var attrName;\n var attrNamespaceURI;\n var attrValue;\n var fromValue;\n\n // document-fragments dont have attributes so lets not do anything\n if (toNode.nodeType === DOCUMENT_FRAGMENT_NODE || fromNode.nodeType === DOCUMENT_FRAGMENT_NODE) {\n return;\n }\n\n // update attributes on original DOM element\n for (var i = toNodeAttrs.length - 1; i >= 0; i--) {\n attr = toNodeAttrs[i];\n attrName = attr.name;\n attrNamespaceURI = attr.namespaceURI;\n attrValue = attr.value;\n\n if (attrNamespaceURI) {\n attrName = attr.localName || attrName;\n fromValue = fromNode.getAttributeNS(attrNamespaceURI, attrName);\n\n if (fromValue !== attrValue) {\n if (attr.prefix === 'xmlns'){\n attrName = attr.name; // It's not allowed to set an attribute with the XMLNS namespace without specifying the `xmlns` prefix\n }\n fromNode.setAttributeNS(attrNamespaceURI, attrName, attrValue);\n }\n } else {\n fromValue = fromNode.getAttribute(attrName);\n\n if (fromValue !== attrValue) {\n fromNode.setAttribute(attrName, attrValue);\n }\n }\n }\n\n // Remove any extra attributes found on the original DOM element that\n // weren't found on the target element.\n var fromNodeAttrs = fromNode.attributes;\n\n for (var d = fromNodeAttrs.length - 1; d >= 0; d--) {\n attr = fromNodeAttrs[d];\n attrName = attr.name;\n attrNamespaceURI = attr.namespaceURI;\n\n if (attrNamespaceURI) {\n attrName = attr.localName || attrName;\n\n if (!toNode.hasAttributeNS(attrNamespaceURI, attrName)) {\n fromNode.removeAttributeNS(attrNamespaceURI, attrName);\n }\n } else {\n if (!toNode.hasAttribute(attrName)) {\n fromNode.removeAttribute(attrName);\n }\n }\n }\n}\n\nvar range; // Create a range object for efficently rendering strings to elements.\nvar NS_XHTML = 'http://www.w3.org/1999/xhtml';\n\nvar doc = typeof document === 'undefined' ? undefined : document;\nvar HAS_TEMPLATE_SUPPORT = !!doc && 'content' in doc.createElement('template');\nvar HAS_RANGE_SUPPORT = !!doc && doc.createRange && 'createContextualFragment' in doc.createRange();\n\nfunction createFragmentFromTemplate(str) {\n var template = doc.createElement('template');\n template.innerHTML = str;\n return template.content.childNodes[0];\n}\n\nfunction createFragmentFromRange(str) {\n if (!range) {\n range = doc.createRange();\n range.selectNode(doc.body);\n }\n\n var fragment = range.createContextualFragment(str);\n return fragment.childNodes[0];\n}\n\nfunction createFragmentFromWrap(str) {\n var fragment = doc.createElement('body');\n fragment.innerHTML = str;\n return fragment.childNodes[0];\n}\n\n/**\n * This is about the same\n * var html = new DOMParser().parseFromString(str, 'text/html');\n * return html.body.firstChild;\n *\n * @method toElement\n * @param {String} str\n */\nfunction toElement(str) {\n str = str.trim();\n if (HAS_TEMPLATE_SUPPORT) {\n // avoid restrictions on content for things like `<tr><th>Hi</th></tr>` which\n // createContextualFragment doesn't support\n // <template> support not available in IE\n return createFragmentFromTemplate(str);\n } else if (HAS_RANGE_SUPPORT) {\n return createFragmentFromRange(str);\n }\n\n return createFragmentFromWrap(str);\n}\n\n/**\n * Returns true if two node's names are the same.\n *\n * NOTE: We don't bother checking `namespaceURI` because you will never find two HTML elements with the same\n * nodeName and different namespace URIs.\n *\n * @param {Element} a\n * @param {Element} b The target element\n * @return {boolean}\n */\nfunction compareNodeNames(fromEl, toEl) {\n var fromNodeName = fromEl.nodeName;\n var toNodeName = toEl.nodeName;\n var fromCodeStart, toCodeStart;\n\n if (fromNodeName === toNodeName) {\n return true;\n }\n\n fromCodeStart = fromNodeName.charCodeAt(0);\n toCodeStart = toNodeName.charCodeAt(0);\n\n // If the target element is a virtual DOM node or SVG node then we may\n // need to normalize the tag name before comparing. Normal HTML elements that are\n // in the \"http://www.w3.org/1999/xhtml\"\n // are converted to upper case\n if (fromCodeStart <= 90 && toCodeStart >= 97) { // from is upper and to is lower\n return fromNodeName === toNodeName.toUpperCase();\n } else if (toCodeStart <= 90 && fromCodeStart >= 97) { // to is upper and from is lower\n return toNodeName === fromNodeName.toUpperCase();\n } else {\n return false;\n }\n}\n\n/**\n * Create an element, optionally with a known namespace URI.\n *\n * @param {string} name the element name, e.g. 'div' or 'svg'\n * @param {string} [namespaceURI] the element's namespace URI, i.e. the value of\n * its `xmlns` attribute or its inferred namespace.\n *\n * @return {Element}\n */\nfunction createElementNS(name, namespaceURI) {\n return !namespaceURI || namespaceURI === NS_XHTML ?\n doc.createElement(name) :\n doc.createElementNS(namespaceURI, name);\n}\n\n/**\n * Copies the children of one DOM element to another DOM element\n */\nfunction moveChildren(fromEl, toEl) {\n var curChild = fromEl.firstChild;\n while (curChild) {\n var nextChild = curChild.nextSibling;\n toEl.appendChild(curChild);\n curChild = nextChild;\n }\n return toEl;\n}\n\nfunction syncBooleanAttrProp(fromEl, toEl, name) {\n if (fromEl[name] !== toEl[name]) {\n fromEl[name] = toEl[name];\n if (fromEl[name]) {\n fromEl.setAttribute(name, '');\n } else {\n fromEl.removeAttribute(name);\n }\n }\n}\n\nvar specialElHandlers = {\n OPTION: function(fromEl, toEl) {\n var parentNode = fromEl.parentNode;\n if (parentNode) {\n var parentName = parentNode.nodeName.toUpperCase();\n if (parentName === 'OPTGROUP') {\n parentNode = parentNode.parentNode;\n parentName = parentNode && parentNode.nodeName.toUpperCase();\n }\n if (parentName === 'SELECT' && !parentNode.hasAttribute('multiple')) {\n if (fromEl.hasAttribute('selected') && !toEl.selected) {\n // Workaround for MS Edge bug where the 'selected' attribute can only be\n // removed if set to a non-empty value:\n // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12087679/\n fromEl.setAttribute('selected', 'selected');\n fromEl.removeAttribute('selected');\n }\n // We have to reset select element's selectedIndex to -1, otherwise setting\n // fromEl.selected using the syncBooleanAttrProp below has no effect.\n // The correct selectedIndex will be set in the SELECT special handler below.\n parentNode.selectedIndex = -1;\n }\n }\n syncBooleanAttrProp(fromEl, toEl, 'selected');\n },\n /**\n * The \"value\" attribute is special for the <input> element since it sets\n * the initial value. Changing the \"value\" attribute without changing the\n * \"value\" property will have no effect since it is only used to the set the\n * initial value. Similar for the \"checked\" attribute, and \"disabled\".\n */\n INPUT: function(fromEl, toEl) {\n syncBooleanAttrProp(fromEl, toEl, 'checked');\n syncBooleanAttrProp(fromEl, toEl, 'disabled');\n\n if (fromEl.value !== toEl.value) {\n fromEl.value = toEl.value;\n }\n\n if (!toEl.hasAttribute('value')) {\n fromEl.removeAttribute('value');\n }\n },\n\n TEXTAREA: function(fromEl, toEl) {\n var newValue = toEl.value;\n if (fromEl.value !== newValue) {\n fromEl.value = newValue;\n }\n\n var firstChild = fromEl.firstChild;\n if (firstChild) {\n // Needed for IE. Apparently IE sets the placeholder as the\n // node value and vise versa. This ignores an empty update.\n var oldValue = firstChild.nodeValue;\n\n if (oldValue == newValue || (!newValue && oldValue == fromEl.placeholder)) {\n return;\n }\n\n firstChild.nodeValue = newValue;\n }\n },\n SELECT: function(fromEl, toEl) {\n if (!toEl.hasAttribute('multiple')) {\n var selectedIndex = -1;\n var i = 0;\n // We have to loop through children of fromEl, not toEl since nodes can be moved\n // from toEl to fromEl directly when morphing.\n // At the time this special handler is invoked, all children have already been morphed\n // and appended to / removed from fromEl, so using fromEl here is safe and correct.\n var curChild = fromEl.firstChild;\n var optgroup;\n var nodeName;\n while(curChild) {\n nodeName = curChild.nodeName && curChild.nodeName.toUpperCase();\n if (nodeName === 'OPTGROUP') {\n optgroup = curChild;\n curChild = optgroup.firstChild;\n // handle empty optgroups\n if (!curChild) {\n curChild = optgroup.nextSibling;\n optgroup = null;\n }\n } else {\n if (nodeName === 'OPTION') {\n if (curChild.hasAttribute('selected')) {\n selectedIndex = i;\n break;\n }\n i++;\n }\n curChild = curChild.nextSibling;\n if (!curChild && optgroup) {\n curChild = optgroup.nextSibling;\n optgroup = null;\n }\n }\n }\n\n fromEl.selectedIndex = selectedIndex;\n }\n }\n};\n\nvar ELEMENT_NODE = 1;\nvar DOCUMENT_FRAGMENT_NODE$1 = 11;\nvar TEXT_NODE = 3;\nvar COMMENT_NODE = 8;\n\nfunction noop() {}\n\nfunction defaultGetNodeKey(node) {\n if (node) {\n return (node.getAttribute && node.getAttribute('id')) || node.id;\n }\n}\n\nfunction morphdomFactory(morphAttrs) {\n\n return function morphdom(fromNode, toNode, options) {\n if (!options) {\n options = {};\n }\n\n if (typeof toNode === 'string') {\n if (fromNode.nodeName === '#document' || fromNode.nodeName === 'HTML' || fromNode.nodeName === 'BODY') {\n var toNodeHtml = toNode;\n toNode = doc.createElement('html');\n toNode.innerHTML = toNodeHtml;\n } else {\n toNode = toElement(toNode);\n }\n } else if (toNode.nodeType === DOCUMENT_FRAGMENT_NODE$1) {\n toNode = toNode.firstElementChild;\n }\n\n var getNodeKey = options.getNodeKey || defaultGetNodeKey;\n var onBeforeNodeAdded = options.onBeforeNodeAdded || noop;\n var onNodeAdded = options.onNodeAdded || noop;\n var onBeforeElUpdated = options.onBeforeElUpdated || noop;\n var onElUpdated = options.onElUpdated || noop;\n var onBeforeNodeDiscarded = options.onBeforeNodeDiscarded || noop;\n var onNodeDiscarded = options.onNodeDiscarded || noop;\n var onBeforeElChildrenUpdated = options.onBeforeElChildrenUpdated || noop;\n var skipFromChildren = options.skipFromChildren || noop;\n var addChild = options.addChild || function(parent, child){ return parent.appendChild(child); };\n var childrenOnly = options.childrenOnly === true;\n\n // This object is used as a lookup to quickly find all keyed elements in the original DOM tree.\n var fromNodesLookup = Object.create(null);\n var keyedRemovalList = [];\n\n function addKeyedRemoval(key) {\n keyedRemovalList.push(key);\n }\n\n function walkDiscardedChildNodes(node, skipKeyedNodes) {\n if (node.nodeType === ELEMENT_NODE) {\n var curChild = node.firstChild;\n while (curChild) {\n\n var key = undefined;\n\n if (skipKeyedNodes && (key = getNodeKey(curChild))) {\n // If we are skipping keyed nodes then we add the key\n // to a list so that it can be handled at the very end.\n addKeyedRemoval(key);\n } else {\n // Only report the node as discarded if it is not keyed. We do this because\n // at the end we loop through all keyed elements that were unmatched\n // and then discard them in one final pass.\n onNodeDiscarded(curChild);\n if (curChild.firstChild) {\n walkDiscardedChildNodes(curChild, skipKeyedNodes);\n }\n }\n\n curChild = curChild.nextSibling;\n }\n }\n }\n\n /**\n * Removes a DOM node out of the original DOM\n *\n * @param {Node} node The node to remove\n * @param {Node} parentNode The nodes parent\n * @param {Boolean} skipKeyedNodes If true then elements with keys will be skipped and not discarded.\n * @return {undefined}\n */\n function removeNode(node, parentNode, skipKeyedNodes) {\n if (onBeforeNodeDiscarded(node) === false) {\n return;\n }\n\n if (parentNode) {\n parentNode.removeChild(node);\n }\n\n onNodeDiscarded(node);\n walkDiscardedChildNodes(node, skipKeyedNodes);\n }\n\n // // TreeWalker implementation is no faster, but keeping this around in case this changes in the future\n // function indexTree(root) {\n // var treeWalker = document.createTreeWalker(\n // root,\n // NodeFilter.SHOW_ELEMENT);\n //\n // var el;\n // while((el = treeWalker.nextNode())) {\n // var key = getNodeKey(el);\n // if (key) {\n // fromNodesLookup[key] = el;\n // }\n // }\n // }\n\n // // NodeIterator implementation is no faster, but keeping this around in case this changes in the future\n //\n // function indexTree(node) {\n // var nodeIterator = document.createNodeIterator(node, NodeFilter.SHOW_ELEMENT);\n // var el;\n // while((el = nodeIterator.nextNode())) {\n // var key = getNodeKey(el);\n // if (key) {\n // fromNodesLookup[key] = el;\n // }\n // }\n // }\n\n function indexTree(node) {\n if (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE$1) {\n var curChild = node.firstChild;\n while (curChild) {\n var key = getNodeKey(curChild);\n if (key) {\n fromNodesLookup[key] = curChild;\n }\n\n // Walk recursively\n indexTree(curChild);\n\n curChild = curChild.nextSibling;\n }\n }\n }\n\n indexTree(fromNode);\n\n function handleNodeAdded(el) {\n onNodeAdded(el);\n\n var curChild = el.firstChild;\n while (curChild) {\n var nextSibling = curChild.nextSibling;\n\n var key = getNodeKey(curChild);\n if (key) {\n var unmatchedFromEl = fromNodesLookup[key];\n // if we find a duplicate #id node in cache, replace `el` with cache value\n // and morph it to the child node.\n if (unmatchedFromEl && compareNodeNames(curChild, unmatchedFromEl)) {\n curChild.parentNode.replaceChild(unmatchedFromEl, curChild);\n morphEl(unmatchedFromEl, curChild);\n } else {\n handleNodeAdded(curChild);\n }\n } else {\n // recursively call for curChild and it's children to see if we find something in\n // fromNodesLookup\n handleNodeAdded(curChild);\n }\n\n curChild = nextSibling;\n }\n }\n\n function cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey) {\n // We have processed all of the \"to nodes\". If curFromNodeChild is\n // non-null then we still have some from nodes left over that need\n // to be removed\n while (curFromNodeChild) {\n var fromNextSibling = curFromNodeChild.nextSibling;\n if ((curFromNodeKey = getNodeKey(curFromNodeChild))) {\n // Since the node is keyed it might be matched up later so we defer\n // the actual removal to later\n addKeyedRemoval(curFromNodeKey);\n } else {\n // NOTE: we skip nested keyed nodes from being removed since there is\n // still a chance they will be matched up later\n removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);\n }\n curFromNodeChild = fromNextSibling;\n }\n }\n\n function morphEl(fromEl, toEl, childrenOnly) {\n var toElKey = getNodeKey(toEl);\n\n if (toElKey) {\n // If an element with an ID is being morphed then it will be in the final\n // DOM so clear it out of the saved elements collection\n delete fromNodesLookup[toElKey];\n }\n\n if (!childrenOnly) {\n // optional\n var beforeUpdateResult = onBeforeElUpdated(fromEl, toEl);\n if (beforeUpdateResult === false) {\n return;\n } else if (beforeUpdateResult instanceof HTMLElement) {\n fromEl = beforeUpdateResult;\n // reindex the new fromEl in case it's not in the same\n // tree as the original fromEl\n // (Phoenix LiveView sometimes returns a cloned tree,\n // but keyed lookups would still point to the original tree)\n indexTree(fromEl);\n }\n\n // update attributes on original DOM element first\n morphAttrs(fromEl, toEl);\n // optional\n onElUpdated(fromEl);\n\n if (onBeforeElChildrenUpdated(fromEl, toEl) === false) {\n return;\n }\n }\n\n if (fromEl.nodeName !== 'TEXTAREA') {\n morphChildren(fromEl, toEl);\n } else {\n specialElHandlers.TEXTAREA(fromEl, toEl);\n }\n }\n\n function morphChildren(fromEl, toEl) {\n var skipFrom = skipFromChildren(fromEl, toEl);\n var curToNodeChild = toEl.firstChild;\n var curFromNodeChild = fromEl.firstChild;\n var curToNodeKey;\n var curFromNodeKey;\n\n var fromNextSibling;\n var toNextSibling;\n var matchingFromEl;\n\n // walk the children\n outer: while (curToNodeChild) {\n toNextSibling = curToNodeChild.nextSibling;\n curToNodeKey = getNodeKey(curToNodeChild);\n\n // walk the fromNode children all the way through\n while (!skipFrom && curFromNodeChild) {\n fromNextSibling = curFromNodeChild.nextSibling;\n\n if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {\n curToNodeChild = toNextSibling;\n curFromNodeChild = fromNextSibling;\n continue outer;\n }\n\n curFromNodeKey = getNodeKey(curFromNodeChild);\n\n var curFromNodeType = curFromNodeChild.nodeType;\n\n // this means if the curFromNodeChild doesnt have a match with the curToNodeChild\n var isCompatible = undefined;\n\n if (curFromNodeType === curToNodeChild.nodeType) {\n if (curFromNodeType === ELEMENT_NODE) {\n // Both nodes being compared are Element nodes\n\n if (curToNodeKey) {\n // The target node has a key so we want to match it up with the correct element\n // in the original DOM tree\n if (curToNodeKey !== curFromNodeKey) {\n // The current element in the original DOM tree does not have a matching key so\n // let's check our lookup to see if there is a matching element in the original\n // DOM tree\n if ((matchingFromEl = fromNodesLookup[curToNodeKey])) {\n if (fromNextSibling === matchingFromEl) {\n // Special case for single element removals. To avoid removing the original\n // DOM node out of the tree (since that can break CSS transitions, etc.),\n // we will instead discard the current node and wait until the next\n // iteration to properly match up the keyed target element with its matching\n // element in the original tree\n isCompatible = false;\n } else {\n // We found a matching keyed element somewhere in the original DOM tree.\n // Let's move the original DOM node into the current position and morph\n // it.\n\n // NOTE: We use insertBefore instead of replaceChild because we want to go through\n // the `removeNode()` function for the node that is being discarded so that\n // all lifecycle hooks are correctly invoked\n fromEl.insertBefore(matchingFromEl, curFromNodeChild);\n\n // fromNextSibling = curFromNodeChild.nextSibling;\n\n if (curFromNodeKey) {\n // Since the node is keyed it might be matched up later so we defer\n // the actual removal to later\n addKeyedRemoval(curFromNodeKey);\n } else {\n // NOTE: we skip nested keyed nodes from being removed since there is\n // still a chance they will be matched up later\n removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);\n }\n\n curFromNodeChild = matchingFromEl;\n curFromNodeKey = getNodeKey(curFromNodeChild);\n }\n } else {\n // The nodes are not compatible since the \"to\" node has a key and there\n // is no matching keyed node in the source tree\n isCompatible = false;\n }\n }\n } else if (curFromNodeKey) {\n // The original has a key\n isCompatible = false;\n }\n\n isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);\n if (isCompatible) {\n // We found compatible DOM elements so transform\n // the current \"from\" node to match the current\n // target DOM node.\n // MORPH\n morphEl(curFromNodeChild, curToNodeChild);\n }\n\n } else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {\n // Both nodes being compared are Text or Comment nodes\n isCompatible = true;\n // Simply update nodeValue on the original node to\n // change the text value\n if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {\n curFromNodeChild.nodeValue = curToNodeChild.nodeValue;\n }\n\n }\n }\n\n if (isCompatible) {\n // Advance both the \"to\" child and the \"from\" child since we found a match\n // Nothing else to do as we already recursively called morphChildren above\n curToNodeChild = toNextSibling;\n curFromNodeChild = fromNextSibling;\n continue outer;\n }\n\n // No compatible match so remove the old node from the DOM and continue trying to find a\n // match in the original DOM. However, we only do this if the from node is not keyed\n // since it is possible that a keyed node might match up with a node somewhere else in the\n // target tree and we don't want to discard it just yet since it still might find a\n // home in the final DOM tree. After everything is done we will remove any keyed nodes\n // that didn't find a home\n if (curFromNodeKey) {\n // Since the node is keyed it might be matched up later so we defer\n // the actual removal to later\n addKeyedRemoval(curFromNodeKey);\n } else {\n // NOTE: we skip nested keyed nodes from being removed since there is\n // still a chance they will be matched up later\n removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);\n }\n\n curFromNodeChild = fromNextSibling;\n } // END: while(curFromNodeChild) {}\n\n // If we got this far then we did not find a candidate match for\n // our \"to node\" and we exhausted all of the children \"from\"\n // nodes. Therefore, we will just append the current \"to\" node\n // to the end\n if (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {\n // MORPH\n if(!skipFrom){ addChild(fromEl, matchingFromEl); }\n morphEl(matchingFromEl, curToNodeChild);\n } else {\n var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);\n if (onBeforeNodeAddedResult !== false) {\n if (onBeforeNodeAddedResult) {\n curToNodeChild = onBeforeNodeAddedResult;\n }\n\n if (curToNodeChild.actualize) {\n curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);\n }\n addChild(fromEl, curToNodeChild);\n handleNodeAdded(curToNodeChild);\n }\n }\n\n curToNodeChild = toNextSibling;\n curFromNodeChild = fromNextSibling;\n }\n\n cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey);\n\n var specialElHandler = specialElHandlers[fromEl.nodeName];\n if (specialElHandler) {\n specialElHandler(fromEl, toEl);\n }\n } // END: morphChildren(...)\n\n var morphedNode = fromNode;\n var morphedNodeType = morphedNode.nodeType;\n var toNodeType = toNode.nodeType;\n\n if (!childrenOnly) {\n // Handle the case where we are given two DOM nodes that are not\n // compatible (e.g. <div> --> <span> or <div> --> TEXT)\n if (morphedNodeType === ELEMENT_NODE) {\n if (toNodeType === ELEMENT_NODE) {\n if (!compareNodeNames(fromNode, toNode)) {\n onNodeDiscarded(fromNode);\n morphedNode = moveChildren(fromNode, createElementNS(toNode.nodeName, toNode.namespaceURI));\n }\n } else {\n // Going from an element node to a text node\n morphedNode = toNode;\n }\n } else if (morphedNodeType === TEXT_NODE || morphedNodeType === COMMENT_NODE) { // Text or comment node\n if (toNodeType === morphedNodeType) {\n if (morphedNode.nodeValue !== toNode.nodeValue) {\n morphedNode.nodeValue = toNode.nodeValue;\n }\n\n return morphedNode;\n } else {\n // Text node to something else\n morphedNode = toNode;\n }\n }\n }\n\n if (morphedNode === toNode) {\n // The \"to node\" was not compatible with the \"from node\" so we had to\n // toss out the \"from node\" and use the \"to node\"\n onNodeDiscarded(fromNode);\n } else {\n if (toNode.isSameNode && toNode.isSameNode(morphedNode)) {\n return;\n }\n\n morphEl(morphedNode, toNode, childrenOnly);\n\n // We now need to loop over any keyed nodes that might need to be\n // removed. We only do the removal if we know that the keyed node\n // never found a match. When a keyed node is matched up we remove\n // it out of fromNodesLookup and we use fromNodesLookup to determine\n // if a keyed node has been matched up or not\n if (keyedRemovalList) {\n for (var i=0, len=keyedRemovalList.length; i<len; i++) {\n var elToRemove = fromNodesLookup[keyedRemovalList[i]];\n if (elToRemove) {\n removeNode(elToRemove, elToRemove.parentNode, false);\n }\n }\n }\n }\n\n if (!childrenOnly && morphedNode !== fromNode && fromNode.parentNode) {\n if (morphedNode.actualize) {\n morphedNode = morphedNode.actualize(fromNode.ownerDocument || doc);\n }\n // If we had to swap out the from node with a new node because the old\n // node was not compatible with the target node then we need to\n // replace the old DOM node in the original DOM tree. This is only\n // possible if the original DOM node was part of a DOM tree which\n // we know is the case if it has a parent node.\n fromNode.parentNode.replaceChild(morphedNode, fromNode);\n }\n\n return morphedNode;\n };\n}\n\nvar morphdom = morphdomFactory(morphAttrs);\n\nexport default morphdom;\n", "export const FOCUSABLE_INPUTS = [\n \"text\",\n \"textarea\",\n \"number\",\n \"email\",\n \"password\",\n \"search\",\n \"tel\",\n \"url\",\n \"date\",\n \"time\",\n \"datetime-local\",\n \"color\",\n \"range\",\n];\n", "import { FOCUSABLE_INPUTS } from \"../constants\";\nimport type { Logger } from \"../utils/logger\";\n\nexport class FocusManager {\n private wrapperElement: Element | null = null;\n private focusableElements: HTMLElement[] = [];\n private lastFocusedElement: HTMLElement | null = null;\n private lastFocusedSelectionStart: number | null = null;\n private lastFocusedSelectionEnd: number | null = null;\n\n constructor(private readonly logger: Logger) {}\n\n attach(wrapper: Element | null): void {\n this.wrapperElement = wrapper;\n\n if (!wrapper) {\n return;\n }\n\n this.updateFocusableElements();\n this.setupFocusTracking();\n }\n\n reset(): void {\n this.wrapperElement = null;\n this.focusableElements = [];\n this.lastFocusedElement = null;\n this.lastFocusedSelectionStart = null;\n this.lastFocusedSelectionEnd = null;\n }\n\n updateFocusableElements(): void {\n if (!this.wrapperElement) return;\n\n const inputSelectors = FOCUSABLE_INPUTS.map((type) =>\n type === \"textarea\"\n ? \"textarea:not([disabled])\"\n : `input[type=\"${type}\"]:not([disabled])`\n ).join(\", \");\n\n const otherFocusable =\n 'select:not([disabled]), button:not([disabled]), [contenteditable=\"true\"], [tabindex]:not([tabindex=\"-1\"])';\n const selector = `${inputSelectors}, ${otherFocusable}`;\n\n this.focusableElements = Array.from(\n this.wrapperElement.querySelectorAll(selector)\n );\n }\n\n setupFocusTracking(): void {\n if (!this.wrapperElement) return;\n\n const wrapperId = this.wrapperElement.getAttribute(\"data-lvt-id\");\n const focusKey = `__lvt_focus_tracker_${wrapperId}`;\n const blurKey = `__lvt_blur_tracker_${wrapperId}`;\n\n const focusListener = (event: Event) => {\n const target = event.target as HTMLElement;\n if (!target || !this.wrapperElement?.contains(target)) return;\n\n if (this.isTextualInput(target) || target instanceof HTMLSelectElement) {\n this.lastFocusedElement = target;\n this.logger.debug(\n \"[Focus] Tracked focus on:\",\n target.tagName,\n target.id || target.getAttribute(\"name\")\n );\n\n if (this.isTextualInput(target)) {\n this.lastFocusedSelectionStart = target.selectionStart;\n this.lastFocusedSelectionEnd = target.selectionEnd;\n }\n }\n };\n\n const blurListener = (event: Event) => {\n const target = event.target as HTMLElement;\n if (!target || !this.wrapperElement?.contains(target)) return;\n\n if (this.isTextualInput(target) && target === this.lastFocusedElement) {\n this.lastFocusedSelectionStart = target.selectionStart;\n this.lastFocusedSelectionEnd = target.selectionEnd;\n this.logger.debug(\n \"[Focus] Saved cursor on blur:\",\n this.lastFocusedSelectionStart,\n \"-\",\n this.lastFocusedSelectionEnd\n );\n }\n };\n\n if ((document as any)[focusKey]) {\n document.removeEventListener(\"focus\", (document as any)[focusKey], true);\n }\n if ((document as any)[blurKey]) {\n document.removeEventListener(\"blur\", (document as any)[blurKey], true);\n }\n\n (document as any)[focusKey] = focusListener;\n (document as any)[blurKey] = blurListener;\n\n document.addEventListener(\"focus\", focusListener, true);\n document.addEventListener(\"blur\", blurListener, true);\n\n this.logger.debug(\"[Focus] Focus tracking set up\");\n }\n\n restoreFocusedElement(): void {\n this.logger.debug(\n \"[Focus] restoreFocusedElement - lastFocusedElement:\",\n this.lastFocusedElement?.tagName,\n this.lastFocusedElement?.id ||\n this.lastFocusedElement?.getAttribute(\"name\")\n );\n\n if (!this.lastFocusedElement || !this.wrapperElement) {\n this.logger.debug(\"[Focus] No element to restore\");\n return;\n }\n\n const selector = this.getElementSelector(this.lastFocusedElement);\n this.logger.debug(\"[Focus] Selector for last focused:\", selector);\n\n if (!selector) {\n this.logger.debug(\"[Focus] Could not generate selector\");\n return;\n }\n\n let element: HTMLElement | null = null;\n\n if (selector.startsWith(\"data-focus-index-\")) {\n this.updateFocusableElements();\n const index = parseInt(selector.replace(\"data-focus-index-\", \"\"), 10);\n element = this.focusableElements[index] || null;\n this.logger.debug(\"[Focus] Found by index:\", index, element?.tagName);\n } else {\n element = this.wrapperElement.querySelector(selector);\n this.logger.debug(\n \"[Focus] Found by selector:\",\n selector,\n element?.tagName\n );\n }\n\n if (!element) {\n this.logger.debug(\"[Focus] Element not found in updated DOM\");\n return;\n }\n\n const wasFocused = element.matches(\":focus\");\n this.logger.debug(\"[Focus] Already focused:\", wasFocused);\n\n if (!wasFocused) {\n element.focus();\n this.logger.debug(\"[Focus] Restored focus\");\n }\n\n if (\n this.isTextualInput(element) &&\n this.lastFocusedSelectionStart !== null &&\n this.lastFocusedSelectionEnd !== null\n ) {\n element.setSelectionRange(\n this.lastFocusedSelectionStart,\n this.lastFocusedSelectionEnd\n );\n this.logger.debug(\n \"[Focus] Restored cursor:\",\n this.lastFocusedSelectionStart,\n \"-\",\n this.lastFocusedSelectionEnd\n );\n }\n }\n\n isTextualInput(el: Element): el is HTMLInputElement | HTMLTextAreaElement {\n if (el instanceof HTMLTextAreaElement) return true;\n if (el instanceof HTMLInputElement) {\n return FOCUSABLE_INPUTS.indexOf(el.type) >= 0;\n }\n return false;\n }\n\n getLastFocusedElement(): HTMLElement | null {\n return this.lastFocusedElement;\n }\n\n private getElementSelector(el: HTMLElement): string | null {\n if (el.id) return `#${el.id}`;\n if ((el as any).name) return `[name=\"${(el as any).name}\"]`;\n if (el.getAttribute(\"data-key\"))\n return `[data-key=\"${el.getAttribute(\"data-key\")}\"]`;\n\n const index = this.focusableElements.indexOf(el);\n return index >= 0 ? `data-focus-index-${index}` : null;\n }\n}\n", "/**\n * Apply scroll directives on elements with lvt-scroll attributes.\n */\nexport function handleScrollDirectives(rootElement: Element): void {\n const scrollElements = rootElement.querySelectorAll(\"[lvt-scroll]\");\n\n scrollElements.forEach((element) => {\n const htmlElement = element as HTMLElement;\n const mode = htmlElement.getAttribute(\"lvt-scroll\");\n const behavior =\n (htmlElement.getAttribute(\"lvt-scroll-behavior\") as ScrollBehavior) ||\n \"auto\";\n const threshold = parseInt(\n htmlElement.getAttribute(\"lvt-scroll-threshold\") || \"100\",\n 10\n );\n\n if (!mode) return;\n\n switch (mode) {\n case \"bottom\":\n htmlElement.scrollTo({\n top: htmlElement.scrollHeight,\n behavior,\n });\n break;\n\n case \"bottom-sticky\": {\n const isNearBottom =\n htmlElement.scrollHeight -\n htmlElement.scrollTop -\n htmlElement.clientHeight <=\n threshold;\n if (isNearBottom) {\n htmlElement.scrollTo({\n top: htmlElement.scrollHeight,\n behavior,\n });\n }\n break;\n }\n\n case \"top\":\n htmlElement.scrollTo({\n top: 0,\n behavior,\n });\n break;\n\n case \"preserve\":\n break;\n\n default:\n console.warn(`Unknown lvt-scroll mode: ${mode}`);\n }\n });\n}\n\n/**\n * Apply highlight directives to elements with lvt-highlight attributes.\n */\nexport function handleHighlightDirectives(rootElement: Element): void {\n const highlightElements = rootElement.querySelectorAll(\"[lvt-highlight]\");\n\n highlightElements.forEach((element) => {\n const mode = element.getAttribute(\"lvt-highlight\");\n const duration = parseInt(\n element.getAttribute(\"lvt-highlight-duration\") || \"500\",\n 10\n );\n const color = element.getAttribute(\"lvt-highlight-color\") || \"#ffc107\";\n\n if (!mode) return;\n\n const htmlElement = element as HTMLElement;\n const originalBackground = htmlElement.style.backgroundColor;\n const originalTransition = htmlElement.style.transition;\n\n htmlElement.style.transition = `background-color ${duration}ms ease-out`;\n htmlElement.style.backgroundColor = color;\n\n setTimeout(() => {\n htmlElement.style.backgroundColor = originalBackground;\n\n setTimeout(() => {\n htmlElement.style.transition = originalTransition;\n }, duration);\n }, 50);\n });\n}\n\n/**\n * Apply animation directives to elements with lvt-animate attributes.\n */\nexport function handleAnimateDirectives(rootElement: Element): void {\n const animateElements = rootElement.querySelectorAll(\"[lvt-animate]\");\n\n animateElements.forEach((element) => {\n const animation = element.getAttribute(\"lvt-animate\");\n const duration = parseInt(\n element.getAttribute(\"lvt-animate-duration\") || \"300\",\n 10\n );\n\n if (!animation) return;\n\n const htmlElement = element as HTMLElement;\n\n htmlElement.style.setProperty(\"--lvt-animate-duration\", `${duration}ms`);\n\n switch (animation) {\n case \"fade\":\n htmlElement.style.animation = `lvt-fade-in var(--lvt-animate-duration) ease-out`;\n break;\n\n case \"slide\":\n htmlElement.style.animation = `lvt-slide-in var(--lvt-animate-duration) ease-out`;\n break;\n\n case \"scale\":\n htmlElement.style.animation = `lvt-scale-in var(--lvt-animate-duration) ease-out`;\n break;\n\n default:\n console.warn(`Unknown lvt-animate mode: ${animation}`);\n }\n\n htmlElement.addEventListener(\n \"animationend\",\n () => {\n htmlElement.style.animation = \"\";\n },\n { once: true }\n );\n });\n\n if (!document.getElementById(\"lvt-animate-styles\")) {\n const style = document.createElement(\"style\");\n style.id = \"lvt-animate-styles\";\n style.textContent = `\n @keyframes lvt-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes lvt-slide-in {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n @keyframes lvt-scale-in {\n from {\n opacity: 0;\n transform: scale(0.95);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n }\n `;\n document.head.appendChild(style);\n }\n}\n", "/**\n * Debounce function: delays execution until after a pause in calls\n */\nexport function debounce<T extends (...args: any[]) => any>(\n func: T,\n wait: number\n): (...args: Parameters<T>) => void {\n let timeout: number | null = null;\n\n return function debounceWrapper(this: unknown, ...args: Parameters<T>) {\n const context = this;\n\n if (timeout !== null) {\n clearTimeout(timeout);\n }\n\n timeout = window.setTimeout(() => {\n func.apply(context, args);\n }, wait);\n };\n}\n\n/**\n * Throttle function: limits execution to at most once per time period\n * First call executes immediately, subsequent calls are delayed\n */\nexport function throttle<T extends (...args: any[]) => any>(\n func: T,\n limit: number\n): (...args: Parameters<T>) => void {\n let inThrottle = false;\n\n return function throttleWrapper(this: unknown, ...args: Parameters<T>) {\n const context = this;\n\n if (!inThrottle) {\n func.apply(context, args);\n inThrottle = true;\n\n setTimeout(() => {\n inThrottle = false;\n }, limit);\n }\n };\n}\n", "/**\n * Check if an element has lvt-confirm attribute and prompt user if needed.\n * Returns true if action should proceed, false if cancelled.\n */\nexport function checkLvtConfirm(element: HTMLElement): boolean {\n if (element.hasAttribute(\"lvt-confirm\")) {\n const confirmMessage = element.getAttribute(\"lvt-confirm\");\n if (confirmMessage && !confirm(confirmMessage)) {\n return false; // User cancelled\n }\n }\n return true; // Proceed\n}\n\n/**\n * Extract lvt-data-* attributes from an element.\n * lvt-data-id=\"123\" becomes { id: \"123\" }\n * lvt-data-user-name=\"john\" becomes { \"user-name\": \"john\" }\n */\nexport function extractLvtData(element: HTMLElement): Record<string, string> {\n const data: Record<string, string> = {};\n const attributes = element.attributes;\n\n for (let i = 0; i < attributes.length; i++) {\n const attr = attributes[i];\n if (attr.name.startsWith(\"lvt-data-\")) {\n const key = attr.name.substring(9); // Remove \"lvt-data-\" prefix\n data[key] = attr.value;\n }\n }\n\n return data;\n}\n", "import { debounce, throttle } from \"../utils/rate-limit\";\nimport { checkLvtConfirm } from \"../utils/confirm\";\nimport type { Logger } from \"../utils/logger\";\n\nexport interface EventDelegationContext {\n getWrapperElement(): Element | null;\n getRateLimitedHandlers(): WeakMap<Element, Map<string, Function>>;\n parseValue(value: string): any;\n send(message: any): void;\n setActiveSubmission(\n form: HTMLFormElement | null,\n button: HTMLButtonElement | null,\n originalButtonText: string | null\n ): void;\n openModal(modalId: string): void;\n closeModal(modalId: string): void;\n getWebSocketReadyState(): number | undefined;\n triggerPendingUploads(uploadName: string): void;\n}\n\n/**\n * Handles all DOM event delegation concerns for LiveTemplateClient.\n */\nexport class EventDelegator {\n constructor(\n private readonly context: EventDelegationContext,\n private readonly logger: Logger\n ) {}\n\n setupEventDelegation(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n const eventTypes = [\n \"click\",\n \"submit\",\n \"change\",\n \"input\",\n \"keydown\",\n \"keyup\",\n \"focus\",\n \"blur\",\n \"mouseenter\",\n \"mouseleave\",\n ];\n const wrapperId = wrapperElement.getAttribute(\"data-lvt-id\");\n const rateLimitedHandlers = this.context.getRateLimitedHandlers();\n\n eventTypes.forEach((eventType) => {\n const listenerKey = `__lvt_delegated_${eventType}_${wrapperId}`;\n const existingListener = (document as any)[listenerKey];\n if (existingListener) {\n document.removeEventListener(eventType, existingListener, false);\n }\n\n const listener = (e: Event) => {\n const currentWrapper = this.context.getWrapperElement();\n if (!currentWrapper) return;\n\n if (eventType === \"submit\") {\n (window as any).__lvtSubmitListenerTriggered = true;\n (window as any).__lvtSubmitEventTarget = (\n e.target as Element\n )?.tagName;\n }\n\n this.logger.debug(\"Event listener triggered:\", eventType, e.target);\n\n const target = e.target as Element;\n if (!target) return;\n\n let element: Element | null = target;\n let inWrapper = false;\n\n while (element) {\n if (element === currentWrapper) {\n inWrapper = true;\n break;\n }\n element = element.parentElement;\n }\n\n if (eventType === \"submit\") {\n (window as any).__lvtInWrapper = inWrapper;\n (window as any).__lvtWrapperElement =\n currentWrapper.getAttribute(\"data-lvt-id\");\n }\n\n if (!inWrapper) return;\n\n const attrName = `lvt-${eventType}`;\n element = target;\n\n while (element && element !== currentWrapper.parentElement) {\n let action = element.getAttribute(attrName);\n let actionElement = element;\n\n // Check for lvt-persist on form submit (auto-persist to database)\n if (!action && eventType === \"submit\" && element instanceof HTMLFormElement) {\n const persistTable = element.getAttribute(\"lvt-persist\");\n if (persistTable) {\n action = `persist:${persistTable}`;\n actionElement = element;\n }\n }\n\n if (!action && (eventType === \"change\" || eventType === \"input\")) {\n const formElement: HTMLFormElement | null = element.closest(\"form\");\n if (formElement && formElement.hasAttribute(\"lvt-change\")) {\n action = formElement.getAttribute(\"lvt-change\");\n actionElement = formElement;\n }\n }\n\n if (action && actionElement) {\n if (eventType === \"submit\") {\n (window as any).__lvtActionFound = action;\n (window as any).__lvtActionElement = actionElement.tagName;\n }\n\n if (eventType === \"submit\") {\n e.preventDefault();\n }\n\n if (\n (eventType === \"keydown\" || eventType === \"keyup\") &&\n actionElement.hasAttribute(\"lvt-key\")\n ) {\n const keyFilter = actionElement.getAttribute(\"lvt-key\");\n const keyboardEvent = e as KeyboardEvent;\n if (keyFilter && keyboardEvent.key !== keyFilter) {\n element = element.parentElement;\n continue;\n }\n }\n\n const targetElement = actionElement;\n\n const handleAction = () => {\n this.logger.debug(\"handleAction called\", {\n action,\n eventType,\n targetElement,\n });\n\n // Handle lvt-confirm for any action\n if (!checkLvtConfirm(targetElement as HTMLElement)) {\n this.logger.debug(\"Action cancelled by user:\", action);\n return;\n }\n\n const message: any = { action, data: {} };\n\n if (targetElement instanceof HTMLFormElement) {\n this.logger.debug(\"Processing form element\");\n const formData = new FormData(targetElement);\n\n const checkboxes = Array.from(\n targetElement.querySelectorAll('input[type=\"checkbox\"][name]')\n ) as HTMLInputElement[];\n const checkboxNames = new Set(checkboxes.map((cb) => cb.name));\n\n checkboxNames.forEach((name) => {\n message.data[name] = false;\n });\n\n // Get password field names to skip parseValue for them\n const passwordFields = new Set(\n Array.from(\n targetElement.querySelectorAll('input[type=\"password\"][name]')\n ).map((el) => (el as HTMLInputElement).name)\n );\n\n formData.forEach((value, key) => {\n if (checkboxNames.has(key)) {\n message.data[key] = true;\n this.logger.debug(\"Converted checkbox\", key, \"to true\");\n } else if (passwordFields.has(key)) {\n // Never parse password values - always keep as string\n message.data[key] = value as string;\n } else {\n message.data[key] = this.context.parseValue(\n value as string\n );\n }\n });\n this.logger.debug(\"Form data collected:\", message.data);\n } else if (eventType === \"change\" || eventType === \"input\") {\n if (targetElement instanceof HTMLInputElement) {\n const key = targetElement.name || \"value\";\n message.data[key] = this.context.parseValue(\n targetElement.value\n );\n } else if (targetElement instanceof HTMLSelectElement) {\n const key = targetElement.name || \"value\";\n message.data[key] = this.context.parseValue(\n targetElement.value\n );\n } else if (targetElement instanceof HTMLTextAreaElement) {\n const key = targetElement.name || \"value\";\n message.data[key] = this.context.parseValue(\n targetElement.value\n );\n }\n }\n\n Array.from(targetElement.attributes).forEach((attr) => {\n if (attr.name.startsWith(\"lvt-data-\")) {\n const key = attr.name.replace(\"lvt-data-\", \"\");\n message.data[key] = this.context.parseValue(attr.value);\n }\n });\n\n Array.from(targetElement.attributes).forEach((attr) => {\n if (attr.name.startsWith(\"lvt-value-\")) {\n const key = attr.name.replace(\"lvt-value-\", \"\");\n message.data[key] = this.context.parseValue(attr.value);\n }\n });\n\n if (\n eventType === \"submit\" &&\n targetElement instanceof HTMLFormElement\n ) {\n const submitEvent = e as SubmitEvent;\n const submitButton =\n submitEvent.submitter as HTMLButtonElement | null;\n let originalButtonText: string | null = null;\n\n if (\n submitButton &&\n submitButton.hasAttribute(\"lvt-disable-with\")\n ) {\n originalButtonText = submitButton.textContent;\n submitButton.disabled = true;\n submitButton.textContent =\n submitButton.getAttribute(\"lvt-disable-with\");\n this.logger.debug(\"Disabled submit button\");\n }\n\n this.context.setActiveSubmission(\n targetElement,\n submitButton || null,\n originalButtonText\n );\n\n // Trigger pending uploads for any file inputs in the form\n const fileInputs = targetElement.querySelectorAll<HTMLInputElement>(\n 'input[type=\"file\"][lvt-upload]'\n );\n fileInputs.forEach((input) => {\n const uploadName = input.getAttribute(\"lvt-upload\");\n if (uploadName) {\n this.logger.debug(\"Triggering pending uploads for:\", uploadName);\n this.context.triggerPendingUploads(uploadName);\n }\n });\n\n targetElement.dispatchEvent(\n new CustomEvent(\"lvt:pending\", { detail: message })\n );\n this.logger.debug(\"Emitted lvt:pending event\");\n }\n\n this.logger.debug(\"About to send message:\", message);\n this.logger.debug(\n \"WebSocket state:\",\n this.context.getWebSocketReadyState()\n );\n\n this.context.send(message);\n this.logger.debug(\"send() called\");\n };\n\n const throttleValue = actionElement.getAttribute(\"lvt-throttle\");\n const debounceValue = actionElement.getAttribute(\"lvt-debounce\");\n\n if (throttleValue || debounceValue) {\n if (!rateLimitedHandlers.has(actionElement)) {\n rateLimitedHandlers.set(actionElement, new Map());\n }\n const handlerCache = rateLimitedHandlers.get(actionElement)!;\n const cacheKey = `${eventType}:${action}`;\n\n let rateLimitedHandler = handlerCache.get(cacheKey);\n if (!rateLimitedHandler) {\n if (throttleValue) {\n const limit = parseInt(throttleValue, 10);\n rateLimitedHandler = throttle(handleAction, limit);\n } else if (debounceValue) {\n const wait = parseInt(debounceValue, 10);\n rateLimitedHandler = debounce(handleAction, wait);\n }\n if (rateLimitedHandler) {\n handlerCache.set(cacheKey, rateLimitedHandler);\n }\n }\n\n if (rateLimitedHandler) {\n rateLimitedHandler();\n }\n } else {\n if (eventType === \"submit\") {\n (window as any).__lvtBeforeHandleAction = true;\n }\n handleAction();\n if (eventType === \"submit\") {\n (window as any).__lvtAfterHandleAction = true;\n }\n }\n\n return;\n }\n element = element.parentElement;\n }\n };\n\n (document as any)[listenerKey] = listener;\n document.addEventListener(eventType, listener, false);\n this.logger.debug(\n \"Registered event listener:\",\n eventType,\n \"with key:\",\n listenerKey\n );\n });\n }\n\n setupWindowEventDelegation(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n const windowEvents = [\n \"keydown\",\n \"keyup\",\n \"scroll\",\n \"resize\",\n \"focus\",\n \"blur\",\n ];\n const wrapperId = wrapperElement.getAttribute(\"data-lvt-id\");\n const rateLimitedHandlers = this.context.getRateLimitedHandlers();\n\n windowEvents.forEach((eventType) => {\n const listenerKey = `__lvt_window_${eventType}_${wrapperId}`;\n const existingListener = (window as any)[listenerKey];\n if (existingListener) {\n window.removeEventListener(eventType, existingListener);\n }\n\n const listener = (e: Event) => {\n const currentWrapper = this.context.getWrapperElement();\n if (!currentWrapper) return;\n\n const attrName = `lvt-window-${eventType}`;\n const elements = currentWrapper.querySelectorAll(`[${attrName}]`);\n\n elements.forEach((element) => {\n const action = element.getAttribute(attrName);\n if (!action) return;\n\n if (\n (eventType === \"keydown\" || eventType === \"keyup\") &&\n element.hasAttribute(\"lvt-key\")\n ) {\n const keyFilter = element.getAttribute(\"lvt-key\");\n const keyboardEvent = e as KeyboardEvent;\n if (keyFilter && keyboardEvent.key !== keyFilter) {\n return;\n }\n }\n\n const message: any = { action, data: {} };\n\n Array.from(element.attributes).forEach((attr) => {\n if (attr.name.startsWith(\"lvt-data-\")) {\n const key = attr.name.replace(\"lvt-data-\", \"\");\n message.data[key] = this.context.parseValue(attr.value);\n }\n });\n\n Array.from(element.attributes).forEach((attr) => {\n if (attr.name.startsWith(\"lvt-value-\")) {\n const key = attr.name.replace(\"lvt-value-\", \"\");\n message.data[key] = this.context.parseValue(attr.value);\n }\n });\n\n const throttleValue = element.getAttribute(\"lvt-throttle\");\n const debounceValue = element.getAttribute(\"lvt-debounce\");\n\n const handleAction = () => this.context.send(message);\n\n if (throttleValue || debounceValue) {\n if (!rateLimitedHandlers.has(element)) {\n rateLimitedHandlers.set(element, new Map());\n }\n const handlerCache = rateLimitedHandlers.get(element)!;\n const cacheKey = `window-${eventType}:${action}`;\n\n let rateLimitedHandler = handlerCache.get(cacheKey);\n if (!rateLimitedHandler) {\n if (throttleValue) {\n const limit = parseInt(throttleValue, 10);\n rateLimitedHandler = throttle(handleAction, limit);\n } else if (debounceValue) {\n const wait = parseInt(debounceValue, 10);\n rateLimitedHandler = debounce(handleAction, wait);\n }\n if (rateLimitedHandler) {\n handlerCache.set(cacheKey, rateLimitedHandler);\n }\n }\n\n if (rateLimitedHandler) {\n rateLimitedHandler();\n }\n } else {\n handleAction();\n }\n });\n };\n\n (window as any)[listenerKey] = listener;\n window.addEventListener(eventType, listener);\n });\n }\n\n setupClickAwayDelegation(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n const wrapperId = wrapperElement.getAttribute(\"data-lvt-id\");\n const listenerKey = `__lvt_click_away_${wrapperId}`;\n const existingListener = (document as any)[listenerKey];\n if (existingListener) {\n document.removeEventListener(\"click\", existingListener);\n }\n\n const listener = (e: Event) => {\n const currentWrapper = this.context.getWrapperElement();\n if (!currentWrapper) return;\n\n const target = e.target as Element;\n const elements = currentWrapper.querySelectorAll(\"[lvt-click-away]\");\n\n elements.forEach((element) => {\n if (!element.contains(target)) {\n const action = element.getAttribute(\"lvt-click-away\");\n if (!action) return;\n\n const message: any = { action, data: {} };\n\n Array.from(element.attributes).forEach((attr) => {\n if (attr.name.startsWith(\"lvt-data-\")) {\n const key = attr.name.replace(\"lvt-data-\", \"\");\n message.data[key] = this.context.parseValue(attr.value);\n }\n });\n\n Array.from(element.attributes).forEach((attr) => {\n if (attr.name.startsWith(\"lvt-value-\")) {\n const key = attr.name.replace(\"lvt-value-\", \"\");\n message.data[key] = this.context.parseValue(attr.value);\n }\n });\n\n this.context.send(message);\n }\n });\n };\n\n (document as any)[listenerKey] = listener;\n document.addEventListener(\"click\", listener);\n }\n\n setupModalDelegation(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n const wrapperId = wrapperElement.getAttribute(\"data-lvt-id\");\n\n const openListenerKey = `__lvt_modal_open_${wrapperId}`;\n const existingOpenListener = (document as any)[openListenerKey];\n if (existingOpenListener) {\n document.removeEventListener(\"click\", existingOpenListener);\n }\n\n const openListener = (e: Event) => {\n const currentWrapper = this.context.getWrapperElement();\n if (!currentWrapper) return;\n\n const target = (e.target as Element)?.closest(\"[lvt-modal-open]\");\n if (!target || !currentWrapper.contains(target)) return;\n\n const modalId = target.getAttribute(\"lvt-modal-open\");\n if (!modalId) return;\n\n e.preventDefault();\n this.context.openModal(modalId);\n };\n\n (document as any)[openListenerKey] = openListener;\n document.addEventListener(\"click\", openListener);\n\n const closeListenerKey = `__lvt_modal_close_${wrapperId}`;\n const existingCloseListener = (document as any)[closeListenerKey];\n if (existingCloseListener) {\n document.removeEventListener(\"click\", existingCloseListener);\n }\n\n // Close listener is intentionally NOT scoped to wrapper (unlike openListener).\n // Close buttons may be inside modals rendered in portals outside the wrapper.\n // Instead, we verify the target modal exists by ID.\n const closeListener = (e: Event) => {\n const target = (e.target as Element)?.closest(\"[lvt-modal-close]\");\n if (!target) return;\n\n const modalId = target.getAttribute(\"lvt-modal-close\");\n if (!modalId) return;\n\n // Verify the modal exists before attempting to close\n const modal = document.getElementById(modalId);\n if (!modal) return;\n\n e.preventDefault();\n this.context.closeModal(modalId);\n };\n\n (document as any)[closeListenerKey] = closeListener;\n document.addEventListener(\"click\", closeListener);\n\n const backdropListenerKey = `__lvt_modal_backdrop_${wrapperId}`;\n const existingBackdropListener = (document as any)[backdropListenerKey];\n if (existingBackdropListener) {\n document.removeEventListener(\"click\", existingBackdropListener);\n }\n\n // Helper to close modal, dispatching action if data-modal-close-action is set\n const closeModalWithAction = (modal: Element, modalId: string) => {\n const closeAction = modal.getAttribute(\"data-modal-close-action\");\n if (closeAction) {\n this.context.send({ action: closeAction, data: {} });\n } else {\n this.context.closeModal(modalId);\n }\n };\n\n const backdropListener = (e: Event) => {\n const target = e.target as Element;\n // Only trigger if clicked directly on the backdrop element itself\n if (!target.hasAttribute(\"data-modal-backdrop\")) return;\n\n const modalId = target.getAttribute(\"data-modal-id\");\n if (!modalId) return;\n\n const modal = document.getElementById(modalId);\n if (!modal) return;\n\n closeModalWithAction(modal, modalId);\n };\n\n (document as any)[backdropListenerKey] = backdropListener;\n document.addEventListener(\"click\", backdropListener);\n\n const escapeListenerKey = `__lvt_modal_escape_${wrapperId}`;\n const existingEscapeListener = (document as any)[escapeListenerKey];\n if (existingEscapeListener) {\n document.removeEventListener(\"keydown\", existingEscapeListener);\n }\n\n const escapeListener = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") return;\n const currentWrapper = this.context.getWrapperElement();\n if (!currentWrapper) return;\n\n const openModals = currentWrapper.querySelectorAll(\n '[role=\"dialog\"]:not([hidden])'\n );\n if (openModals.length > 0) {\n const lastModal = openModals[openModals.length - 1];\n if (lastModal.id) {\n closeModalWithAction(lastModal, lastModal.id);\n }\n }\n };\n\n (document as any)[escapeListenerKey] = escapeListener;\n document.addEventListener(\"keydown\", escapeListener);\n }\n\n /**\n * Sets up focus trapping for elements with lvt-focus-trap attribute.\n * Focus is trapped within the element, cycling through focusable elements\n * when Tab/Shift+Tab is pressed.\n */\n setupFocusTrapDelegation(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n const wrapperId = wrapperElement.getAttribute(\"data-lvt-id\");\n const listenerKey = `__lvt_focus_trap_${wrapperId}`;\n const existingListener = (document as any)[listenerKey];\n if (existingListener) {\n document.removeEventListener(\"keydown\", existingListener);\n }\n\n const getFocusableElements = (container: Element): HTMLElement[] => {\n const selector = [\n 'a[href]:not([disabled])',\n 'button:not([disabled])',\n 'textarea:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"]):not([disabled])',\n '[contenteditable=\"true\"]'\n ].join(', ');\n\n return Array.from(container.querySelectorAll(selector)).filter(\n (el) => {\n const htmlEl = el as HTMLElement;\n // Check if element is visible\n const style = window.getComputedStyle(htmlEl);\n const isNotDisplayNone = style.display !== 'none';\n const isNotVisibilityHidden = style.visibility !== 'hidden';\n // offsetParent can be null in JSDOM or for fixed/absolute positioned elements\n const hasLayoutContext = htmlEl.offsetParent !== null ||\n style.position === 'fixed' ||\n style.position === 'absolute' ||\n // In test environments, offsetParent may always be null\n (typeof process !== 'undefined' && (process as any).env?.NODE_ENV === 'test');\n return isNotDisplayNone && isNotVisibilityHidden && hasLayoutContext;\n }\n ) as HTMLElement[];\n };\n\n const listener = (e: KeyboardEvent) => {\n if (e.key !== \"Tab\") return;\n\n const currentWrapper = this.context.getWrapperElement();\n if (!currentWrapper) return;\n\n // Find the active focus trap container (innermost one containing the focused element)\n const focusTrapElements = currentWrapper.querySelectorAll(\"[lvt-focus-trap]\");\n let activeTrap: Element | null = null;\n\n focusTrapElements.forEach((trap) => {\n if (trap.contains(document.activeElement)) {\n // Check if this is the innermost trap containing the focused element\n if (!activeTrap || trap.contains(activeTrap)) {\n activeTrap = trap;\n }\n }\n });\n\n // If there's a visible focus trap that doesn't contain the active element,\n // and is visible, trap focus there (for newly opened modals/dropdowns)\n if (!activeTrap) {\n focusTrapElements.forEach((trap) => {\n const htmlTrap = trap as HTMLElement;\n const style = window.getComputedStyle(htmlTrap);\n if (style.display !== 'none' && style.visibility !== 'hidden') {\n activeTrap = trap;\n }\n });\n }\n\n if (!activeTrap) return;\n\n const focusableElements = getFocusableElements(activeTrap);\n if (focusableElements.length === 0) return;\n\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (e.shiftKey) {\n // Shift+Tab: moving backwards\n if (document.activeElement === firstElement) {\n e.preventDefault();\n lastElement.focus();\n }\n } else {\n // Tab: moving forwards\n if (document.activeElement === lastElement) {\n e.preventDefault();\n firstElement.focus();\n }\n }\n };\n\n (document as any)[listenerKey] = listener;\n document.addEventListener(\"keydown\", listener);\n this.logger.debug(\"Focus trap delegation set up\");\n }\n\n /**\n * Sets up autofocus for elements with lvt-autofocus attribute.\n * Automatically focuses the first element with lvt-autofocus when it becomes visible.\n * Uses MutationObserver to detect when elements are added or become visible.\n */\n setupAutofocusDelegation(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n const wrapperId = wrapperElement.getAttribute(\"data-lvt-id\");\n const observerKey = `__lvt_autofocus_observer_${wrapperId}`;\n\n // Disconnect existing observer if any\n const existingObserver = (wrapperElement as any)[observerKey];\n if (existingObserver) {\n existingObserver.disconnect();\n }\n\n const processAutofocus = () => {\n const currentWrapper = this.context.getWrapperElement();\n if (!currentWrapper) return;\n\n // Find all elements with lvt-autofocus that are visible\n const autofocusElements = currentWrapper.querySelectorAll(\"[lvt-autofocus]\");\n\n autofocusElements.forEach((element) => {\n const htmlElement = element as HTMLElement;\n const style = window.getComputedStyle(htmlElement);\n\n // Check if element is visible and hasn't been focused yet in this visibility state\n // Note: offsetParent can be null in JSDOM or for fixed/absolute positioned elements,\n // so we only use it as a secondary check when it's available\n const isNotDisplayNone = style.display !== 'none';\n const isNotVisibilityHidden = style.visibility !== 'hidden';\n const hasLayoutContext = htmlElement.offsetParent !== null ||\n style.position === 'fixed' ||\n style.position === 'absolute' ||\n htmlElement.tagName === 'BODY' ||\n // In test environments, offsetParent may always be null\n (typeof process !== 'undefined' && (process as any).env?.NODE_ENV === 'test');\n const isVisible = isNotDisplayNone && isNotVisibilityHidden && hasLayoutContext;\n\n const wasFocused = htmlElement.getAttribute(\"data-lvt-autofocused\") === \"true\";\n\n if (isVisible && !wasFocused) {\n // Mark as focused to prevent re-focusing on every mutation\n htmlElement.setAttribute(\"data-lvt-autofocused\", \"true\");\n\n // Use requestAnimationFrame to ensure DOM is ready\n requestAnimationFrame(() => {\n htmlElement.focus();\n this.logger.debug(\"Autofocused element:\", htmlElement.tagName, htmlElement.id || htmlElement.getAttribute(\"name\"));\n });\n } else if (!isVisible && wasFocused) {\n // Reset the flag when element becomes hidden so it can be refocused when shown again\n htmlElement.removeAttribute(\"data-lvt-autofocused\");\n }\n });\n };\n\n // Process autofocus immediately for any existing elements\n processAutofocus();\n\n // Set up MutationObserver to watch for new autofocus elements or visibility changes\n const observer = new MutationObserver((mutations) => {\n let shouldProcess = false;\n\n mutations.forEach((mutation) => {\n // Check for added nodes\n if (mutation.type === \"childList\" && mutation.addedNodes.length > 0) {\n mutation.addedNodes.forEach((node) => {\n if (node instanceof Element) {\n if (node.hasAttribute(\"lvt-autofocus\") || node.querySelector(\"[lvt-autofocus]\")) {\n shouldProcess = true;\n }\n }\n });\n }\n\n // Check for attribute changes that might affect visibility\n // We intentionally check \"class\" changes to handle CSS-based visibility\n // (e.g., Tailwind's \"hidden\" class, Bootstrap's \"d-none\", etc.)\n // This may cause some extra processing but ensures visibility changes\n // via class toggles are detected.\n if (mutation.type === \"attributes\") {\n const target = mutation.target as Element;\n if (target.hasAttribute(\"lvt-autofocus\") ||\n mutation.attributeName === \"hidden\" ||\n mutation.attributeName === \"style\" ||\n mutation.attributeName === \"class\") {\n shouldProcess = true;\n }\n }\n });\n\n if (shouldProcess) {\n processAutofocus();\n }\n });\n\n observer.observe(wrapperElement, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"hidden\", \"style\", \"class\", \"lvt-autofocus\"]\n });\n\n (wrapperElement as any)[observerKey] = observer;\n this.logger.debug(\"Autofocus delegation set up\");\n }\n}\n", "import type { Logger } from \"../utils/logger\";\n\nexport interface ObserverContext {\n getWrapperElement(): Element | null;\n send(message: any): void;\n}\n\n/**\n * Manages LiveTemplate observers such as infinite scroll and DOM mutations.\n */\nexport class ObserverManager {\n private infiniteScrollObserver: IntersectionObserver | null = null;\n private mutationObserver: MutationObserver | null = null;\n\n constructor(\n private readonly context: ObserverContext,\n private readonly logger: Logger\n ) {}\n\n setupInfiniteScrollObserver(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n const sentinel = document.getElementById(\"scroll-sentinel\");\n if (!sentinel) {\n return;\n }\n\n if (this.infiniteScrollObserver) {\n this.infiniteScrollObserver.disconnect();\n }\n\n this.infiniteScrollObserver = new IntersectionObserver(\n (entries) => {\n if (entries[0].isIntersecting) {\n this.logger.debug(\"Sentinel visible, sending load_more action\");\n this.context.send({ action: \"load_more\" });\n }\n },\n {\n rootMargin: \"200px\",\n }\n );\n\n this.infiniteScrollObserver.observe(sentinel);\n this.logger.debug(\"Observer set up successfully\");\n }\n\n setupInfiniteScrollMutationObserver(): void {\n const wrapperElement = this.context.getWrapperElement();\n if (!wrapperElement) return;\n\n if (this.mutationObserver) {\n this.mutationObserver.disconnect();\n }\n\n this.mutationObserver = new MutationObserver(() => {\n this.setupInfiniteScrollObserver();\n });\n\n this.mutationObserver.observe(wrapperElement, {\n childList: true,\n subtree: true,\n });\n\n this.logger.debug(\"MutationObserver set up successfully\");\n }\n\n teardown(): void {\n if (this.infiniteScrollObserver) {\n this.infiniteScrollObserver.disconnect();\n this.infiniteScrollObserver = null;\n }\n if (this.mutationObserver) {\n this.mutationObserver.disconnect();\n this.mutationObserver = null;\n }\n }\n}\n", "import type { Logger } from \"../utils/logger\";\n\n/**\n * Manages client-side modal interactions for LiveTemplate.\n */\nexport class ModalManager {\n constructor(private readonly logger: Logger) {}\n\n open(modalId: string): void {\n const modal = document.getElementById(modalId);\n if (!modal) {\n this.logger.warn(`Modal with id=\"${modalId}\" not found`);\n return;\n }\n\n modal.removeAttribute(\"hidden\");\n modal.style.display = \"flex\";\n modal.setAttribute(\"aria-hidden\", \"false\");\n modal.dispatchEvent(new CustomEvent(\"lvt:modal-opened\", { bubbles: true }));\n\n this.logger.info(`Opened modal: ${modalId}`);\n\n const firstInput = modal.querySelector(\n \"input, textarea, select\"\n ) as HTMLElement | null;\n if (firstInput) {\n setTimeout(() => {\n const activeElement = document.activeElement as HTMLElement | null;\n const isVisible = (element: HTMLElement | null): boolean => {\n if (!element) {\n return false;\n }\n\n if (element === document.body) {\n return true;\n }\n\n // Use offsetParent and bounding rects to determine visibility without hitting layout too hard.\n if (element.offsetParent !== null) {\n return true;\n }\n\n return element.getClientRects().length > 0;\n };\n\n const shouldMoveFocus =\n !activeElement ||\n !modal.contains(activeElement) ||\n !isVisible(activeElement);\n\n if (shouldMoveFocus) {\n firstInput.focus();\n }\n }, 100);\n }\n }\n\n close(modalId: string): void {\n const modal = document.getElementById(modalId);\n if (!modal) {\n this.logger.warn(`Modal with id=\"${modalId}\" not found`);\n return;\n }\n\n modal.setAttribute(\"hidden\", \"\");\n modal.style.display = \"none\";\n modal.setAttribute(\"aria-hidden\", \"true\");\n modal.dispatchEvent(new CustomEvent(\"lvt:modal-closed\", { bubbles: true }));\n\n this.logger.info(`Closed modal: ${modalId}`);\n }\n}\n", "/**\n * Handles showing and hiding the global LiveTemplate loading indicator.\n */\nexport class LoadingIndicator {\n private bar: HTMLElement | null = null;\n\n show(): void {\n if (this.bar) return;\n\n const bar = document.createElement(\"div\");\n bar.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: linear-gradient(90deg, #3b82f6 0%, #60a5fa 50%, #3b82f6 100%);\n background-size: 200% 100%;\n z-index: 9999;\n animation: lvt-loading-shimmer 1.5s ease-in-out infinite;\n `;\n\n if (!document.getElementById(\"lvt-loading-styles\")) {\n const style = document.createElement(\"style\");\n style.id = \"lvt-loading-styles\";\n style.textContent = `\n @keyframes lvt-loading-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n `;\n document.head.appendChild(style);\n }\n\n document.body.insertBefore(bar, document.body.firstChild);\n this.bar = bar;\n }\n\n hide(): void {\n if (!this.bar) return;\n\n if (this.bar.parentNode) {\n this.bar.parentNode.removeChild(this.bar);\n }\n this.bar = null;\n }\n}\n", "/**\n * Enables and disables all form controls inside the LiveTemplate wrapper.\n */\nexport class FormDisabler {\n disable(wrapper: Element | null): void {\n if (!wrapper) return;\n\n const forms = wrapper.querySelectorAll(\"form\");\n forms.forEach((form) => {\n const inputs = form.querySelectorAll(\"input, textarea, select, button\");\n inputs.forEach((input) => {\n (input as HTMLInputElement).disabled = true;\n });\n });\n }\n\n enable(wrapper: Element | null): void {\n if (!wrapper) return;\n\n const forms = wrapper.querySelectorAll(\"form\");\n forms.forEach((form) => {\n const inputs = form.querySelectorAll(\"input, textarea, select, button\");\n inputs.forEach((input) => {\n (input as HTMLInputElement).disabled = false;\n });\n });\n }\n}\n", "/**\n * Reactive Attributes - Declarative DOM actions triggered by LiveTemplate lifecycle events.\n *\n * Attribute Pattern: lvt-{action}-on:{event}=\"param\"\n *\n * Events:\n * - pending: Action started, waiting for server response\n * - success: Action completed successfully\n * - error: Action completed with validation errors\n * - done: Action completed (regardless of success/error)\n *\n * Event Scope:\n * - Global: lvt-reset-on:success (any action)\n * - Action-specific: lvt-reset-on:create-todo:success (specific action only)\n *\n * Actions:\n * - reset: Calls form.reset()\n * - disable: Sets element.disabled = true\n * - enable: Sets element.disabled = false\n * - addClass: Adds CSS class(es)\n * - removeClass: Removes CSS class(es)\n * - toggleClass: Toggles CSS class(es)\n * - setAttr: Sets an attribute (name:value format)\n * - toggleAttr: Toggles a boolean attribute\n */\n\nexport type ReactiveAction =\n | \"reset\"\n | \"disable\"\n | \"enable\"\n | \"addClass\"\n | \"removeClass\"\n | \"toggleClass\"\n | \"setAttr\"\n | \"toggleAttr\";\n\nexport type LifecycleEvent = \"pending\" | \"success\" | \"error\" | \"done\";\n\nexport interface ReactiveBinding {\n action: ReactiveAction;\n lifecycle: LifecycleEvent;\n actionName?: string;\n param?: string;\n}\n\nconst LIFECYCLE_EVENTS: LifecycleEvent[] = [\"pending\", \"success\", \"error\", \"done\"];\n\nconst REACTIVE_ACTIONS: ReactiveAction[] = [\n \"reset\",\n \"disable\",\n \"enable\",\n \"addClass\",\n \"removeClass\",\n \"toggleClass\",\n \"setAttr\",\n \"toggleAttr\",\n];\n\n// Lowercase versions for case-insensitive matching (HTML attributes are lowercased)\nconst ACTION_MAP: Record<string, ReactiveAction> = {\n reset: \"reset\",\n disable: \"disable\",\n enable: \"enable\",\n addclass: \"addClass\",\n removeclass: \"removeClass\",\n toggleclass: \"toggleClass\",\n setattr: \"setAttr\",\n toggleattr: \"toggleAttr\",\n};\n\n/**\n * Parse a reactive attribute name and value into a binding.\n *\n * Examples:\n * parseReactiveAttribute(\"lvt-reset-on:success\", \"\") => { action: \"reset\", lifecycle: \"success\" }\n * parseReactiveAttribute(\"lvt-addClass-on:pending\", \"loading\") => { action: \"addClass\", lifecycle: \"pending\", param: \"loading\" }\n * parseReactiveAttribute(\"lvt-reset-on:create-todo:success\", \"\") => { action: \"reset\", lifecycle: \"success\", actionName: \"create-todo\" }\n */\nexport function parseReactiveAttribute(\n attrName: string,\n attrValue: string\n): ReactiveBinding | null {\n // Pattern: lvt-{action}-on:{actionName?}:{lifecycle}\n // The lifecycle must be at the end, action name is optional in the middle\n // Note: HTML attributes are lowercased by browsers, so we match case-insensitively\n const match = attrName.toLowerCase().match(/^lvt-(\\w+)-on:(.+)$/);\n if (!match) return null;\n\n const actionKey = match[1];\n const action = ACTION_MAP[actionKey];\n if (!action) return null;\n\n const eventPart = match[2];\n\n // Check if the last segment is a lifecycle event\n // Format: \"{actionName}:{lifecycle}\" or just \"{lifecycle}\"\n const segments = eventPart.split(\":\");\n const lastSegment = segments[segments.length - 1] as LifecycleEvent;\n\n if (!LIFECYCLE_EVENTS.includes(lastSegment)) return null;\n\n const lifecycle = lastSegment;\n const actionName = segments.length > 1 ? segments.slice(0, -1).join(\":\") : undefined;\n\n return {\n action,\n lifecycle,\n actionName: actionName || undefined,\n param: attrValue || undefined,\n };\n}\n\n/**\n * Execute a reactive action on an element.\n */\nexport function executeAction(\n element: Element,\n action: ReactiveAction,\n param?: string\n): void {\n switch (action) {\n case \"reset\":\n if (element instanceof HTMLFormElement) {\n element.reset();\n }\n break;\n\n case \"disable\":\n if (\"disabled\" in element) {\n (element as HTMLButtonElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement).disabled = true;\n }\n break;\n\n case \"enable\":\n if (\"disabled\" in element) {\n (element as HTMLButtonElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement).disabled = false;\n }\n break;\n\n case \"addClass\":\n if (param) {\n const classes = param.split(/\\s+/).filter(Boolean);\n element.classList.add(...classes);\n }\n break;\n\n case \"removeClass\":\n if (param) {\n const classes = param.split(/\\s+/).filter(Boolean);\n element.classList.remove(...classes);\n }\n break;\n\n case \"toggleClass\":\n if (param) {\n const classes = param.split(/\\s+/).filter(Boolean);\n classes.forEach((c) => element.classList.toggle(c));\n }\n break;\n\n case \"setAttr\":\n if (param) {\n const colonIndex = param.indexOf(\":\");\n if (colonIndex > 0) {\n const name = param.substring(0, colonIndex);\n const value = param.substring(colonIndex + 1);\n element.setAttribute(name, value);\n }\n }\n break;\n\n case \"toggleAttr\":\n if (param) {\n element.toggleAttribute(param);\n }\n break;\n }\n}\n\n/**\n * Check if an event matches a binding.\n *\n * @param binding The reactive binding to check\n * @param lifecycle The lifecycle event that fired\n * @param actionName The action name that triggered the event (optional)\n */\nexport function matchesEvent(\n binding: ReactiveBinding,\n lifecycle: LifecycleEvent,\n actionName?: string\n): boolean {\n // Lifecycle must match\n if (binding.lifecycle !== lifecycle) return false;\n\n // If binding has no actionName, it's global (matches any action)\n if (!binding.actionName) return true;\n\n // If binding has actionName, it must match the fired action\n return binding.actionName === actionName;\n}\n\n/**\n * Process all reactive attributes for a lifecycle event.\n *\n * Instead of building complex selectors, we iterate all elements with any lvt-*-on:* attribute\n * and check each one against the fired event.\n */\nexport function processReactiveAttributes(\n lifecycle: LifecycleEvent,\n actionName?: string\n): void {\n // Find all elements that might have reactive attributes\n // This is a broad selector but avoids escaping issues with attribute names containing colons\n const allElements = document.querySelectorAll(\"*\");\n\n allElements.forEach((element) => {\n // Check all attributes on this element for reactive bindings\n Array.from(element.attributes).forEach((attr) => {\n // Quick filter: only process lvt-*-on: attributes\n if (!attr.name.startsWith(\"lvt-\") || !attr.name.includes(\"-on:\")) {\n return;\n }\n\n const binding = parseReactiveAttribute(attr.name, attr.value);\n if (binding && matchesEvent(binding, lifecycle, actionName)) {\n executeAction(element, binding.action, binding.param);\n }\n });\n });\n}\n\n/**\n * Set up document-level event listeners for reactive attributes.\n * This should be called once during client initialization.\n */\nexport function setupReactiveAttributeListeners(): void {\n LIFECYCLE_EVENTS.forEach((lifecycle) => {\n // Listen in capture phase to process before event bubbles\n document.addEventListener(\n `lvt:${lifecycle}`,\n (e: Event) => {\n const customEvent = e as CustomEvent;\n const actionName = customEvent.detail?.action;\n processReactiveAttributes(lifecycle, actionName);\n },\n true // capture phase\n );\n });\n}\n", "import type { TreeNode, UpdateResult } from \"../types\";\nimport type { Logger } from \"../utils/logger\";\n\ninterface RangeStateEntry {\n items: any[];\n statics: any[];\n staticsMap?: Record<string, string[]>;\n}\n\n/**\n * Deep clone an object. Uses structuredClone if available (Node 17+, modern browsers),\n * falls back to JSON.parse/stringify for older environments.\n */\nfunction deepClone<T>(obj: T): T {\n if (typeof structuredClone === \"function\") {\n return structuredClone(obj);\n }\n return JSON.parse(JSON.stringify(obj));\n}\n\n/**\n * Checks if a node is a valid range structure.\n *\n * A \"range\" in LiveTemplate represents a {{range .Items}}...{{end}} construct.\n * It has:\n * - `d` (dynamics): Array of rendered items\n * - `s` (statics): Array of static HTML fragments between dynamic slots\n *\n * A \"non-range\" is any other tree node (e.g., an {{else}} clause with simple content).\n *\n * @param node - The tree node to check\n * @returns true if the node has both `d` and `s` arrays (valid range structure)\n */\nfunction isRangeNode(node: any): boolean {\n return (\n node != null &&\n typeof node === \"object\" &&\n Array.isArray(node.d) &&\n Array.isArray(node.s)\n );\n}\n\n/**\n * Handles tree state management and HTML reconstruction logic for LiveTemplate.\n */\nexport class TreeRenderer {\n private treeState: TreeNode = {};\n private rangeState: Record<string, RangeStateEntry> = {};\n private rangeIdKeys: Record<string, string> = {};\n\n constructor(private readonly logger: Logger) {}\n\n applyUpdate(update: TreeNode): UpdateResult {\n let changed = false;\n\n for (const [key, value] of Object.entries(update)) {\n const isDifferentialOps =\n Array.isArray(value) &&\n value.length > 0 &&\n Array.isArray(value[0]) &&\n typeof value[0][0] === \"string\";\n\n if (isDifferentialOps) {\n // Check if there's an existing range structure to apply operations to\n const existing = this.treeState[key];\n const existingIsRange =\n existing &&\n typeof existing === \"object\" &&\n !Array.isArray(existing) &&\n Array.isArray(existing.d) &&\n Array.isArray(existing.s);\n\n if (existingIsRange) {\n // Apply differential operations to existing range structure\n this.treeState[key] = deepClone(existing);\n this.applyDifferentialOpsToRange(this.treeState[key], value, key);\n } else {\n // No existing range, store operations directly (will use rangeState later)\n this.treeState[key] = value;\n }\n changed = true;\n } else {\n const oldValue = this.treeState[key];\n const newValue =\n typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? this.deepMergeTreeNodes(oldValue, value, key)\n : value;\n\n if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) {\n this.treeState[key] = newValue;\n changed = true;\n }\n }\n }\n\n const html = this.reconstructFromTree(this.treeState, \"\");\n return { html, changed };\n }\n\n reset(): void {\n this.treeState = {};\n this.rangeState = {};\n this.rangeIdKeys = {};\n }\n\n getTreeState(): TreeNode {\n return { ...this.treeState };\n }\n\n getStaticStructure(): string[] | null {\n return this.treeState.s || null;\n }\n\n private deepMergeTreeNodes(\n existing: any,\n update: any,\n currentPath: string = \"\"\n ): any {\n if (\n typeof update !== \"object\" ||\n update === null ||\n Array.isArray(update)\n ) {\n return update;\n }\n\n if (\n typeof existing !== \"object\" ||\n existing === null ||\n Array.isArray(existing)\n ) {\n return update;\n }\n\n // Detect range\u2192non-range transition: when existing has a range structure\n // but update does NOT, we must do a full replacement instead of merge.\n // Otherwise, the old range items would be preserved and rendered with\n // the new (else clause) statics, causing wrong content.\n // See isRangeNode() for definition of \"range\" vs \"non-range\" structures.\n if (isRangeNode(existing) && !isRangeNode(update)) {\n this.logger.debug(\n `[deepMerge] Range\u2192non-range transition at path ${currentPath}, replacing instead of merging`\n );\n return update;\n }\n\n const merged: any = { ...existing };\n\n for (const [key, value] of Object.entries(update)) {\n const fieldPath = currentPath ? `${currentPath}.${key}` : key;\n\n // Check if value is a differential operations array\n const isDifferentialOps =\n Array.isArray(value) &&\n value.length > 0 &&\n Array.isArray(value[0]) &&\n typeof value[0][0] === \"string\";\n\n // Check if existing value is a range structure\n const existingIsRange =\n merged[key] &&\n typeof merged[key] === \"object\" &&\n !Array.isArray(merged[key]) &&\n Array.isArray(merged[key].d) &&\n Array.isArray(merged[key].s);\n\n if (isDifferentialOps && existingIsRange) {\n // Deep clone the range structure before modifying to avoid mutating the original\n // (shallow copy {...existing} keeps shared references to nested objects)\n merged[key] = deepClone(merged[key]);\n // Apply differential operations to the cloned range\n this.logger.debug(\n `[deepMerge] Applying diff ops at path ${fieldPath}`,\n { ops: value, rangeItems: merged[key].d?.length }\n );\n this.applyDifferentialOpsToRange(merged[key], value, fieldPath);\n } else if (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n typeof merged[key] === \"object\" &&\n merged[key] !== null &&\n !Array.isArray(merged[key])\n ) {\n merged[key] = this.deepMergeTreeNodes(merged[key], value, fieldPath);\n } else {\n merged[key] = value;\n }\n }\n\n return merged;\n }\n\n /**\n * Applies differential operations to the provided range structure in-place.\n * The caller is responsible for passing the object to be mutated (typically a clone).\n * This is called when merging nested updates that contain range operations.\n */\n private applyDifferentialOpsToRange(\n rangeStructure: any,\n operations: any[],\n statePath: string\n ): void {\n // Validate rangeStructure before proceeding\n if (\n !rangeStructure ||\n typeof rangeStructure !== \"object\" ||\n !Array.isArray(rangeStructure.d) ||\n !Array.isArray(rangeStructure.s)\n ) {\n this.logger.error(\n `[applyDiffOpsToRange] Invalid rangeStructure at path ${statePath}`,\n { rangeStructure }\n );\n return;\n }\n\n const currentItems = rangeStructure.d;\n\n // Ensure rangeState is synchronized\n if (!this.rangeState[statePath]) {\n this.rangeState[statePath] = {\n items: currentItems,\n statics: rangeStructure.s,\n staticsMap: rangeStructure.sm,\n };\n }\n // Also check for idKey metadata\n if (\n rangeStructure.m &&\n typeof rangeStructure.m === \"object\" &&\n typeof rangeStructure.m.idKey === \"string\"\n ) {\n this.rangeIdKeys[statePath] = rangeStructure.m.idKey;\n }\n\n this.logger.debug(\n `[applyDiffOpsToRange] path=${statePath}, idKey=${this.rangeIdKeys[statePath]}, items=${currentItems.length}, ops=${operations.length}`\n );\n\n for (const operation of operations) {\n if (!Array.isArray(operation) || operation.length < 2) {\n continue;\n }\n\n const opType = operation[0];\n\n switch (opType) {\n case \"r\": {\n const key = operation[1];\n const removeIndex = this.findItemIndexByKey(\n currentItems,\n key,\n rangeStructure.s,\n statePath\n );\n this.logger.debug(\n `[applyDiffOpsToRange] Remove: key=${key}, index=${removeIndex}, total=${currentItems.length}`\n );\n if (removeIndex >= 0) {\n currentItems.splice(removeIndex, 1);\n this.logger.debug(`[applyDiffOpsToRange] After removal: ${currentItems.length} items`);\n } else {\n this.logger.debug(`[applyDiffOpsToRange] Remove failed: key ${key} not found`);\n }\n break;\n }\n case \"u\": {\n const updateIndex = this.findItemIndexByKey(\n currentItems,\n operation[1],\n rangeStructure.s,\n statePath\n );\n const changes = operation[2];\n if (updateIndex >= 0 && changes) {\n currentItems[updateIndex] = {\n ...currentItems[updateIndex],\n ...changes,\n };\n }\n break;\n }\n case \"a\": {\n const itemsToAdd = Array.isArray(operation[1])\n ? operation[1]\n : [operation[1]];\n if (operation[2]) {\n rangeStructure.s = operation[2];\n }\n currentItems.push(...itemsToAdd);\n if (\n operation[3] &&\n typeof operation[3] === \"object\" &&\n operation[3].idKey\n ) {\n this.rangeIdKeys[statePath] = operation[3].idKey;\n }\n break;\n }\n case \"p\": {\n const itemsToPrepend = Array.isArray(operation[1])\n ? operation[1]\n : [operation[1]];\n if (operation[2]) {\n rangeStructure.s = operation[2];\n }\n currentItems.unshift(...itemsToPrepend);\n break;\n }\n case \"i\": {\n const targetIndex = this.findItemIndexByKey(\n currentItems,\n operation[1],\n rangeStructure.s,\n statePath\n );\n if (targetIndex >= 0) {\n const itemsToInsert = Array.isArray(operation[2])\n ? operation[2]\n : [operation[2]];\n currentItems.splice(targetIndex + 1, 0, ...itemsToInsert);\n }\n break;\n }\n case \"o\": {\n const newOrder = operation[1] as string[];\n const reorderedItems: any[] = [];\n const itemsByKey = new Map<string, any>();\n\n for (const item of currentItems) {\n const itemKey = this.getItemKey(item, rangeStructure.s, statePath);\n if (itemKey) {\n itemsByKey.set(itemKey, item);\n }\n }\n\n for (const orderedKey of newOrder) {\n const item = itemsByKey.get(orderedKey);\n if (item) {\n reorderedItems.push(item);\n }\n }\n\n currentItems.length = 0;\n currentItems.push(...reorderedItems);\n break;\n }\n default:\n break;\n }\n }\n\n // Update rangeState to reflect the changes\n this.rangeState[statePath] = {\n items: currentItems,\n statics: rangeStructure.s,\n staticsMap: rangeStructure.sm,\n };\n }\n\n private reconstructFromTree(node: TreeNode, statePath: string): string {\n if (node.s && Array.isArray(node.s)) {\n let html = \"\";\n\n for (let i = 0; i < node.s.length; i++) {\n const staticSegment = node.s[i];\n html += staticSegment;\n\n if (i < node.s.length - 1) {\n const dynamicKey = i.toString();\n if (node[dynamicKey] !== undefined) {\n const newStatePath = statePath\n ? `${statePath}.${dynamicKey}`\n : dynamicKey;\n html += this.renderValue(\n node[dynamicKey],\n dynamicKey,\n newStatePath\n );\n }\n }\n }\n\n html = html.replace(/<root>/g, \"\").replace(/<\\/root>/g, \"\");\n return html;\n }\n\n return this.renderValue(node, \"\", statePath);\n }\n\n private renderValue(\n value: any,\n fieldKey?: string,\n statePath?: string\n ): string {\n if (value === null || value === undefined) {\n return \"\";\n }\n\n if (\n typeof value === \"string\" &&\n value.startsWith(\"{{\") &&\n value.endsWith(\"}}\")\n ) {\n return \"\";\n }\n\n if (typeof value === \"object\" && !Array.isArray(value)) {\n if (\n value.d &&\n Array.isArray(value.d) &&\n value.s &&\n Array.isArray(value.s)\n ) {\n const stateKey = statePath || fieldKey || \"\";\n if (stateKey) {\n this.rangeState[stateKey] = {\n items: value.d,\n statics: value.s,\n staticsMap: value.sm,\n };\n if (\n value.m &&\n typeof value.m === \"object\" &&\n typeof value.m.idKey === \"string\"\n ) {\n this.rangeIdKeys[stateKey] = value.m.idKey;\n }\n }\n return this.renderRangeStructure(value, fieldKey, statePath);\n }\n\n if (\"s\" in value && Array.isArray((value as TreeNode).s)) {\n return this.reconstructFromTree(value as TreeNode, statePath || \"\");\n }\n\n // Handle objects with only numeric keys (dynamics without statics)\n // This occurs when server sends partial updates for nested TreeNodes\n const keys = Object.keys(value);\n const numericKeys = keys.filter((k) => /^\\d+$/.test(k)).sort((a, b) => parseInt(a) - parseInt(b));\n if (numericKeys.length > 0 && numericKeys.length === keys.length) {\n // All keys are numeric - render each dynamic value in order\n return numericKeys\n .map((k) => {\n const itemStatePath = statePath ? `${statePath}.${k}` : k;\n return this.renderValue((value as Record<string, unknown>)[k], k, itemStatePath);\n })\n .join(\"\");\n }\n }\n\n if (Array.isArray(value)) {\n if (\n value.length > 0 &&\n Array.isArray(value[0]) &&\n typeof value[0][0] === \"string\"\n ) {\n return this.applyDifferentialOperations(value, statePath);\n }\n\n return value\n .map((item, idx) => {\n const itemKey = idx.toString();\n const itemStatePath = statePath ? `${statePath}.${itemKey}` : itemKey;\n if (typeof item === \"object\" && item && (item as TreeNode).s) {\n return this.reconstructFromTree(item as TreeNode, itemStatePath);\n }\n return this.renderValue(item, itemKey, itemStatePath);\n })\n .join(\"\");\n }\n\n if (typeof value === \"object\") {\n // Plain data objects (without tree structure) are state values that shouldn't be rendered.\n // This happens when state contains objects like EditingItem that are used by server-side\n // templates but aren't meant to be rendered directly in the DOM.\n // Skip them silently instead of converting to \"[object Object]\".\n this.logger.debug(\n \"Skipping plain object value (not a tree node) - this is normal for state-only data\"\n );\n return \"\";\n }\n\n return String(value);\n }\n\n private renderRangeStructure(\n rangeNode: any,\n fieldKey?: string,\n statePath?: string\n ): string {\n const { d: dynamics, s: statics, sm: staticsMap } = rangeNode;\n\n if (!dynamics || !Array.isArray(dynamics)) {\n return \"\";\n }\n\n if (dynamics.length === 0) {\n if (rangeNode[\"else\"]) {\n const elseKey = \"else\";\n const elseStatePath = statePath ? `${statePath}.else` : \"else\";\n return this.renderValue(rangeNode[\"else\"], elseKey, elseStatePath);\n }\n return \"\";\n }\n\n // Check if we have per-item statics via StaticsMap\n const hasStaticsMap = staticsMap && typeof staticsMap === \"object\";\n\n if (statics && Array.isArray(statics)) {\n return dynamics\n .map((item: any, itemIdx: number) => {\n // Get per-item statics from StaticsMap if available, otherwise use shared statics\n let itemStatics = statics;\n if (hasStaticsMap && item._sk && staticsMap[item._sk]) {\n itemStatics = staticsMap[item._sk];\n }\n\n let html = \"\";\n\n for (let i = 0; i < itemStatics.length; i++) {\n html += itemStatics[i];\n\n if (i < itemStatics.length - 1) {\n const localKey = i.toString();\n if (item[localKey] !== undefined) {\n const itemStatePath = statePath\n ? `${statePath}.${itemIdx}.${localKey}`\n : `${itemIdx}.${localKey}`;\n html += this.renderValue(\n item[localKey],\n localKey,\n itemStatePath\n );\n }\n }\n }\n\n return html;\n })\n .join(\"\");\n }\n\n return dynamics\n .map((item: any, idx: number) => {\n const itemKey = idx.toString();\n const itemStatePath = statePath ? `${statePath}.${itemKey}` : itemKey;\n return this.renderValue(item, itemKey, itemStatePath);\n })\n .join(\"\");\n }\n\n private applyDifferentialOperations(\n operations: any[],\n statePath?: string\n ): string {\n if (!statePath || !this.rangeState[statePath]) {\n return \"\";\n }\n\n const rangeData = this.rangeState[statePath];\n const currentItems = [...rangeData.items];\n const statics = rangeData.statics;\n\n for (const operation of operations) {\n if (!Array.isArray(operation) || operation.length < 2) {\n continue;\n }\n\n const opType = operation[0];\n\n switch (opType) {\n case \"r\": {\n const removeIndex = this.findItemIndexByKey(\n currentItems,\n operation[1],\n statics,\n statePath\n );\n if (removeIndex >= 0) {\n currentItems.splice(removeIndex, 1);\n }\n break;\n }\n case \"u\": {\n const updateIndex = this.findItemIndexByKey(\n currentItems,\n operation[1],\n statics,\n statePath\n );\n const changes = operation[2];\n if (updateIndex >= 0 && changes) {\n currentItems[updateIndex] = {\n ...currentItems[updateIndex],\n ...changes,\n };\n }\n break;\n }\n case \"a\": {\n this.addItemsToRange(\n currentItems,\n operation[1],\n operation[2],\n rangeData,\n false\n );\n if (\n operation[3] &&\n typeof operation[3] === \"object\" &&\n operation[3].idKey\n ) {\n this.rangeIdKeys[statePath || \"\"] = operation[3].idKey;\n }\n break;\n }\n case \"p\": {\n this.addItemsToRange(\n currentItems,\n operation[1],\n operation[2],\n rangeData,\n true\n );\n break;\n }\n case \"i\": {\n const targetIndex = this.findItemIndexByKey(\n currentItems,\n operation[1],\n statics,\n statePath\n );\n if (targetIndex >= 0) {\n const itemsToInsert = Array.isArray(operation[2])\n ? operation[2]\n : [operation[2]];\n currentItems.splice(targetIndex + 1, 0, ...itemsToInsert);\n }\n break;\n }\n case \"o\": {\n const newOrder = operation[1] as string[];\n const reorderedItems: any[] = [];\n const itemsByKey = new Map<string, any>();\n\n for (const item of currentItems) {\n const itemKey = this.getItemKey(item, statics, statePath);\n if (itemKey) {\n itemsByKey.set(itemKey, item);\n }\n }\n\n for (const orderedKey of newOrder) {\n const item = itemsByKey.get(orderedKey);\n if (item) {\n reorderedItems.push(item);\n }\n }\n\n currentItems.length = 0;\n currentItems.push(...reorderedItems);\n break;\n }\n default:\n break;\n }\n }\n\n this.rangeState[statePath] = {\n items: currentItems,\n statics: rangeData.statics,\n staticsMap: rangeData.staticsMap,\n };\n\n this.treeState[statePath] = {\n d: currentItems,\n s: rangeData.statics,\n sm: rangeData.staticsMap,\n };\n\n const rangeStructure = this.getCurrentRangeStructure(statePath);\n if (rangeStructure && rangeStructure.s) {\n return this.renderItemsWithStatics(\n currentItems,\n rangeStructure.s,\n rangeStructure.sm,\n statePath\n );\n }\n\n return currentItems.map((item) => this.renderValue(item)).join(\"\");\n }\n\n private getCurrentRangeStructure(stateKey: string): any {\n if (this.rangeState[stateKey]) {\n return {\n d: this.rangeState[stateKey].items,\n s: this.rangeState[stateKey].statics,\n sm: this.rangeState[stateKey].staticsMap,\n };\n }\n\n const fieldValue = this.treeState[stateKey];\n if (\n fieldValue &&\n typeof fieldValue === \"object\" &&\n (fieldValue as TreeNode).s\n ) {\n return fieldValue;\n }\n\n return null;\n }\n\n private renderItemsWithStatics(\n items: any[],\n statics: string[],\n staticsMap?: Record<string, string[]>,\n statePath?: string\n ): string {\n const result = items\n .map((item: any, itemIdx: number) => {\n // Get per-item statics from StaticsMap if available, otherwise use shared statics\n let itemStatics = statics;\n if (\n staticsMap &&\n typeof staticsMap === \"object\" &&\n item._sk &&\n staticsMap[item._sk]\n ) {\n itemStatics = staticsMap[item._sk];\n }\n\n let html = \"\";\n\n for (let i = 0; i < itemStatics.length; i++) {\n html += itemStatics[i];\n\n if (i < itemStatics.length - 1) {\n const fieldKey = i.toString();\n if (item[fieldKey] !== undefined) {\n const itemStatePath = statePath\n ? `${statePath}.${itemIdx}.${fieldKey}`\n : `${itemIdx}.${fieldKey}`;\n html += this.renderValue(item[fieldKey], fieldKey, itemStatePath);\n }\n }\n }\n\n return html;\n })\n .join(\"\");\n\n if (this.logger.isDebugEnabled()) {\n this.logger.debug(\"[renderItemsWithStatics] statics:\", statics);\n this.logger.debug(\"[renderItemsWithStatics] items count:\", items.length);\n this.logger.debug(\n \"[renderItemsWithStatics] result snippet:\",\n result.substring(0, 200)\n );\n }\n\n return result;\n }\n\n private addItemsToRange(\n currentItems: any[],\n items: any,\n statics: any[] | undefined,\n rangeData: RangeStateEntry,\n prepend: boolean\n ): void {\n if (statics) {\n rangeData.statics = statics;\n }\n\n if (!items) return;\n\n const itemsArray = Array.isArray(items) ? items : [items];\n if (prepend) {\n currentItems.unshift(...itemsArray);\n } else {\n currentItems.push(...itemsArray);\n }\n }\n\n private getItemKey(\n item: any,\n statics: any[],\n statePath?: string\n ): string | null {\n if (!statePath || !this.rangeIdKeys[statePath]) {\n return null;\n }\n\n const keyPosStr = this.rangeIdKeys[statePath];\n return item[keyPosStr] || null;\n }\n\n private findItemIndexByKey(\n items: any[],\n key: string,\n statics: any[],\n statePath?: string\n ): number {\n return items.findIndex(\n (item: any) => this.getItemKey(item, statics, statePath) === key\n );\n }\n}\n", "import type { ResponseMetadata } from \"../types\";\nimport { ModalManager } from \"../dom/modal-manager\";\n\n/**\n * Tracks form submission lifecycle for LiveTemplate actions.\n */\nexport class FormLifecycleManager {\n private activeForm: HTMLFormElement | null = null;\n private activeButton: HTMLButtonElement | null = null;\n private originalButtonText: string | null = null;\n\n constructor(private readonly modalManager: ModalManager) {}\n\n setActiveSubmission(\n form: HTMLFormElement | null,\n button: HTMLButtonElement | null,\n originalButtonText: string | null\n ): void {\n this.activeForm = form;\n this.activeButton = button;\n this.originalButtonText = originalButtonText;\n }\n\n handleResponse(meta: ResponseMetadata): void {\n if (this.activeForm) {\n this.activeForm.dispatchEvent(\n new CustomEvent(\"lvt:done\", { detail: meta })\n );\n }\n\n if (meta.success) {\n this.handleSuccess(meta);\n } else {\n this.handleError(meta);\n }\n\n this.restoreFormState();\n }\n\n reset(): void {\n this.restoreFormState();\n }\n\n private handleSuccess(meta: ResponseMetadata): void {\n if (!this.activeForm) {\n return;\n }\n\n this.activeForm.dispatchEvent(\n new CustomEvent(\"lvt:success\", { detail: meta })\n );\n\n const modalParent = this.activeForm.closest('[role=\"dialog\"]');\n if (modalParent && modalParent.id) {\n this.modalManager.close(modalParent.id);\n }\n\n if (!this.activeForm.hasAttribute(\"lvt-preserve\")) {\n this.activeForm.reset();\n }\n }\n\n private handleError(meta: ResponseMetadata): void {\n if (!this.activeForm) {\n return;\n }\n\n this.activeForm.dispatchEvent(\n new CustomEvent(\"lvt:error\", { detail: meta })\n );\n }\n\n private restoreFormState(): void {\n if (this.activeButton && this.originalButtonText !== null) {\n this.activeButton.disabled = false;\n this.activeButton.textContent = this.originalButtonText;\n }\n\n this.activeForm = null;\n this.activeButton = null;\n this.originalButtonText = null;\n }\n}\n", "import type { LiveTemplateClientOptions, UpdateResponse } from \"../types\";\nimport type { Logger } from \"../utils/logger\";\n\nexport interface WebSocketTransportOptions {\n url: string;\n autoReconnect?: boolean;\n reconnectDelay?: number;\n maxReconnectDelay?: number; // Maximum delay between reconnect attempts (default: 16000ms)\n maxReconnectAttempts?: number; // Maximum number of reconnect attempts (default: 10, 0 = unlimited)\n onOpen?: (socket: WebSocket) => void;\n onMessage?: (event: MessageEvent<string>) => void;\n onClose?: (event: CloseEvent) => void;\n onReconnectAttempt?: (attempt: number, delay: number) => void;\n onReconnectFailed?: () => void; // Called when max reconnect attempts reached\n onError?: (event: Event) => void;\n}\n\n/**\n * Lightweight wrapper around browser WebSocket with optional auto-reconnect support.\n * Implements exponential backoff with jitter to prevent thundering herd.\n */\nexport class WebSocketTransport {\n private socket: WebSocket | null = null;\n private reconnectTimer: number | null = null;\n private manuallyClosed = false;\n private reconnectAttempts = 0;\n\n constructor(private readonly options: WebSocketTransportOptions) {}\n\n connect(): void {\n this.manuallyClosed = false;\n this.clearReconnectTimer();\n\n this.socket = new WebSocket(this.options.url);\n const socket = this.socket;\n\n socket.onopen = () => {\n // Reset reconnect attempts on successful connection\n this.reconnectAttempts = 0;\n this.options.onOpen?.(socket);\n };\n\n socket.onmessage = (event: MessageEvent<string>) => {\n this.options.onMessage?.(event);\n };\n\n socket.onclose = (event: CloseEvent) => {\n this.options.onClose?.(event);\n if (!this.manuallyClosed && this.options.autoReconnect) {\n this.scheduleReconnect();\n }\n };\n\n socket.onerror = (event: Event) => {\n this.options.onError?.(event);\n };\n }\n\n send(data: string): void {\n if (this.socket && this.socket.readyState === 1) { // WebSocket.OPEN = 1\n this.socket.send(data);\n }\n }\n\n disconnect(): void {\n this.manuallyClosed = true;\n this.clearReconnectTimer();\n if (this.socket) {\n this.socket.close();\n this.socket = null;\n }\n }\n\n getSocket(): WebSocket | null {\n return this.socket;\n }\n\n private scheduleReconnect(): void {\n this.clearReconnectTimer();\n\n // Check if max reconnect attempts reached\n const maxAttempts = this.options.maxReconnectAttempts ?? 10;\n if (maxAttempts > 0 && this.reconnectAttempts >= maxAttempts) {\n this.options.onReconnectFailed?.();\n return;\n }\n\n this.reconnectAttempts++;\n\n // Calculate exponential backoff: baseDelay * 2^attempt\n const baseDelay = this.options.reconnectDelay ?? 1000;\n const maxDelay = this.options.maxReconnectDelay ?? 16000;\n const exponentialDelay = baseDelay * Math.pow(2, this.reconnectAttempts - 1);\n\n // Add jitter: random value between 0 and 1000ms to prevent thundering herd\n const jitter = Math.random() * 1000;\n\n // Calculate final delay with maximum cap\n const delay = Math.min(exponentialDelay + jitter, maxDelay);\n\n this.reconnectTimer = window.setTimeout(() => {\n this.options.onReconnectAttempt?.(this.reconnectAttempts, delay);\n this.connect();\n }, delay);\n }\n\n private clearReconnectTimer(): void {\n if (this.reconnectTimer !== null) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n}\n\nexport interface WebSocketManagerConfig {\n options: LiveTemplateClientOptions;\n onConnected: () => void;\n onDisconnected: () => void;\n onMessage: (response: UpdateResponse, event: MessageEvent<string>) => void;\n onReconnectAttempt?: (attempt: number, delay: number) => void;\n onReconnectFailed?: () => void;\n onError?: (event: Event) => void;\n logger: Logger;\n}\n\nexport interface WebSocketConnectResult {\n usingWebSocket: boolean;\n initialState?: UpdateResponse | null;\n}\n\nexport class WebSocketManager {\n private transport: WebSocketTransport | null = null;\n\n constructor(private readonly config: WebSocketManagerConfig) {}\n\n async connect(): Promise<WebSocketConnectResult> {\n const liveUrl = this.getLiveUrl();\n\n const wsAvailable = await checkWebSocketAvailability(\n liveUrl,\n this.config.logger\n );\n if (!wsAvailable) {\n const initialState = await fetchInitialState(liveUrl, this.config.logger);\n return { usingWebSocket: false, initialState };\n }\n\n this.transport = new WebSocketTransport({\n url: this.getWebSocketUrl(),\n autoReconnect: this.config.options.autoReconnect,\n reconnectDelay: this.config.options.reconnectDelay,\n maxReconnectDelay: 16000, // 16 seconds maximum\n maxReconnectAttempts: 10, // 10 attempts before giving up\n onOpen: () => {\n this.config.onConnected();\n },\n onMessage: (event) => {\n try {\n const payload: UpdateResponse = JSON.parse(event.data);\n this.config.onMessage(payload, event);\n } catch (error) {\n this.config.logger.error(\"Failed to parse WebSocket message:\", error);\n }\n },\n onClose: () => {\n this.config.onDisconnected();\n },\n onReconnectAttempt: (attempt, delay) => {\n this.config.onReconnectAttempt?.(attempt, delay);\n },\n onReconnectFailed: () => {\n this.config.onReconnectFailed?.();\n },\n onError: (event) => {\n this.config.onError?.(event);\n },\n });\n\n this.transport.connect();\n return { usingWebSocket: true };\n }\n\n disconnect(): void {\n this.transport?.disconnect();\n this.transport = null;\n }\n\n send(data: string): void {\n this.transport?.send(data);\n }\n\n getReadyState(): number | undefined {\n return this.transport?.getSocket()?.readyState;\n }\n\n getSocket(): WebSocket | null {\n return this.transport?.getSocket() ?? null;\n }\n\n private getWebSocketUrl(): string {\n const liveUrl = this.config.options.liveUrl || \"/live\";\n const baseUrl = this.config.options.wsUrl;\n if (baseUrl) {\n return baseUrl;\n }\n return `ws://${window.location.host}${liveUrl}`;\n }\n\n private getLiveUrl(): string {\n return this.config.options.liveUrl || window.location.pathname + window.location.search;\n }\n}\n\nexport async function checkWebSocketAvailability(\n liveUrl: string,\n logger?: Logger\n): Promise<boolean> {\n try {\n const response = await fetch(liveUrl, {\n method: \"HEAD\",\n });\n\n const wsHeader = response.headers.get(\"X-LiveTemplate-WebSocket\");\n if (wsHeader) {\n return wsHeader === \"enabled\";\n }\n\n return true;\n } catch (error) {\n logger?.warn(\"Failed to check WebSocket availability:\", error);\n return true;\n }\n}\n\nexport async function fetchInitialState(\n liveUrl: string,\n logger?: Logger\n): Promise<UpdateResponse | null> {\n try {\n const response = await fetch(liveUrl, {\n method: \"GET\",\n credentials: \"include\",\n headers: {\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch initial state: ${response.status}`);\n }\n\n return (await response.json()) as UpdateResponse;\n } catch (error) {\n logger?.warn(\"Failed to fetch initial state:\", error);\n return null;\n }\n}\n", "/**\n * S3Uploader - Handles direct uploads to S3 using presigned URLs\n */\n\nimport type {\n ExternalUploadMeta,\n UploadEntry,\n UploadProgressCallback,\n Uploader,\n} from \"./types\";\n\nexport class S3Uploader implements Uploader {\n /**\n * Upload a file directly to S3 using presigned PUT URL\n */\n async upload(\n entry: UploadEntry,\n meta: ExternalUploadMeta,\n onProgress?: UploadProgressCallback\n ): Promise<void> {\n const { file } = entry;\n\n // Create abort controller for cancellation\n entry.abortController = new AbortController();\n\n try {\n // Create XMLHttpRequest for progress tracking\n const xhr = new XMLHttpRequest();\n\n // Track upload progress and notify handler\n xhr.upload.addEventListener(\"progress\", (e) => {\n if (e.lengthComputable) {\n entry.bytesUploaded = e.loaded;\n entry.progress = Math.round((e.loaded / e.total) * 100);\n // Notify progress callback\n if (onProgress) {\n onProgress(entry);\n }\n }\n });\n\n // Handle abort\n entry.abortController.signal.addEventListener(\"abort\", () => {\n xhr.abort();\n });\n\n // Create promise for upload completion\n const uploadPromise = new Promise<void>((resolve, reject) => {\n xhr.addEventListener(\"load\", () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n entry.done = true;\n entry.progress = 100;\n resolve();\n } else {\n reject(new Error(`S3 upload failed with status ${xhr.status}: ${xhr.statusText}`));\n }\n });\n\n xhr.addEventListener(\"error\", () => {\n reject(new Error(\"S3 upload failed: Network error\"));\n });\n\n xhr.addEventListener(\"abort\", () => {\n reject(new Error(\"S3 upload cancelled\"));\n });\n });\n\n // Open connection\n xhr.open(\"PUT\", meta.url);\n\n // Set headers\n if (meta.headers) {\n for (const [key, value] of Object.entries(meta.headers)) {\n xhr.setRequestHeader(key, value);\n }\n }\n\n // Send file\n xhr.send(file);\n\n await uploadPromise;\n } catch (error) {\n entry.error = error instanceof Error ? error.message : String(error);\n throw error;\n }\n }\n}\n", "/**\n * UploadHandler - Manages file uploads for LiveTemplate\n *\n * Handles:\n * - File input detection and change events\n * - Chunked uploads to server via WebSocket\n * - External uploads (S3) via presigned URLs\n * - Progress tracking and callbacks\n * - Upload cancellation\n */\n\nimport { S3Uploader } from \"./s3-uploader\";\nimport type {\n ExternalUploadMeta,\n FileMetadata,\n UploadChunkMessage,\n UploadCompleteMessage,\n UploadEntry,\n UploadHandlerOptions,\n UploadProgressMessage,\n UploadStartMessage,\n UploadStartResponse,\n Uploader,\n} from \"./types\";\n\nexport class UploadHandler {\n private entries: Map<string, UploadEntry> = new Map();\n private pendingFiles: Map<string, File[]> = new Map(); // uploadName -> files\n private autoUploadConfig: Map<string, boolean> = new Map(); // uploadName -> autoUpload\n private chunkSize: number;\n private uploaders: Map<string, Uploader> = new Map();\n private onProgress?: (entry: UploadEntry) => void;\n private onComplete?: (uploadName: string, entries: UploadEntry[]) => void;\n private onError?: (entry: UploadEntry, error: string) => void;\n private inputHandlers: WeakMap<HTMLInputElement, EventListener> = new WeakMap();\n\n constructor(\n private sendMessage: (message: any) => void,\n options: UploadHandlerOptions = {}\n ) {\n this.chunkSize = options.chunkSize || 256 * 1024; // 256KB default\n this.onProgress = options.onProgress;\n this.onComplete = options.onComplete;\n this.onError = options.onError;\n\n // Register default uploaders\n this.uploaders.set(\"s3\", new S3Uploader());\n }\n\n /**\n * Initialize upload detection on file inputs with lvt-upload attribute\n */\n initializeFileInputs(container: Element) {\n const inputs = container.querySelectorAll<HTMLInputElement>(\n 'input[type=\"file\"][lvt-upload]'\n );\n\n inputs.forEach((input) => {\n const uploadName = input.getAttribute(\"lvt-upload\");\n if (!uploadName) return;\n\n // Remove existing listener if any\n const existingHandler = this.inputHandlers.get(input);\n if (existingHandler) {\n input.removeEventListener(\"change\", existingHandler);\n }\n\n // Create new handler\n const handler = (e: Event) => {\n const files = (e.target as HTMLInputElement).files;\n if (!files || files.length === 0) return;\n\n // Always send upload_start to get validation and config\n // But only proceed with chunks if autoUpload is true\n this.startUpload(uploadName, Array.from(files));\n };\n\n input.addEventListener(\"change\", handler);\n // Store handler in WeakMap to prevent memory leaks\n this.inputHandlers.set(input, handler);\n });\n }\n\n /**\n * Start upload process for selected files\n */\n async startUpload(uploadName: string, files: File[]): Promise<void> {\n // Store files temporarily for when server response arrives\n this.pendingFiles.set(uploadName, files);\n\n // Create file metadata\n const fileMetadata: FileMetadata[] = files.map((file) => ({\n name: file.name,\n type: file.type || \"application/octet-stream\",\n size: file.size,\n }));\n\n // Send upload_start message to server\n const startMessage: UploadStartMessage = {\n action: \"upload_start\",\n upload_name: uploadName,\n files: fileMetadata,\n };\n\n this.sendMessage(startMessage);\n }\n\n /**\n * Handle upload_start response from server\n */\n async handleUploadStartResponse(\n response: UploadStartResponse\n ): Promise<void> {\n const { upload_name, entries: entryInfos } = response;\n\n // Store autoUpload configuration from first entry\n if (entryInfos.length > 0) {\n this.autoUploadConfig.set(upload_name, entryInfos[0].auto_upload);\n }\n\n // Get pending files for this upload\n const files = this.pendingFiles.get(upload_name);\n if (!files) {\n console.error(`No pending files found for upload: ${upload_name}`);\n return;\n }\n\n // Clear pending files\n this.pendingFiles.delete(upload_name);\n\n // Build a map from file name to file object for lookup\n const fileMap = new Map<string, File>();\n for (const file of files) {\n fileMap.set(file.name, file);\n }\n\n // Create upload entries\n const entries: UploadEntry[] = [];\n\n for (const info of entryInfos) {\n const file = fileMap.get(info.client_name);\n\n if (!file) {\n console.warn(\n `No file found for entry ${info.entry_id} (client_name: ${info.client_name})`\n );\n continue;\n }\n\n const entry: UploadEntry = {\n id: info.entry_id,\n file,\n uploadName: upload_name,\n progress: 0,\n bytesUploaded: 0,\n valid: info.valid,\n done: false,\n error: info.error,\n external: info.external,\n };\n\n this.entries.set(entry.id, entry);\n entries.push(entry);\n\n // Handle invalid entries\n if (!info.valid) {\n if (this.onError && info.error) {\n this.onError(entry, info.error);\n }\n continue;\n }\n\n // Only start upload immediately if autoUpload is true\n // Otherwise, entries are stored and will be uploaded on form submit\n if (info.auto_upload) {\n // Start upload (external or chunked)\n if (info.external) {\n this.uploadExternal(entry, info.external);\n } else {\n this.uploadChunked(entry);\n }\n }\n }\n }\n\n /**\n * Upload file using external uploader (S3, etc.)\n */\n private async uploadExternal(\n entry: UploadEntry,\n meta: ExternalUploadMeta\n ): Promise<void> {\n try {\n const uploader = this.uploaders.get(meta.uploader);\n if (!uploader) {\n throw new Error(`Unknown uploader: ${meta.uploader}`);\n }\n\n // Start external upload with progress callback\n await uploader.upload(entry, meta, this.onProgress);\n\n // Notify server of completion\n const completeMessage: UploadCompleteMessage = {\n action: \"upload_complete\",\n upload_name: entry.uploadName,\n entry_ids: [entry.id],\n };\n\n this.sendMessage(completeMessage);\n\n if (this.onComplete) {\n this.onComplete(entry.uploadName, [entry]);\n }\n\n // Clear the file input to prevent re-upload of the same file\n this.clearFileInput(entry.uploadName);\n\n // Schedule cleanup after completion\n this.cleanupEntries(entry.uploadName);\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n entry.error = errorMsg;\n if (this.onError) {\n this.onError(entry, errorMsg);\n }\n // Schedule cleanup after error\n this.cleanupEntries(entry.uploadName);\n }\n }\n\n /**\n * Upload file in chunks via WebSocket\n */\n private async uploadChunked(entry: UploadEntry): Promise<void> {\n const { file, id } = entry;\n let offset = 0;\n\n // Create abort controller for cancellation\n entry.abortController = new AbortController();\n\n try {\n while (offset < file.size) {\n // Check if upload was cancelled\n if (entry.abortController.signal.aborted) {\n throw new Error(\"Upload cancelled\");\n }\n // Read chunk\n const end = Math.min(offset + this.chunkSize, file.size);\n const chunk = file.slice(offset, end);\n\n // Convert to base64\n const base64 = await this.fileToBase64(chunk);\n\n // Send chunk\n const chunkMessage: UploadChunkMessage = {\n action: \"upload_chunk\",\n entry_id: id,\n chunk_base64: base64,\n offset,\n total: file.size,\n };\n\n this.sendMessage(chunkMessage);\n\n // Update progress\n offset = end;\n entry.bytesUploaded = offset;\n entry.progress = Math.round((offset / file.size) * 100);\n\n if (this.onProgress) {\n this.onProgress(entry);\n }\n }\n\n // Send complete message\n entry.done = true;\n const completeMessage: UploadCompleteMessage = {\n action: \"upload_complete\",\n upload_name: entry.uploadName,\n entry_ids: [id],\n };\n\n this.sendMessage(completeMessage);\n\n if (this.onComplete) {\n this.onComplete(entry.uploadName, [entry]);\n }\n\n // Clear the file input to prevent re-upload of the same file\n this.clearFileInput(entry.uploadName);\n\n // Schedule cleanup after completion\n this.cleanupEntries(entry.uploadName);\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n entry.error = errorMsg;\n if (this.onError) {\n this.onError(entry, errorMsg);\n }\n // Schedule cleanup after error\n this.cleanupEntries(entry.uploadName);\n }\n }\n\n /**\n * Handle progress message from server (for chunked uploads)\n */\n handleProgressMessage(message: UploadProgressMessage): void {\n const entry = this.entries.get(message.entry_id);\n if (!entry) return;\n\n entry.progress = message.progress;\n entry.bytesUploaded = message.bytes_recv;\n\n if (this.onProgress) {\n this.onProgress(entry);\n }\n }\n\n /**\n * Cancel upload\n */\n cancelUpload(entryId: string): void {\n const entry = this.entries.get(entryId);\n if (!entry) return;\n\n // Abort external upload if in progress\n if (entry.abortController) {\n entry.abortController.abort();\n }\n\n // Send cancel message to server\n this.sendMessage({\n action: \"cancel_upload\",\n entry_id: entryId,\n });\n\n // Remove entry\n this.entries.delete(entryId);\n }\n\n /**\n * Get all entries for an upload name\n */\n getEntries(uploadName: string): UploadEntry[] {\n const entries: UploadEntry[] = [];\n for (const entry of this.entries.values()) {\n if (entry.uploadName === uploadName) {\n entries.push(entry);\n }\n }\n return entries;\n }\n\n /**\n * Trigger upload for all pending entries (used when autoUpload is false)\n * Called by LiveTemplate client on form submit\n */\n triggerPendingUploads(uploadName: string): void {\n // Get all entries for this upload that haven't started yet\n const pendingEntries: UploadEntry[] = [];\n for (const entry of this.entries.values()) {\n if (\n entry.uploadName === uploadName &&\n entry.progress === 0 &&\n !entry.done &&\n !entry.error\n ) {\n pendingEntries.push(entry);\n }\n }\n\n // Start uploads\n for (const entry of pendingEntries) {\n if (entry.external) {\n this.uploadExternal(entry, entry.external);\n } else {\n this.uploadChunked(entry);\n }\n }\n }\n\n /**\n * Register custom uploader\n */\n registerUploader(name: string, uploader: Uploader): void {\n this.uploaders.set(name, uploader);\n }\n\n /**\n * Clear file input to prevent re-upload of the same file\n * Called after successful upload completion\n */\n private clearFileInput(uploadName: string): void {\n // Find all file inputs with this upload name\n const inputs = document.querySelectorAll<HTMLInputElement>(\n `input[type=\"file\"][lvt-upload=\"${uploadName}\"]`\n );\n\n inputs.forEach((input) => {\n // Clear the file input value\n input.value = '';\n });\n }\n\n /**\n * Clean up completed or errored upload entries to prevent memory leaks\n * Automatically called after completion/error, but can be called manually\n * @param uploadName - Optional upload name to clean specific uploads\n * @param delay - Optional delay in ms before cleanup (default: 5000ms)\n */\n cleanupEntries(uploadName?: string, delay: number = 5000): void {\n setTimeout(() => {\n const entriesToRemove: string[] = [];\n\n for (const [id, entry] of this.entries) {\n // Skip if uploadName is specified and doesn't match\n if (uploadName && entry.uploadName !== uploadName) continue;\n\n // Remove completed or errored entries\n if (entry.done || entry.error) {\n entriesToRemove.push(id);\n }\n }\n\n for (const id of entriesToRemove) {\n this.entries.delete(id);\n }\n }, delay);\n }\n\n /**\n * Convert File/Blob to base64 string\n */\n private fileToBase64(blob: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = reader.result as string;\n // Remove data URL prefix\n const base64 = result.split(\",\")[1];\n resolve(base64);\n };\n reader.onerror = reject;\n reader.readAsDataURL(blob);\n });\n }\n}\n", "export type LogLevel = \"silent\" | \"error\" | \"warn\" | \"info\" | \"debug\";\n\nconst levelPriority: Record<LogLevel, number> = {\n silent: 0,\n error: 1,\n warn: 2,\n info: 3,\n debug: 4,\n};\n\ninterface LogState {\n level: LogLevel;\n}\n\ntype ConsoleMethod = \"error\" | \"warn\" | \"info\" | \"debug\";\n\nconst DEFAULT_SCOPE = \"LiveTemplate\";\n\n/**\n * Lightweight console logger with support for log levels and scoped prefixes.\n */\nexport class Logger {\n constructor(\n private readonly state: LogState,\n private readonly scope: string[] = [],\n private readonly sink: Console = console\n ) {}\n\n setLevel(level: LogLevel): void {\n this.state.level = level;\n }\n\n getLevel(): LogLevel {\n return this.state.level;\n }\n\n child(scope: string): Logger {\n return new Logger(this.state, [...this.scope, scope], this.sink);\n }\n\n isDebugEnabled(): boolean {\n return this.shouldLog(\"debug\");\n }\n\n error(...args: unknown[]): void {\n this.log(\"error\", \"error\", args);\n }\n\n warn(...args: unknown[]): void {\n this.log(\"warn\", \"warn\", args);\n }\n\n info(...args: unknown[]): void {\n this.log(\"info\", \"info\", args);\n }\n\n debug(...args: unknown[]): void {\n this.log(\"debug\", \"debug\", args);\n }\n\n private log(level: LogLevel, method: ConsoleMethod, args: unknown[]): void {\n if (!this.shouldLog(level)) {\n return;\n }\n\n const target =\n (this.sink[method] as (...args: unknown[]) => void) ||\n (console[method] as (...args: unknown[]) => void) ||\n console.log;\n target.apply(this.sink, [this.formatPrefix(), ...args]);\n }\n\n private shouldLog(level: LogLevel): boolean {\n return levelPriority[level] <= levelPriority[this.state.level];\n }\n\n private formatPrefix(): string {\n if (this.scope.length === 0) {\n return `[${DEFAULT_SCOPE}]`;\n }\n\n return `[${DEFAULT_SCOPE}:${this.scope.join(\":\")}]`;\n }\n}\n\nexport interface LoggerOptions {\n level?: LogLevel;\n scope?: string | string[];\n sink?: Console;\n}\n\nexport function createLogger(options: LoggerOptions = {}): Logger {\n const state: LogState = {\n level: options.level ?? \"info\",\n };\n\n const scope = Array.isArray(options.scope)\n ? options.scope\n : options.scope\n ? [options.scope]\n : [];\n\n return new Logger(state, scope, options.sink ?? console);\n}\n", "import type { LiveTemplateClient } from \"../livetemplate-client\";\nimport type { UpdateResult } from \"../types\";\n\n/**\n * Utility function to load and apply updates from JSON files.\n */\nexport async function loadAndApplyUpdate(\n client: LiveTemplateClient,\n updatePath: string\n): Promise<UpdateResult> {\n try {\n const nodeRequire = (globalThis as any)?.require;\n if (typeof nodeRequire === \"function\") {\n const fs = nodeRequire(\"fs\");\n const updateData = JSON.parse(fs.readFileSync(updatePath, \"utf8\"));\n return client.applyUpdate(updateData);\n }\n\n const response = await fetch(updatePath);\n const updateData = await response.json();\n return client.applyUpdate(updateData);\n } catch (error) {\n throw new Error(`Failed to load update from ${updatePath}: ${error}`);\n }\n}\n\n/**\n * Compare two HTML strings, ignoring whitespace differences.\n */\nexport function compareHTML(\n expected: string,\n actual: string\n): {\n match: boolean;\n differences: string[];\n} {\n const differences: string[] = [];\n\n const normalizeHTML = (html: string) => {\n return html.replace(/\\s+/g, \" \").replace(/>\\s+</g, \"><\").trim();\n };\n\n const normalizedExpected = normalizeHTML(expected);\n const normalizedActual = normalizeHTML(actual);\n\n if (normalizedExpected === normalizedActual) {\n return { match: true, differences: [] };\n }\n\n const expectedLines = normalizedExpected.split(\"\\n\");\n const actualLines = normalizedActual.split(\"\\n\");\n const maxLines = Math.max(expectedLines.length, actualLines.length);\n\n for (let i = 0; i < maxLines; i++) {\n const expectedLine = expectedLines[i] || \"\";\n const actualLine = actualLines[i] || \"\";\n\n if (expectedLine !== actualLine) {\n differences.push(`Line ${i + 1}:`);\n differences.push(` Expected: ${expectedLine}`);\n differences.push(` Actual: ${actualLine}`);\n }\n }\n\n return { match: false, differences };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["livetemplate_client_exports", "__export", "LiveTemplateClient", "checkLvtConfirm", "compareHTML", "extractLvtData", "loadAndApplyUpdate", "setupReactiveAttributeListeners", "DOCUMENT_FRAGMENT_NODE", "morphAttrs", "fromNode", "toNode", "toNodeAttrs", "attr", "attrName", "attrNamespaceURI", "attrValue", "fromValue", "i", "fromNodeAttrs", "range", "NS_XHTML", "doc", "HAS_TEMPLATE_SUPPORT", "HAS_RANGE_SUPPORT", "createFragmentFromTemplate", "str", "template", "createFragmentFromRange", "fragment", "createFragmentFromWrap", "toElement", "compareNodeNames", "fromEl", "toEl", "fromNodeName", "toNodeName", "fromCodeStart", "toCodeStart", "createElementNS", "name", "namespaceURI", "moveChildren", "curChild", "nextChild", "syncBooleanAttrProp", "specialElHandlers", "parentNode", "parentName", "newValue", "firstChild", "oldValue", "selectedIndex", "optgroup", "nodeName", "ELEMENT_NODE", "DOCUMENT_FRAGMENT_NODE$1", "TEXT_NODE", "COMMENT_NODE", "noop", "defaultGetNodeKey", "node", "morphdomFactory", "options", "toNodeHtml", "getNodeKey", "onBeforeNodeAdded", "onNodeAdded", "onBeforeElUpdated", "onElUpdated", "onBeforeNodeDiscarded", "onNodeDiscarded", "onBeforeElChildrenUpdated", "skipFromChildren", "addChild", "parent", "child", "childrenOnly", "fromNodesLookup", "keyedRemovalList", "addKeyedRemoval", "key", "walkDiscardedChildNodes", "skipKeyedNodes", "removeNode", "indexTree", "handleNodeAdded", "el", "nextSibling", "unmatchedFromEl", "morphEl", "cleanupFromEl", "curFromNodeChild", "curFromNodeKey", "fromNextSibling", "toElKey", "beforeUpdateResult", "morphChildren", "skipFrom", "curToNodeChild", "curToNodeKey", "toNextSibling", "matchingFromEl", "outer", "curFromNodeType", "isCompatible", "onBeforeNodeAddedResult", "specialElHandler", "morphedNode", "morphedNodeType", "toNodeType", "len", "elToRemove", "morphdom", "morphdom_esm_default", "FOCUSABLE_INPUTS", "FocusManager", "logger", "wrapper", "selector", "FOCUSABLE_INPUTS", "type", "wrapperId", "focusKey", "blurKey", "focusListener", "event", "_a", "target", "blurListener", "_b", "_c", "element", "index", "wasFocused", "el", "handleScrollDirectives", "rootElement", "element", "htmlElement", "mode", "behavior", "threshold", "handleHighlightDirectives", "duration", "color", "originalBackground", "originalTransition", "handleAnimateDirectives", "animation", "style", "debounce", "func", "wait", "timeout", "args", "context", "throttle", "limit", "inThrottle", "checkLvtConfirm", "element", "confirmMessage", "extractLvtData", "data", "attributes", "i", "attr", "key", "EventDelegator", "context", "logger", "wrapperElement", "eventTypes", "wrapperId", "rateLimitedHandlers", "eventType", "listenerKey", "existingListener", "listener", "e", "_a", "currentWrapper", "target", "element", "inWrapper", "attrName", "action", "actionElement", "persistTable", "formElement", "keyFilter", "targetElement", "handleAction", "checkLvtConfirm", "message", "formData", "checkboxes", "checkboxNames", "cb", "name", "passwordFields", "el", "value", "key", "attr", "submitButton", "originalButtonText", "input", "uploadName", "throttleValue", "debounceValue", "handlerCache", "cacheKey", "rateLimitedHandler", "limit", "throttle", "wait", "debounce", "windowEvents", "openListenerKey", "existingOpenListener", "openListener", "modalId", "closeListenerKey", "existingCloseListener", "closeListener", "backdropListenerKey", "existingBackdropListener", "closeModalWithAction", "modal", "closeAction", "backdropListener", "escapeListenerKey", "existingEscapeListener", "escapeListener", "openModals", "lastModal", "getFocusableElements", "container", "selector", "htmlEl", "style", "isNotDisplayNone", "isNotVisibilityHidden", "hasLayoutContext", "focusTrapElements", "activeTrap", "trap", "htmlTrap", "focusableElements", "firstElement", "lastElement", "observerKey", "existingObserver", "processAutofocus", "htmlElement", "isVisible", "wasFocused", "observer", "mutations", "shouldProcess", "mutation", "node", "ObserverManager", "context", "logger", "sentinel", "entries", "wrapperElement", "ModalManager", "logger", "modalId", "modal", "firstInput", "activeElement", "isVisible", "element", "LoadingIndicator", "bar", "style", "FormDisabler", "wrapper", "form", "input", "LIFECYCLE_EVENTS", "ACTION_MAP", "parseReactiveAttribute", "attrName", "attrValue", "match", "actionKey", "action", "segments", "lastSegment", "LIFECYCLE_EVENTS", "lifecycle", "actionName", "executeAction", "element", "param", "classes", "c", "colonIndex", "name", "value", "matchesEvent", "binding", "processReactiveAttributes", "attr", "setupReactiveAttributeListeners", "_a", "deepClone", "obj", "isRangeNode", "node", "TreeRenderer", "logger", "update", "changed", "key", "value", "existing", "oldValue", "newValue", "currentPath", "_a", "merged", "fieldPath", "isDifferentialOps", "existingIsRange", "rangeStructure", "operations", "statePath", "currentItems", "operation", "removeIndex", "updateIndex", "changes", "itemsToAdd", "itemsToPrepend", "targetIndex", "itemsToInsert", "newOrder", "reorderedItems", "itemsByKey", "item", "itemKey", "orderedKey", "html", "i", "staticSegment", "dynamicKey", "newStatePath", "fieldKey", "stateKey", "keys", "numericKeys", "k", "a", "b", "itemStatePath", "idx", "rangeNode", "dynamics", "statics", "staticsMap", "elseKey", "elseStatePath", "hasStaticsMap", "itemIdx", "itemStatics", "localKey", "rangeData", "fieldValue", "items", "result", "prepend", "itemsArray", "keyPosStr", "FormLifecycleManager", "modalManager", "form", "button", "originalButtonText", "meta", "modalParent", "WebSocketTransport", "options", "socket", "_a", "_b", "event", "data", "_c", "_d", "_e", "maxAttempts", "baseDelay", "maxDelay", "exponentialDelay", "jitter", "delay", "WebSocketManager", "config", "liveUrl", "checkWebSocketAvailability", "payload", "error", "attempt", "fetchInitialState", "baseUrl", "logger", "wsHeader", "response", "S3Uploader", "entry", "meta", "onProgress", "file", "xhr", "e", "uploadPromise", "resolve", "reject", "key", "value", "error", "UploadHandler", "sendMessage", "options", "S3Uploader", "container", "input", "uploadName", "existingHandler", "handler", "e", "files", "fileMetadata", "file", "startMessage", "response", "upload_name", "entryInfos", "fileMap", "entries", "info", "entry", "meta", "uploader", "completeMessage", "error", "errorMsg", "id", "offset", "end", "chunk", "base64", "chunkMessage", "message", "entryId", "pendingEntries", "name", "delay", "entriesToRemove", "blob", "resolve", "reject", "reader", "levelPriority", "DEFAULT_SCOPE", "Logger", "_Logger", "state", "scope", "sink", "level", "args", "method", "createLogger", "options", "_a", "_b", "loadAndApplyUpdate", "client", "updatePath", "nodeRequire", "fs", "updateData", "error", "compareHTML", "expected", "actual", "differences", "normalizeHTML", "html", "normalizedExpected", "normalizedActual", "expectedLines", "actualLines", "maxLines", "i", "expectedLine", "actualLine", "LiveTemplateClient", "_LiveTemplateClient", "options", "providedLogger", "logLevel", "debug", "restOptions", "resolvedLevel", "baseLogger", "createLogger", "TreeRenderer", "FocusManager", "ModalManager", "FormLifecycleManager", "LoadingIndicator", "FormDisabler", "UploadHandler", "message", "entry", "uploadName", "entries", "error", "EventDelegator", "value", "form", "button", "originalButtonText", "modalId", "ObserverManager", "WebSocketManager", "_a", "_b", "_c", "response", "event", "autoInitLogger", "init", "wrapper", "client", "uploadMessage", "startResponse", "wrapperSelector", "connectionResult", "setupReactiveAttributeListeners", "url", "flashParams", "hasFlashParams", "param", "readyState", "liveUrl", "updateResponse", "num", "update", "existingHTML", "result", "match", "innerContent", "newBodyContent", "element", "meta", "hasStaticsInTree", "node", "v", "tempWrapper", "morphdom_esm_default", "fromEl", "toEl", "lastFocused", "handleScrollDirectives", "handleHighlightDirectives", "handleAnimateDirectives", "hookName", "hookValue"]
7
7
  }