blob: 4921bec93929932c1095050a8ae1f1d16810ece3 [file] [log] [blame]
/**
@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
*/
'use strict';
import ApplyShim from '../src/apply-shim.js';
import templateMap from '../src/template-map.js';
import {getIsExtends, toCssText, elementHasBuiltCss} from '../src/style-util.js';
import * as ApplyShimUtils from '../src/apply-shim-utils.js';
import {getComputedStyleValue, updateNativeProperties} from '../src/common-utils.js';
import {CustomStyleInterfaceInterface} from '../src/custom-style-interface.js'; // eslint-disable-line no-unused-vars
import {nativeCssVariables, nativeShadow, cssBuild} from '../src/style-settings.js';
/** @const {ApplyShim} */
const applyShim = new ApplyShim();
class ApplyShimInterface {
constructor() {
/** @type {?CustomStyleInterfaceInterface} */
this.customStyleInterface = null;
applyShim['invalidCallback'] = ApplyShimUtils.invalidate;
}
ensure() {
if (this.customStyleInterface) {
return;
}
if (window.ShadyCSS.CustomStyleInterface) {
this.customStyleInterface =
/** @type {!CustomStyleInterfaceInterface} */ (
window.ShadyCSS.CustomStyleInterface);
this.customStyleInterface['transformCallback'] = (style) => {
applyShim.transformCustomStyle(style);
};
this.customStyleInterface['validateCallback'] = () => {
requestAnimationFrame(() => {
if (this.customStyleInterface['enqueued']) {
this.flushCustomStyles();
}
});
}
}
}
/**
* @param {!HTMLTemplateElement} template
* @param {string} elementName
*/
prepareTemplate(template, elementName) {
this.ensure();
if (elementHasBuiltCss(template)) {
return;
}
templateMap[elementName] = template;
let ast = applyShim.transformTemplate(template, elementName);
// save original style ast to use for revalidating instances
template['_styleAst'] = ast;
}
flushCustomStyles() {
this.ensure();
if (!this.customStyleInterface) {
return;
}
let styles = this.customStyleInterface['processStyles']();
if (!this.customStyleInterface['enqueued']) {
return;
}
for (let i = 0; i < styles.length; i++ ) {
let cs = styles[i];
let style = this.customStyleInterface['getStyleForCustomStyle'](cs);
if (style) {
applyShim.transformCustomStyle(style);
}
}
this.customStyleInterface['enqueued'] = false;
}
/**
* @param {HTMLElement} element
* @param {Object=} properties
*/
styleSubtree(element, properties) {
this.ensure();
if (properties) {
updateNativeProperties(element, properties);
}
if (element.shadowRoot) {
this.styleElement(element);
let shadowChildren =
/** @type {!ParentNode} */ (element.shadowRoot).children ||
element.shadowRoot.childNodes;
for (let i = 0; i < shadowChildren.length; i++) {
this.styleSubtree(/** @type {HTMLElement} */(shadowChildren[i]));
}
} else {
let children = element.children || element.childNodes;
for (let i = 0; i < children.length; i++) {
this.styleSubtree(/** @type {HTMLElement} */(children[i]));
}
}
}
/**
* @param {HTMLElement} element
*/
styleElement(element) {
this.ensure();
let {is} = getIsExtends(element);
let template = templateMap[is];
if (template && elementHasBuiltCss(template)) {
return;
}
if (template && !ApplyShimUtils.templateIsValid(template)) {
// only revalidate template once
if (!ApplyShimUtils.templateIsValidating(template)) {
this.prepareTemplate(template, is);
ApplyShimUtils.startValidatingTemplate(template);
}
// update this element instance
let root = element.shadowRoot;
if (root) {
let style = /** @type {HTMLStyleElement} */(root.querySelector('style'));
if (style) {
// reuse the template's style ast, it has all the original css text
style['__cssRules'] = template['_styleAst'];
style.textContent = toCssText(template['_styleAst'])
}
}
}
}
/**
* @param {Object=} properties
*/
styleDocument(properties) {
this.ensure();
this.styleSubtree(document.body, properties);
}
}
if (!window.ShadyCSS || !window.ShadyCSS.ScopingShim) {
const applyShimInterface = new ApplyShimInterface();
let CustomStyleInterface = window.ShadyCSS && window.ShadyCSS.CustomStyleInterface;
/** @suppress {duplicate} */
window.ShadyCSS = {
/**
* @param {!HTMLTemplateElement} template
* @param {string} elementName
* @param {string=} elementExtends
*/
prepareTemplate(template, elementName, elementExtends) { // eslint-disable-line no-unused-vars
applyShimInterface.flushCustomStyles();
applyShimInterface.prepareTemplate(template, elementName);
},
/**
* @param {!HTMLTemplateElement} template
* @param {string} elementName
* @param {string=} elementExtends
*/
prepareTemplateStyles(template, elementName, elementExtends) {
window.ShadyCSS.prepareTemplate(template, elementName, elementExtends);
},
/**
* @param {!HTMLTemplateElement} template
* @param {string} elementName
*/
prepareTemplateDom(template, elementName) {}, // eslint-disable-line no-unused-vars
/**
* @param {!HTMLElement} element
* @param {Object=} properties
*/
styleSubtree(element, properties) {
applyShimInterface.flushCustomStyles();
applyShimInterface.styleSubtree(element, properties);
},
/**
* @param {!HTMLElement} element
*/
styleElement(element) {
applyShimInterface.flushCustomStyles();
applyShimInterface.styleElement(element);
},
/**
* @param {Object=} properties
*/
styleDocument(properties) {
applyShimInterface.flushCustomStyles();
applyShimInterface.styleDocument(properties);
},
/**
* @param {Element} element
* @param {string} property
* @return {string}
*/
getComputedStyleValue(element, property) {
return getComputedStyleValue(element, property);
},
flushCustomStyles() {
applyShimInterface.flushCustomStyles();
},
nativeCss: nativeCssVariables,
nativeShadow: nativeShadow,
cssBuild: cssBuild
};
if (CustomStyleInterface) {
window.ShadyCSS.CustomStyleInterface = CustomStyleInterface;
}
}
window.ShadyCSS.ApplyShim = applyShim;