UNPKG

@polymer/polymer

Version:

The Polymer library makes it easy to create your own web components. Give your element some markup and properties, and then use it on a site. Polymer provides features like dynamic templates and data binding to reduce the amount of boilerplate you need to

165 lines (153 loc) 5.57 kB
/** @license Copyright (c) 2017 The Polymer Project Authors. All rights reserved. This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ import '../utils/boot.js'; import { resolveUrl, pathFromUrl } from '../utils/resolve-url.js'; import { strictTemplatePolicy } from '../utils/settings.js'; let modules = {}; let lcModules = {}; /** * Sets a dom-module into the global registry by id. * * @param {string} id dom-module id * @param {DomModule} module dom-module instance * @return {void} */ function setModule(id, module) { // store id separate from lowercased id so that // in all cases mixedCase id will stored distinctly // and lowercase version is a fallback modules[id] = lcModules[id.toLowerCase()] = module; } /** * Retrieves a dom-module from the global registry by id. * * @param {string} id dom-module id * @return {DomModule!} dom-module instance */ function findModule(id) { return modules[id] || lcModules[id.toLowerCase()]; } function styleOutsideTemplateCheck(inst) { if (inst.querySelector('style')) { console.warn('dom-module %s has style outside template', inst.id); } } /** * The `dom-module` element registers the dom it contains to the name given * by the module's id attribute. It provides a unified database of dom * accessible via its static `import` API. * * A key use case of `dom-module` is for providing custom element `<template>`s * via HTML imports that are parsed by the native HTML parser, that can be * relocated during a bundling pass and still looked up by `id`. * * Example: * * <dom-module id="foo"> * <img src="stuff.png"> * </dom-module> * * Then in code in some other location that cannot access the dom-module above * * let img = customElements.get('dom-module').import('foo', 'img'); * * @customElement * @extends HTMLElement * @summary Custom element that provides a registry of relocatable DOM content * by `id` that is agnostic to bundling. * @unrestricted */ export class DomModule extends HTMLElement { /** @override */ static get observedAttributes() { return ['id']; } /** * Retrieves the element specified by the css `selector` in the module * registered by `id`. For example, this.import('foo', 'img'); * @param {string} id The id of the dom-module in which to search. * @param {string=} selector The css selector by which to find the element. * @return {Element} Returns the element which matches `selector` in the * module registered at the specified `id`. * * @export * @nocollapse Referred to indirectly in style-gather.js */ static import(id, selector) { if (id) { let m = findModule(id); if (m && selector) { return m.querySelector(selector); } return m; } return null; } /* eslint-disable no-unused-vars */ /** * @param {string} name Name of attribute. * @param {?string} old Old value of attribute. * @param {?string} value Current value of attribute. * @param {?string} namespace Attribute namespace. * @return {void} * @override */ attributeChangedCallback(name, old, value, namespace) { if (old !== value) { this.register(); } } /* eslint-enable no-unused-args */ /** * The absolute URL of the original location of this `dom-module`. * * This value will differ from this element's `ownerDocument` in the * following ways: * - Takes into account any `assetpath` attribute added during bundling * to indicate the original location relative to the bundled location * - Uses the HTMLImports polyfill's `importForElement` API to ensure * the path is relative to the import document's location since * `ownerDocument` is not currently polyfilled */ get assetpath() { // Don't override existing assetpath. if (!this.__assetpath) { // note: assetpath set via an attribute must be relative to this // element's location; accommodate polyfilled HTMLImports const owner = window.HTMLImports && HTMLImports.importForElement ? HTMLImports.importForElement(this) || document : this.ownerDocument; const url = resolveUrl( this.getAttribute('assetpath') || '', owner.baseURI); this.__assetpath = pathFromUrl(url); } return this.__assetpath; } /** * Registers the dom-module at a given id. This method should only be called * when a dom-module is imperatively created. For * example, `document.createElement('dom-module').register('foo')`. * @param {string=} id The id at which to register the dom-module. * @return {void} */ register(id) { id = id || this.id; if (id) { // Under strictTemplatePolicy, reject and null out any re-registered // dom-module since it is ambiguous whether first-in or last-in is trusted if (strictTemplatePolicy && findModule(id) !== undefined) { setModule(id, null); throw new Error(`strictTemplatePolicy: dom-module ${id} re-registered`); } this.id = id; setModule(id, this); styleOutsideTemplateCheck(this); } } } DomModule.prototype['modules'] = modules; customElements.define('dom-module', DomModule);