| Index: pkg/shadow_dom/lib/shadow_dom.debug.js
|
| diff --git a/pkg/shadow_dom/lib/shadow_dom.debug.js b/pkg/shadow_dom/lib/shadow_dom.debug.js
|
| index 91f2c301dc6a232c377d4ebb7286a3cd09fc2721..158c3d186e5f09b49ee9adfe118f96c0949b1776 100644
|
| --- a/pkg/shadow_dom/lib/shadow_dom.debug.js
|
| +++ b/pkg/shadow_dom/lib/shadow_dom.debug.js
|
| @@ -1375,9 +1375,7 @@ if (!HTMLElement.prototype.createShadowRoot
|
| * license that can be found in the LICENSE file.
|
| */
|
|
|
| -// If WeakMap is not available, the association is stored as an expando property on the "key".
|
| -// TODO(arv): WeakMap does not allow for Node etc to be keys in Firefox
|
| -if (typeof WeakMap === 'undefined' || navigator.userAgent.indexOf('Firefox/') > -1) {
|
| +if (typeof WeakMap === 'undefined') {
|
| (function() {
|
| var defineProperty = Object.defineProperty;
|
| var counter = Date.now() % 1e9;
|
| @@ -5460,6 +5458,19 @@ var Platform = {};
|
|
|
| x-foo [pseudo=x-special] { ... }
|
|
|
| + * ::part(): These rules are converted to rules that take advantage of the
|
| + part attribute. For example, a shadowRoot like this inside an x-foo
|
| +
|
| + <div part="special">Special</div>
|
| +
|
| + with a rule like this:
|
| +
|
| + x-foo::part(special) { ... }
|
| +
|
| + becomes:
|
| +
|
| + x-foo [part=special] { ... }
|
| +
|
| Unaddressed ShadowDOM styling features:
|
|
|
| * upper/lower bound encapsulation: Styles which are defined outside a
|
| @@ -5511,27 +5522,37 @@ var ShadowCSS = {
|
| // Shim styles for a given root associated with a name and extendsName
|
| // 1. cache root styles by name
|
| // 2. optionally tag root nodes with scope name
|
| - // 3. shim polyfill directives /* @polyfill */
|
| + // 3. shim polyfill directives /* @polyfill */ and /* @polyfill-rule */
|
| // 4. shim @host and scoping
|
| shimStyling: function(root, name, extendsName) {
|
| + var typeExtension = this.isTypeExtension(extendsName);
|
| + // use caching to make working with styles nodes easier and to facilitate
|
| + // lookup of extendee
|
| + var def = this.registerDefinition(root, name, extendsName);
|
| + // find styles and apply shimming...
|
| + if (this.strictStyling) {
|
| + this.applyScopeToContent(root, name);
|
| + }
|
| + // insert @polyfill and @polyfill-rule rules into style elements
|
| + // scoping process takes care of shimming these
|
| + this.insertPolyfillDirectives(def.rootStyles);
|
| + this.insertPolyfillRules(def.rootStyles);
|
| + var cssText = this.stylesToShimmedCssText(def.scopeStyles, name,
|
| + typeExtension);
|
| + // note: we only need to do rootStyles since these are unscoped.
|
| + cssText += this.extractPolyfillUnscopedRules(def.rootStyles);
|
| + // provide shimmedStyle for user extensibility
|
| + def.shimmedStyle = cssTextToStyle(cssText);
|
| if (root) {
|
| - // use caching to make working with styles nodes easier and to facilitate
|
| - // lookup of extendee
|
| - var def = this.registerDefinition(root, name, extendsName);
|
| - // find styles and apply shimming...
|
| - if (this.strictStyling) {
|
| - this.applyScopeToContent(root, name);
|
| - }
|
| - this.shimPolyfillDirectives(def.rootStyles, name);
|
| - this.applyShimming(def.scopeStyles, name);
|
| + root.shimmedStyle = def.shimmedStyle;
|
| }
|
| - },
|
| - // Shim styles to be placed inside a shadowRoot.
|
| - // 1. shim polyfill directives /* @polyfill */
|
| - // 2. shim @host and scoping
|
| - shimShadowDOMStyling: function(styles, name) {
|
| - this.shimPolyfillDirectives(styles, name);
|
| - this.applyShimming(styles, name);
|
| + // remove existing style elements
|
| + for (var i=0, l=def.rootStyles.length, s; (i<l) && (s=def.rootStyles[i]);
|
| + i++) {
|
| + s.parentNode.removeChild(s);
|
| + }
|
| + // add style to document
|
| + addCssToDocument(cssText);
|
| },
|
| registerDefinition: function(root, name, extendsName) {
|
| var def = this.registry[name] = {
|
| @@ -5539,16 +5560,19 @@ var ShadowCSS = {
|
| name: name,
|
| extendsName: extendsName
|
| }
|
| - var styles = root.querySelectorAll('style');
|
| + var styles = root ? root.querySelectorAll('style') : [];
|
| styles = styles ? Array.prototype.slice.call(styles, 0) : [];
|
| def.rootStyles = styles;
|
| def.scopeStyles = def.rootStyles;
|
| var extendee = this.registry[def.extendsName];
|
| - if (extendee) {
|
| - def.scopeStyles = def.scopeStyles.concat(extendee.scopeStyles);
|
| + if (extendee && (!root || root.querySelector('shadow'))) {
|
| + def.scopeStyles = extendee.scopeStyles.concat(def.scopeStyles);
|
| }
|
| return def;
|
| },
|
| + isTypeExtension: function(extendsName) {
|
| + return extendsName && extendsName.indexOf('-') < 0;
|
| + },
|
| applyScopeToContent: function(root, name) {
|
| if (root) {
|
| // add the name attribute to each node in root.
|
| @@ -5570,78 +5594,123 @@ var ShadowCSS = {
|
| *
|
| * For example, we convert this rule:
|
| *
|
| - * (comment start) @polyfill @host g-menu-item (comment end)
|
| - * shadow::-webkit-distributed(g-menu-item) {
|
| + * (comment start) @polyfill :host menu-item (comment end)
|
| + * shadow::-webkit-distributed(menu-item) {
|
| + *
|
| + * to this:
|
| + *
|
| + * scopeName menu-item {
|
| + *
|
| + **/
|
| + insertPolyfillDirectives: function(styles) {
|
| + if (styles) {
|
| + Array.prototype.forEach.call(styles, function(s) {
|
| + s.textContent = this.insertPolyfillDirectivesInCssText(s.textContent);
|
| + }, this);
|
| + }
|
| + },
|
| + insertPolyfillDirectivesInCssText: function(cssText) {
|
| + return cssText.replace(cssPolyfillCommentRe, function(match, p1) {
|
| + // remove end comment delimiter and add block start
|
| + return p1.slice(0, -2) + '{';
|
| + });
|
| + },
|
| + /*
|
| + * Process styles to add rules which will only apply under the polyfill
|
| + *
|
| + * For example, we convert this rule:
|
| + *
|
| + * (comment start) @polyfill-rule :host menu-item {
|
| + * ... } (comment end)
|
| + *
|
| + * to this:
|
| + *
|
| + * scopeName menu-item {...}
|
| + *
|
| + **/
|
| + insertPolyfillRules: function(styles) {
|
| + if (styles) {
|
| + Array.prototype.forEach.call(styles, function(s) {
|
| + s.textContent = this.insertPolyfillRulesInCssText(s.textContent);
|
| + }, this);
|
| + }
|
| + },
|
| + insertPolyfillRulesInCssText: function(cssText) {
|
| + return cssText.replace(cssPolyfillRuleCommentRe, function(match, p1) {
|
| + // remove end comment delimiter
|
| + return p1.slice(0, -1);
|
| + });
|
| + },
|
| + /*
|
| + * Process styles to add rules which will only apply under the polyfill
|
| + * and do not process via CSSOM. (CSSOM is destructive to rules on rare
|
| + * occasions, e.g. -webkit-calc on Safari.)
|
| + * For example, we convert this rule:
|
| + *
|
| + * (comment start) @polyfill-unscoped-rule menu-item {
|
| + * ... } (comment end)
|
| *
|
| * to this:
|
| *
|
| - * scopeName g-menu-item {
|
| + * menu-item {...}
|
| *
|
| **/
|
| - shimPolyfillDirectives: function(styles, name) {
|
| + extractPolyfillUnscopedRules: function(styles) {
|
| + var cssText = '';
|
| if (styles) {
|
| Array.prototype.forEach.call(styles, function(s) {
|
| - s.textContent = this.convertPolyfillDirectives(s.textContent, name);
|
| + cssText += this.extractPolyfillUnscopedRulesFromCssText(
|
| + s.textContent) + '\n\n';
|
| }, this);
|
| }
|
| + return cssText;
|
| },
|
| - convertPolyfillDirectives: function(cssText, name) {
|
| - var r = '', l = 0, matches, selector;
|
| - while (matches = cssPolyfillCommentRe.exec(cssText)) {
|
| - r += cssText.substring(l, matches.index);
|
| - // remove end comment delimiter (*/)
|
| - selector = matches[1].slice(0, -2).replace(hostRe, name);
|
| - r += this.scopeSelector(selector, name) + '{';
|
| - l = cssPolyfillCommentRe.lastIndex;
|
| - }
|
| - r += cssText.substring(l, cssText.length);
|
| + extractPolyfillUnscopedRulesFromCssText: function(cssText) {
|
| + var r = '', matches;
|
| + while (matches = cssPolyfillUnscopedRuleCommentRe.exec(cssText)) {
|
| + r += matches[1].slice(0, -1) + '\n\n';
|
| + }
|
| return r;
|
| },
|
| // apply @host and scope shimming
|
| - applyShimming: function(styles, name) {
|
| - var cssText = this.shimAtHost(styles, name);
|
| - cssText += this.shimScoping(styles, name);
|
| - addCssToDocument(cssText);
|
| + stylesToShimmedCssText: function(styles, name, typeExtension) {
|
| + return this.shimAtHost(styles, name, typeExtension) +
|
| + this.shimScoping(styles, name, typeExtension);
|
| },
|
| // form: @host { .foo { declarations } }
|
| // becomes: scopeName.foo { declarations }
|
| - shimAtHost: function(styles, name) {
|
| + shimAtHost: function(styles, name, typeExtension) {
|
| if (styles) {
|
| - return this.convertAtHostStyles(styles, name);
|
| + return this.convertAtHostStyles(styles, name, typeExtension);
|
| }
|
| },
|
| - convertAtHostStyles: function(styles, name) {
|
| - var cssText = stylesToCssText(styles);
|
| - var r = '', l=0, matches;
|
| - while (matches = hostRuleRe.exec(cssText)) {
|
| - r += cssText.substring(l, matches.index);
|
| - r += this.scopeHostCss(matches[1], name);
|
| - l = hostRuleRe.lastIndex;
|
| - }
|
| - r += cssText.substring(l, cssText.length);
|
| - var re = new RegExp('^' + name + selectorReSuffix, 'm');
|
| - var cssText = rulesToCss(this.findAtHostRules(cssToRules(r),
|
| - re));
|
| + convertAtHostStyles: function(styles, name, typeExtension) {
|
| + var cssText = stylesToCssText(styles), self = this;
|
| + cssText = cssText.replace(hostRuleRe, function(m, p1) {
|
| + return self.scopeHostCss(p1, name, typeExtension);
|
| + });
|
| + cssText = rulesToCss(this.findAtHostRules(cssToRules(cssText),
|
| + new RegExp('^' + name + selectorReSuffix, 'm')));
|
| return cssText;
|
| },
|
| - scopeHostCss: function(cssText, name) {
|
| - var r = '', matches;
|
| - while (matches = selectorRe.exec(cssText)) {
|
| - r += this.scopeHostSelector(matches[1], name) +' ' + matches[2] + '\n\t';
|
| - }
|
| - return r;
|
| + scopeHostCss: function(cssText, name, typeExtension) {
|
| + var self = this;
|
| + return cssText.replace(selectorRe, function(m, p1, p2) {
|
| + return self.scopeHostSelector(p1, name, typeExtension) + ' ' + p2 + '\n\t';
|
| + });
|
| },
|
| // supports scopig by name and [is=name] syntax
|
| - scopeHostSelector: function(selector, name) {
|
| + scopeHostSelector: function(selector, name, typeExtension) {
|
| var r = [], parts = selector.split(','), is = '[is=' + name + ']';
|
| parts.forEach(function(p) {
|
| p = p.trim();
|
| // selector: *|:scope -> name
|
| if (p.match(hostElementRe)) {
|
| - p = p.replace(hostElementRe, name + '$1$3, ' + is + '$1$3');
|
| - // selector: .foo -> name.foo, [bar] -> name[bar]
|
| + p = p.replace(hostElementRe, typeExtension ? is + '$1$3' :
|
| + name + '$1$3');
|
| + // selector: .foo -> name.foo (OR) [bar] -> name[bar]
|
| } else if (p.match(hostFixableRe)) {
|
| - p = name + p + ', ' + is + p;
|
| + p = typeExtension ? is + p : name + p;
|
| }
|
| r.push(p);
|
| }, this);
|
| @@ -5667,32 +5736,57 @@ var ShadowCSS = {
|
| *
|
| * scopeName .foo { ... }
|
| */
|
| - shimScoping: function(styles, name) {
|
| + shimScoping: function(styles, name, typeExtension) {
|
| if (styles) {
|
| - return this.convertScopedStyles(styles, name);
|
| + return this.convertScopedStyles(styles, name, typeExtension);
|
| }
|
| },
|
| - convertScopedStyles: function(styles, name) {
|
| - Array.prototype.forEach.call(styles, function(s) {
|
| - if (s.parentNode) {
|
| - s.parentNode.removeChild(s);
|
| - }
|
| - });
|
| + convertScopedStyles: function(styles, name, typeExtension) {
|
| var cssText = stylesToCssText(styles).replace(hostRuleRe, '');
|
| + cssText = this.insertPolyfillHostInCssText(cssText);
|
| + cssText = this.convertColonHost(cssText);
|
| cssText = this.convertPseudos(cssText);
|
| + cssText = this.convertParts(cssText);
|
| + cssText = this.convertCombinators(cssText);
|
| var rules = cssToRules(cssText);
|
| - cssText = this.scopeRules(rules, name);
|
| + cssText = this.scopeRules(rules, name, typeExtension);
|
| return cssText;
|
| },
|
| convertPseudos: function(cssText) {
|
| return cssText.replace(cssPseudoRe, ' [pseudo=$1]');
|
| },
|
| + convertParts: function(cssText) {
|
| + return cssText.replace(cssPartRe, ' [part=$1]');
|
| + },
|
| + /*
|
| + * convert a rule like :host(.foo) > .bar { }
|
| + *
|
| + * to
|
| + *
|
| + * scopeName.foo > .bar, .foo scopeName > .bar { }
|
| + * TODO(sorvell): file bug since native impl does not do the former yet.
|
| + * http://jsbin.com/OganOCI/2/edit
|
| + */
|
| + convertColonHost: function(cssText) {
|
| + // p1 = :host, p2 = contents of (), p3 rest of rule
|
| + return cssText.replace(cssColonHostRe, function(m, p1, p2, p3) {
|
| + return p2 ? polyfillHostNoCombinator + p2 + p3 + ', '
|
| + + p2 + ' ' + p1 + p3 :
|
| + p1 + p3;
|
| + });
|
| + },
|
| + /*
|
| + * Convert ^ and ^^ combinators by replacing with space.
|
| + */
|
| + convertCombinators: function(cssText) {
|
| + return cssText.replace('^^', ' ').replace('^', ' ');
|
| + },
|
| // change a selector like 'div' to 'name div'
|
| - scopeRules: function(cssRules, name) {
|
| + scopeRules: function(cssRules, name, typeExtension) {
|
| var cssText = '';
|
| Array.prototype.forEach.call(cssRules, function(rule) {
|
| if (rule.selectorText && (rule.style && rule.style.cssText)) {
|
| - cssText += this.scopeSelector(rule.selectorText, name,
|
| + cssText += this.scopeSelector(rule.selectorText, name, typeExtension,
|
| this.strictStyling) + ' {\n\t';
|
| cssText += this.propertiesFromRule(rule) + '\n}\n\n';
|
| } else if (rule.media) {
|
| @@ -5705,26 +5799,32 @@ var ShadowCSS = {
|
| }, this);
|
| return cssText;
|
| },
|
| - scopeSelector: function(selector, name, strict) {
|
| + scopeSelector: function(selector, name, typeExtension, strict) {
|
| var r = [], parts = selector.split(',');
|
| parts.forEach(function(p) {
|
| p = p.trim();
|
| - if (this.selectorNeedsScoping(p, name)) {
|
| + if (this.selectorNeedsScoping(p, name, typeExtension)) {
|
| p = strict ? this.applyStrictSelectorScope(p, name) :
|
| - this.applySimpleSelectorScope(p, name);
|
| + this.applySimpleSelectorScope(p, name, typeExtension);
|
| }
|
| r.push(p);
|
| }, this);
|
| return r.join(', ');
|
| },
|
| - selectorNeedsScoping: function(selector, name) {
|
| - var matchScope = '(' + name + '|\\[is=' + name + '\\])';
|
| - var re = new RegExp('^' + matchScope + selectorReSuffix, 'm');
|
| + selectorNeedsScoping: function(selector, name, typeExtension) {
|
| + var matchScope = typeExtension ? name : '\\[is=' + name + '\\]';
|
| + var re = new RegExp('^(' + matchScope + ')' + selectorReSuffix, 'm');
|
| return !selector.match(re);
|
| },
|
| // scope via name and [is=name]
|
| - applySimpleSelectorScope: function(selector, name) {
|
| - return name + ' ' + selector + ', ' + '[is=' + name + '] ' + selector;
|
| + applySimpleSelectorScope: function(selector, name, typeExtension) {
|
| + var scoper = typeExtension ? '[is=' + name + ']' : name;
|
| + if (selector.match(polyfillHostRe)) {
|
| + selector = selector.replace(polyfillHostNoCombinator, scoper);
|
| + return selector.replace(polyfillHostRe, scoper + ' ');
|
| + } else {
|
| + return scoper + ' ' + selector;
|
| + }
|
| },
|
| // return a selector with [name] suffix on each simple selector
|
| // e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name]
|
| @@ -5735,7 +5835,8 @@ var ShadowCSS = {
|
| splits.forEach(function(sep) {
|
| var parts = scoped.split(sep);
|
| scoped = parts.map(function(p) {
|
| - var t = p.trim();
|
| + // remove :host since it should be unnecessary
|
| + var t = p.trim().replace(polyfillHostRe, '');
|
| if (t && (splits.indexOf(t) < 0) && (t.indexOf(attrName) < 0)) {
|
| p = t.replace(/([^:]*)(:*)(.*)/, '$1' + attrName + '$2$3')
|
| }
|
| @@ -5744,6 +5845,10 @@ var ShadowCSS = {
|
| });
|
| return scoped;
|
| },
|
| + insertPolyfillHostInCssText: function(selector) {
|
| + return selector.replace(hostRe, polyfillHost).replace(colonHostRe,
|
| + polyfillHost);
|
| + },
|
| propertiesFromRule: function(rule) {
|
| var properties = rule.style.cssText;
|
| // TODO(sorvell): Chrome cssom incorrectly removes quotes from the content
|
| @@ -5762,9 +5867,19 @@ var hostRuleRe = /@host[^{]*{(([^}]*?{[^{]*?}[\s\S]*?)+)}/gim,
|
| hostFixableRe = /^[.\[:]/,
|
| cssCommentRe = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
|
| cssPolyfillCommentRe = /\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^{]*?){/gim,
|
| + cssPolyfillRuleCommentRe = /\/\*\s@polyfill-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim,
|
| + cssPolyfillUnscopedRuleCommentRe = /\/\*\s@polyfill-unscoped-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim,
|
| cssPseudoRe = /::(x-[^\s{,(]*)/gim,
|
| + cssPartRe = /::part\(([^)]*)\)/gim,
|
| + // note: :host pre-processed to -host.
|
| + cssColonHostRe = /(-host)(?:\(([^)]*)\))?([^,{]*)/gim,
|
| selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$',
|
| - hostRe = /@host/gim;
|
| + hostRe = /@host/gim,
|
| + colonHostRe = /\:host/gim,
|
| + polyfillHost = '-host',
|
| + /* host name without combinator */
|
| + polyfillHostNoCombinator = '-host-no-combinator',
|
| + polyfillHostRe = /-host/gim;
|
|
|
| function stylesToCssText(styles, preserveComments) {
|
| var cssText = '';
|
| @@ -5778,9 +5893,14 @@ function stylesToCssText(styles, preserveComments) {
|
| return cssText;
|
| }
|
|
|
| -function cssToRules(cssText) {
|
| +function cssTextToStyle(cssText) {
|
| var style = document.createElement('style');
|
| style.textContent = cssText;
|
| + return style;
|
| +}
|
| +
|
| +function cssToRules(cssText) {
|
| + var style = cssTextToStyle(cssText);
|
| document.head.appendChild(style);
|
| var rules = style.sheet.cssRules;
|
| style.parentNode.removeChild(style);
|
| @@ -5820,70 +5940,5 @@ if (window.ShadowDOMPolyfill) {
|
| scope.ShadowCSS = ShadowCSS;
|
|
|
| })(window.Platform);
|
| -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -(function(scope) {
|
| - // TODO(terry): Remove shimShadowDOMStyling2 until wrap/unwrap from a
|
| - // dart:html Element to a JS DOM node is available.
|
| - /**
|
| - * Given the content of a STYLE tag and the name of a component shim the CSS
|
| - * and return the new scoped CSS to replace the STYLE's content. The content
|
| - * is replaced in Dart's implementation of PolymerElement.
|
| - */
|
| - function shimShadowDOMStyling2(styleContent, name) {
|
| - if (window.ShadowDOMPolyfill) {
|
| - var content = this.convertPolyfillDirectives(styleContent, name);
|
| -
|
| - // applyShimming calls shimAtHost and shipScoping
|
| - // shimAtHost code:
|
| - var r = '', l=0, matches;
|
| - while (matches = hostRuleRe.exec(content)) {
|
| - r += content.substring(l, matches.index);
|
| - r += this.scopeHostCss(matches[1], name);
|
| - l = hostRuleRe.lastIndex;
|
| - }
|
| - r += content.substring(l, content.length);
|
| - var re = new RegExp('^' + name + selectorReSuffix, 'm');
|
| - var atHostCssText = rulesToCss(this.findAtHostRules(cssToRules(r), re));
|
| -
|
| - // shimScoping code:
|
| - // strip comments for easier processing
|
| - content = content.replace(cssCommentRe, '');
|
| -
|
| - content = this.convertPseudos(content);
|
| - var rules = cssToRules(content);
|
| - var cssText = this.scopeRules(rules, name);
|
| -
|
| - return atHostCssText + cssText;
|
| - }
|
| - }
|
| -
|
| - // Minimal copied code from ShadowCSS, that is not exposed in
|
| - // PlatForm.ShadowCSS (local code).
|
| - var hostRuleRe = /@host[^{]*{(([^}]*?{[^{]*?}[\s\S]*?)+)}/gim,
|
| - cssCommentRe = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim,
|
| - selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$';
|
| -
|
| - function cssToRules(cssText) {
|
| - var style = document.createElement('style');
|
| - style.textContent = cssText;
|
| - document.head.appendChild(style);
|
| - var rules = style.sheet.cssRules;
|
| - style.parentNode.removeChild(style);
|
| - return rules;
|
| - }
|
| -
|
| - function rulesToCss(cssRules) {
|
| - for (var i=0, css=[]; i < cssRules.length; i++) {
|
| - css.push(cssRules[i].cssText);
|
| - }
|
| - return css.join('\n\n');
|
| - }
|
| -
|
| - // exports
|
| - scope.ShadowCSS.shimShadowDOMStyling2 = shimShadowDOMStyling2;
|
| -})(window.Platform);
|
|
|
| }
|
|
|