1(function () { 2 const CRKIT_WORDPRESS_VERSION = '4.5'; 3 const CONTROL_HOST_SELECTOR = '[data-crkit-control-host="true"]'; 4 const CONTROL_ATTR_NAMES = ['data-crkit-control', 'data-crkit-control-host', 'data-crkit-state-token']; 5 const DEFAULT_THEME = 'charcoal-ice'; 6 const DEFAULT_THEME_STYLES = Object.freeze([ 7 'fonts/source-code-pro/source-code-pro.css', 8 'css/globals.css', 9 'css/style.css', 10 'css/dark.css' 11 ]); 12 const BARE_THEME_STYLES = Object.freeze([ 13 'fonts/source-code-pro/source-code-pro.css', 14 'css/globals.css', 15 'css/dark.css' 16 ]); 17 const DEFAULT_CORE_MODULES = Object.freeze(['events', 'strings', 'dom', 'primitive', 'theme', 'control', 'toast']); 18 const GLOBAL_KEY = '__crkitWordPressRuntime'; 19 const PUBLIC_SCHEMA_ACCESS = 'public'; 20 const MOUNT_ROOT_SELECTOR = '[data-crkit-wp-mount-root="true"]'; 21 const CRKIT_ID_ATTR = 'data-crkit-id'; 22 const CRKIT_STATE_TOKEN_ATTR = 'data-crkit-state-token'; 23 const CRKIT_PROPS_COMMENT_PREFIX = 'crkit:props '; 24 const SERIALIZED_PROPERTY_VALUES_KEY = '__crkitSerializedPropertyValues'; 25 const PREVIEW_CLIENT_ID_ATTR = 'data-crkit-editor-client-id'; 26 const RUNTIME_STATUS_ATTR_NAMES = new Set(['aria-busy', 'data-crkit-wp-status']); 27 const RUNTIME_READY_EVENT = 'crkit:wordpress-ready'; 28 const DEFERRED_HOST_STATUS = 'deferred'; 29 const DEFERRED_TOGGLE_REPLAY_KEY = '__crkitDeferredToggleReplay'; 30 const DEFERRED_SURFACE_CONTROL_TYPES = new Set(['panel', 'dialog', 'popover', 'surface']); 31 const EDITOR_PREVIEW_NAMESPACE_ATTR = 'data-crkit-editor-preview-root'; 32 const EDITOR_CHILD_BLOCKS_ATTR = 'data-crkit-editor-child-blocks'; 33 const WORDPRESS_EDITOR_BLOCK_ID_PATTERN = /^block-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; 34 35 const globalState = globalThis[GLOBAL_KEY] || (globalThis[GLOBAL_KEY] = { 36 version: CRKIT_WORDPRESS_VERSION, 37 runtimePromise: null, 38 runtimeApi: null, 39 scanScheduled: false, 40 scanRunning: false, 41 pendingHosts: new Set(), 42 bootingHosts: new Set(), 43 hostState: new WeakMap(), 44 observer: null, 45 deferredHostsById: new Map(), 46 deferredToggleBootsById: new Map(), 47 deferredToggleBootHandlerInstalled: false, 48 pageLifecycleRecoveryInstalled: false, 49 pageLifecycleSuspended: false, 50 editorPreviewNamespaceByHost: new WeakMap(), 51 editorPreviewNamespaceCounter: 0, 52 schemaByControl: new Map(), 53 schemaPromiseByControl: new Map(), 54 preloadedAssetKeys: new Set(), 55 activeBoots: 0, 56 bootstrapped: false, 57 readyDispatched: false, 58 emailTemplatesCaptchaPageToken: '', 59 emailTemplatesCaptchaPageTokenPromise: null 60 }); 61 if (typeof globalState.bootstrapped !== 'boolean') { 62 globalState.bootstrapped = false; 63 } 64 if (typeof globalState.readyDispatched !== 'boolean') { 65 globalState.readyDispatched = false; 66 } 67 if (!(globalState.deferredHostsById instanceof Map)) { 68 globalState.deferredHostsById = new Map(); 69 } 70 if (!(globalState.deferredToggleBootsById instanceof Map)) { 71 globalState.deferredToggleBootsById = new Map(); 72 } 73 if (!(globalState.bootingHosts instanceof Set)) { 74 globalState.bootingHosts = new Set(); 75 } 76 if (typeof globalState.deferredToggleBootHandlerInstalled !== 'boolean') { 77 globalState.deferredToggleBootHandlerInstalled = false; 78 } 79 if (typeof globalState.pageLifecycleRecoveryInstalled !== 'boolean') { 80 globalState.pageLifecycleRecoveryInstalled = false; 81 } 82 if (typeof globalState.pageLifecycleSuspended !== 'boolean') { 83 globalState.pageLifecycleSuspended = false; 84 } 85 if (!Number.isFinite(globalState.activeBoots)) { 86 globalState.activeBoots = 0; 87 } 88 if (!(globalState.editorPreviewNamespaceByHost instanceof WeakMap)) { 89 globalState.editorPreviewNamespaceByHost = new WeakMap(); 90 } 91 if (!Number.isFinite(globalState.editorPreviewNamespaceCounter)) { 92 globalState.editorPreviewNamespaceCounter = 0; 93 } 94 if (typeof globalState.emailTemplatesCaptchaPageToken !== 'string') { 95 globalState.emailTemplatesCaptchaPageToken = ''; 96 } 97 if ( 98 globalState.emailTemplatesCaptchaPageTokenPromise 99 && typeof globalState.emailTemplatesCaptchaPageTokenPromise.then !== 'function' 100 ) { 101 globalState.emailTemplatesCaptchaPageTokenPromise = null; 102 } 103 104 const normalizeText = (value) => String(value ?? '').trim(); 105 const escapeSelectorValue = (value) => { 106 const normalized = normalizeText(value); 107 if (!normalized) { 108 return ''; 109 } 110 if (globalThis.CSS && typeof globalThis.CSS.escape === 'function') { 111 return globalThis.CSS.escape(normalized); 112 } 113 return normalized.replace(/["\\]/g, '\\$&'); 114 }; 115 const decodeTextPropertyValue = (value) => { 116 const normalized = String(value ?? ''); 117 if (!normalized || normalized.indexOf('&') === -1) { 118 return normalized; 119 } 120 121 const decodeHtmlEntities = globalState.runtimeApi?.crkitlib?.strings?.decodeHtmlEntities; 122 return typeof decodeHtmlEntities === 'function' 123 ? String(decodeHtmlEntities(normalized, document, { maxPasses: 4 }) ?? normalized) 124 : normalized; 125 }; 126 const appendVersionQuery = (href, version) => { 127 const normalizedHref = normalizeText(href); 128 const normalizedVersion = normalizeText(version); 129 if (!normalizedHref || !normalizedVersion) { 130 return normalizedHref; 131 } 132 133 const hashIndex = normalizedHref.indexOf('#'); 134 const baseHref = hashIndex >= 0 ? normalizedHref.slice(0, hashIndex) : normalizedHref; 135 const hash = hashIndex >= 0 ? normalizedHref.slice(hashIndex) : ''; 136 const encodedVersion = encodeURIComponent(normalizedVersion); 137 return `${baseHref}${baseHref.includes('?') ? '&' : '?'}ver=${encodedVersion}${hash}`; 138 }; 139 const removeQueryParam = (href, paramName) => { 140 const normalizedHref = normalizeText(href); 141 const normalizedParamName = normalizeText(paramName); 142 if (!normalizedHref || !normalizedParamName) { 143 return normalizedHref; 144 } 145 146 const hashIndex = normalizedHref.indexOf('#'); 147 const beforeHash = hashIndex >= 0 ? normalizedHref.slice(0, hashIndex) : normalizedHref; 148 const hash = hashIndex >= 0 ? normalizedHref.slice(hashIndex) : ''; 149 const queryIndex = beforeHash.indexOf('?'); 150 if (queryIndex < 0) { 151 return normalizedHref; 152 } 153 154 const baseHref = beforeHash.slice(0, queryIndex); 155 const query = beforeHash.slice(queryIndex + 1);
156 const keptParams = query.split('&').filter((entry) => { 157 if (!entry) { 158 return false; 159 } 160 const [rawKey] = entry.split('='); 161 let key = rawKey; 162 try { 163 key = decodeURIComponent(rawKey.replace(/\+/g, ' ')); 164 } catch (_) { 165 key = rawKey; 166 } 167 return key !== normalizedParamName; 168 }); 169 return `${baseHref}${keptParams.length ? `?${keptParams.join('&')}` : ''}${hash}`; 170 }; 171 const replaceVersionQuery = (href, version) => { 172 const normalizedVersion = normalizeText(version); 173 if (!normalizedVersion) { 174 return normalizeText(href); 175 } 176 return appendVersionQuery(removeQueryParam(href, 'ver'), normalizedVersion); 177 }; 178 const readVersionQuery = (href) => { 179 const normalizedHref = normalizeText(href); 180 if (!normalizedHref) { 181 return ''; 182 } 183 184 try { 185 return normalizeText(new URL(normalizedHref, window.location.href).searchParams.get('ver')); 186 } catch { 187 return ''; 188 } 189 }; 190 const appendResolvedVersionQuery = (href, importHref = '', fallbackVersion = '') => { 191 const normalizedHref = normalizeText(href); 192 if (!normalizedHref) { 193 return normalizedHref; 194 } 195 196 const resolvedVersion = readVersionQuery(importHref) || normalizeText(fallbackVersion); 197 return resolvedVersion 198 ? replaceVersionQuery(normalizedHref, resolvedVersion) 199 : normalizedHref; 200 }; 201 const normalizeTagName = (value, fallback = 'div') => { 202 const normalized = String(value ?? '').trim().toLowerCase(); 203 return /^[a-z][a-z0-9-]*$/.test(normalized) ? normalized : fallback; 204 }; 205 const isPlainObject = (value) => !!value && typeof value === 'object' && !Array.isArray(value); 206 const isAppControlType = (value) => normalizeText(value).toLowerCase() === 'app'; 207 const resolveDocument = () => document; 208 const resolveBody = () => resolveDocument().body || resolveDocument().documentElement; 209 const isElement = (value) => value instanceof Element; 210 const isWordPressEditorGeneratedBlockId = (value) => WORDPRESS_EDITOR_BLOCK_ID_PATTERN.test(normalizeText(value)); 211 const hasCrkitPreviewIdentity = (value) => isElement(value) && ( 212 value.matches(CONTROL_HOST_SELECTOR) 213 || value.hasAttribute(CRKIT_ID_ATTR) 214 || value.hasAttribute(PREVIEW_CLIENT_ID_ATTR) 215 ); 216 const isWordPressEditorChromeNode = (value) => { 217 if (!(value instanceof Element)) { 218 return false; 219 } 220 221 if (isWordPressEditorGeneratedBlockId(value.id) && !hasCrkitPreviewIdentity(value)) { 222 return true; 223 } 224 225 if (value.hasAttribute('data-block')) { 226 return true; 227 } 228 229 if (value.hasAttribute(EDITOR_CHILD_BLOCKS_ATTR)) { 230 return true; 231 } 232 233 const classList = value.classList; 234 return classList.contains('block-editor-inner-blocks') 235 || classList.contains('block-editor-block-list__layout') 236 || classList.contains('block-editor-block-list__block'); 237 }; 238 const stripWordPressEditorGeneratedBlockIds = (rootEl) => { 239 if (!isElement(rootEl)) { 240 return; 241 } 242 243 if (isWordPressEditorGeneratedBlockId(rootEl.id)) { 244 rootEl.removeAttribute('id'); 245 } 246 247 rootEl.querySelectorAll?.('[id^="block-"]').forEach((el) => { 248 if (isWordPressEditorGeneratedBlockId(el.id)) { 249 el.removeAttribute('id'); 250 } 251 }); 252 }; 253 const installWordPressEditorChromeBootResolver = (crkitlib) => { 254 const registerBootResolver = crkitlib?.control?.registerBootResolver; 255 if (typeof registerBootResolver !== 'function') { 256 return; 257 } 258 259 registerBootResolver('wordpress-editor-chrome', (el) => { 260 if (!isWordPressEditorChromeNode(el)) { 261 return null; 262 } 263 264 return { 265 claimed: true, 266 descend: false 267 }; 268 }); 269 }; 270 const installWordPressDeferredHostBootResolver = (crkitlib) => { 271 const registerBootResolver = crkitlib?.control?.registerBootResolver; 272 if (typeof registerBootResolver !== 'function') { 273 return; 274 } 275 276 registerBootResolver('wordpress-deferred-host', (el) => { 277 if (!isElement(el) || normalizeText(el.dataset?.crkitWpStatus) !== DEFERRED_HOST_STATUS) { 278 return null; 279 } 280 281 return { 282 claimed: true, 283 descend: false 284 }; 285 }); 286 }; 287 const isRuntimeMountedTreeNode = (value) => isElement(value) && value.matches(MOUNT_ROOT_SELECTOR); 288 const dispatchRuntimeReady = () => { 289 if (globalState.readyDispatched) { 290 return; 291 } 292 293 globalState.readyDispatched = true; 294 const documentEl = resolveDocument().documentElement; 295 if (documentEl) { 296 documentEl.dataset.crkitWpReady = 'true'; 297 } 298 299 resolveDocument().dispatchEvent(new CustomEvent(RUNTIME_READY_EVENT, { 300 detail: { 301 version: CRKIT_WORDPRESS_VERSION 302 } 303 })); 304 }; 305 const dispatchRuntimeReadyIfSettled = () => { 306 if ( 307 !globalState.bootstrapped 308 || globalState.readyDispatched 309 || globalState.scanScheduled 310 || globalState.scanRunning 311 || globalState.activeBoots > 0 312 || globalState.pendingHosts.size > 0 313 ) { 314 return; 315 } 316 317 dispatchRuntimeReady(); 318 }; 319 const collectBootControlTypes = (preparedBoots) => { 320 const controlTypes = new Set();
321 preparedBoots.forEach((preparedBoot) => { 322 preparedBoot.controlTypes.forEach((controlType) => { 323 const normalizedType = normalizeText(controlType); 324 if (normalizedType) { 325 controlTypes.add(normalizedType); 326 } 327 }); 328 }); 329 return controlTypes; 330 }; 331 const containsOnlyMountedTree = (hostEl, mountedTreeEl) => { 332 if (!isElement(hostEl) || !isElement(mountedTreeEl) || mountedTreeEl.parentElement !== hostEl) { 333 return false; 334 } 335 336 let mountedTreeCount = 0; 337 for (const node of hostEl.childNodes) { 338 if (node === mountedTreeEl) { 339 mountedTreeCount += 1; 340 continue; 341 } 342 343 if (node.nodeType === Node.TEXT_NODE && !normalizeText(node.textContent)) { 344 continue; 345 } 346 347 return false; 348 } 349 350 return mountedTreeCount === 1; 351 }; 352 353 const cloneValue = (value) => { 354 if (value == null || typeof value !== 'object') { 355 return value; 356 } 357 if (typeof structuredClone === 'function') { 358 return structuredClone(value); 359 } 360 return JSON.parse(JSON.stringify(value)); 361 }; 362 const decodeBase64Url = (value) => { 363 const normalizedValue = normalizeText(value); 364 if (!normalizedValue) { 365 return ''; 366 } 367 const paddedValue = normalizedValue 368 .replace(/-/g, '+') 369 .replace(/_/g, '/') 370 .padEnd(Math.ceil(normalizedValue.length / 4) * 4, '='); 371 const binary = atob(paddedValue); 372 if (typeof TextDecoder === 'function') { 373 const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0)); 374 return new TextDecoder().decode(bytes); 375 } 376 return decodeURIComponent(escape(binary)); 377 }; 378 const readHostPropsPayload = (hostEl) => { 379 if (!isElement(hostEl)) { 380 return null; 381 } 382 383 for (const node of hostEl.childNodes) { 384 if (node.nodeType !== Node.COMMENT_NODE) { 385 continue; 386 } 387 const text = normalizeText(node.textContent); 388 if (!text.startsWith(CRKIT_PROPS_COMMENT_PREFIX)) { 389 continue; 390 } 391 392 try { 393 const encodedPayload = text.slice(CRKIT_PROPS_COMMENT_PREFIX.length); 394 const payload = JSON.parse(decodeBase64Url(encodedPayload)); 395 return isPlainObject(payload) ? payload : null; 396 } catch (err) { 397 console.error('CRKIT WordPress failed to read serialized props payload', err); 398 return null; 399 } 400 } 401 402 return null; 403 }; 404 const normalizeModuleHref = (value) => { 405 const normalized = normalizeText(value); 406 if (!normalized) { 407 return ''; 408 } 409 410 try { 411 return new URL(normalized, PLUGIN_BASE_URL).href; 412 } catch { 413 return normalized; 414 } 415 }; 416 const normalizeStyleRefs = (value, fallback = []) => { 417 if (!Array.isArray(value) || value.length === 0) { 418 return Object.freeze([...(fallback || [])]); 419 } 420 421 return Object.freeze(value 422 .map((entry) => normalizeText(entry)) 423 .filter(Boolean)); 424 }; 425 const normalizeHrefRoots = (value) => Object.freeze( 426 Array.isArray(value) 427 ? value 428 .map((entry) => normalizeModuleHref(entry).split('?')[0].split('#')[0]) 429 .filter(Boolean) 430 .map((entry) => entry.endsWith('/') ? entry : `${entry}/`) 431 : [] 432 ); 433 const normalizeStringList = (value) => Object.freeze( 434 Array.isArray(value) 435 ? value.map((entry) => normalizeText(entry)).filter(Boolean) 436 : [] 437 ); 438 const normalizeOptionalBoolean = (value) => ( 439 value === true 440 ? true 441 : (value === false ? false : null) 442 ); 443 const normalizePropertyName = (value) => { 444 const normalized = normalizeText(value); 445 return /^[A-Za-z][A-Za-z0-9_]*$/.test(normalized) ? normalized : ''; 446 }; 447 const normalizePublicShellKey = (value) => { 448 const normalized = normalizeText(value).toLowerCase(); 449 return /^[a-z][a-z0-9-]*$/.test(normalized) 450 ? normalized 451 : ''; 452 }; 453 const normalizeThemePathHref = (value, fallback = THEME_PATH) => { 454 const normalized = normalizeText(value); 455 if (!normalized) { 456 return fallback; 457 } 458 459 try { 460 return new URL(normalized, PLUGIN_BASE_URL).href; 461 } catch { 462 return fallback; 463 } 464 }; 465 const normalizeThemeBridgeRegistryEntry = (entry) => { 466 if (!isPlainObject(entry)) { 467 return null; 468 } 469 470 const name = normalizeText(entry.name); 471 const moduleUrl = normalizeModuleHref(entry.moduleUrl || entry.url); 472 if (!name || !moduleUrl) { 473 return null; 474 } 475 476 return Object.freeze({ 477 name, 478 title: normalizeText(entry.title), 479 iconUrl: normalizeModuleHref(entry.iconUrl), 480 moduleUrl, 481 hostTagName: normalizeTagName(entry.hostTagName, ''), 482 hostTagProperty: normalizePropertyName(entry.hostTagProperty),
483 hostsControls: normalizeOptionalBoolean(entry.hostsControls) ?? false, 484 childContent: normalizeText(entry.childContent), 485 styles: normalizeStringList(entry.styles), 486 deps: normalizeStringList(entry.deps), 487 publicShellKey: normalizePublicShellKey(entry.publicShellKey), 488 renderMode: normalizeText(entry.renderMode).toLowerCase() === 'dynamic' ? 'dynamic' : 'static' 489 }); 490 }; 491 const normalizeWordPressControlRegistryEntry = (entry) => { 492 if (!isPlainObject(entry)) { 493 return null; 494 } 495 496 const name = normalizeText(entry.name); 497 if (!name) { 498 return null; 499 } 500 501 return Object.freeze({ 502 name, 503 title: normalizeText(entry.title), 504 hostTagName: normalizeTagName(entry.hostTagName, ''), 505 hostTagProperty: normalizePropertyName(entry.hostTagProperty), 506 hostsControls: normalizeOptionalBoolean(entry.hostsControls) ?? false, 507 childContent: normalizeText(entry.childContent), 508 editorChildSlotPart: normalizeText(entry.editorChildSlotPart), 509 publicShellKey: normalizePublicShellKey(entry.publicShellKey), 510 renderMode: normalizeText(entry.renderMode).toLowerCase() === 'dynamic' ? 'dynamic' : 'static' 511 }); 512 }; 513 514 const resolveCurrentScriptUrl = () => { 515 const currentSrc = document.currentScript?.src; 516 if (currentSrc) { 517 return currentSrc; 518 } 519 520 const scriptNodes = document.querySelectorAll('script[src]'); 521 for (const scriptNode of scriptNodes) { 522 const src = scriptNode.getAttribute('src') || ''; 523 if (src.includes('/assets/js/crkit-runtime.js')) { 524 return scriptNode.src; 525 } 526 } 527 528 return new URL('./assets/js/crkit-runtime.js', window.location.href).href; 529 }; 530 531 const SCRIPT_URL = resolveCurrentScriptUrl(); 532 const PLUGIN_BASE_URL = new URL('../../', SCRIPT_URL).href; 533 const SCRIPT_QUERY = (() => { 534 try { 535 return new URL(SCRIPT_URL).searchParams; 536 } catch { 537 return new URLSearchParams(); 538 } 539 })(); 540 const THEME_PATH = new URL('kit/themes/', PLUGIN_BASE_URL).href; 541 const IMAGES_PATH = new URL('kit/themes/charcoal-ice/vector-images.js', PLUGIN_BASE_URL).href; 542 const REFLECTOR_MODULE_URL = new URL('kit/controls/reflector/index.js', PLUGIN_BASE_URL).href; 543 const IDENTITY_ENABLED = normalizeText(SCRIPT_QUERY.get('crkit_identity_enabled')) === '1'; 544 const IDENTITY_API_BASE_URL = normalizeText(SCRIPT_QUERY.get('crkit_identity_api_root')); 545 const IDENTITY_DRIVER_MODULE_URL = normalizeText(SCRIPT_QUERY.get('crkit_identity_driver_url')); 546 const IDENTITY_NONCE = normalizeText(SCRIPT_QUERY.get('crkit_identity_nonce')); 547 const REGISTRY_API_BASE_URL = normalizeText(SCRIPT_QUERY.get('crkit_registry_api_root')); 548 const REGISTRY_PROVIDER_MODULE_URL = normalizeText(SCRIPT_QUERY.get('crkit_registry_provider_url')); 549 const REGISTRY_NONCE = normalizeText(SCRIPT_QUERY.get('crkit_registry_nonce')); 550 const EMAIL_TEMPLATES_ENABLED = normalizeText(SCRIPT_QUERY.get('crkit_email_templates_enabled')) === '1'; 551 const EMAIL_TEMPLATES_API_BASE_URL = normalizeText(SCRIPT_QUERY.get('crkit_email_templates_api_root')); 552 const EMAIL_TEMPLATES_DRIVER_MODULE_URL = normalizeText(SCRIPT_QUERY.get('crkit_email_templates_driver_url')); 553 const EMAIL_TEMPLATES_NONCE = normalizeText(SCRIPT_QUERY.get('crkit_email_templates_nonce')); 554 const EMAIL_TEMPLATES_CAPTCHA_PAGE_TOKEN = normalizeText(SCRIPT_QUERY.get('crkit_email_templates_captcha_page_token')); 555 const FORM_PROCESSOR_PROVIDER = normalizeText(SCRIPT_QUERY.get('crkit_form_processor_provider')) || 'wordpress'; 556 const CRKIT_DEBUG_ENABLED = normalizeText(SCRIPT_QUERY.get('crkit_debug')) === '1'; 557 const CRKIT_PACKAGED_STYLES = normalizeText(SCRIPT_QUERY.get('crkit_packaged')) === '1'; 558 const CRKIT_THEME_NAME = normalizeText(SCRIPT_QUERY.get('crkit_theme_name')) || DEFAULT_THEME; 559 const CRKIT_THEME_PATH = normalizeThemePathHref(SCRIPT_QUERY.get('crkit_theme_path'), THEME_PATH); 560 const CRKIT_THEME_MODE = normalizeText(SCRIPT_QUERY.get('crkit_theme_mode')); 561 const RUNTIME_ASSET_VERSION = normalizeText(SCRIPT_QUERY.get('crkit_core_version')) 562 || normalizeText(SCRIPT_QUERY.get('ver')) 563 || CRKIT_WORDPRESS_VERSION; 564 if (EMAIL_TEMPLATES_CAPTCHA_PAGE_TOKEN) { 565 globalState.emailTemplatesCaptchaPageToken = EMAIL_TEMPLATES_CAPTCHA_PAGE_TOKEN; 566 } 567 const resolveEmailTemplatesApiEndpoint = (path) => { 568 const normalizedBaseUrl = normalizeText(EMAIL_TEMPLATES_API_BASE_URL); 569 const normalizedPath = normalizeText(path); 570 if (!normalizedBaseUrl || !normalizedPath) { 571 return ''; 572 } 573 574 try { 575 const baseUrl = normalizedBaseUrl.endsWith('/') ? normalizedBaseUrl : `${normalizedBaseUrl}/`; 576 return new URL(normalizedPath, baseUrl).href; 577 } catch { 578 return ''; 579 } 580 }; 581 const readEmailTemplatesCaptchaPageToken = (payload) => { 582 const source = isPlainObject(payload?.data) ? payload.data : payload; 583 return normalizeText(isPlainObject(source) ? source.token : ''); 584 }; 585 const requestEmailTemplatesCaptchaPageToken = async () => {
586 const endpoint = resolveEmailTemplatesApiEndpoint('captcha/page-token'); 587 if (!endpoint || typeof fetch !== 'function') { 588 return ''; 589 } 590 591 const headers = EMAIL_TEMPLATES_NONCE ? { 'X-WP-Nonce': EMAIL_TEMPLATES_NONCE } : {}; 592 const response = await fetch(endpoint, { 593 method: 'GET', 594 credentials: 'same-origin', 595 headers 596 }); 597 if (!response.ok) { 598 return ''; 599 } 600 601 try { 602 return readEmailTemplatesCaptchaPageToken(await response.json()); 603 } catch { 604 return ''; 605 } 606 }; 607 const getEmailTemplatesCaptchaPageToken = () => { 608 const existingToken = normalizeText(globalState.emailTemplatesCaptchaPageToken); 609 if (existingToken) { 610 return Promise.resolve(existingToken); 611 } 612 if ( 613 globalState.emailTemplatesCaptchaPageTokenPromise 614 && typeof globalState.emailTemplatesCaptchaPageTokenPromise.then === 'function' 615 ) { 616 return globalState.emailTemplatesCaptchaPageTokenPromise; 617 } 618 if (!EMAIL_TEMPLATES_ENABLED || typeof fetch !== 'function') { 619 return Promise.resolve(''); 620 } 621 622 const tokenPromise = requestEmailTemplatesCaptchaPageToken() 623 .then((token) => { 624 const normalizedToken = normalizeText(token); 625 if (normalizedToken) { 626 globalState.emailTemplatesCaptchaPageToken = normalizedToken; 627 } else { 628 globalState.emailTemplatesCaptchaPageTokenPromise = null; 629 } 630 return normalizedToken; 631 }) 632 .catch(() => { 633 globalState.emailTemplatesCaptchaPageTokenPromise = null; 634 return ''; 635 }); 636 globalState.emailTemplatesCaptchaPageTokenPromise = tokenPromise; 637 return tokenPromise; 638 }; 639 const EMAIL_TEMPLATES_CAPTCHA_PAGE_TOKEN_PROMISE = getEmailTemplatesCaptchaPageToken(); 640 const normalizeThemeMode = (value) => { 641 const normalized = normalizeText(value).toLowerCase(); 642 return normalized === 'light' || normalized === 'dark' 643 ? normalized 644 : 'auto'; 645 }; 646 const THEME_BRIDGE = (() => { 647 const rawBridge = isPlainObject(globalThis.CRKIT_WP_THEME_BRIDGE) 648 ? globalThis.CRKIT_WP_THEME_BRIDGE 649 : {}; 650 const controlRegistry = Array.isArray(rawBridge.controlRegistry) 651 ? rawBridge.controlRegistry 652 .map((entry) => normalizeThemeBridgeRegistryEntry(entry)) 653 .filter(Boolean) 654 : []; 655 const controlsByName = new Map(controlRegistry.map((entry) => [entry.name, entry])); 656 657 const bridgeActiveTheme = normalizeText(rawBridge.activeTheme); 658 const bridgeThemePath = normalizeModuleHref(rawBridge.themePath); 659 const bridgeAppImagesPath = normalizeModuleHref(rawBridge.appImagesPath); 660 const selectedThemeExtendsBase = CRKIT_THEME_NAME !== DEFAULT_THEME || CRKIT_THEME_PATH !== THEME_PATH; 661 const hasThemeExtension = !!bridgeActiveTheme || !!bridgeThemePath || !!bridgeAppImagesPath || selectedThemeExtendsBase; 662 const activeTheme = bridgeActiveTheme || CRKIT_THEME_NAME; 663 664 return Object.freeze({ 665 activeTheme, 666 baseTheme: normalizeText(rawBridge.baseTheme) || DEFAULT_THEME, 667 policy: hasThemeExtension ? 'extend' : 'replace', 668 themeMode: normalizeThemeMode(rawBridge.themeMode || CRKIT_THEME_MODE), 669 themePath: normalizeThemePathHref(rawBridge.themePath, CRKIT_THEME_PATH), 670 baseThemePath: normalizeThemePathHref(rawBridge.baseThemePath, THEME_PATH), 671 themeStyles: normalizeStyleRefs(rawBridge.themeStyles, DEFAULT_THEME_STYLES), 672 baseThemeStyles: normalizeStyleRefs(rawBridge.baseThemeStyles, DEFAULT_THEME_STYLES), 673 bareThemeStyles: normalizeStyleRefs(rawBridge.bareThemeStyles, BARE_THEME_STYLES), 674 bundledStyleRoots: normalizeHrefRoots(rawBridge.bundledStyleRoots), 675 imagesPath: normalizeModuleHref(rawBridge.imagesPath) || (hasThemeExtension ? '' : IMAGES_PATH), 676 baseImagesPath: normalizeModuleHref(rawBridge.baseImagesPath) || IMAGES_PATH, 677 appImagesPath: bridgeAppImagesPath, 678 controlRegistry: Object.freeze(controlRegistry), 679 controlsByName 680 }); 681 })(); 682 const resolveThemeBridgeControlEntry = (controlType) => THEME_BRIDGE.controlsByName.get(normalizeText(controlType)) || null; 683 const resolveWordPressControlRegistry = () => ( 684 Array.isArray(globalThis.CRKIT_WP_CONTROL_REGISTRY) 685 ? globalThis.CRKIT_WP_CONTROL_REGISTRY 686 .map((entry) => normalizeWordPressControlRegistryEntry(entry)) 687 .filter(Boolean) 688 : [] 689 ); 690 const resolveWordPressControlRegistryEntry = (controlType) => ( 691 resolveWordPressControlRegistry() 692 .find((entry) => entry.name === normalizeText(controlType)) 693 || null 694 );
695 const mergeControlSchemaMeta = (controlType, rawSchema) => { 696 const registryEntry = resolveThemeBridgeControlEntry(controlType) 697 || resolveWordPressControlRegistryEntry(controlType); 698 return { 699 ...rawSchema, 700 title: normalizeText(rawSchema?.title) || normalizeText(registryEntry?.title), 701 hostTagName: normalizeTagName(rawSchema?.hostTagName || registryEntry?.hostTagName, ''), 702 hostTagProperty: normalizePropertyName(rawSchema?.hostTagProperty || registryEntry?.hostTagProperty), 703 hostsControls: rawSchema?.hostsControls === true || registryEntry?.hostsControls === true, 704 childContent: normalizeText(rawSchema?.childContent || registryEntry?.childContent), 705 editorChildSlotPart: normalizeText(rawSchema?.editorChildSlotPart || registryEntry?.editorChildSlotPart), 706 publicShellKey: normalizePublicShellKey(rawSchema?.publicShellKey || registryEntry?.publicShellKey), 707 renderMode: normalizeText(rawSchema?.renderMode || registryEntry?.renderMode).toLowerCase() === 'dynamic' ? 'dynamic' : 'static' 708 }; 709 }; 710 const resolveControlModuleUrl = (controlType) => { 711 const normalizedType = normalizeText(controlType); 712 const themeEntry = resolveThemeBridgeControlEntry(normalizedType); 713 if (themeEntry?.moduleUrl) { 714 return appendResolvedVersionQuery(themeEntry.moduleUrl, '', RUNTIME_ASSET_VERSION); 715 } 716 717 return appendResolvedVersionQuery( 718 new URL(`kit/controls/${normalizedType}/index.js`, PLUGIN_BASE_URL).href, 719 '', 720 RUNTIME_ASSET_VERSION 721 ); 722 }; 723 const hasAncestorMountRoot = (hostEl) => { 724 if (!isElement(hostEl)) { 725 return false; 726 } 727 728 const ancestorMountRoot = hostEl.parentElement?.closest?.(MOUNT_ROOT_SELECTOR) || null; 729 if (ancestorMountRoot) { 730 return true; 731 } 732 733 const ancestorThemeRoot = hostEl.parentElement?.closest?.('[data-crkit-theme]') || null; 734 return !!ancestorThemeRoot; 735 }; 736 737 const resolveMountedThemeConfig = (isAppRoot = false, hostEl = null) => { 738 const usesBridgeTheme = isAppRoot || !hasAncestorMountRoot(hostEl); 739 if (!usesBridgeTheme) { 740 return { 741 policy: 'inherit', 742 mode: THEME_BRIDGE.themeMode, 743 appImagesPath: THEME_BRIDGE.appImagesPath 744 }; 745 } 746 747 return { 748 policy: THEME_BRIDGE.policy, 749 baseTheme: THEME_BRIDGE.baseTheme, 750 baseThemePath: THEME_BRIDGE.baseThemePath, 751 baseStyles: [...THEME_BRIDGE.baseThemeStyles], 752 baseImagesPath: THEME_BRIDGE.baseImagesPath, 753 activeTheme: THEME_BRIDGE.activeTheme, 754 themePath: THEME_BRIDGE.themePath, 755 styles: [...THEME_BRIDGE.themeStyles], 756 mode: THEME_BRIDGE.themeMode, 757 imagesPath: THEME_BRIDGE.imagesPath, 758 appImagesPath: THEME_BRIDGE.appImagesPath 759 }; 760 }; 761 762 const resolveMountRoot = (node) => { 763 const element = isElement(node) ? node : node?.parentElement; 764 return element?.closest?.(MOUNT_ROOT_SELECTOR) || null; 765 }; 766 767 const isWithinMountedTree = (node) => !!resolveMountRoot(node); 768 769 const resolveHostElement = (node) => { 770 let current = isElement(node) ? node : node?.parentElement || null; 771 while (current) { 772 if (current.matches(CONTROL_HOST_SELECTOR)) { 773 return current; 774 } 775 current = current.parentElement; 776 } 777 return null; 778 }; 779 780 const resolveParentHost = (hostEl) => { 781 let current = hostEl?.parentElement || null; 782 while (current) { 783 if (current.matches(CONTROL_HOST_SELECTOR)) { 784 return current; 785 } 786 current = current.parentElement; 787 } 788 return null; 789 }; 790 791 const resolveTopLevelHost = (node) => { 792 let current = resolveHostElement(node); 793 let topLevelHost = current; 794 while (current) { 795 topLevelHost = current; 796 current = resolveParentHost(current); 797 } 798 return topLevelHost || null; 799 }; 800 const isBlockEditorDocument = (ownerDocument = document) => { 801 const bodyEl = ownerDocument?.body || null; 802 if (!bodyEl) { 803 return false;
804 } 805 806 return bodyEl.classList.contains('block-editor-page') 807 || bodyEl.classList.contains('edit-site') 808 || !!ownerDocument.querySelector?.('.edit-post-layout, .edit-site-layout, .block-editor'); 809 }; 810 const isWithinBlockEditorCanvas = (node) => !!node?.closest?.( 811 '.block-editor-writing-flow, .block-editor-block-list__layout, .editor-styles-wrapper, .is-root-container, .edit-post-visual-editor' 812 ); 813 const isEditorPreviewHost = (hostEl) => { 814 const previewContext = normalizeText(hostEl?.getAttribute?.('data-crkit-preview-context')).toLowerCase(); 815 return previewContext === 'editor' 816 || !!normalizeText(hostEl?.getAttribute?.(PREVIEW_CLIENT_ID_ATTR)); 817 }; 818 const normalizeControlIdSegment = (value) => { 819 const normalizeSafeId = globalState.runtimeApi?.crkitlib?.strings?.normalizeSafeId; 820 if (typeof normalizeSafeId === 'function') { 821 return normalizeSafeId(value, 'preview') || 'preview'; 822 } 823 824 const normalized = normalizeText(value) 825 .replace(/[^a-zA-Z0-9_-]+/g, '-') 826 .replace(/^-+|-+$/g, ''); 827 return normalized || 'preview'; 828 }; 829 const allocateEditorPreviewNamespaceId = (hostEl) => { 830 if (!isEditorPreviewHost(hostEl)) { 831 return ''; 832 } 833 834 const existingId = globalState.editorPreviewNamespaceByHost.get(hostEl); 835 if (existingId) { 836 return existingId; 837 } 838 839 globalState.editorPreviewNamespaceCounter += 1; 840 const sourceId = normalizeText(hostEl.getAttribute(PREVIEW_CLIENT_ID_ATTR)) 841 || normalizeText(hostEl.getAttribute(CRKIT_STATE_TOKEN_ATTR)) 842 || normalizeText(hostEl.getAttribute(CRKIT_ID_ATTR)) 843 || normalizeText(hostEl.id) 844 || normalizeText(hostEl.dataset?.crkitControl) 845 || 'control'; 846 const namespaceId = `wp-editor-preview-${normalizeControlIdSegment(sourceId)}-${globalState.editorPreviewNamespaceCounter}`; 847 globalState.editorPreviewNamespaceByHost.set(hostEl, namespaceId); 848 return namespaceId; 849 }; 850 const shouldBootHost = (hostEl) => { 851 if (!isElement(hostEl)) { 852 return false; 853 } 854 855 const ownerDocument = hostEl.ownerDocument || document; 856 if (isBlockEditorDocument(ownerDocument) && isWithinBlockEditorCanvas(hostEl)) { 857 return isEditorPreviewHost(hostEl); 858 } 859 860 return true; 861 }; 862 const resolveHostRuntimeId = (hostEl) => ( 863 normalizeText(hostEl?.id) 864 || normalizeText(hostEl?.dataset?.crkitId) 865 ); 866 const normalizeHostControlType = (hostEl) => normalizeText(hostEl?.dataset?.crkitControl).toLowerCase(); 867 const isDeferredSurfaceControlType = (hostEl) => DEFERRED_SURFACE_CONTROL_TYPES.has(normalizeHostControlType(hostEl)); 868 const isHostOpen = (hostEl) => { 869 const surfaceOpenState = normalizeText(hostEl?.dataset?.surfaceOpenState).toLowerCase(); 870 if (surfaceOpenState) { 871 return surfaceOpenState === 'open'; 872 } 873 874 if (Object.prototype.hasOwnProperty.call(hostEl?.dataset || {}, 'open')) { 875 return normalizeText(hostEl.dataset.open).toLowerCase() === 'true'; 876 } 877 878 return false; 879 }; 880 const shouldDeferHostBoot = (hostEl) => ( 881 isElement(hostEl) 882 && isDeferredSurfaceControlType(hostEl) 883 && !isEditorPreviewHost(hostEl) 884 && !isHostOpen(hostEl) 885 && !!resolveHostRuntimeId(hostEl) 886 && !hasVisibleToggleForHost(hostEl) 887 ); 888 const registerDeferredHost = (hostEl) => { 889 const hostId = resolveHostRuntimeId(hostEl); 890 if (!isElement(hostEl) || !hostId) { 891 return false; 892 } 893 894 globalState.deferredHostsById.set(hostId, hostEl); 895 setHostBootStatus(hostEl, DEFERRED_HOST_STATUS); 896 return true; 897 }; 898 const unregisterDeferredHost = (hostEl) => { 899 const hostId = resolveHostRuntimeId(hostEl); 900 if (hostId && globalState.deferredHostsById.get(hostId) === hostEl) { 901 globalState.deferredHostsById.delete(hostId); 902 } 903 if (normalizeText(hostEl?.dataset?.crkitWpStatus) === DEFERRED_HOST_STATUS) { 904 setHostBootStatus(hostEl, ''); 905 } 906 }; 907 const canBootDeferredHostForToggle = (hostEl) => ( 908 isElement(hostEl) 909 && hostEl.isConnected 910 && normalizeText(hostEl.dataset?.crkitWpStatus) === DEFERRED_HOST_STATUS 911 && isDeferredSurfaceControlType(hostEl) 912 && !isEditorPreviewHost(hostEl)
913 && !!resolveHostRuntimeId(hostEl) 914 ); 915 const resolveDeferredHostById = (hostId) => { 916 const normalizedId = normalizeText(hostId); 917 if (!normalizedId) { 918 return null; 919 } 920 921 const registeredHost = globalState.deferredHostsById.get(normalizedId) || null; 922 if ( 923 registeredHost?.isConnected 924 && ( 925 canBootDeferredHostForToggle(registeredHost) 926 || shouldDeferHostBoot(registeredHost) 927 ) 928 ) { 929 return registeredHost; 930 } 931 if (registeredHost) { 932 globalState.deferredHostsById.delete(normalizedId); 933 } 934 935 const escapedId = escapeSelectorValue(normalizedId); 936 const discoveredHost = document.querySelector?.([ 937 `${CONTROL_HOST_SELECTOR}[id="${escapedId}"]`, 938 `${CONTROL_HOST_SELECTOR}[data-crkit-id="${escapedId}"]` 939 ].join(', ')) || null; 940 if ( 941 discoveredHost?.isConnected 942 && ( 943 canBootDeferredHostForToggle(discoveredHost) 944 || shouldDeferHostBoot(discoveredHost) 945 ) 946 ) { 947 registerDeferredHost(discoveredHost); 948 return discoveredHost; 949 } 950 951 return null; 952 }; 953 const isVisibleToggleForHost = (toggleEl) => { 954 if (!isElement(toggleEl) || !toggleEl.isConnected) { 955 return false; 956 } 957 958 const style = toggleEl.ownerDocument?.defaultView?.getComputedStyle?.(toggleEl) || null; 959 if (style && (style.display === 'none' || style.visibility === 'hidden')) { 960 return false; 961 } 962 963 const rect = toggleEl.getBoundingClientRect?.() || null; 964 return !!rect && rect.width > 0 && rect.height > 0; 965 }; 966 const hasVisibleToggleForHost = (hostEl) => { 967 const ownerDocument = hostEl?.ownerDocument || document; 968 const hostId = normalizeText(hostEl?.id) || normalizeText(hostEl?.dataset?.crkitId); 969 if (!hostId || !ownerDocument?.querySelectorAll) { 970 return false; 971 } 972 973 const escapedId = escapeSelectorValue(hostId); 974 const toggles = ownerDocument.querySelectorAll( 975 `[data-toggle-control-id="${escapedId}"], [data-toggle-id="${escapedId}"]` 976 ); 977 for (const toggleEl of toggles) { 978 if (isVisibleToggleForHost(toggleEl)) { 979 return true; 980 } 981 } 982 return false; 983 }; 984 const getHostBootPriority = (hostEl) => { 985 const controlType = normalizeText(hostEl?.dataset?.crkitControl).toLowerCase(); 986 if (!controlType) { 987 return 40; 988 } 989 990 if ( 991 controlType === 'panel' 992 || controlType === 'dialog' 993 || controlType === 'popover' 994 || controlType === 'surface' 995 ) { 996 const openState = normalizeText(hostEl?.dataset?.surfaceOpenState).toLowerCase(); 997 if (hasVisibleToggleForHost(hostEl)) { 998 return openState === 'open' ? 5 : 6; 999 } 1000 return openState === 'open' ? 10 : 30; 1001 } 1002 1003 if (controlType === 'footer') { 1004 return 20; 1005 } 1006 1007 return 0; 1008 }; 1009 const sortHostsForBoot = (hosts) => hosts 1010 .map((hostEl, index) => ({ 1011 hostEl, 1012 index, 1013 priority: getHostBootPriority(hostEl) 1014 })) 1015 .sort((a, b) => a.priority - b.priority || a.index - b.index) 1016 .map((entry) => entry.hostEl); 1017 1018 const collectSearchDocuments = (rootDocument) => { 1019 const documents = []; 1020 const visited = new Set(); 1021 const queue = []; 1022 1023 if (rootDocument?.querySelector) { 1024 queue.push(rootDocument); 1025 } 1026 1027 while (queue.length > 0) { 1028 const currentDocument = queue.shift(); 1029 if (!currentDocument || visited.has(currentDocument)) { 1030 continue; 1031 } 1032 1033 visited.add(currentDocument); 1034 documents.push(currentDocument); 1035 1036 const frameElements = currentDocument.querySelectorAll 1037 ? currentDocument.querySelectorAll('iframe') 1038 : []; 1039 for (const frameEl of frameElements) { 1040 let frameDocument = null; 1041 try { 1042 frameDocument = frameEl.contentDocument || frameEl.contentWindow?.document || null; 1043 } catch { 1044 frameDocument = null; 1045 } 1046 1047 if (frameDocument?.querySelector && !visited.has(frameDocument)) { 1048 queue.push(frameDocument); 1049 } 1050 } 1051 } 1052 1053 return documents; 1054 }; 1055 1056 const resolveMountedControlElementByPreviewClientId = (previewClientId, ownerDocument = document) => { 1057 const normalizedClientId = normalizeText(previewClientId); 1058 if (!normalizedClientId || !ownerDocument?.querySelector) { 1059 return null; 1060 } 1061 1062 const escapedClientId = typeof CSS !== 'undefined' && typeof CSS.escape === 'function' 1063 ? CSS.escape(normalizedClientId) 1064 : normalizedClientId.replace(/["\\]/g, '\\$&'); 1065 1066 for (const searchDocument of collectSearchDocuments(ownerDocument)) { 1067 const mountedControlEl = searchDocument.querySelector( 1068 `[data-crkit-wp-mount-root] [data-type][${PREVIEW_CLIENT_ID_ATTR}="${escapedClientId}"]` 1069 ); 1070 if (mountedControlEl) { 1071 return mountedControlEl; 1072 } 1073 } 1074 1075 return null; 1076 }; 1077 1078 const resolveMountedControlRuntime = (controlEl) => { 1079 const runtimeWindow = controlEl?.ownerDocument?.defaultView || null; 1080 if (!runtimeWindow || runtimeWindow === globalThis) { 1081 return null; 1082 } 1083 1084 const runtimeApi = runtimeWindow.CRKIT_WORDPRESS_RUNTIME || null; 1085 return runtimeApi && typeof runtimeApi === 'object' ? runtimeApi : null; 1086 }; 1087 const collectDirectChildHosts = (hostEl) => { 1088 const childHosts = []; 1089 const stack = []; 1090 1091 for (let index = hostEl?.children?.length - 1; index >= 0; index -= 1) { 1092 stack.push(hostEl.children[index]); 1093 } 1094 1095 while (stack.length) { 1096 const candidate = stack.pop(); 1097 if (!isElement(candidate)) { 1098 continue; 1099 } 1100 1101 if (candidate.matches(CONTROL_HOST_SELECTOR)) { 1102 childHosts.push(candidate); 1103 continue; 1104 } 1105 1106 for (let index = candidate.children.length - 1; index >= 0; index -= 1) { 1107 stack.push(candidate.children[index]); 1108 } 1109 } 1110 1111 return childHosts; 1112 }; 1113 1114 const collectHostedControlTypes = (hostEl, sink = new Set()) => { 1115 const controlType = normalizeText(hostEl?.dataset?.crkitControl); 1116 if (controlType) { 1117 sink.add(controlType); 1118 } 1119 1120 const childHosts = collectDirectChildHosts(hostEl); 1121 for (const childHost of childHosts) { 1122 if (shouldDeferHostBoot(childHost)) {
1123 registerDeferredHost(childHost); 1124 continue; 1125 } 1126 collectHostedControlTypes(childHost, sink); 1127 } 1128 1129 return sink; 1130 }; 1131 const CORE_STYLE_PRELOAD_TYPES = new Set(['container', 'loading', 'section']); 1132 const resolveAssetHref = (baseHref, assetRef) => { 1133 const normalizedAssetRef = normalizeText(assetRef); 1134 if (!normalizedAssetRef) { 1135 return ''; 1136 } 1137 1138 try { 1139 const assetUrl = new URL(normalizedAssetRef, baseHref); 1140 return appendResolvedVersionQuery(assetUrl.href, baseHref); 1141 } catch { 1142 return ''; 1143 } 1144 }; 1145 const appendPreloadLink = (href, rel, asValue = '') => { 1146 const normalizedHref = normalizeText(href); 1147 const headEl = document.head || document.documentElement; 1148 if (!normalizedHref || !headEl) { 1149 return; 1150 } 1151 1152 const cacheKey = `${rel}:${asValue}:${normalizedHref}`; 1153 if (globalState.preloadedAssetKeys.has(cacheKey)) { 1154 return; 1155 } 1156 1157 globalState.preloadedAssetKeys.add(cacheKey); 1158 const linkEl = document.createElement('link'); 1159 linkEl.rel = rel; 1160 linkEl.href = normalizedHref; 1161 if (asValue) { 1162 linkEl.as = asValue; 1163 } 1164 headEl.append(linkEl); 1165 }; 1166 const collectControlAssetPlan = (controlType, plan = null, visited = new Set()) => { 1167 const normalizedType = normalizeText(controlType); 1168 if (!normalizedType || visited.has(normalizedType)) { 1169 return plan || { moduleHrefs: new Set(), styleHrefs: new Set() }; 1170 } 1171 1172 visited.add(normalizedType); 1173 const nextPlan = plan || { 1174 moduleHrefs: new Set(), 1175 styleHrefs: new Set() 1176 }; 1177 const themeEntry = resolveThemeBridgeControlEntry(normalizedType); 1178 const moduleUrl = appendResolvedVersionQuery( 1179 themeEntry?.moduleUrl || resolveControlModuleUrl(normalizedType), 1180 '', 1181 RUNTIME_ASSET_VERSION 1182 ); 1183 if (moduleUrl) { 1184 nextPlan.moduleHrefs.add(moduleUrl); 1185 } 1186 1187 if (CRKIT_PACKAGED_STYLES) { 1188 return nextPlan; 1189 } 1190 1191 if (themeEntry?.styles?.length) { 1192 themeEntry.styles.forEach((styleRef) => { 1193 const styleHref = resolveAssetHref(moduleUrl, styleRef); 1194 if (styleHref) { 1195 nextPlan.styleHrefs.add(styleHref); 1196 } 1197 }); 1198 } else if (CORE_STYLE_PRELOAD_TYPES.has(normalizedType) && moduleUrl) { 1199 const styleHref = resolveAssetHref(moduleUrl, 'view.css'); 1200 if (styleHref) { 1201 nextPlan.styleHrefs.add(styleHref); 1202 } 1203 } 1204 1205 const deps = themeEntry?.deps || []; 1206 deps.forEach((dependencyType) => { 1207 collectControlAssetPlan(dependencyType, nextPlan, visited); 1208 }); 1209 1210 return nextPlan; 1211 }; 1212 const primeControlAssetPreloads = (targets) => { 1213 const controlTypes = new Set(); 1214 const normalizedTargets = Array.isArray(targets) || targets instanceof Set 1215 ? [...targets] 1216 : [targets]; 1217 1218 normalizedTargets.forEach((target) => { 1219 if (typeof target === 'string') { 1220 const controlType = normalizeText(target); 1221 if (controlType) { 1222 controlTypes.add(controlType); 1223 } 1224 return; 1225 } 1226 1227 if (isElement(target)) { 1228 collectHostedControlTypes(target, controlTypes); 1229 } 1230 }); 1231 1232 if (controlTypes.size === 0) { 1233 return; 1234 } 1235 1236 const plan = { 1237 moduleHrefs: new Set([ 1238 appendResolvedVersionQuery(new URL('kit/crkit.js', PLUGIN_BASE_URL).href, '', RUNTIME_ASSET_VERSION), 1239 appendResolvedVersionQuery(REFLECTOR_MODULE_URL, '', RUNTIME_ASSET_VERSION) 1240 ]), 1241 styleHrefs: new Set() 1242 }; 1243 1244 controlTypes.forEach((controlType) => { 1245 collectControlAssetPlan(controlType, plan); 1246 }); 1247
1248 plan.moduleHrefs.forEach((moduleHref) => { 1249 appendPreloadLink(moduleHref, 'modulepreload'); 1250 }); 1251 plan.styleHrefs.forEach((styleHref) => { 1252 appendPreloadLink(styleHref, 'preload', 'style'); 1253 }); 1254 }; 1255 const sanitizeHostShell = (hostEl) => { 1256 if (!isElement(hostEl)) { 1257 return; 1258 } 1259 1260 hostEl.removeAttribute('data-type'); 1261 sanitizeHostShellIds(hostEl); 1262 }; 1263 const sanitizeHostedControlShells = (rootEl) => { 1264 if (!isElement(rootEl) && rootEl !== document && rootEl !== document.body) { 1265 return; 1266 } 1267 1268 const scopeRoot = rootEl === document ? document.body : rootEl; 1269 if (!scopeRoot) { 1270 return; 1271 } 1272 1273 if (isElement(scopeRoot) && scopeRoot.matches(CONTROL_HOST_SELECTOR)) { 1274 sanitizeHostShell(scopeRoot); 1275 } 1276 1277 if (typeof scopeRoot.querySelectorAll !== 'function') { 1278 return; 1279 } 1280 1281 scopeRoot.querySelectorAll(CONTROL_HOST_SELECTOR).forEach((hostEl) => { 1282 sanitizeHostShell(hostEl); 1283 }); 1284 }; 1285 const sanitizeHostShellIds = (hostEl) => { 1286 if (!isElement(hostEl)) { 1287 return; 1288 } 1289 1290 const hostControlId = normalizeText(hostEl.getAttribute(CRKIT_ID_ATTR)); 1291 if (!hostControlId) { 1292 return; 1293 } 1294 1295 if (normalizeText(hostEl.getAttribute('id')) === hostControlId) { 1296 hostEl.removeAttribute('id'); 1297 } 1298 hostEl.removeAttribute('aria-labelledby'); 1299 hostEl.removeAttribute('role'); 1300 }; 1301 const sanitizeNestedHostShellIdentity = (hostEl) => { 1302 sanitizeHostShellIds(hostEl); 1303 hostEl.removeAttribute(CRKIT_ID_ATTR); 1304 }; 1305 const buildHostTreeSignature = (hostEl) => JSON.stringify([ 1306 normalizeText(hostEl?.dataset?.crkitControl), 1307 hostEl?.getAttribute(CRKIT_STATE_TOKEN_ATTR) || '', 1308 collectDirectChildHosts(hostEl) 1309 .filter((childHost) => !shouldDeferHostBoot(childHost)) 1310 .map((childHost) => buildHostTreeSignature(childHost)) 1311 ]); 1312 const setHostBootStatus = (hostEl, status = '') => { 1313 if (!isElement(hostEl)) { 1314 return; 1315 } 1316 1317 const normalizedStatus = normalizeText(status); 1318 if (normalizedStatus) { 1319 hostEl.dataset.crkitWpStatus = normalizedStatus; 1320 } else { 1321 delete hostEl.dataset.crkitWpStatus; 1322 } 1323 1324 if (normalizedStatus === 'booting') { 1325 hostEl.setAttribute('aria-busy', 'true'); 1326 return; 1327 } 1328 1329 hostEl.removeAttribute('aria-busy'); 1330 }; 1331 1332 const setMountedTreePendingState = (themeRootEl, pending) => { 1333 if (!isElement(themeRootEl)) { 1334 return; 1335 } 1336 1337 if (pending) { 1338 themeRootEl.dataset.crkitWpPending = 'true'; 1339 themeRootEl.setAttribute('aria-busy', 'true'); 1340 return; 1341 } 1342 1343 delete themeRootEl.dataset.crkitWpPending; 1344 themeRootEl.removeAttribute('aria-busy'); 1345 }; 1346 1347 const renderHostError = (hostEl, message) => { 1348 if (!isElement(hostEl)) { 1349 return; 1350 } 1351 1352 const pre = hostEl.ownerDocument.createElement('pre'); 1353 pre.textContent = String(message ?? '').trim() || 'Unknown CRKIT error'; 1354 hostEl.replaceChildren(pre); 1355 setHostBootStatus(hostEl, 'error'); 1356 }; 1357 1358 const normalizePropertySchema = (schema) => { 1359 const properties = Array.isArray(schema?.properties) ? schema.properties : []; 1360 return Object.freeze({ 1361 ...schema, 1362 properties: Object.freeze(properties 1363 .filter((property) => normalizeText(property?.name)) 1364 .map((property) => Object.freeze({ 1365 ...property, 1366 name: normalizeText(property.name), 1367 type: normalizeText(property.type) || 'data', 1368 access: normalizeText(property.access) || null, 1369 attrName: normalizeText(property.attrName), 1370 label: normalizeText(property.label) || null, 1371 description: normalizeText(property.description) || null, 1372 options: cloneValue(property.options), 1373 visibleWhen: cloneValue(property.visibleWhen), 1374 readonlyWhen: cloneValue(property.readonlyWhen), 1375 inspectorVariant: normalizeText(property.inspectorVariant) || null, 1376 editorVariant: normalizeText(property.editorVariant) || null, 1377 editorTagProperty: normalizeText(property.editorTagProperty) || null, 1378 editorSourceProperty: normalizeText(property.editorSourceProperty) || null, 1379 editorOutputProperty: normalizeText(property.editorOutputProperty) || null, 1380 editorSourceFormat: normalizeText(property.editorSourceFormat) || null, 1381 editorPlaceholder: normalizeText(property.editorPlaceholder) || null, 1382 editorFormats: cloneValue(property.editorFormats), 1383 commitOnBlur: property.commitOnBlur === true, 1384 reflectToAttribute: property.reflectToAttribute === true, 1385 defaultValue: cloneValue(property.defaultValue), 1386 value: cloneValue(property.value) 1387 }))) 1388 }); 1389 }; 1390 1391 const serializePropertyValue = (value, propertyType = 'data') => { 1392 if (value == null) { 1393 return null; 1394 } 1395 1396 switch (normalizeText(propertyType).toLowerCase()) { 1397 case 'boolean': 1398 return value === true || value === 'true' ? 'true' : 'false'; 1399 case 'int': 1400 case 'datetime': { 1401 const numericValue = Number(value); 1402 return Number.isFinite(numericValue) ? String(numericValue) : null; 1403 } 1404 case 'json': 1405 return JSON.stringify(value); 1406 case 'text': 1407 return decodeTextPropertyValue(value); 1408 case 'imgurl': 1409 case 'data': 1410 default: 1411 return String(value); 1412 } 1413 }; 1414 1415 const copyHostPropertyAttributes = (hostEl, mountEl, schema, payload = null) => { 1416 const properties = Array.isArray(schema?.properties) ? schema.properties : []; 1417 const payloadValues = isPlainObject(payload?.propertyValues) ? payload.propertyValues : null; 1418 for (const property of properties) { 1419 if (property.access !== PUBLIC_SCHEMA_ACCESS || !property.attrName) { 1420 continue; 1421 } 1422 1423 if (property.attrName === 'id') { 1424 const sourceId = normalizeText( 1425 (payloadValues && Object.prototype.hasOwnProperty.call(payloadValues, property.name) 1426 ? payloadValues[property.name] 1427 : '') 1428 || hostEl.getAttribute(CRKIT_ID_ATTR) 1429 || hostEl.getAttribute('id') 1430 ); 1431 if (sourceId) { 1432 mountEl.setAttribute(CRKIT_ID_ATTR, sourceId); 1433 if (normalizeText(mountEl.getAttribute('id')) === sourceId) { 1434 mountEl.removeAttribute('id'); 1435 } 1436 continue; 1437 } 1438 1439 mountEl.removeAttribute(CRKIT_ID_ATTR); 1440 mountEl.removeAttribute('id'); 1441 continue; 1442 } 1443 1444 if (payloadValues && Object.prototype.hasOwnProperty.call(payloadValues, property.name)) { 1445 const serializedPayloadValue = serializePropertyValue(payloadValues[property.name], property.type || 'data'); 1446 if (serializedPayloadValue == null) { 1447 mountEl.removeAttribute(property.attrName); 1448 } else { 1449 mountEl.setAttribute(property.attrName, serializedPayloadValue); 1450 } 1451 continue; 1452 } 1453 1454 if (hostEl.hasAttribute(property.attrName)) { 1455 mountEl.setAttribute(property.attrName, hostEl.getAttribute(property.attrName) || ''); 1456 continue; 1457 } 1458 1459 mountEl.removeAttribute(property.attrName); 1460 } 1461 }; 1462 1463 const setSerializedPropertyValues = (el, propertyValues) => { 1464 if (!isElement(el)) { 1465 return; 1466 } 1467 1468 if (!isPlainObject(propertyValues)) { 1469 delete el[SERIALIZED_PROPERTY_VALUES_KEY]; 1470 return; 1471 } 1472 1473 Object.defineProperty(el, SERIALIZED_PROPERTY_VALUES_KEY, { 1474 configurable: true, 1475 enumerable: false, 1476 writable: true, 1477 value: propertyValues 1478 }); 1479 }; 1480 1481 const resolveControlMeta = (controlType, metaMap) => metaMap.get(normalizeText(controlType)) || null; 1482 const resolveHostTagName = (controlType, hostEl, metaMap) => normalizeTagName( 1483 hostEl?.getAttribute?.('data-tag') 1484 || hostEl?.dataset?.crkitHostTag 1485 || resolveControlMeta(controlType, metaMap)?.hostTagName 1486 || hostEl?.tagName 1487 || 'div' 1488 ); 1489 const createMountElementFromHost = (hostEl, controlType, metaMap) => { 1490 const mountEl = hostEl.cloneNode(false); 1491 const resolvedTagName = resolveHostTagName(controlType, hostEl, metaMap); 1492 if (normalizeTagName(mountEl.tagName, 'div') !== resolvedTagName) { 1493 const replacementEl = hostEl.ownerDocument.createElement(resolvedTagName); 1494 for (const attribute of [...mountEl.attributes]) { 1495 replacementEl.setAttribute(attribute.name, attribute.value); 1496 } 1497 replacementEl.className = mountEl.className; 1498 mountEl.replaceWith?.(); 1499 return replacementEl; 1500 } 1501 1502 return mountEl; 1503 }; 1504 const sanitizeMountElement = (mountEl) => { 1505 if (!isElement(mountEl)) { 1506 return mountEl; 1507 } 1508 1509 mountEl.removeAttribute('data-crkit-control-host'); 1510 mountEl.removeAttribute(CRKIT_STATE_TOKEN_ATTR); 1511 mountEl.removeAttribute('data-crkit-host-tag'); 1512 mountEl.removeAttribute('data-crkit-wp-status'); 1513 mountEl.removeAttribute('data-crkit-wp-mount-root'); 1514 mountEl.removeAttribute('data-crkit-wp-pending'); 1515 mountEl.removeAttribute('data-app-root'); 1516 mountEl.removeAttribute('aria-busy'); 1517 mountEl.classList.remove('wp-block-crkit-control', 'crkit-wp-control-host'); 1518 return mountEl; 1519 }; 1520 1521 const compileHostChildNode = (node, schemaMap, metaMap) => { 1522 if (node?.nodeType === Node.TEXT_NODE) { 1523 return node.ownerDocument.createTextNode(node.textContent || ''); 1524 } 1525 1526 if (!isElement(node)) { 1527 return null; 1528 } 1529 1530 if (isWordPressEditorChromeNode(node) || isRuntimeMountedTreeNode(node)) { 1531 return null; 1532 } 1533 1534 if (node.matches(CONTROL_HOST_SELECTOR)) { 1535 if (shouldDeferHostBoot(node)) { 1536 registerDeferredHost(node); 1537 return node; 1538 } 1539 return buildControlTreeFromHost(node, schemaMap, metaMap)?.compiledNode || null; 1540 } 1541 1542 const clonedNode = node.cloneNode(false);
1543 stripWordPressEditorGeneratedBlockIds(clonedNode); 1544 if (Object.prototype.hasOwnProperty.call(node, '__crkitNativeBlockPreview')) { 1545 clonedNode.__crkitNativeBlockPreview = cloneValue(node.__crkitNativeBlockPreview); 1546 } 1547 for (const childNode of node.childNodes) { 1548 const compiledChildNode = compileHostChildNode(childNode, schemaMap, metaMap); 1549 if (compiledChildNode) { 1550 clonedNode.append(compiledChildNode); 1551 } 1552 } 1553 1554 return clonedNode; 1555 }; 1556 1557 const buildControlTreeFromHost = (hostEl, schemaMap, metaMap) => { 1558 const controlType = normalizeText(hostEl?.dataset?.crkitControl); 1559 if (!controlType) { 1560 return null; 1561 } 1562 1563 const payload = readHostPropsPayload(hostEl); 1564 const mountEl = sanitizeMountElement(createMountElementFromHost(hostEl, controlType, metaMap)); 1565 mountEl.dataset.type = controlType; 1566 const previewClientId = normalizeText(hostEl.getAttribute(PREVIEW_CLIENT_ID_ATTR)); 1567 if (previewClientId) { 1568 mountEl.setAttribute(PREVIEW_CLIENT_ID_ATTR, previewClientId); 1569 } 1570 copyHostPropertyAttributes(hostEl, mountEl, schemaMap.get(controlType), payload); 1571 setSerializedPropertyValues(mountEl, payload?.propertyValues); 1572 stripWordPressEditorGeneratedBlockIds(mountEl); 1573 1574 const compiledChildren = []; 1575 for (const childNode of hostEl.childNodes) { 1576 const compiledChildNode = compileHostChildNode(childNode, schemaMap, metaMap); 1577 if (compiledChildNode) { 1578 compiledChildren.push(compiledChildNode); 1579 } 1580 } 1581 1582 compiledChildren.forEach((childNode) => { 1583 mountEl.append(childNode); 1584 }); 1585 1586 return { 1587 compiledNode: mountEl, 1588 controlRootEl: mountEl 1589 }; 1590 }; 1591 const namespaceEditorPreviewTree = (controlType, compiledTree, hostEl = null) => { 1592 if (!isEditorPreviewHost(hostEl) || !compiledTree?.compiledNode || !compiledTree?.controlRootEl) { 1593 return { 1594 controlType, 1595 compiledTree 1596 }; 1597 } 1598 1599 const namespaceId = allocateEditorPreviewNamespaceId(hostEl); 1600 if (!namespaceId) { 1601 return { 1602 controlType, 1603 compiledTree 1604 }; 1605 } 1606 1607 const ownerDocument = compiledTree.compiledNode.ownerDocument || hostEl.ownerDocument || document; 1608 const namespaceEl = ownerDocument.createElement('div'); 1609 namespaceEl.dataset.type = 'control'; 1610 namespaceEl.setAttribute(CRKIT_ID_ATTR, namespaceId); 1611 namespaceEl.setAttribute(EDITOR_PREVIEW_NAMESPACE_ATTR, 'true'); 1612 namespaceEl.append(compiledTree.compiledNode); 1613 1614 return { 1615 controlType: 'control', 1616 compiledTree: { 1617 compiledNode: namespaceEl, 1618 controlRootEl: namespaceEl 1619 } 1620 }; 1621 }; 1622 1623 const syncElementToTemplate = (targetEl, templateEl) => { 1624 if (!isElement(targetEl) || !isElement(templateEl)) { 1625 return targetEl; 1626 } 1627 1628 const nextAttributes = new Map(); 1629 for (const attribute of [...templateEl.attributes]) { 1630 nextAttributes.set(attribute.name, attribute.value); 1631 } 1632 1633 for (const attribute of [...targetEl.attributes]) { 1634 if ( 1635 attribute.name === 'data-crkit-wp-status' 1636 || attribute.name === 'data-crkit-wp-pending' 1637 || attribute.name === 'aria-busy' 1638 ) { 1639 continue; 1640 } 1641 if (!nextAttributes.has(attribute.name)) { 1642 targetEl.removeAttribute(attribute.name); 1643 } 1644 } 1645 1646 for (const [name, value] of nextAttributes.entries()) { 1647 targetEl.setAttribute(name, value); 1648 } 1649 1650 if (Object.prototype.hasOwnProperty.call(templateEl, SERIALIZED_PROPERTY_VALUES_KEY)) { 1651 setSerializedPropertyValues(targetEl, templateEl[SERIALIZED_PROPERTY_VALUES_KEY]); 1652 } else { 1653 delete targetEl[SERIALIZED_PROPERTY_VALUES_KEY]; 1654 } 1655 1656 targetEl.replaceChildren(...[...templateEl.childNodes]); 1657 return targetEl; 1658 }; 1659 1660 const canInlineHostTree = (hostEl, compiledTree) => { 1661 if (!isElement(hostEl) || !compiledTree?.controlRootEl || compiledTree.compiledNode !== compiledTree.controlRootEl) { 1662 return false;
1663 } 1664 1665 if (normalizeText(hostEl.dataset?.crkitPreviewContext)) { 1666 return false; 1667 } 1668 1669 return normalizeTagName(hostEl.tagName, 'div') === normalizeTagName(compiledTree.controlRootEl.tagName, 'div'); 1670 }; 1671 1672 const createMountedTree = (controlType, compiledTree, hostEl = null) => { 1673 const isAppRoot = isAppControlType(controlType); 1674 const inlineHostRoot = !isAppRoot && canInlineHostTree(hostEl, compiledTree); 1675 const themeRootEl = isAppRoot 1676 ? compiledTree.controlRootEl 1677 : (inlineHostRoot 1678 ? syncElementToTemplate(hostEl, compiledTree.controlRootEl) 1679 : compiledTree.controlRootEl.ownerDocument.createElement('div')); 1680 1681 if (!isAppRoot && !inlineHostRoot) { 1682 themeRootEl.append(compiledTree.compiledNode); 1683 } 1684 1685 themeRootEl.dataset.crkitWpMountRoot = 'true'; 1686 if (isAppRoot) { 1687 themeRootEl.dataset.appRoot = 'true'; 1688 } else { 1689 delete themeRootEl.dataset.appRoot; 1690 } 1691 1692 setMountedTreePendingState(themeRootEl, true); 1693 1694 return { 1695 themeRootEl, 1696 controlRootEl: inlineHostRoot ? themeRootEl : compiledTree.controlRootEl, 1697 themeConfig: resolveMountedThemeConfig(isAppRoot, hostEl) 1698 }; 1699 }; 1700 1701 const ensureRuntime = async () => { 1702 if (globalState.runtimeApi) { 1703 return globalState.runtimeApi; 1704 } 1705 1706 if (globalState.runtimePromise) { 1707 return globalState.runtimePromise; 1708 } 1709 1710 globalState.runtimePromise = (async () => { 1711 const [{ crkitlib }] = await Promise.all([ 1712 import(/* @vite-ignore */ appendResolvedVersionQuery( 1713 new URL('kit/crkit.js', PLUGIN_BASE_URL).href, 1714 '', 1715 RUNTIME_ASSET_VERSION 1716 )) 1717 ]); 1718 installWordPressEditorChromeBootResolver(crkitlib); 1719 installWordPressDeferredHostBootResolver(crkitlib); 1720 1721 sanitizeHostedControlShells(resolveBody()); 1722 1723 if (!crkitlib.globals?.initialized) { 1724 await crkitlib.create({ 1725 version: RUNTIME_ASSET_VERSION, 1726 debug: CRKIT_DEBUG_ENABLED, 1727 rootEl: resolveBody(), 1728 coreModules: [...DEFAULT_CORE_MODULES], 1729 dynamicLoadCSS: !CRKIT_PACKAGED_STYLES, 1730 packagedStyles: CRKIT_PACKAGED_STYLES, 1731 bundledStyleRoots: [...THEME_BRIDGE.bundledStyleRoots], 1732 strongCSP: true, 1733 activeTheme: DEFAULT_THEME, 1734 themePath: THEME_PATH, 1735 themeStyles: [...DEFAULT_THEME_STYLES], 1736 themeMode: THEME_BRIDGE.themeMode, 1737 imagesPath: IMAGES_PATH, 1738 appImagesPath: '', 1739 packagedTheme: DEFAULT_THEME, 1740 packagedThemePath: THEME_PATH 1741 }, resolveBody()); 1742 } else { 1743 if (crkitlib.globals.version !== RUNTIME_ASSET_VERSION) { 1744 crkitlib.globals.version = RUNTIME_ASSET_VERSION; 1745 } 1746 } 1747 crkitlib.setDebug?.(CRKIT_DEBUG_ENABLED); 1748 1749 const getControlSchema = async (controlType) => { 1750 const normalizedType = normalizeText(controlType); 1751 if (!normalizedType) { 1752 throw new Error('CRKIT control type is required'); 1753 } 1754 1755 if (globalState.schemaByControl.has(normalizedType)) { 1756 return globalState.schemaByControl.get(normalizedType); 1757 } 1758 1759 if (globalState.schemaPromiseByControl.has(normalizedType)) { 1760 return globalState.schemaPromiseByControl.get(normalizedType); 1761 } 1762 1763 const schemaPromise = (async () => { 1764 primeControlAssetPreloads([normalizedType]); 1765 const controlModuleUrl = resolveControlModuleUrl(normalizedType); 1766 const reflectorModuleUrl = appendResolvedVersionQuery( 1767 REFLECTOR_MODULE_URL, 1768 '', 1769 RUNTIME_ASSET_VERSION 1770 ); 1771 await crkitlib.loadModule(reflectorModuleUrl); 1772 await crkitlib.loadModule(controlModuleUrl); 1773 1774 const rawSchema = crkitlib.reflector?.controlSchema?.(normalizedType); 1775 if (!rawSchema || !Array.isArray(rawSchema.properties)) { 1776 throw new Error(`CRKIT schema is unavailable for "${normalizedType}"`); 1777 } 1778 1779 const schema = normalizePropertySchema( 1780 mergeControlSchemaMeta(normalizedType, rawSchema) 1781 ); 1782 globalState.schemaByControl.set(normalizedType, schema); 1783 globalState.schemaPromiseByControl.delete(normalizedType); 1784 return schema; 1785 })().catch((err) => { 1786 globalState.schemaPromiseByControl.delete(normalizedType); 1787 throw err; 1788 }); 1789 1790 globalState.schemaPromiseByControl.set(normalizedType, schemaPromise); 1791 return schemaPromise; 1792 }; 1793 1794 const getControlSchemaForPreviewClientId = async (previewClientId, ownerDocument = document) => { 1795 const normalizedClientId = normalizeText(previewClientId); 1796 if (!normalizedClientId) { 1797 throw new Error('CRKIT preview client ID is required'); 1798 } 1799 1800 const controlEl = resolveMountedControlElementByPreviewClientId(normalizedClientId, ownerDocument); 1801 if (!controlEl) { 1802 return null; 1803 } 1804 1805 const delegatedRuntime = resolveMountedControlRuntime(controlEl); 1806 if (delegatedRuntime && typeof delegatedRuntime.getControlSchemaForPreviewClientId === 'function') { 1807 return delegatedRuntime.getControlSchemaForPreviewClientId(normalizedClientId, controlEl.ownerDocument); 1808 } 1809 1810 await crkitlib.loadModule(appendResolvedVersionQuery( 1811 REFLECTOR_MODULE_URL, 1812 '', 1813 RUNTIME_ASSET_VERSION 1814 )); 1815 1816 const instance = crkitlib.control?.getInstance?.(controlEl); 1817 if (!instance) { 1818 return null; 1819 } 1820 1821 const rawSchema = crkitlib.reflector?.controlSchema?.(instance); 1822 if (!rawSchema || !Array.isArray(rawSchema.properties)) { 1823 return null; 1824 } 1825 1826 return normalizePropertySchema( 1827 mergeControlSchemaMeta(normalizeText(instance?.type), rawSchema) 1828 ); 1829 }; 1830 1831 const getControlPropertyValuesForPreviewClientId = async (previewClientId, ownerDocument = document) => { 1832 const normalizedClientId = normalizeText(previewClientId); 1833 if (!normalizedClientId) { 1834 throw new Error('CRKIT preview client ID is required'); 1835 } 1836 1837 const controlEl = resolveMountedControlElementByPreviewClientId(normalizedClientId, ownerDocument); 1838 if (!controlEl) { 1839 return null; 1840 } 1841 1842 const delegatedRuntime = resolveMountedControlRuntime(controlEl); 1843 if (delegatedRuntime && typeof delegatedRuntime.getControlPropertyValuesForPreviewClientId === 'function') { 1844 return delegatedRuntime.getControlPropertyValuesForPreviewClientId(normalizedClientId, controlEl.ownerDocument); 1845 } 1846 1847 const instance = crkitlib.control?.getInstance?.(controlEl); 1848 if (!instance || typeof crkitlib.control?.listProperties !== 'function') { 1849 return null; 1850 } 1851 1852 const propertyValues = {}; 1853 crkitlib.control.listProperties(instance).forEach((property) => {
1854 const propertyName = normalizeText(property?.name); 1855 if (!propertyName) { 1856 return; 1857 } 1858 1859 const access = normalizeText(property?.access).toLowerCase(); 1860 if (access !== PUBLIC_SCHEMA_ACCESS && access !== 'readonly') { 1861 return; 1862 } 1863 1864 propertyValues[propertyName] = cloneValue(property?.value); 1865 }); 1866 1867 return propertyValues; 1868 }; 1869 1870 const getControlInspectorPropertyValuesForPreviewClientId = async (previewClientId, ownerDocument = document) => { 1871 const normalizedClientId = normalizeText(previewClientId); 1872 if (!normalizedClientId) { 1873 throw new Error('CRKIT preview client ID is required'); 1874 } 1875 1876 const controlEl = resolveMountedControlElementByPreviewClientId(normalizedClientId, ownerDocument); 1877 if (!controlEl) { 1878 return null; 1879 } 1880 1881 const delegatedRuntime = resolveMountedControlRuntime(controlEl); 1882 if (delegatedRuntime && typeof delegatedRuntime.getControlInspectorPropertyValuesForPreviewClientId === 'function') { 1883 return delegatedRuntime.getControlInspectorPropertyValuesForPreviewClientId(normalizedClientId, controlEl.ownerDocument); 1884 } 1885 1886 const instance = crkitlib.control?.getInstance?.(controlEl); 1887 const listMethod = typeof crkitlib.control?.listInspectorProperties === 'function' 1888 ? crkitlib.control.listInspectorProperties 1889 : crkitlib.control?.listProperties; 1890 if (!instance || typeof listMethod !== 'function') { 1891 return null; 1892 } 1893 1894 const propertyValues = {}; 1895 const reflectedProperties = await listMethod(instance); 1896 reflectedProperties.forEach((property) => { 1897 const propertyName = normalizeText(property?.name); 1898 if (!propertyName) { 1899 return; 1900 } 1901 1902 const access = normalizeText(property?.access).toLowerCase(); 1903 if (access !== PUBLIC_SCHEMA_ACCESS && access !== 'readonly') { 1904 return; 1905 } 1906 1907 propertyValues[propertyName] = cloneValue(property?.value); 1908 }); 1909 1910 return propertyValues; 1911 }; 1912 1913 globalState.runtimeApi = { 1914 crkitlib, 1915 getControlSchema, 1916 getControlSchemaForPreviewClientId, 1917 getControlPropertyValuesForPreviewClientId, 1918 getControlInspectorPropertyValuesForPreviewClientId 1919 }; 1920 1921 return globalState.runtimeApi; 1922 })().catch((err) => { 1923 globalState.runtimePromise = null; 1924 throw err; 1925 }); 1926 1927 return globalState.runtimePromise; 1928 }; 1929 1930 const cleanupControlHost = async (hostEl, expectedToken = null) => { 1931 const state = globalState.hostState.get(hostEl); 1932 if (!state) { 1933 return; 1934 } 1935 if (expectedToken != null && state.token !== expectedToken) { 1936 return; 1937 } 1938 1939 try { 1940 const runtimeApi = globalState.runtimeApi; 1941 const crkitlib = runtimeApi?.crkitlib; 1942 if (crkitlib?.control && state.controlRootEl) { 1943 crkitlib.control.destroy(state.controlRootEl); 1944 } 1945 if (crkitlib?.theme && state.themeRootEl) { 1946 crkitlib.theme.unbind(state.themeRootEl); 1947 } 1948 } catch (err) { 1949 console.error('CRKIT WordPress cleanup failed', err); 1950 } 1951 1952 if (state.themeRootEl?.isConnected && state.themeRootEl !== hostEl) { 1953 state.themeRootEl.remove(); 1954 } 1955 1956 const currentState = globalState.hostState.get(hostEl); 1957 if (expectedToken != null && currentState?.token !== expectedToken) { 1958 return; 1959 } 1960 1961 globalState.hostState.delete(hostEl); 1962 globalState.bootingHosts.delete(hostEl); 1963 setHostBootStatus(hostEl, ''); 1964 }; 1965 1966 const discardHost = (hostEl) => { 1967 unregisterDeferredHost(hostEl); 1968 const topLevelHost = resolveTopLevelHost(hostEl); 1969 if (!topLevelHost) { 1970 return false;
1971 } 1972 1973 globalState.pendingHosts.delete(topLevelHost); 1974 globalState.bootingHosts.delete(topLevelHost); 1975 const state = globalState.hostState.get(topLevelHost) || null; 1976 void cleanupControlHost(topLevelHost, state?.token ?? null); 1977 setHostBootStatus(topLevelHost, ''); 1978 return true; 1979 }; 1980 1981 const prepareControlHostBoot = (hostEl) => { 1982 if (!isElement(hostEl) || !hostEl.isConnected) { 1983 return null; 1984 } 1985 1986 unregisterDeferredHost(hostEl); 1987 sanitizeHostShell(hostEl); 1988 1989 if (resolveParentHost(hostEl)) { 1990 void cleanupControlHost(hostEl); 1991 return null; 1992 } 1993 1994 const controlType = normalizeText(hostEl.dataset.crkitControl); 1995 if (!controlType) { 1996 void cleanupControlHost(hostEl); 1997 return null; 1998 } 1999 2000 const previousState = globalState.hostState.get(hostEl) || null; 2001 if (previousState?.ready && containsOnlyMountedTree(hostEl, previousState.themeRootEl)) { 2002 return null; 2003 } 2004 2005 const signature = buildHostTreeSignature(hostEl); 2006 if ( 2007 previousState?.booting && 2008 previousState.signature === signature 2009 ) { 2010 return null; 2011 } 2012 2013 if ( 2014 previousState?.signature === signature && 2015 previousState.ready && 2016 previousState.themeRootEl && 2017 previousState.themeRootEl.isConnected && 2018 hostEl.contains(previousState.themeRootEl) 2019 ) { 2020 return null; 2021 } 2022 2023 const token = (previousState?.token || 0) + 1; 2024 const controlTypes = [...collectHostedControlTypes(hostEl)]; 2025 globalState.hostState.set(hostEl, { 2026 token, 2027 signature, 2028 themeRootEl: previousState?.themeRootEl || null, 2029 controlRootEl: previousState?.controlRootEl || null, 2030 ready: false, 2031 booting: true 2032 }); 2033 globalState.bootingHosts.add(hostEl); 2034 setHostBootStatus(hostEl, 'booting'); 2035 2036 return { 2037 hostEl, 2038 controlType, 2039 controlTypes, 2040 token, 2041 signature 2042 }; 2043 }; 2044 2045 const loadControlSchemaMaps = async (runtimeApi, controlTypes) => { 2046 const { crkitlib } = runtimeApi; 2047 const schemaMap = new Map(); 2048 const metaMap = new Map(); 2049 2050 for (const typeName of controlTypes) { 2051 const normalizedType = normalizeText(typeName); 2052 if (!normalizedType || schemaMap.has(normalizedType)) { 2053 continue; 2054 } 2055 2056 schemaMap.set(normalizedType, await runtimeApi.getControlSchema(normalizedType)); 2057 metaMap.set(normalizedType, crkitlib.control?.getTypeMeta?.(normalizedType) || null); 2058 } 2059 2060 return { schemaMap, metaMap }; 2061 }; 2062 2063 const failPreparedControlHostBoot = async (preparedBoot, err) => { 2064 await cleanupControlHost(preparedBoot.hostEl); 2065 renderHostError(preparedBoot.hostEl, err?.message || err); 2066 console.error(`CRKIT WordPress failed to boot control "${preparedBoot.controlType}"`, err); 2067 }; 2068 2069 const bootPreparedControlHost = async (preparedBoot, runtimeApi, schemaMap, metaMap) => { 2070 const { 2071 hostEl, 2072 controlType, 2073 token, 2074 signature 2075 } = preparedBoot; 2076 const { crkitlib } = runtimeApi; 2077 2078 try { 2079 const currentState = globalState.hostState.get(hostEl); 2080 if (!currentState || currentState.token !== token || !hostEl.isConnected) { 2081 return; 2082 } 2083 2084 if (currentState.controlRootEl) { 2085 crkitlib.control.destroy(currentState.controlRootEl); 2086 } 2087 if (currentState.themeRootEl) { 2088 crkitlib.theme.unbind(currentState.themeRootEl); 2089 } 2090 2091 const compiledTree = buildControlTreeFromHost(hostEl, schemaMap, metaMap); 2092 if (!compiledTree?.controlRootEl) { 2093 await cleanupControlHost(hostEl); 2094 return; 2095 } 2096 2097 if (!canInlineHostTree(hostEl, compiledTree)) { 2098 sanitizeNestedHostShellIdentity(hostEl); 2099 } 2100 2101 const mountSource = namespaceEditorPreviewTree(controlType, compiledTree, hostEl); 2102 const mountedTree = createMountedTree(mountSource.controlType, mountSource.compiledTree, hostEl); 2103 if (mountedTree.themeRootEl !== hostEl) { 2104 hostEl.replaceChildren(mountedTree.themeRootEl); 2105 }
2106 stripWordPressEditorGeneratedBlockIds(mountedTree.themeRootEl); 2107 2108 await crkitlib.theme.bind(mountedTree.themeRootEl, mountedTree.themeConfig); 2109 2110 crkitlib.bootControls(mountedTree.themeRootEl); 2111 setMountedTreePendingState(mountedTree.themeRootEl, false); 2112 2113 const finalState = globalState.hostState.get(hostEl); 2114 if (!finalState || finalState.token !== token || !hostEl.isConnected) { 2115 crkitlib.control.destroy(mountedTree.controlRootEl); 2116 crkitlib.theme.unbind(mountedTree.themeRootEl); 2117 if (mountedTree.themeRootEl.isConnected && mountedTree.themeRootEl !== hostEl) { 2118 mountedTree.themeRootEl.remove(); 2119 } 2120 return; 2121 } 2122 2123 globalState.hostState.set(hostEl, { 2124 token, 2125 signature, 2126 themeRootEl: mountedTree.themeRootEl, 2127 controlRootEl: mountedTree.controlRootEl, 2128 ready: true, 2129 booting: false 2130 }); 2131 globalState.bootingHosts.delete(hostEl); 2132 setHostBootStatus(hostEl, 'ready'); 2133 } catch (err) { 2134 await failPreparedControlHostBoot(preparedBoot, err); 2135 } 2136 }; 2137 2138 const bootPreparedControlHosts = async (preparedBoots) => { 2139 if (preparedBoots.length === 0) { 2140 return; 2141 } 2142 2143 let runtimeApi = null; 2144 let schemaMaps = null; 2145 try { 2146 primeControlAssetPreloads(preparedBoots.map((preparedBoot) => preparedBoot.hostEl)); 2147 runtimeApi = await ensureRuntime(); 2148 schemaMaps = await loadControlSchemaMaps(runtimeApi, collectBootControlTypes(preparedBoots)); 2149 } catch (err) { 2150 await Promise.all(preparedBoots.map((preparedBoot) => failPreparedControlHostBoot(preparedBoot, err))); 2151 return; 2152 } 2153 2154 await Promise.all(preparedBoots.map((preparedBoot) => bootPreparedControlHost( 2155 preparedBoot, 2156 runtimeApi, 2157 schemaMaps.schemaMap, 2158 schemaMaps.metaMap 2159 ))); 2160 }; 2161 2162 const bootControlHosts = async (hosts) => { 2163 const preparedBoots = []; 2164 for (const hostEl of hosts) { 2165 const preparedBoot = prepareControlHostBoot(hostEl); 2166 if (preparedBoot) { 2167 preparedBoots.push(preparedBoot); 2168 } 2169 } 2170 2171 await bootPreparedControlHosts(preparedBoots); 2172 }; 2173 2174 const schedulePendingHosts = () => { 2175 if (globalState.scanScheduled || globalState.scanRunning) { 2176 return; 2177 } 2178 2179 globalState.scanScheduled = true; 2180 queueMicrotask(() => { 2181 globalState.scanScheduled = false; 2182 globalState.scanRunning = true; 2183 2184 try { 2185 const hosts = sortHostsForBoot([...globalState.pendingHosts]); 2186 globalState.pendingHosts.clear(); 2187 if (hosts.length > 0) { 2188 globalState.activeBoots += 1; 2189 Promise.resolve(bootControlHosts(hosts)) 2190 .catch((err) => { 2191 hosts.forEach((hostEl) => { 2192 setHostBootStatus(hostEl, 'error'); 2193 }); 2194 console.error('CRKIT WordPress failed to boot queued control hosts', err, hosts); 2195 }) 2196 .finally(() => { 2197 globalState.activeBoots = Math.max(0, globalState.activeBoots - 1); 2198 if (globalState.pendingHosts.size > 0) { 2199 schedulePendingHosts(); 2200 return; 2201 } 2202 dispatchRuntimeReadyIfSettled(); 2203 }); 2204 } 2205 } finally { 2206 globalState.scanRunning = false; 2207 } 2208 2209 if (globalState.pendingHosts.size > 0) { 2210 schedulePendingHosts(); 2211 return; 2212 } 2213 2214 dispatchRuntimeReadyIfSettled(); 2215 }); 2216 }; 2217 2218 const enqueueHost = (hostEl) => { 2219 const topLevelHost = resolveTopLevelHost(hostEl); 2220 if (!topLevelHost || !shouldBootHost(topLevelHost)) { 2221 return; 2222 } 2223 2224 globalState.pendingHosts.add(topLevelHost); 2225 primeControlAssetPreloads([topLevelHost]); 2226 schedulePendingHosts(); 2227 }; 2228 2229 const bootControlHost = async (hostEl) => { 2230 await bootControlHosts([hostEl]); 2231 }; 2232 2233 const collectControlHosts = (node, sink = globalState.pendingHosts) => { 2234 if (!isElement(node) || isWithinMountedTree(node)) { 2235 return sink; 2236 } 2237 2238 if (node.matches(CONTROL_HOST_SELECTOR)) { 2239 if (shouldDeferHostBoot(node)) { 2240 registerDeferredHost(node); 2241 return sink; 2242 } 2243 const topLevelHost = resolveTopLevelHost(node); 2244 if (topLevelHost && shouldBootHost(topLevelHost)) { 2245 sink.add(topLevelHost); 2246 } 2247 } 2248 2249 const descendants = node.querySelectorAll(CONTROL_HOST_SELECTOR); 2250 for (const hostEl of descendants) { 2251 if (shouldDeferHostBoot(hostEl)) { 2252 registerDeferredHost(hostEl); 2253 continue; 2254 } 2255 const topLevelHost = resolveTopLevelHost(hostEl); 2256 if (topLevelHost && topLevelHost !== hostEl && shouldDeferHostBoot(topLevelHost)) { 2257 registerDeferredHost(topLevelHost); 2258 continue; 2259 } 2260 if (topLevelHost && shouldBootHost(topLevelHost)) { 2261 sink.add(topLevelHost); 2262 } 2263 } 2264 2265 return sink; 2266 }; 2267 2268 const cleanupRemovedHosts = (node) => { 2269 if (!isElement(node) || isWithinMountedTree(node)) { 2270 return; 2271 } 2272 2273 const hosts = []; 2274 if (node.matches(CONTROL_HOST_SELECTOR)) { 2275 hosts.push(node); 2276 }
2277 hosts.push(...node.querySelectorAll(CONTROL_HOST_SELECTOR)); 2278 hosts.forEach((hostEl) => { 2279 try { 2280 unregisterDeferredHost(hostEl); 2281 if (hostEl.isConnected) { 2282 return; 2283 } 2284 discardHost(hostEl); 2285 } catch (err) { 2286 console.error('CRKIT WordPress failed to clean up a removed host', err); 2287 } 2288 }); 2289 }; 2290 2291 const bootDeferredHostForToggle = async (hostEl, hostId) => { 2292 const normalizedId = normalizeText(hostId) || resolveHostRuntimeId(hostEl); 2293 if (!isElement(hostEl) || !hostEl.isConnected || !normalizedId) { 2294 return false; 2295 } 2296 2297 const existingBoot = globalState.deferredToggleBootsById.get(normalizedId); 2298 if (existingBoot) { 2299 return existingBoot; 2300 } 2301 2302 const bootPromise = (async () => { 2303 unregisterDeferredHost(hostEl); 2304 globalState.pendingHosts.delete(hostEl); 2305 await bootControlHost(hostEl); 2306 const state = globalState.hostState.get(hostEl); 2307 return state?.ready === true; 2308 })().finally(() => { 2309 globalState.deferredToggleBootsById.delete(normalizedId); 2310 }); 2311 2312 globalState.deferredToggleBootsById.set(normalizedId, bootPromise); 2313 return bootPromise; 2314 }; 2315 2316 const reconcileDeferredHosts = () => { 2317 if (!(globalState.deferredHostsById instanceof Map) || globalState.deferredHostsById.size === 0) { 2318 return; 2319 } 2320 2321 for (const [hostId, hostEl] of [...globalState.deferredHostsById.entries()]) { 2322 if (!isElement(hostEl) || !hostEl.isConnected) { 2323 globalState.deferredHostsById.delete(hostId); 2324 continue; 2325 } 2326 2327 if (shouldDeferHostBoot(hostEl)) { 2328 continue; 2329 } 2330 2331 void bootDeferredHostForToggle(hostEl, hostId) 2332 .catch((err) => { 2333 console.error('CRKIT WordPress failed to boot deferred host after toggle reconciliation', err, hostEl); 2334 }); 2335 } 2336 }; 2337 2338 const replayDeferredToggleEvent = (event, detail) => { 2339 const eventTarget = event?.target instanceof EventTarget 2340 ? event.target 2341 : document; 2342 const replayDetail = { 2343 ...detail, 2344 visible: true, 2345 [DEFERRED_TOGGLE_REPLAY_KEY]: true 2346 }; 2347 eventTarget.dispatchEvent(new CustomEvent('toggle', { 2348 bubbles: true, 2349 composed: true, 2350 cancelable: true, 2351 detail: replayDetail 2352 })); 2353 }; 2354 2355 const installDeferredToggleBootHandler = () => { 2356 if (globalState.deferredToggleBootHandlerInstalled) { 2357 return; 2358 } 2359 2360 document.addEventListener('toggle', (event) => { 2361 if (!event || event.defaultPrevented) { 2362 return; 2363 } 2364 2365 const detail = isPlainObject(event.detail) ? event.detail : {}; 2366 if (detail[DEFERRED_TOGGLE_REPLAY_KEY] === true) { 2367 return; 2368 } 2369 2370 const targetId = normalizeText(detail.toggleControlId || detail.controlId || detail.id); 2371 if (!targetId) { 2372 return; 2373 } 2374 2375 const hostEl = resolveDeferredHostById(targetId); 2376 if (!hostEl) { 2377 return; 2378 } 2379 2380 const requestedVisible = typeof detail.visible === 'boolean' 2381 ? detail.visible 2382 : true; 2383 if (requestedVisible !== true) { 2384 return; 2385 } 2386 2387 event.preventDefault(); 2388 void bootDeferredHostForToggle(hostEl, targetId) 2389 .then((ready) => { 2390 if (ready) { 2391 replayDeferredToggleEvent(event, detail); 2392 } 2393 }) 2394 .catch((err) => { 2395 console.error('CRKIT WordPress failed to boot deferred host for toggle', err, hostEl); 2396 }); 2397 }, true); 2398 2399 globalState.deferredToggleBootHandlerInstalled = true; 2400 }; 2401 2402 const installObserver = () => { 2403 if (globalState.observer || !resolveBody()) { 2404 return; 2405 } 2406 2407 const observer = new MutationObserver((mutations) => { 2408 for (const mutation of mutations) { 2409 if (mutation.type === 'attributes') { 2410 if (RUNTIME_STATUS_ATTR_NAMES.has(mutation.attributeName)) { 2411 continue; 2412 } 2413 2414 if (isWithinMountedTree(mutation.target)) { 2415 continue; 2416 } 2417 2418 if (isElement(mutation.target) && mutation.target.matches(CONTROL_HOST_SELECTOR)) { 2419 enqueueHost(mutation.target); 2420 } 2421 continue; 2422 } 2423 2424 if (isWithinMountedTree(mutation.target)) { 2425 continue; 2426 } 2427
2428 mutation.addedNodes.forEach((node) => { 2429 if (isWithinMountedTree(node)) { 2430 return; 2431 } 2432 collectControlHosts(node); 2433 }); 2434 mutation.removedNodes.forEach((node) => { 2435 if (isWithinMountedTree(node)) { 2436 return; 2437 } 2438 cleanupRemovedHosts(node); 2439 }); 2440 } 2441 2442 if (globalState.pendingHosts.size > 0) { 2443 schedulePendingHosts(); 2444 } 2445 reconcileDeferredHosts(); 2446 }); 2447 2448 observer.observe(resolveBody(), { 2449 subtree: true, 2450 childList: true, 2451 attributes: true, 2452 attributeFilter: CONTROL_ATTR_NAMES 2453 }); 2454 2455 globalState.observer = observer; 2456 }; 2457 2458 const markPageLifecycleSuspended = () => { 2459 globalState.pageLifecycleSuspended = true; 2460 }; 2461 2462 const recoverPageLifecycleBoots = () => { 2463 if (!globalState.pageLifecycleSuspended) { 2464 return; 2465 } 2466 2467 globalState.pageLifecycleSuspended = false; 2468 const hosts = [...globalState.bootingHosts.values()]; 2469 if (hosts.length === 0) { 2470 return; 2471 } 2472 2473 hosts.forEach((hostEl) => { 2474 if (!isElement(hostEl) || !hostEl.isConnected) { 2475 globalState.bootingHosts.delete(hostEl); 2476 return; 2477 } 2478 2479 const state = globalState.hostState.get(hostEl) || null; 2480 if (!state?.booting) { 2481 globalState.bootingHosts.delete(hostEl); 2482 return; 2483 } 2484 2485 globalState.hostState.delete(hostEl); 2486 globalState.bootingHosts.delete(hostEl); 2487 setHostBootStatus(hostEl, ''); 2488 enqueueHost(hostEl); 2489 }); 2490 }; 2491 2492 const installPageLifecycleRecovery = () => { 2493 if (globalState.pageLifecycleRecoveryInstalled) { 2494 return; 2495 } 2496 2497 document.addEventListener('visibilitychange', () => { 2498 if (document.visibilityState === 'hidden') { 2499 markPageLifecycleSuspended(); 2500 return; 2501 } 2502 2503 recoverPageLifecycleBoots(); 2504 }, true); 2505 2506 window.addEventListener('pagehide', markPageLifecycleSuspended, true); 2507 window.addEventListener('pageshow', recoverPageLifecycleBoots, true); 2508 globalState.pageLifecycleRecoveryInstalled = true; 2509 }; 2510 2511 const bootstrap = () => { 2512 const bodyEl = resolveBody(); 2513 if (!bodyEl) { 2514 return; 2515 } 2516 2517 globalState.bootstrapped = true; 2518 sanitizeHostedControlShells(bodyEl); 2519 collectControlHosts(bodyEl); 2520 reconcileDeferredHosts(); 2521 if (globalState.pendingHosts.size > 0) { 2522 primeControlAssetPreloads([...globalState.pendingHosts]); 2523 } 2524 2525 ensureRuntime().catch((err) => { 2526 console.error('CRKIT WordPress runtime failed to initialize', err); 2527 }); 2528 2529 if (globalState.pendingHosts.size > 0) { 2530 schedulePendingHosts(); 2531 } 2532 dispatchRuntimeReadyIfSettled(); 2533 installDeferredToggleBootHandler(); 2534 installPageLifecycleRecovery(); 2535 installObserver(); 2536 }; 2537 2538 globalThis.CRKIT_WORDPRESS_RUNTIME = Object.freeze({ 2539 version: CRKIT_WORDPRESS_VERSION, 2540 identityEnabled: IDENTITY_ENABLED, 2541 identityApiBaseUrl: IDENTITY_API_BASE_URL, 2542 identityWordPressDriverModuleUrl: IDENTITY_DRIVER_MODULE_URL, 2543 identityNonce: IDENTITY_NONCE, 2544 registryApiBaseUrl: REGISTRY_API_BASE_URL, 2545 registryWordPressProviderModuleUrl: REGISTRY_PROVIDER_MODULE_URL, 2546 registryNonce: REGISTRY_NONCE, 2547 emailTemplatesEnabled: EMAIL_TEMPLATES_ENABLED, 2548 emailTemplatesApiBaseUrl: EMAIL_TEMPLATES_API_BASE_URL, 2549 emailTemplatesDriverUrl: EMAIL_TEMPLATES_DRIVER_MODULE_URL, 2550 emailTemplatesWordPressDriverModuleUrl: EMAIL_TEMPLATES_DRIVER_MODULE_URL, 2551 emailTemplatesNonce: EMAIL_TEMPLATES_NONCE, 2552 emailTemplatesCaptchaPageToken: globalState.emailTemplatesCaptchaPageToken, 2553 emailTemplatesCaptchaPageTokenPromise: EMAIL_TEMPLATES_CAPTCHA_PAGE_TOKEN_PROMISE, 2554 getEmailTemplatesCaptchaPageToken, 2555 formProcessorProvider: FORM_PROCESSOR_PROVIDER, 2556 ensureRuntime, 2557 getControlSchema: async (controlType) => { 2558 const runtimeApi = await ensureRuntime(); 2559 return runtimeApi.getControlSchema(controlType); 2560 }, 2561 getControlSchemaForPreviewClientId: async (previewClientId, ownerDocument = document) => { 2562 const runtimeApi = await ensureRuntime(); 2563 return runtimeApi.getControlSchemaForPreviewClientId(previewClientId, ownerDocument); 2564 }, 2565 getControlPropertyValuesForPreviewClientId: async (previewClientId, ownerDocument = document) => { 2566 const runtimeApi = await ensureRuntime(); 2567 return runtimeApi.getControlPropertyValuesForPreviewClientId(previewClientId, ownerDocument); 2568 }, 2569 getControlInspectorPropertyValuesForPreviewClientId: async (previewClientId, ownerDocument = document) => { 2570 const runtimeApi = await ensureRuntime(); 2571 return runtimeApi.getControlInspectorPropertyValuesForPreviewClientId(previewClientId, ownerDocument); 2572 }, 2573 discardHost, 2574 enqueueHost 2575 }); 2576 2577 if (resolveBody()) { 2578 bootstrap(); 2579 } else if (document.readyState === 'loading') { 2580 document.addEventListener('DOMContentLoaded', bootstrap, { once: true }); 2581 } 2582})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.