vendor: 45,060 bytes, lines 1-1139
1/** 2 * This file contains the WeakMaps used throughout this project. The WeakMaps exist to tie 3 * objects together without polluting the objects themselves with references we'd rather keep 4 * hidden. This allows the polyfill to work as transparently as possible. 5 */ 6/** Use an ElementInternals instance to get a reference to the element it is attached to */ 7const refMap = new WeakMap(); 8/** Usee an ElementsInternals instance to get its ValidityState object */ 9const validityMap = new WeakMap(); 10/** Use an ElementInternals instance to get its attached input[type="hidden"] */ 11const hiddenInputMap = new WeakMap(); 12/** Use a custom element to get its attached ElementInternals instance */ 13const internalsMap = new WeakMap(); 14/** Use an ElementInternals instance to get the attached validation message */ 15const validationMessageMap = new WeakMap(); 16/** Use a form element to get attached custom elements and ElementInternals instances */ 17const formsMap = new WeakMap(); 18/** Use a custom element or other object to get their associated MutationObservers */ 19const shadowHostsMap = new WeakMap(); 20/** Use a form element to get a set of attached custom elements */ 21const formElementsMap = new WeakMap(); 22/** Use an ElementInternals instance to get a reference to an element's value */ 23const refValueMap = new WeakMap(); 24/** Elements that need to be upgraded once added to the DOM */ 25const upgradeMap = new WeakMap(); 26/** Save references to shadow roots for inclusion in internals instance */ 27const shadowRootMap = new WeakMap(); 28/** Save a reference to the internals' validation anchor */ 29const validationAnchorMap = new WeakMap(); 30/** Map DocumentFragments to their MutationObservers so we can disconnect once elements are removed */ 31const documentFragmentMap = new WeakMap(); 32/** Whether connectedCallback has already been called. */ 33const connectedCallbackMap = new WeakMap(); 34/** Save a reference to validity state for elements that need to upgrade after being connected */ 35const validityUpgradeMap = new WeakMap(); 36 37/** 38 * Set attribute if its value differs from existing one. 39 * 40 * In comparison to other attribute modification methods (removeAttribute and 41 * toggleAttribute), setAttribute always triggers attributeChangedCallback 42 * even if the actual value has not changed. 43 * 44 * This polyfill relies heavily on attributes to pass aria information to 45 * screen readers. This behaviour differs from native implementation which does 46 * not change attributes. 47 * 48 * To limit this difference we only set attribute value when it is different 49 * from the current state. 50 * 51 * @param {ICustomElement | Element} ref - The custom element instance 52 * @param {string} name - The attribute name 53 * @param {string} value - The attribute value 54 * @returns 55 */ 56const setAttribute = (ref, name, value) => { 57 if (ref.getAttribute(name) === value) { 58 return; 59 } 60 ref.setAttribute(name, value); 61}; 62/** 63 * Toggle's the disabled state (attributes & callback) on the given element 64 * @param {HTMLElement} ref - The custom element instance 65 * @param {boolean} disabled - The disabled state 66 */ 67const setDisabled = (ref, disabled) => { 68 ref.toggleAttribute("internals-disabled", disabled); 69 if (disabled) { 70 setAttribute(ref, "aria-disabled", "true"); 71 } 72 else { 73 ref.removeAttribute("aria-disabled"); 74 } 75 if (ref.formDisabledCallback) { 76 ref.formDisabledCallback.apply(ref, [disabled]); 77 } 78}; 79/** 80 * Removes all hidden inputs for the given element internals instance 81 * @param {ElementInternals} internals - The element internals instance 82 * @return {void} 83 */ 84const removeHiddenInputs = (internals) => { 85 const hiddenInputs = hiddenInputMap.get(internals); 86 hiddenInputs.forEach((hiddenInput) => { 87 hiddenInput.remove(); 88 }); 89 hiddenInputMap.set(internals, []); 90}; 91/** 92 * Creates a hidden input for the given ref 93 * @param {HTMLElement} ref - The element to watch 94 * @param {ElementInternals} internals - The element internals instance for the ref 95 * @return {HTMLInputElement} The hidden input 96 */ 97const createHiddenInput = (ref, internals) => { 98 const input = document.createElement("input"); 99 input.type = "hidden"; 100 input.name = ref.getAttribute("name"); 101 ref.after(input); 102 hiddenInputMap.get(internals).push(input); 103 return input; 104}; 105/** 106 * Set up labels for the ref 107 * @param {HTMLElement} ref - The ref to add labels to 108 * @param {NodeList} labels - A list of the labels 109 * @return {void} 110 */ 111const initLabels = (ref, labels) => { 112 if (labels.length) { 113 const labelList = Array.from(labels); 114 labelList.forEach((label) => label.addEventListener("click", ref.click.bind(ref))); 115 const [firstLabel] = labelList; 116 let firstLabelId = firstLabel.id; 117 if (!firstLabel.id) { 118 firstLabelId = `${firstLabel.htmlFor}_Label`; 119 firstLabel.id = firstLabelId; 120 } 121 setAttribute(ref, "aria-labelledby", firstLabelId); 122 } 123}; 124/** 125 * Sets the internals-valid and internals-invalid attributes 126 * based on form validity. 127 * @param {HTMLFormElement} - The target form 128 * @return {void} 129 */ 130const setFormValidity = (form) => { 131 const nativeControlValidity = Array.from(form.elements) 132 .filter((element) => !element.tagName.includes("-") && element.validity) 133 .map((element) => element.validity.valid); 134 const polyfilledElements = formElementsMap.get(form) || []; 135 const polyfilledValidity = Array.from(polyfilledElements) 136 .filter((control) => control.isConnected) 137 .map((control) => internalsMap.get(control).validity.valid); 138 const hasInvalid = [...nativeControlValidity, ...polyfilledValidity].includes(false); 139 form.toggleAttribute("internals-invalid", hasInvalid); 140 form.toggleAttribute("internals-valid", !hasInvalid); 141}; 142/** 143 * The global form input callback. Updates the form's validity 144 * attributes on input. 145 * @param {Event} - The form input event 146 * @return {void} 147 */ 148const formInputCallback = (event) => { 149 setFormValidity(findParentForm(event.target)); 150}; 151/** 152 * The global form change callback. Updates the form's validity 153 * attributes on change. 154 * @param {Event} - The form change event 155 * @return {void} 156 */ 157const formChangeCallback = (event) => { 158 setFormValidity(findParentForm(event.target)); 159}; 160/** 161 * The global form submit callback. We need to cancel any submission 162 * if a nested internals is invalid. 163 * @param {HTMLFormElement} - The form element 164 * @return {void} 165 */ 166const wireSubmitLogic = (form) => { 167 const submitButtonSelector = [ 168 "button[type=submit]", 169 "input[type=submit]", 170 "button:not([type])", 171 ] 172 .map((sel) => `${sel}:not([disabled])`) 173 .map((sel) => `${sel}:not([form])${form.id ? `,${sel}[form='${form.id}']` : ""}`) 174 .join(","); 175 form.addEventListener("click", (event) => { 176 const target = event.target; 177 if (target.closest(submitButtonSelector)) { 178 // validate 179 const elements = formElementsMap.get(form); 180 /** 181 * If this form does not validate then we're done 182 */ 183 if (form.noValidate) { 184 return; 185 } 186 /** If the Set has items, continue */ 187 if (elements.size) { 188 const nodes = Array.from(elements); 189 /** Check the internals.checkValidity() of all nodes */ 190 const validityList = nodes.reverse().map((node) => { 191 const internals = internalsMap.get(node); 192 return internals.reportValidity(); 193 }); 194 /** If any node is false, stop the event */ 195 if (validityList.includes(false)) { 196 event.preventDefault(); 197 } 198 } 199 } 200 }); 201}; 202/** 203 * The global form reset callback. This will loop over added 204 * inputs and call formResetCallback if applicable 205 * @return {void} 206 */ 207const formResetCallback = (event) => { 208 /** Get the Set of elements attached to this form */ 209 const elements = formElementsMap.get(event.target); 210 /** Some forms won't contain form associated custom elements */ 211 if (elements && elements.size) { 212 /** Loop over the elements and call formResetCallback if applicable */ 213 elements.forEach((element) => { 214 if (element.constructor.formAssociated && 215 element.formResetCallback) { 216 element.formResetCallback.apply(element); 217 } 218 }); 219 } 220}; 221/** 222 * Initialize the form. We will need to add submit and reset listeners 223 * if they don't already exist. If they do, just add the new ref to the form. 224 * @param {HTMLElement} ref - The element ref that includes internals 225 * @param {HTMLFormElement} form - The form the ref belongs to 226 * @param {ElementInternals} internals - The internals for ref 227 * @return {void} 228 */ 229const initForm = (ref, form, internals) => { 230 if (form) { 231 /** This will be a WeakMap<HTMLFormElement, Set<HTMLElement> */ 232 const formElements = formElementsMap.get(form); 233 if (formElements) { 234 /** If formElements exists, add to it */ 235 formElements.add(ref); 236 } 237 else { 238 /** If formElements doesn't exist, create it and add to it */ 239 const initSet = new Set(); 240 initSet.add(ref); 241 formElementsMap.set(form, initSet); 242 /** Add listeners to emulate validation and reset behavior */ 243 wireSubmitLogic(form); 244 form.addEventListener("reset", formResetCallback); 245 form.addEventListener("input", formInputCallback); 246 form.addEventListener("change", formChangeCallback); 247 } 248 formsMap.set(form, { ref, internals }); 249 /** Call formAssociatedCallback if applicable */ 250 if (ref.constructor["formAssociated"] && ref.formAssociatedCallback) { 251 setTimeout(() => { 252 ref.formAssociatedCallback.apply(ref, [form]); 253 }, 0); 254 } 255 setFormValidity(form); 256 } 257}; 258/** 259 * Recursively look for an element's parent form 260 * @param {Element} elem - The element to look for a parent form 261 * @return {HTMLFormElement|null} - The parent form, if one exists 262 */ 263const findParentForm = (elem) => { 264 let parent = elem.parentNode; 265 if (parent && parent.tagName !== "FORM") { 266 parent = findParentForm(parent); 267 } 268 return parent; 269}; 270/** 271 * Throw an error if the element ref is not form associated 272 * @param ref {HTMLElement} - The element to check if it is form associated 273 * @param message {string} - The error message to throw 274 * @param ErrorType {any} - The error type to throw, defaults to DOMException 275 */ 276const throwIfNotFormAssociated = (ref, message, ErrorType = DOMException) => { 277 if (!ref.constructor["formAssociated"]) { 278 throw new ErrorType(message); 279 } 280}; 281/** 282 * Called for each HTMLFormElement.checkValidity|reportValidity 283 * will loop over a form's added components and call the respective 284 * method modifying the default return value if needed 285 * @param form {HTMLFormElement} - The form element to run the method on 286 * @param returnValue {boolean} - The initial result of the original method 287 * @param method {'checkValidity'|'reportValidity'} - The original method 288 * @returns {boolean} The form's validity state 289 */ 290const overrideFormMethod = (form, returnValue, method) => { 291 const elements = formElementsMap.get(form); 292 /** Some forms won't contain form associated custom elements */ 293 if (elements && elements.size) { 294 elements.forEach((element) => { 295 const internals = internalsMap.get(element); 296 const valid = internals[method](); 297 if (!valid) { 298 returnValue = false; 299 } 300 }); 301 } 302 return returnValue; 303}; 304/** 305 * Will upgrade an ElementInternals instance by initializing the 306 * instance's form and labels. This is called when the element is 307 * either constructed or appended from a DocumentFragment 308 * @param ref {HTMLElement} - The custom element to upgrade 309 */ 310const upgradeInternals = (ref) => { 311 let attached = false; 312 if (ref.constructor["formAssociated"]) { 313 let internals = internalsMap.get(ref); 314 // we might have cases where the internals are not set 315 if (internals === undefined) { 316 ref.attachInternals(); 317 internals = internalsMap.get(ref); 318 attached = true; 319 } 320 const { labels, form } = internals; 321 initLabels(ref, labels); 322 initForm(ref, form, internals); 323 } 324 return attached; 325}; 326/** 327 * Check to see if MutationObserver exists in the current 328 * execution context. Will likely return false on the server 329 * @returns {boolean} 330 */ 331function mutationObserverExists() { 332 return typeof MutationObserver !== "undefined"; 333} 334 335const aom = { 336 ariaAtomic: "aria-atomic", 337 ariaAutoComplete: "aria-autocomplete", 338 ariaBrailleLabel: "aria-braillelabel", 339 ariaBrailleRoleDescription: "aria-brailleroledescription", 340 ariaBusy: "aria-busy", 341 ariaChecked: "aria-checked", 342 ariaColCount: "aria-colcount", 343 ariaColIndex: "aria-colindex", 344 ariaColIndexText: "aria-colindextext", 345 ariaColSpan: "aria-colspan", 346 ariaCurrent: "aria-current", 347 ariaDescription: "aria-description", 348 ariaDisabled: "aria-disabled", 349 ariaExpanded: "aria-expanded", 350 ariaHasPopup: "aria-haspopup", 351 ariaHidden: "aria-hidden", 352 ariaInvalid: "aria-invalid", 353 ariaKeyShortcuts: "aria-keyshortcuts", 354 ariaLabel: "aria-label", 355 ariaLevel: "aria-level", 356 ariaLive: "aria-live", 357 ariaModal: "aria-modal", 358 ariaMultiLine: "aria-multiline", 359 ariaMultiSelectable: "aria-multiselectable", 360 ariaOrientation: "aria-orientation", 361 ariaPlaceholder: "aria-placeholder", 362 ariaPosInSet: "aria-posinset", 363 ariaPressed: "aria-pressed", 364 ariaReadOnly: "aria-readonly", 365 ariaRelevant: "aria-relevant", 366 ariaRequired: "aria-required", 367 ariaRoleDescription: "aria-roledescription", 368 ariaRowCount: "aria-rowcount", 369 ariaRowIndex: "aria-rowindex", 370 ariaRowIndexText: "aria-rowindextext", 371 ariaRowSpan: "aria-rowspan", 372 ariaSelected: "aria-selected", 373 ariaSetSize: "aria-setsize", 374 ariaSort: "aria-sort", 375 ariaValueMax: "aria-valuemax", 376 ariaValueMin: "aria-valuemin", 377 ariaValueNow: "aria-valuenow", 378 ariaValueText: "aria-valuetext", 379 role: "role", 380}; 381const initAom = (ref, internals) => { 382 for (let key in aom) { 383 internals[key] = null; 384 let closureValue = null; 385 const attributeName = aom[key]; 386 Object.defineProperty(internals, key, { 387 get() { 388 return closureValue; 389 }, 390 set(value) { 391 closureValue = value; 392 if (ref.isConnected) { 393 setAttribute(ref, attributeName, value); 394 } 395 else { 396 upgradeMap.set(ref, internals); 397 } 398 }, 399 }); 400 } 401}; 402 403/** 404 * Initialize a ref by setting up an attribute observe on it 405 * looking for changes to disabled 406 * @param {HTMLElement} ref - The element to watch 407 * @param {ElementInternals} internals - The element internals instance for the ref 408 * @return {void} 409 */ 410const initRef = (ref, internals) => { 411 hiddenInputMap.set(internals, []); 412 disabledOrNameObserver.observe?.(ref, disabledOrNameObserverConfig); 413}; 414function initNode(node) { 415 const internals = internalsMap.get(node); 416 const { form } = internals; 417 initForm(node, form, internals); 418 initLabels(node, internals.labels); 419} 420/** 421 * If a fieldset's disabled state is toggled, the formDisabledCallback 422 * on any child form-associated cusotm elements. 423 */ 424const walkFieldset = (node, firstRender = false) => { 425 const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT, { 426 acceptNode(node) { 427 return internalsMap.has(node) ? 428 NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; 429 } 430 }); 431 let current = walker.nextNode(); 432 /** 433 * We don't need to call anything on first render if 434 * the element isn't disabled 435 */ 436 const isCallNecessary = (!firstRender || node.disabled); 437 while (current) { 438 if (current.formDisabledCallback && isCallNecessary) { 439 setDisabled(current, node.disabled); 440 } 441 current = walker.nextNode(); 442 } 443}; 444const disabledOrNameObserverConfig = { attributes: true, attributeFilter: ['disabled', 'name'] }; 445const disabledOrNameObserver = mutationObserverExists() ? new MutationObserver((mutationsList) => { 446 for (const mutation of mutationsList) { 447 const target = mutation.target; 448 /** Manage changes to the ref's disabled state */ 449 if (mutation.attributeName === 'disabled') { 450 if (target.constructor['formAssociated']) { 451 setDisabled(target, target.hasAttribute('disabled')); 452 } 453 else if (target.localName === 'fieldset') { 454 /** 455 * Repurpose the observer for fieldsets which need 456 * to be walked whenever the disabled attribute is set 457 */ 458 walkFieldset(target); 459 } 460 } 461 /** Manage changes to the ref's name */ 462 if (mutation.attributeName === 'name') { 463 if (target.constructor['formAssociated']) { 464 const internals = internalsMap.get(target); 465 const value = refValueMap.get(target); 466 internals.setFormValue(value); 467 } 468 } 469 } 470}) : {}; 471function observerCallback(mutationList) { 472 mutationList.forEach(mutationRecord => { 473 const { addedNodes, removedNodes } = mutationRecord; 474 const added = Array.from(addedNodes); 475 const removed = Array.from(removedNodes); 476 added.forEach(node => { 477 /** Allows for dynamic addition of elements to forms */ 478 if (internalsMap.has(node) && node.constructor['formAssociated']) { 479 initNode(node); 480 } 481 /** Upgrade the accessibility information on any previously connected */ 482 if (upgradeMap.has(node)) { 483 const internals = upgradeMap.get(node); 484 const aomKeys = Object.keys(aom); 485 aomKeys 486 .filter(key => internals[key] !== null) 487 .forEach(key => { 488 setAttribute(node, aom[key], internals[key]); 489 }); 490 upgradeMap.delete(node); 491 } 492 /** Upgrade the validity state when the element is connected */ 493 if (validityUpgradeMap.has(node)) { 494 const internals = validityUpgradeMap.get(node); 495 setAttribute(node, 'internals-valid', internals.validity.valid.toString()); 496 setAttribute(node, 'internals-invalid', (!internals.validity.valid).toString()); 497 setAttribute(node, 'aria-invalid', (!internals.validity.valid).toString()); 498 validityUpgradeMap.delete(node); 499 } 500 /** If the node that's added is a form, check the validity */ 501 if (node.localName === 'form') { 502 const formElements = formElementsMap.get(node); 503 const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT, { 504 acceptNode(node) { 505 return (internalsMap.has(node) && node.constructor['formAssociated'] && !(formElements && formElements.has(node))) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; 506 } 507 }); 508 let current = walker.nextNode(); 509 while (current) { 510 initNode(current); 511 current = walker.nextNode(); 512 } 513 } 514 if (node.localName === 'fieldset') { 515 disabledOrNameObserver.observe?.(node, disabledOrNameObserverConfig); 516 walkFieldset(node, true); 517 } 518 }); 519 removed.forEach(node => { 520 const internals = internalsMap.get(node); 521 /** Clean up any hidden input elements left after an element is disconnected */ 522 if (internals && hiddenInputMap.get(internals)) { 523 removeHiddenInputs(internals); 524 } 525 /** Disconnect any unneeded MutationObservers */ 526 if (shadowHostsMap.has(node)) { 527 const observer = shadowHostsMap.get(node); 528 observer.disconnect(); 529 } 530 }); 531 }); 532} 533/** 534 * This observer callback is just for document fragments 535 * it will upgrade an ElementInternals instance if was appended 536 * from a document fragment. 537 */ 538function fragmentObserverCallback(mutationList) { 539 mutationList.forEach(mutation => { 540 const { removedNodes } = mutation; 541 removedNodes.forEach(node => { 542 const observer = documentFragmentMap.get(mutation.target); 543 if (internalsMap.has(node)) { 544 upgradeInternals(node); 545 } 546 observer.disconnect(); 547 }); 548 }); 549} 550/** 551 * Defer the upgrade of nodes withing a DocumentFragment 552 * @param fragment {DocumentFragment} 553 */ 554const deferUpgrade = (fragment) => { 555 const observer = new MutationObserver(fragmentObserverCallback); 556 // is this using shady DOM and is not actually a DocumentFragment? 557 if (window?.ShadyDOM?.inUse && 558 fragment.mode && 559 fragment.host) { 560 // using shady DOM polyfill. Best to just observe the host. 561 fragment = fragment.host; 562 } 563 observer.observe?.(fragment, { childList: true }); 564 documentFragmentMap.set(fragment, observer); 565}; 566mutationObserverExists() ? new MutationObserver(observerCallback) : {}; 567const observerConfig = { 568 childList: true, 569 subtree: true 570}; 571 572/** Emulate the browser's default ValidityState object */ 573class ValidityState { 574 constructor() { 575 this.badInput = false; 576 this.customError = false; 577 this.patternMismatch = false; 578 this.rangeOverflow = false; 579 this.rangeUnderflow = false; 580 this.stepMismatch = false; 581 this.tooLong = false; 582 this.tooShort = false; 583 this.typeMismatch = false; 584 this.valid = true; 585 this.valueMissing = false; 586 Object.seal(this); 587 } 588} 589/** 590 * Reset a ValidityState object back to valid 591 * @param {ValidityState} validityObject - The object to modify 592 * @return {ValidityState} - The modified ValidityStateObject 593 */ 594const setValid = (validityObject) => { 595 validityObject.badInput = false; 596 validityObject.customError = false; 597 validityObject.patternMismatch = false; 598 validityObject.rangeOverflow = false; 599 validityObject.rangeUnderflow = false; 600 validityObject.stepMismatch = false; 601 validityObject.tooLong = false; 602 validityObject.tooShort = false; 603 validityObject.typeMismatch = false; 604 validityObject.valid = true; 605 validityObject.valueMissing = false; 606 return validityObject; 607}; 608/** 609 * Reconcile a ValidityState object with a new state object 610 * @param {ValidityState} - The base object to reconcile with new state 611 * @param {Object} - A partial ValidityState object to override the original 612 * @return {ValidityState} - The updated ValidityState object 613 */ 614const reconcileValidity = (validityObject, newState, form) => { 615 validityObject.valid = isValid(newState); 616 Object.keys(newState).forEach(key => validityObject[key] = newState[key]); 617 if (form) { 618 setFormValidity(form); 619 } 620 return validityObject; 621}; 622/** 623 * Check if a partial ValidityState object should be valid 624 * @param {Object} - A partial ValidityState object 625 * @return {Boolean} - Should the new object be valid 626 */ 627const isValid = (validityState) => { 628 let valid = true; 629 for (let key in validityState) { 630 if (key !== 'valid' && validityState[key] !== false) { 631 valid = false; 632 } 633 } 634 return valid; 635}; 636 637/** Save a reference to the ref for the CustomStateSet */ 638const customStateMap = new WeakMap(); 639function addState(ref, stateName) { 640 ref.toggleAttribute(stateName, true); 641 if (ref.part) { 642 ref.part.add(stateName); 643 } 644} 645class CustomStateSet extends Set { 646 static get isPolyfilled() { 647 return true; 648 } 649 constructor(ref) { 650 super(); 651 if (!ref || !ref.tagName || ref.tagName.indexOf("-") === -1) { 652 throw new TypeError("Illegal constructor"); 653 } 654 customStateMap.set(this, ref); 655 } 656 add(state) { 657 if (!/^--/.test(state) || typeof state !== "string") { 658 throw new DOMException(`Failed to execute 'add' on 'CustomStateSet': The specified value ${state} must start with '--'.`); 659 } 660 const result = super.add(state); 661 const ref = customStateMap.get(this); 662 const stateName = `state${state}`; 663 /** 664 * Only add the state immediately if the ref is connected to the DOM; 665 * otherwise, wait a tick because the element is likely being constructed 666 * by document.createElement and would throw otherwise. 667 */ 668 if (ref.isConnected) { 669 addState(ref, stateName); 670 } 671 else { 672 setTimeout(() => { 673 addState(ref, stateName); 674 }); 675 } 676 return result; 677 } 678 clear() { 679 for (let [entry] of this.entries()) { 680 this.delete(entry); 681 } 682 super.clear(); 683 } 684 delete(state) { 685 const result = super.delete(state); 686 const ref = customStateMap.get(this); 687 /** 688 * Only toggle the state/attr immediately if the ref is connected to the DOM; 689 * otherwise, wait a tick because the element is likely being constructed 690 * by document.createElement and would throw otherwise. 691 */ 692 if (ref.isConnected) { 693 ref.toggleAttribute(`state${state}`, false); 694 if (ref.part) { 695 ref.part.remove(`state${state}`); 696 } 697 } 698 else { 699 setTimeout(() => { 700 ref.toggleAttribute(`state${state}`, false); 701 if (ref.part) { 702 ref.part.remove(`state${state}`); 703 } 704 }); 705 } 706 return result; 707 } 708} 709 710var __classPrivateFieldSet = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { 711 if (kind === "m") throw new TypeError("Private method is not writable"); 712 if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); 713 if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); 714 return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; 715}; 716var __classPrivateFieldGet = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) { 717 if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); 718 if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); 719 return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); 720}; 721var _HTMLFormControlsCollection_elements; 722class HTMLFormControlsCollection { 723 constructor(elements) { 724 _HTMLFormControlsCollection_elements.set(this, void 0); 725 __classPrivateFieldSet(this, _HTMLFormControlsCollection_elements, elements, "f"); 726 for (let i = 0; i < elements.length; i++) { 727 let element = elements[i]; 728 this[i] = element; 729 if (element.hasAttribute('name')) { 730 this[element.getAttribute('name')] = element; 731 } 732 } 733 Object.freeze(this); 734 } 735 get length() { 736 return __classPrivateFieldGet(this, _HTMLFormControlsCollection_elements, "f").length; 737 } 738 [(_HTMLFormControlsCollection_elements = new WeakMap(), Symbol.iterator)]() { 739 return __classPrivateFieldGet(this, _HTMLFormControlsCollection_elements, "f")[Symbol.iterator](); 740 } 741 item(i) { 742 return this[i] == null ? null : this[i]; 743 } 744 namedItem(name) { 745 return this[name] == null ? null : this[name]; 746 } 747} 748 749/** 750 * Patch the HTMLElement prototype 751 * 752 * This function patches checkValidity, reportValidity and elements 753 */ 754function patchFormPrototype() { 755 const checkValidity = HTMLFormElement.prototype.checkValidity; 756 HTMLFormElement.prototype.checkValidity = checkValidityOverride; 757 const reportValidity = HTMLFormElement.prototype.reportValidity; 758 HTMLFormElement.prototype.reportValidity = reportValidityOverride; 759 function checkValidityOverride(...args) { 760 let returnValue = checkValidity.apply(this, args); 761 return overrideFormMethod(this, returnValue, 'checkValidity'); 762 } 763 function reportValidityOverride(...args) { 764 let returnValue = reportValidity.apply(this, args); 765 return overrideFormMethod(this, returnValue, 'reportValidity'); 766 } 767 const { get } = Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, 'elements'); 768 Object.defineProperty(HTMLFormElement.prototype, 'elements', { 769 get(...args) { 770 const elements = get.call(this, ...args); 771 const polyfilledElements = Array.from(formElementsMap.get(this) || []); 772 // If there are no polyfilled elements, return the native elements collection 773 if (polyfilledElements.length === 0) { 774 return elements; 775 } 776 // Merge the native elements with the polyfilled elements 777 // and order them by their position in the DOM 778 const orderedElements = Array.from(elements).concat(polyfilledElements).sort((a, b) => { 779 if (a.compareDocumentPosition) { 780 return a.compareDocumentPosition(b) & 2 ? 1 : -1; 781 } 782 return 0; 783 }); 784 return new HTMLFormControlsCollection(orderedElements); 785 }, 786 }); 787} 788 789class ElementInternals { 790 static get isPolyfilled() { 791 return true; 792 } 793 constructor(ref) { 794 if (!ref || !ref.tagName || ref.tagName.indexOf("-") === -1) { 795 throw new TypeError("Illegal constructor"); 796 } 797 const rootNode = ref.getRootNode(); 798 const validity = new ValidityState(); 799 this.states = new CustomStateSet(ref); 800 refMap.set(this, ref); 801 validityMap.set(this, validity); 802 internalsMap.set(ref, this); 803 initAom(ref, this); 804 initRef(ref, this); 805 Object.seal(this); 806 /** 807 * If appended from a DocumentFragment, wait until it is connected 808 * before attempting to upgrade the internals instance 809 */ 810 if (rootNode instanceof DocumentFragment) { 811 deferUpgrade(rootNode); 812 } 813 } 814 /** 815 * Will return true if the element is in a valid state 816 */ 817 checkValidity() { 818 const ref = refMap.get(this); 819 throwIfNotFormAssociated(ref, `Failed to execute 'checkValidity' on 'ElementInternals': The target element is not a form-associated custom element.`); 820 /** If the element will not validate, it is necessarily valid by default */ 821 if (!this.willValidate) { 822 return true; 823 } 824 const validity = validityMap.get(this); 825 if (!validity.valid) { 826 const validityEvent = new Event("invalid", { 827 bubbles: false, 828 cancelable: true, 829 composed: false, 830 }); 831 ref.dispatchEvent(validityEvent); 832 } 833 return validity.valid; 834 } 835 /** The form element the custom element is associated with */ 836 get form() { 837 const ref = refMap.get(this); 838 throwIfNotFormAssociated(ref, `Failed to read the 'form' property from 'ElementInternals': The target element is not a form-associated custom element.`); 839 let form; 840 if (ref.constructor["formAssociated"] === true) { 841 form = findParentForm(ref); 842 } 843 return form; 844 } 845 /** A list of all relative form labels for this element */ 846 get labels() { 847 const ref = refMap.get(this); 848 throwIfNotFormAssociated(ref, `Failed to read the 'labels' property from 'ElementInternals': The target element is not a form-associated custom element.`); 849 const id = ref.getAttribute("id"); 850 const hostRoot = ref.getRootNode(); 851 if (hostRoot && id) { 852 return hostRoot.querySelectorAll(`[for="${id}"]`); 853 } 854 return []; 855 } 856 /** Will report the elements validity state */ 857 reportValidity() { 858 const ref = refMap.get(this); 859 throwIfNotFormAssociated(ref, `Failed to execute 'reportValidity' on 'ElementInternals': The target element is not a form-associated custom element.`); 860 /** If the element will not validate, it is valid by default */ 861 if (!this.willValidate) { 862 return true; 863 } 864 const valid = this.checkValidity(); 865 const anchor = validationAnchorMap.get(this); 866 if (anchor && !ref.constructor["formAssociated"]) { 867 throw new DOMException(`Failed to execute 'reportValidity' on 'ElementInternals': The target element is not a form-associated custom element.`); 868 } 869 if (!valid && anchor) { 870 ref.focus(); 871 anchor.focus(); 872 } 873 return valid; 874 } 875 /** Sets the element's value within the form */ 876 setFormValue(value) { 877 const ref = refMap.get(this); 878 throwIfNotFormAssociated(ref, `Failed to execute 'setFormValue' on 'ElementInternals': The target element is not a form-associated custom element.`); 879 removeHiddenInputs(this); 880 if (value != null && !(value instanceof FormData)) { 881 if (ref.getAttribute("name")) { 882 const hiddenInput = createHiddenInput(ref, this); 883 hiddenInput.value = value; 884 } 885 } 886 else if (value != null && value instanceof FormData) { 887 Array.from(value) 888 .reverse() 889 .forEach(([formDataKey, formDataValue]) => { 890 if (typeof formDataValue === "string") { 891 const hiddenInput = createHiddenInput(ref, this); 892 hiddenInput.name = formDataKey; 893 hiddenInput.value = formDataValue; 894 } 895 }); 896 } 897 refValueMap.set(ref, value); 898 } 899 /** 900 * Sets the element's validity. The first argument is a partial ValidityState object 901 * reflecting the changes to be made to the element's validity. If the element is invalid, 902 * the second argument sets the element's validation message. 903 * 904 * If the field is valid and a message is specified, the method will throw a TypeError. 905 */ 906 setValidity(validityChanges, validationMessage, anchor) { 907 const ref = refMap.get(this); 908 throwIfNotFormAssociated(ref, `Failed to execute 'setValidity' on 'ElementInternals': The target element is not a form-associated custom element.`); 909 if (!validityChanges) { 910 throw new TypeError("Failed to execute 'setValidity' on 'ElementInternals': 1 argument required, but only 0 present."); 911 } 912 validationAnchorMap.set(this, anchor); 913 const validity = validityMap.get(this); 914 const validityChangesObj = {}; 915 for (const key in validityChanges) { 916 validityChangesObj[key] = validityChanges[key]; 917 } 918 if (Object.keys(validityChangesObj).length === 0) { 919 setValid(validity); 920 } 921 const check = { ...validity, ...validityChangesObj }; 922 delete check.valid; 923 const { valid } = reconcileValidity(validity, check, this.form); 924 if (!valid && !validationMessage) { 925 throw new DOMException(`Failed to execute 'setValidity' on 'ElementInternals': The second argument should not be empty if one or more flags in the first argument are true.`); 926 } 927 validationMessageMap.set(this, valid ? "" : validationMessage); 928 // check to make sure the host element is connected before adding attributes 929 // because safari doesnt allow elements to have attributes added in the constructor 930 if (ref.isConnected) { 931 ref.toggleAttribute("internals-invalid", !valid); 932 ref.toggleAttribute("internals-valid", valid); 933 setAttribute(ref, "aria-invalid", `${!valid}`); 934 } 935 else { 936 validityUpgradeMap.set(ref, this); 937 } 938 } 939 get shadowRoot() { 940 const ref = refMap.get(this); 941 const shadowRoot = shadowRootMap.get(ref); 942 if (shadowRoot) { 943 return shadowRoot; 944 } 945 return null; 946 } 947 /** The element's validation message set during a call to ElementInternals.setValidity */ 948 get validationMessage() { 949 const ref = refMap.get(this); 950 throwIfNotFormAssociated(ref, `Failed to read the 'validationMessage' property from 'ElementInternals': The target element is not a form-associated custom element.`); 951 return validationMessageMap.get(this); 952 } 953 /** The current validity state of the object */ 954 get validity() { 955 const ref = refMap.get(this); 956 throwIfNotFormAssociated(ref, `Failed to read the 'validity' property from 'ElementInternals': The target element is not a form-associated custom element.`); 957 const validity = validityMap.get(this); 958 return validity; 959 } 960 /** If true the element will participate in a form's constraint validation. */ 961 get willValidate() { 962 const ref = refMap.get(this); 963 throwIfNotFormAssociated(ref, `Failed to read the 'willValidate' property from 'ElementInternals': The target element is not a form-associated custom element.`); 964 if (ref.matches(":disabled") || 965 ref["disabled"] || 966 ref.hasAttribute("disabled") || 967 ref.hasAttribute("readonly")) { 968 return false; 969 } 970 return true; 971 } 972} 973function isElementInternalsSupported() { 974 if (typeof window === "undefined" || 975 !window.ElementInternals || 976 !HTMLElement.prototype.attachInternals) { 977 return false; 978 } 979 class ElementInternalsFeatureDetection extends HTMLElement { 980 constructor() { 981 super(); 982 this.internals = this.attachInternals(); 983 } 984 } 985 const randomName = `element-internals-feature-detection-${Math.random() 986 .toString(36) 987 .replace(/[^a-z]+/g, "")}`; 988 customElements.define(randomName, ElementInternalsFeatureDetection); 989 const featureDetectionElement = new ElementInternalsFeatureDetection(); 990 return [ 991 "shadowRoot", 992 "form", 993 "willValidate", 994 "validity", 995 "validationMessage", 996 "labels", 997 "setFormValue", 998 "setValidity", 999 "checkValidity", 1000 "reportValidity", 1001 ].every((prop) => prop in featureDetectionElement.internals); 1002} 1003let hasElementInternalsPolyfillBeenApplied = false; 1004let hasCustomStateSetPolyfillBeenApplied = false; 1005/** 1006 * Forcibly applies the polyfill for CustomStateSet. 1007 * 1008 * https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet 1009 */ 1010function forceCustomStateSetPolyfill(attachInternals) { 1011 if (hasCustomStateSetPolyfillBeenApplied) { 1012 return; 1013 } 1014 hasCustomStateSetPolyfillBeenApplied = true; 1015 /** @ts-expect-error These types won't match because this is a polyfill */ 1016 window.CustomStateSet = CustomStateSet; 1017 if (attachInternals) { 1018 HTMLElement.prototype.attachInternals = function (...args) { 1019 const internals = attachInternals.call(this, args); 1020 internals.states = new CustomStateSet(this); 1021 return internals; 1022 }; 1023 } 1024} 1025/** 1026 * Forcibly applies the polyfill for ElementInternals. Useful for situations 1027 * like Chrome extensions where Chrome supports ElementInternals, but the 1028 * CustomElements polyfill is required. 1029 * 1030 * https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals 1031 * 1032 * @param forceCustomStateSet Optional: when true, forces the 1033 * [CustomStateSet](https://developer.mozilla.org/en-US/docs/Web/API/CustomStateSet) 1034 * polyfill as well. 1035 */ 1036function forceElementInternalsPolyfill(forceCustomStateSet = true) { 1037 /** 1038 * This is a flag to prevent a DOMException from being thrown when 1039 * attachInternals is called in upgradeInternals. 1040 */ 1041 let attachedFlag = false; 1042 if (hasElementInternalsPolyfillBeenApplied) { 1043 return; 1044 } 1045 hasElementInternalsPolyfillBeenApplied = true; 1046 if (typeof window !== "undefined") { 1047 /** @ts-expect-error: we need to replace the default ElementInternals */ 1048 window.ElementInternals = ElementInternals; 1049 } 1050 if (typeof CustomElementRegistry !== "undefined") { 1051 const define = CustomElementRegistry.prototype.define; 1052 CustomElementRegistry.prototype.define = function (name, constructor, options) { 1053 if (constructor.formAssociated) { 1054 const connectedCallback = constructor.prototype.connectedCallback; 1055 constructor.prototype.connectedCallback = function () { 1056 if (!connectedCallbackMap.has(this)) { 1057 connectedCallbackMap.set(this, true); 1058 if (this.hasAttribute("disabled")) { 1059 setDisabled(this, true); 1060 } 1061 } 1062 if (connectedCallback != null) { 1063 connectedCallback.apply(this); 1064 } 1065 // always upgradeInternals in connectedCallback instead of constructor 1066 attachedFlag = upgradeInternals(this); 1067 }; 1068 } 1069 define.call(this, name, constructor, options); 1070 }; 1071 } 1072 /** 1073 * Attaches an ElementInternals instance to a custom element. Calling this method 1074 * on a built-in element will throw an error. 1075 */ 1076 if (typeof HTMLElement !== "undefined") { 1077 HTMLElement.prototype.attachInternals = function () { 1078 if (!this.tagName) { 1079 /** This happens in the LitSSR environment. Here we can generally ignore internals for now */ 1080 return {}; 1081 } 1082 else if (this.tagName.indexOf("-") === -1) { 1083 throw new Error(`Failed to execute 'attachInternals' on 'HTMLElement': Unable to attach ElementInternals to non-custom elements.`); 1084 } 1085 if (internalsMap.has(this) && !attachedFlag) { 1086 throw new DOMException(`DOMException: Failed to execute 'attachInternals' on 'HTMLElement': ElementInternals for the specified element was already attached.`); 1087 } 1088 return new ElementInternals(this); 1089 }; 1090 } 1091 if (typeof Element !== "undefined") { 1092 function attachShadowObserver(...args) { 1093 const shadowRoot = attachShadow.apply(this, args); 1094 shadowRootMap.set(this, shadowRoot); 1095 if (mutationObserverExists()) { 1096 const observer = new MutationObserver(observerCallback); 1097 if (window.ShadyDOM) { 1098 observer.observe(this, observerConfig); 1099 } 1100 else { 1101 observer.observe(shadowRoot, observerConfig); 1102 } 1103 shadowHostsMap.set(this, observer); 1104 } 1105 return shadowRoot; 1106 } 1107 const attachShadow = Element.prototype.attachShadow; 1108 Element.prototype.attachShadow = attachShadowObserver; 1109 } 1110 if (mutationObserverExists() && typeof document !== "undefined") { 1111 const documentObserver = new MutationObserver(observerCallback); 1112 documentObserver.observe(document.documentElement, observerConfig); 1113 } 1114 /** 1115 * Keeps the polyfill from throwing in environments where HTMLFormElement 1116 * is undefined like in a server environment 1117 */ 1118 if (typeof HTMLFormElement !== "undefined") { 1119 patchFormPrototype(); 1120 } 1121 if (forceCustomStateSet || 1122 (typeof window !== "undefined" && !window.CustomStateSet)) { 1123 forceCustomStateSetPolyfill(); 1124 } 1125} 1126 1127// Deteermine whether the webcomponents polyfill has been applied. 1128const isCePolyfill = !!customElements.polyfillWrapFlushCallback; 1129// custom elements polyfill is on. Do not auto-apply. User should determine 1130// whether to force or not. 1131if (!isCePolyfill) { 1132 if (!isElementInternalsSupported()) { 1133 forceElementInternalsPolyfill(false); 1134 } 1135 else if (typeof window !== "undefined" && !window.CustomStateSet) { 1136 forceCustomStateSetPolyfill(HTMLElement.prototype.attachInternals); 1137 } 1138} 1139
1140export { forceCustomStateSetPolyfill, forceElementInternalsPolyfill };
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.