@webcomponents/custom-elements
Version:
HTML Custom Elements Polyfill
55 lines (45 loc) • 2 kB
JavaScript
import Native from './Native.js';
import CustomElementInternals from '../CustomElementInternals.js';
import CEState from '../CustomElementState.js';
import AlreadyConstructedMarker from '../AlreadyConstructedMarker.js';
/**
* @param {!CustomElementInternals} internals
*/
export default function(internals) {
window['HTMLElement'] = (function() {
/**
* @type {function(new: HTMLElement): !HTMLElement}
*/
function HTMLElement() {
// This should really be `new.target` but `new.target` can't be emulated
// in ES5. Assuming the user keeps the default value of the constructor's
// prototype's `constructor` property, this is equivalent.
/** @type {!Function} */
const constructor = this.constructor;
const definition = internals.constructorToDefinition(constructor);
if (!definition) {
throw new Error('The custom element being constructed was not registered with `customElements`.');
}
const constructionStack = definition.constructionStack;
if (constructionStack.length === 0) {
const element = Native.Document_createElement.call(document, definition.localName);
Object.setPrototypeOf(element, constructor.prototype);
element.__CE_state = CEState.custom;
element.__CE_definition = definition;
internals.patch(element);
return element;
}
const lastIndex = constructionStack.length - 1;
const element = constructionStack[lastIndex];
if (element === AlreadyConstructedMarker) {
throw new Error('The HTMLElement constructor was either called reentrantly for this constructor or called multiple times.');
}
constructionStack[lastIndex] = AlreadyConstructedMarker;
Object.setPrototypeOf(element, constructor.prototype);
internals.patch(/** @type {!HTMLElement} */ (element));
return element;
}
HTMLElement.prototype = Native.HTMLElement.prototype;
return HTMLElement;
})();
};