@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
275 lines (253 loc) • 9.07 kB
JavaScript
/**
@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
*/
/**
* Module with utilities for collection CSS text from `<templates>`, external
* stylesheets, and `dom-module`s.
*
* @summary Module with utilities for collection CSS text from various sources.
*/
import { DomModule } from '../elements/dom-module.js';
import { resolveCss } from './resolve-url.js';
const MODULE_STYLE_LINK_SELECTOR = 'link[rel=import][type~=css]';
const INCLUDE_ATTR = 'include';
const SHADY_UNSCOPED_ATTR = 'shady-unscoped';
/**
* @param {string} moduleId .
* @return {?DomModule} .
*/
function importModule(moduleId) {
return /** @type {?DomModule} */(DomModule.import(moduleId));
}
function styleForImport(importDoc) {
// NOTE: polyfill affordance.
// under the HTMLImports polyfill, there will be no 'body',
// but the import pseudo-doc can be used directly.
let container = importDoc.body ? importDoc.body : importDoc;
const importCss = resolveCss(container.textContent,
importDoc.baseURI);
const style = document.createElement('style');
style.textContent = importCss;
return style;
}
/** @typedef {{assetpath: string}} */
let templateWithAssetPath; // eslint-disable-line no-unused-vars
/**
* Returns a list of <style> elements in a space-separated list of `dom-module`s.
*
* @function
* @param {string} moduleIds List of dom-module id's within which to
* search for css.
* @return {!Array<!HTMLStyleElement>} Array of contained <style> elements
*/
export function stylesFromModules(moduleIds) {
const modules = moduleIds.trim().split(/\s+/);
const styles = [];
for (let i=0; i < modules.length; i++) {
styles.push(...stylesFromModule(modules[i]));
}
return styles;
}
/**
* Returns a list of <style> elements in a given `dom-module`.
* Styles in a `dom-module` can come either from `<style>`s within the
* first `<template>`, or else from one or more
* `<link rel="import" type="css">` links outside the template.
*
* @param {string} moduleId dom-module id to gather styles from
* @return {!Array<!HTMLStyleElement>} Array of contained styles.
*/
export function stylesFromModule(moduleId) {
const m = importModule(moduleId);
if (!m) {
console.warn('Could not find style data in module named', moduleId);
return [];
}
if (m._styles === undefined) {
const styles = [];
// module imports: <link rel="import" type="css">
styles.push(..._stylesFromModuleImports(m));
// include css from the first template in the module
const template = /** @type {?HTMLTemplateElement} */(
m.querySelector('template'));
if (template) {
styles.push(...stylesFromTemplate(template,
/** @type {templateWithAssetPath} */(m).assetpath));
}
m._styles = styles;
}
return m._styles;
}
/**
* Returns the `<style>` elements within a given template.
*
* @param {!HTMLTemplateElement} template Template to gather styles from
* @param {string=} baseURI baseURI for style content
* @return {!Array<!HTMLStyleElement>} Array of styles
*/
export function stylesFromTemplate(template, baseURI) {
if (!template._styles) {
const styles = [];
// if element is a template, get content from its .content
const e$ = template.content.querySelectorAll('style');
for (let i=0; i < e$.length; i++) {
let e = e$[i];
// support style sharing by allowing styles to "include"
// other dom-modules that contain styling
let include = e.getAttribute(INCLUDE_ATTR);
if (include) {
styles.push(...stylesFromModules(include).filter(function(item, index, self) {
return self.indexOf(item) === index;
}));
}
if (baseURI) {
e.textContent =
resolveCss(e.textContent, /** @type {string} */ (baseURI));
}
styles.push(e);
}
template._styles = styles;
}
return template._styles;
}
/**
* Returns a list of <style> elements from stylesheets loaded via `<link rel="import" type="css">` links within the specified `dom-module`.
*
* @param {string} moduleId Id of `dom-module` to gather CSS from
* @return {!Array<!HTMLStyleElement>} Array of contained styles.
*/
export function stylesFromModuleImports(moduleId) {
let m = importModule(moduleId);
return m ? _stylesFromModuleImports(m) : [];
}
/**
* @param {!HTMLElement} module dom-module element that could contain `<link rel="import" type="css">` styles
* @return {!Array<!HTMLStyleElement>} Array of contained styles
*/
function _stylesFromModuleImports(module) {
const styles = [];
const p$ = module.querySelectorAll(MODULE_STYLE_LINK_SELECTOR);
for (let i=0; i < p$.length; i++) {
let p = p$[i];
if (p.import) {
const importDoc = p.import;
const unscoped = p.hasAttribute(SHADY_UNSCOPED_ATTR);
if (unscoped && !importDoc._unscopedStyle) {
const style = styleForImport(importDoc);
style.setAttribute(SHADY_UNSCOPED_ATTR, '');
importDoc._unscopedStyle = style;
} else if (!importDoc._style) {
importDoc._style = styleForImport(importDoc);
}
styles.push(unscoped ? importDoc._unscopedStyle : importDoc._style);
}
}
return styles;
}
/**
*
* Returns CSS text of styles in a space-separated list of `dom-module`s.
* Note: This method is deprecated, use `stylesFromModules` instead.
*
* @deprecated
* @param {string} moduleIds List of dom-module id's within which to
* search for css.
* @return {string} Concatenated CSS content from specified `dom-module`s
*/
export function cssFromModules(moduleIds) {
let modules = moduleIds.trim().split(/\s+/);
let cssText = '';
for (let i=0; i < modules.length; i++) {
cssText += cssFromModule(modules[i]);
}
return cssText;
}
/**
* Returns CSS text of styles in a given `dom-module`. CSS in a `dom-module`
* can come either from `<style>`s within the first `<template>`, or else
* from one or more `<link rel="import" type="css">` links outside the
* template.
*
* Any `<styles>` processed are removed from their original location.
* Note: This method is deprecated, use `styleFromModule` instead.
*
* @deprecated
* @param {string} moduleId dom-module id to gather styles from
* @return {string} Concatenated CSS content from specified `dom-module`
*/
export function cssFromModule(moduleId) {
let m = importModule(moduleId);
if (m && m._cssText === undefined) {
// module imports: <link rel="import" type="css">
let cssText = _cssFromModuleImports(m);
// include css from the first template in the module
let t = /** @type {?HTMLTemplateElement} */(m.querySelector('template'));
if (t) {
cssText += cssFromTemplate(t,
/** @type {templateWithAssetPath} */(m).assetpath);
}
m._cssText = cssText || null;
}
if (!m) {
console.warn('Could not find style data in module named', moduleId);
}
return m && m._cssText || '';
}
/**
* Returns CSS text of `<styles>` within a given template.
*
* Any `<styles>` processed are removed from their original location.
* Note: This method is deprecated, use `styleFromTemplate` instead.
*
* @deprecated
* @param {!HTMLTemplateElement} template Template to gather styles from
* @param {string} baseURI Base URI to resolve the URL against
* @return {string} Concatenated CSS content from specified template
*/
export function cssFromTemplate(template, baseURI) {
let cssText = '';
const e$ = stylesFromTemplate(template, baseURI);
// if element is a template, get content from its .content
for (let i=0; i < e$.length; i++) {
let e = e$[i];
if (e.parentNode) {
e.parentNode.removeChild(e);
}
cssText += e.textContent;
}
return cssText;
}
/**
* Returns CSS text from stylesheets loaded via `<link rel="import" type="css">`
* links within the specified `dom-module`.
*
* Note: This method is deprecated, use `stylesFromModuleImports` instead.
*
* @deprecated
*
* @param {string} moduleId Id of `dom-module` to gather CSS from
* @return {string} Concatenated CSS content from links in specified `dom-module`
*/
export function cssFromModuleImports(moduleId) {
let m = importModule(moduleId);
return m ? _cssFromModuleImports(m) : '';
}
/**
* @deprecated
* @param {!HTMLElement} module dom-module element that could contain `<link rel="import" type="css">` styles
* @return {string} Concatenated CSS content from links in the dom-module
*/
function _cssFromModuleImports(module) {
let cssText = '';
let styles = _stylesFromModuleImports(module);
for (let i=0; i < styles.length; i++) {
cssText += styles[i].textContent;
}
return cssText;
}