Index: pkg/web_components/lib/platform.concat.js |
diff --git a/pkg/web_components/lib/platform.concat.js b/pkg/web_components/lib/platform.concat.js |
index 0be0a0855658dd28524f3a915c89381299ee6dc0..6fa2b7b4f1830554045bbb76fead092f966584bc 100644 |
--- a/pkg/web_components/lib/platform.concat.js |
+++ b/pkg/web_components/lib/platform.concat.js |
@@ -494,10 +494,6 @@ if (typeof WeakMap === 'undefined') { |
} |
function reset() { |
- resetScheduled = false; |
- if (!resetNeeded) |
- return; |
- |
var objs = toRemove === emptyArray ? [] : toRemove; |
toRemove = objects; |
objects = objs; |
@@ -520,16 +516,26 @@ if (typeof WeakMap === 'undefined') { |
toRemove.length = 0; |
} |
+ function scheduledReset() { |
+ resetScheduled = false; |
+ if (!resetNeeded) |
+ return; |
+ |
+ reset(); |
+ } |
+ |
function scheduleReset() { |
if (resetScheduled) |
return; |
resetNeeded = true; |
resetScheduled = true; |
- runEOM(reset); |
+ runEOM(scheduledReset); |
} |
function callback() { |
+ reset(); |
+ |
var observer; |
for (var id in observers) { |
@@ -539,8 +545,6 @@ if (typeof WeakMap === 'undefined') { |
observer.check_(); |
} |
- |
- scheduleReset(); |
} |
var record = { |
@@ -1921,6 +1925,8 @@ window.ShadowDOMPolyfill = {}; |
enumerable: false, |
writable: true |
}); |
+ // Set it again. Some VMs optimizes objects that are used as prototypes. |
+ wrapperConstructor.prototype = wrapperPrototype; |
} |
function isWrapperFor(wrapperConstructor, nativeConstructor) { |
@@ -1948,9 +1954,9 @@ window.ShadowDOMPolyfill = {}; |
function GeneratedWrapper(node) { |
superWrapperConstructor.call(this, node); |
} |
- GeneratedWrapper.prototype = |
- Object.create(superWrapperConstructor.prototype); |
- GeneratedWrapper.prototype.constructor = GeneratedWrapper; |
+ var p = Object.create(superWrapperConstructor.prototype); |
+ p.constructor = GeneratedWrapper; |
+ GeneratedWrapper.prototype = p; |
return GeneratedWrapper; |
} |
@@ -2524,6 +2530,70 @@ window.ShadowDOMPolyfill = {}; |
})(window.ShadowDOMPolyfill); |
+/** |
+ * Copyright 2014 The Polymer Authors. All rights reserved. |
+ * Use of this source code is goverened by a BSD-style |
+ * license that can be found in the LICENSE file. |
+ */ |
+ |
+(function(scope) { |
+ 'use strict'; |
+ |
+ /** |
+ * A tree scope represents the root of a tree. All nodes in a tree point to |
+ * the same TreeScope object. The tree scope of a node get set the first time |
+ * it is accessed or when a node is added or remove to a tree. |
+ * @constructor |
+ */ |
+ function TreeScope(root, parent) { |
+ this.root = root; |
+ this.parent = parent; |
+ } |
+ |
+ TreeScope.prototype = { |
+ get renderer() { |
+ if (this.root instanceof scope.wrappers.ShadowRoot) { |
+ return scope.getRendererForHost(this.root.host); |
+ } |
+ return null; |
+ }, |
+ |
+ contains: function(treeScope) { |
+ for (; treeScope; treeScope = treeScope.parent) { |
+ if (treeScope === this) |
+ return true; |
+ } |
+ return false; |
+ } |
+ }; |
+ |
+ function setTreeScope(node, treeScope) { |
+ if (node.treeScope_ !== treeScope) { |
+ node.treeScope_ = treeScope; |
+ for (var child = node.firstChild; child; child = child.nextSibling) { |
+ setTreeScope(child, treeScope); |
+ } |
+ } |
+ } |
+ |
+ function getTreeScope(node) { |
+ if (node.treeScope_) |
+ return node.treeScope_; |
+ var parent = node.parentNode; |
+ var treeScope; |
+ if (parent) |
+ treeScope = getTreeScope(parent); |
+ else |
+ treeScope = new TreeScope(node, null); |
+ return node.treeScope_ = treeScope; |
+ } |
+ |
+ scope.TreeScope = TreeScope; |
+ scope.getTreeScope = getTreeScope; |
+ scope.setTreeScope = setTreeScope; |
+ |
+})(window.ShadowDOMPolyfill); |
+ |
// Copyright 2013 The Polymer Authors. All rights reserved. |
// Use of this source code is goverened by a BSD-style |
// license that can be found in the LICENSE file. |
@@ -2532,6 +2602,7 @@ window.ShadowDOMPolyfill = {}; |
'use strict'; |
var forwardMethodsToWrapper = scope.forwardMethodsToWrapper; |
+ var getTreeScope = scope.getTreeScope; |
var mixin = scope.mixin; |
var registerWrapper = scope.registerWrapper; |
var unwrap = scope.unwrap; |
@@ -2686,27 +2757,10 @@ window.ShadowDOMPolyfill = {}; |
return getInsertionParent(node); |
} |
- function rootOfNode(node) { |
- var p; |
- while (p = node.parentNode) { |
- node = p; |
- } |
- return node; |
- } |
- |
function inSameTree(a, b) { |
- return rootOfNode(a) === rootOfNode(b); |
+ return getTreeScope(a) === getTreeScope(b); |
} |
- function enclosedBy(a, b) { |
- if (a === b) |
- return true; |
- if (a instanceof wrappers.ShadowRoot) |
- return enclosedBy(rootOfNode(a.host), b); |
- return false; |
- } |
- |
- |
function dispatchOriginalEvent(originalEvent) { |
// Make sure this event is only dispatched once. |
if (handledEventsTable.get(originalEvent)) |
@@ -2921,12 +2975,12 @@ window.ShadowDOMPolyfill = {}; |
if (eventPath) { |
var index = 0; |
var lastIndex = eventPath.length - 1; |
- var baseRoot = rootOfNode(currentTargetTable.get(this)); |
+ var baseRoot = getTreeScope(currentTargetTable.get(this)); |
for (var i = 0; i <= lastIndex; i++) { |
var currentTarget = eventPath[i].currentTarget; |
- var currentRoot = rootOfNode(currentTarget); |
- if (enclosedBy(baseRoot, currentRoot) && |
+ var currentRoot = getTreeScope(currentTarget); |
+ if (currentRoot.contains(baseRoot) && |
// Make sure we do not add Window to the path. |
(i !== lastIndex || currentTarget instanceof wrappers.Node)) { |
nodeList[index++] = currentTarget; |
@@ -3394,22 +3448,27 @@ window.ShadowDOMPolyfill = {}; |
})(window.ShadowDOMPolyfill); |
-// Copyright 2012 The Polymer Authors. All rights reserved. |
-// Use of this source code is goverened by a BSD-style |
-// license that can be found in the LICENSE file. |
+/** |
+ * Copyright 2012 The Polymer Authors. All rights reserved. |
+ * Use of this source code is goverened by a BSD-style |
+ * license that can be found in the LICENSE file. |
+ */ |
(function(scope) { |
'use strict'; |
var EventTarget = scope.wrappers.EventTarget; |
var NodeList = scope.wrappers.NodeList; |
+ var TreeScope = scope.TreeScope; |
var assert = scope.assert; |
var defineWrapGetter = scope.defineWrapGetter; |
var enqueueMutation = scope.enqueueMutation; |
+ var getTreeScope = scope.getTreeScope; |
var isWrapper = scope.isWrapper; |
var mixin = scope.mixin; |
var registerTransientObservers = scope.registerTransientObservers; |
var registerWrapper = scope.registerWrapper; |
+ var setTreeScope = scope.setTreeScope; |
var unwrap = scope.unwrap; |
var wrap = scope.wrap; |
var wrapIfNeeded = scope.wrapIfNeeded; |
@@ -3526,23 +3585,27 @@ window.ShadowDOMPolyfill = {}; |
} |
// http://dom.spec.whatwg.org/#node-is-inserted |
- function nodeWasAdded(node) { |
+ function nodeWasAdded(node, treeScope) { |
+ setTreeScope(node, treeScope); |
node.nodeIsInserted_(); |
} |
- function nodesWereAdded(nodes) { |
+ function nodesWereAdded(nodes, parent) { |
+ var treeScope = getTreeScope(parent); |
for (var i = 0; i < nodes.length; i++) { |
- nodeWasAdded(nodes[i]); |
+ nodeWasAdded(nodes[i], treeScope); |
} |
} |
// http://dom.spec.whatwg.org/#node-is-removed |
function nodeWasRemoved(node) { |
- // Nothing at this point in time. |
+ setTreeScope(node, new TreeScope(node, null)); |
} |
function nodesWereRemoved(nodes) { |
- // Nothing at this point in time. |
+ for (var i = 0; i < nodes.length; i++) { |
+ nodeWasRemoved(nodes[i]); |
+ } |
} |
function ensureSameOwnerDocument(parent, child) { |
@@ -3660,6 +3723,17 @@ window.ShadowDOMPolyfill = {}; |
return clone; |
} |
+ function contains(self, child) { |
+ if (!child || getTreeScope(self) !== getTreeScope(child)) |
+ return false; |
+ |
+ for (var node = child; node; node = node.parentNode) { |
+ if (node === self) |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
var OriginalNode = window.Node; |
/** |
@@ -3706,6 +3780,8 @@ window.ShadowDOMPolyfill = {}; |
* @private |
*/ |
this.previousSibling_ = undefined; |
+ |
+ this.treeScope_ = undefined; |
} |
var OriginalDocumentFragment = window.DocumentFragment; |
@@ -3794,7 +3870,7 @@ window.ShadowDOMPolyfill = {}; |
previousSibling: previousNode |
}); |
- nodesWereAdded(nodes); |
+ nodesWereAdded(nodes, this); |
return childWrapper; |
}, |
@@ -3926,7 +4002,7 @@ window.ShadowDOMPolyfill = {}; |
}); |
nodeWasRemoved(oldChildWrapper); |
- nodesWereAdded(nodes); |
+ nodesWereAdded(nodes, this); |
return oldChildWrapper; |
}, |
@@ -4018,7 +4094,7 @@ window.ShadowDOMPolyfill = {}; |
}); |
nodesWereRemoved(removedNodes); |
- nodesWereAdded(addedNodes); |
+ nodesWereAdded(addedNodes, this); |
}, |
get childNodes() { |
@@ -4036,18 +4112,7 @@ window.ShadowDOMPolyfill = {}; |
}, |
contains: function(child) { |
- if (!child) |
- return false; |
- |
- child = wrapIfNeeded(child); |
- |
- // TODO(arv): Optimize using ownerDocument etc. |
- if (child === this) |
- return true; |
- var parentNode = child.parentNode; |
- if (!parentNode) |
- return false; |
- return this.contains(parentNode); |
+ return contains(this, wrapIfNeeded(child)); |
}, |
compareDocumentPosition: function(otherNode) { |
@@ -4104,13 +4169,13 @@ window.ShadowDOMPolyfill = {}; |
delete Node.prototype.querySelectorAll; |
Node.prototype = mixin(Object.create(EventTarget.prototype), Node.prototype); |
+ scope.cloneNode = cloneNode; |
scope.nodeWasAdded = nodeWasAdded; |
scope.nodeWasRemoved = nodeWasRemoved; |
scope.nodesWereAdded = nodesWereAdded; |
scope.nodesWereRemoved = nodesWereRemoved; |
scope.snapshotNodeList = snapshotNodeList; |
scope.wrappers.Node = Node; |
- scope.cloneNode = cloneNode; |
})(window.ShadowDOMPolyfill); |
@@ -4682,7 +4747,7 @@ window.ShadowDOMPolyfill = {}; |
}); |
nodesWereRemoved(removedNodes); |
- nodesWereAdded(addedNodes); |
+ nodesWereAdded(addedNodes, this); |
}, |
get outerHTML() { |
@@ -5723,8 +5788,10 @@ window.ShadowDOMPolyfill = {}; |
'use strict'; |
var DocumentFragment = scope.wrappers.DocumentFragment; |
+ var TreeScope = scope.TreeScope; |
var elementFromPoint = scope.elementFromPoint; |
var getInnerHTML = scope.getInnerHTML; |
+ var getTreeScope = scope.getTreeScope; |
var mixin = scope.mixin; |
var rewrap = scope.rewrap; |
var setInnerHTML = scope.setInnerHTML; |
@@ -5743,6 +5810,8 @@ window.ShadowDOMPolyfill = {}; |
// DocumentFragment instance. Override that. |
rewrap(node, this); |
+ this.treeScope_ = new TreeScope(this, getTreeScope(hostWrapper)); |
+ |
var oldShadowRoot = hostWrapper.shadowRoot; |
nextOlderShadowTreeTable.set(this, oldShadowRoot); |
@@ -5798,6 +5867,7 @@ window.ShadowDOMPolyfill = {}; |
var Node = scope.wrappers.Node; |
var ShadowRoot = scope.wrappers.ShadowRoot; |
var assert = scope.assert; |
+ var getTreeScope = scope.getTreeScope; |
var mixin = scope.mixin; |
var oneOf = scope.oneOf; |
var unwrap = scope.unwrap; |
@@ -6017,7 +6087,11 @@ window.ShadowDOMPolyfill = {}; |
// TODO(arv): Order these in document order. That way we do not have to |
// render something twice. |
for (var i = 0; i < pendingDirtyRenderers.length; i++) { |
- pendingDirtyRenderers[i].render(); |
+ var renderer = pendingDirtyRenderers[i]; |
+ var parentRenderer = renderer.parentRenderer; |
+ if (parentRenderer && parentRenderer.dirty) |
+ continue; |
+ renderer.render(); |
} |
pendingDirtyRenderers = []; |
@@ -6043,10 +6117,9 @@ window.ShadowDOMPolyfill = {}; |
} |
function getShadowRootAncestor(node) { |
- for (; node; node = node.parentNode) { |
- if (node instanceof ShadowRoot) |
- return node; |
- } |
+ var root = getTreeScope(node).root; |
+ if (root instanceof ShadowRoot) |
+ return root; |
return null; |
} |
@@ -6163,6 +6236,10 @@ window.ShadowDOMPolyfill = {}; |
this.dirty = false; |
}, |
+ get parentRenderer() { |
+ return getTreeScope(this.host).renderer; |
+ }, |
+ |
invalidate: function() { |
if (!this.dirty) { |
this.dirty = true; |
@@ -6193,8 +6270,7 @@ window.ShadowDOMPolyfill = {}; |
if (isShadowHost(node)) { |
var renderer = getRendererForHost(node); |
- // renderNode.skip = !renderer.dirty; |
- renderer.invalidate(); |
+ renderNode.skip = !renderer.dirty; |
renderer.render(renderNode); |
} else { |
for (var child = node.firstChild; child; child = child.nextSibling) { |
@@ -6593,6 +6669,7 @@ window.ShadowDOMPolyfill = {}; |
var Selection = scope.wrappers.Selection; |
var SelectorsInterface = scope.SelectorsInterface; |
var ShadowRoot = scope.wrappers.ShadowRoot; |
+ var TreeScope = scope.TreeScope; |
var cloneNode = scope.cloneNode; |
var defineWrapGetter = scope.defineWrapGetter; |
var elementFromPoint = scope.elementFromPoint; |
@@ -6611,6 +6688,7 @@ window.ShadowDOMPolyfill = {}; |
function Document(node) { |
Node.call(this, node); |
+ this.treeScope_ = new TreeScope(this, null); |
} |
Document.prototype = Object.create(Node.prototype); |
@@ -7238,55 +7316,68 @@ var ShadowCSS = { |
// 2. optionally tag root nodes with scope name |
// 3. shim polyfill directives /* @polyfill */ and /* @polyfill-rule */ |
// 4. shim :host and scoping |
- shimStyling: function(root, name, extendsName, ownSheet) { |
+ shimStyling: function(root, name, extendsName) { |
+ var scopeStyles = this.prepareRoot(root, name, extendsName); |
var typeExtension = this.isTypeExtension(extendsName); |
+ var scopeSelector = this.makeScopeSelector(name, typeExtension); |
// 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); |
- } |
- var cssText = this.stylesToShimmedCssText(def.rootStyles, def.scopeStyles, |
- name, typeExtension); |
- // provide shimmedStyle for user extensibility |
- def.shimmedStyle = cssTextToStyle(cssText); |
+ var cssText = stylesToCssText(scopeStyles, true); |
+ cssText = this.scopeCssText(cssText, scopeSelector); |
+ // cache shimmed css on root for user extensibility |
if (root) { |
- root.shimmedStyle = def.shimmedStyle; |
- } |
- // remove existing style elements |
- for (var i=0, l=def.rootStyles.length, s; (i<l) && (s=def.rootStyles[i]); |
- i++) { |
- s.parentNode.removeChild(s); |
+ root.shimmedStyle = cssText; |
} |
// add style to document |
- if (ownSheet) { |
- addOwnSheet(cssText, name); |
- } else { |
- addCssToDocument(cssText); |
+ this.addCssToDocument(cssText, name); |
+ }, |
+ /* |
+ * Shim a style element with the given selector. Returns cssText that can |
+ * be included in the document via Platform.ShadowCSS.addCssToDocument(css). |
+ */ |
+ shimStyle: function(style, selector) { |
+ return this.shimCssText(style.textContent, selector); |
+ }, |
+ /* |
+ * Shim some cssText with the given selector. Returns cssText that can |
+ * be included in the document via Platform.ShadowCSS.addCssToDocument(css). |
+ */ |
+ shimCssText: function(cssText, selector) { |
+ cssText = this.insertDirectives(cssText); |
+ return this.scopeCssText(cssText, selector); |
+ }, |
+ makeScopeSelector: function(name, typeExtension) { |
+ if (name) { |
+ return typeExtension ? '[is=' + name + ']' : name; |
} |
+ return ''; |
}, |
- // apply @polyfill rules + :host and scope shimming |
- stylesToShimmedCssText: function(rootStyles, scopeStyles, name, |
- typeExtension) { |
- name = name || ''; |
- // insert @polyfill and @polyfill-rule rules into style elements |
- // scoping process takes care of shimming these |
- this.insertPolyfillDirectives(rootStyles); |
- this.insertPolyfillRules(rootStyles); |
- var cssText = this.shimScoping(scopeStyles, name, typeExtension); |
- // note: we only need to do rootStyles since these are unscoped. |
- cssText += this.extractPolyfillUnscopedRules(rootStyles); |
- return cssText.trim(); |
+ isTypeExtension: function(extendsName) { |
+ return extendsName && extendsName.indexOf('-') < 0; |
+ }, |
+ prepareRoot: function(root, name, extendsName) { |
+ var def = this.registerRoot(root, name, extendsName); |
+ this.replaceTextInStyles(def.rootStyles, this.insertDirectives); |
+ // remove existing style elements |
+ this.removeStyles(root, def.rootStyles); |
+ // apply strict attr |
+ if (this.strictStyling) { |
+ this.applyScopeToContent(root, name); |
+ } |
+ return def.scopeStyles; |
}, |
- registerDefinition: function(root, name, extendsName) { |
+ removeStyles: function(root, styles) { |
+ for (var i=0, l=styles.length, s; (i<l) && (s=styles[i]); i++) { |
+ s.parentNode.removeChild(s); |
+ } |
+ }, |
+ registerRoot: function(root, name, extendsName) { |
var def = this.registry[name] = { |
root: root, |
name: name, |
extendsName: extendsName |
} |
- var styles = root ? root.querySelectorAll('style') : []; |
- styles = styles ? Array.prototype.slice.call(styles, 0) : []; |
+ var styles = this.findStyles(root); |
def.rootStyles = styles; |
def.scopeStyles = def.rootStyles; |
var extendee = this.registry[def.extendsName]; |
@@ -7295,8 +7386,14 @@ var ShadowCSS = { |
} |
return def; |
}, |
- isTypeExtension: function(extendsName) { |
- return extendsName && extendsName.indexOf('-') < 0; |
+ findStyles: function(root) { |
+ if (!root) { |
+ return []; |
+ } |
+ var styles = root.querySelectorAll('style'); |
+ return Array.prototype.filter.call(styles, function(s) { |
+ return !s.hasAttribute(NO_SHIM_ATTRIBUTE); |
+ }); |
}, |
applyScopeToContent: function(root, name) { |
if (root) { |
@@ -7313,89 +7410,59 @@ var ShadowCSS = { |
this); |
} |
}, |
+ insertDirectives: function(cssText) { |
+ cssText = this.insertPolyfillDirectivesInCssText(cssText); |
+ return this.insertPolyfillRulesInCssText(cssText); |
+ }, |
/* |
* Process styles to convert native ShadowDOM rules that will trip |
- * up the css parser; we rely on decorating the stylesheet with comments. |
+ * up the css parser; we rely on decorating the stylesheet with inert rules. |
* |
* For example, we convert this rule: |
* |
- * (comment start) @polyfill :host menu-item (comment end) |
- * shadow::-webkit-distributed(menu-item) { |
+ * polyfill-next-selector { content: ':host menu-item'; } |
+ * ::content 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) { |
+ // TODO(sorvell): remove either content or comment |
+ cssText = cssText.replace(cssCommentNextSelectorRe, function(match, p1) { |
// remove end comment delimiter and add block start |
return p1.slice(0, -2) + '{'; |
}); |
+ return cssText.replace(cssContentNextSelectorRe, function(match, p1) { |
+ return p1 + ' {'; |
+ }); |
}, |
/* |
* 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) |
+ * polyfill-rule { |
+ * content: ':host menu-item'; |
+ * ... |
+ * } |
* |
* 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) { |
+ // TODO(sorvell): remove either content or comment |
+ cssText = cssText.replace(cssCommentRuleRe, 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: |
- * |
- * menu-item {...} |
- * |
- **/ |
- extractPolyfillUnscopedRules: function(styles) { |
- var cssText = ''; |
- if (styles) { |
- Array.prototype.forEach.call(styles, function(s) { |
- cssText += this.extractPolyfillUnscopedRulesFromCssText( |
- s.textContent) + '\n\n'; |
- }, this); |
- } |
- return cssText; |
- }, |
- extractPolyfillUnscopedRulesFromCssText: function(cssText) { |
- var r = '', matches; |
- while (matches = cssPolyfillUnscopedRuleCommentRe.exec(cssText)) { |
- r += matches[1].slice(0, -1) + '\n\n'; |
- } |
- return r; |
+ return cssText.replace(cssContentRuleRe, function(match, p1, p2, p3) { |
+ var rule = match.replace(p1, '').replace(p2, ''); |
+ return p3 + rule; |
+ }); |
}, |
/* Ensure styles are scoped. Pseudo-scoping takes a rule like: |
* |
@@ -7405,26 +7472,46 @@ var ShadowCSS = { |
* |
* scopeName .foo { ... } |
*/ |
- shimScoping: function(styles, name, typeExtension) { |
- if (styles) { |
- return this.convertScopedStyles(styles, name, typeExtension); |
- } |
- }, |
- convertScopedStyles: function(styles, name, typeExtension) { |
- var cssText = stylesToCssText(styles); |
+ scopeCssText: function(cssText, scopeSelector) { |
+ var unscoped = this.extractUnscopedRulesFromCssText(cssText); |
cssText = this.insertPolyfillHostInCssText(cssText); |
cssText = this.convertColonHost(cssText); |
cssText = this.convertColonAncestor(cssText); |
cssText = this.convertCombinators(cssText); |
- if (name) { |
+ if (scopeSelector) { |
var self = this, cssText; |
- |
withCssRules(cssText, function(rules) { |
- cssText = self.scopeRules(rules, name, typeExtension); |
+ cssText = self.scopeRules(rules, scopeSelector); |
}); |
} |
- return cssText; |
+ cssText = cssText + '\n' + unscoped; |
+ return cssText.trim(); |
+ }, |
+ /* |
+ * 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: |
+ * |
+ * menu-item {...} |
+ * |
+ **/ |
+ extractUnscopedRulesFromCssText: function(cssText) { |
+ // TODO(sorvell): remove either content or comment |
+ var r = '', m; |
+ while (m = cssCommentUnscopedRuleRe.exec(cssText)) { |
+ r += m[1].slice(0, -1) + '\n\n'; |
+ } |
+ while (m = cssContentUnscopedRuleRe.exec(cssText)) { |
+ r += m[0].replace(m[2], '').replace(m[1], m[3]) + '\n\n'; |
+ } |
+ return r; |
}, |
/* |
* convert a rule like :host(.foo) > .bar { } |
@@ -7489,17 +7576,17 @@ var ShadowCSS = { |
return cssText.replace(/\^\^/g, ' ').replace(/\^/g, ' '); |
}, |
// change a selector like 'div' to 'name div' |
- scopeRules: function(cssRules, name, typeExtension) { |
+ scopeRules: function(cssRules, scopeSelector) { |
var cssText = ''; |
if (cssRules) { |
Array.prototype.forEach.call(cssRules, function(rule) { |
if (rule.selectorText && (rule.style && rule.style.cssText)) { |
- cssText += this.scopeSelector(rule.selectorText, name, typeExtension, |
+ cssText += this.scopeSelector(rule.selectorText, scopeSelector, |
this.strictStyling) + ' {\n\t'; |
cssText += this.propertiesFromRule(rule) + '\n}\n\n'; |
} else if (rule.type === CSSRule.MEDIA_RULE) { |
cssText += '@media ' + rule.media.mediaText + ' {\n'; |
- cssText += this.scopeRules(rule.cssRules, name, typeExtension); |
+ cssText += this.scopeRules(rule.cssRules, scopeSelector); |
cssText += '\n}\n\n'; |
} else if (rule.cssText) { |
cssText += rule.cssText + '\n\n'; |
@@ -7508,43 +7595,43 @@ var ShadowCSS = { |
} |
return cssText; |
}, |
- scopeSelector: function(selector, name, typeExtension, strict) { |
+ scopeSelector: function(selector, scopeSelector, strict) { |
var r = [], parts = selector.split(','); |
parts.forEach(function(p) { |
p = p.trim(); |
- if (this.selectorNeedsScoping(p, name, typeExtension)) { |
+ if (this.selectorNeedsScoping(p, scopeSelector)) { |
p = (strict && !p.match(polyfillHostNoCombinator)) ? |
- this.applyStrictSelectorScope(p, name) : |
- this.applySimpleSelectorScope(p, name, typeExtension); |
+ this.applyStrictSelectorScope(p, scopeSelector) : |
+ this.applySimpleSelectorScope(p, scopeSelector); |
} |
r.push(p); |
}, this); |
return r.join(', '); |
}, |
- selectorNeedsScoping: function(selector, name, typeExtension) { |
- var re = this.makeScopeMatcher(name, typeExtension); |
+ selectorNeedsScoping: function(selector, scopeSelector) { |
+ var re = this.makeScopeMatcher(scopeSelector); |
return !selector.match(re); |
}, |
- makeScopeMatcher: function(name, typeExtension) { |
- var matchScope = typeExtension ? '\\[is=[\'"]?' + name + '[\'"]?\\]' : name; |
- return new RegExp('^(' + matchScope + ')' + selectorReSuffix, 'm'); |
+ makeScopeMatcher: function(scopeSelector) { |
+ scopeSelector = scopeSelector.replace(/\[/g, '\\[').replace(/\[/g, '\\]'); |
+ return new RegExp('^(' + scopeSelector + ')' + selectorReSuffix, 'm'); |
}, |
// scope via name and [is=name] |
- applySimpleSelectorScope: function(selector, name, typeExtension) { |
- var scoper = typeExtension ? '[is=' + name + ']' : name; |
+ applySimpleSelectorScope: function(selector, scopeSelector) { |
if (selector.match(polyfillHostRe)) { |
- selector = selector.replace(polyfillHostNoCombinator, scoper); |
- return selector.replace(polyfillHostRe, scoper + ' '); |
+ selector = selector.replace(polyfillHostNoCombinator, scopeSelector); |
+ return selector.replace(polyfillHostRe, scopeSelector + ' '); |
} else { |
- return scoper + ' ' + selector; |
+ return scopeSelector + ' ' + selector; |
} |
}, |
// return a selector with [name] suffix on each simple selector |
// e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name] |
- applyStrictSelectorScope: function(selector, name) { |
+ applyStrictSelectorScope: function(selector, scopeSelector) { |
+ scopeSelector = scopeSelector.replace(/\[is=([^\]]*)\]/g, '$1'); |
var splits = [' ', '>', '+', '~'], |
scoped = selector, |
- attrName = '[' + name + ']'; |
+ attrName = '[' + scopeSelector + ']'; |
splits.forEach(function(sep) { |
var parts = scoped.split(sep); |
scoped = parts.map(function(p) { |
@@ -7570,14 +7657,37 @@ var ShadowCSS = { |
rule.style.content + '\';'); |
} |
return rule.style.cssText; |
+ }, |
+ replaceTextInStyles: function(styles, action) { |
+ if (styles && action) { |
+ if (!(styles instanceof Array)) { |
+ styles = [styles]; |
+ } |
+ Array.prototype.forEach.call(styles, function(s) { |
+ s.textContent = action.call(this, s.textContent); |
+ }, this); |
+ } |
+ }, |
+ addCssToDocument: function(cssText, name) { |
+ if (cssText.match('@import')) { |
+ addOwnSheet(cssText, name); |
+ } else { |
+ addCssToDocument(cssText); |
+ } |
} |
}; |
var selectorRe = /([^{]*)({[\s\S]*?})/gim, |
cssCommentRe = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//gim, |
- cssPolyfillCommentRe = /\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^{]*?){/gim, |
- cssPolyfillRuleCommentRe = /\/\*\s@polyfill-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim, |
- cssPolyfillUnscopedRuleCommentRe = /\/\*\s@polyfill-unscoped-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim, |
+ // TODO(sorvell): remove either content or comment |
+ cssCommentNextSelectorRe = /\/\*\s*@polyfill ([^*]*\*+([^/*][^*]*\*+)*\/)([^{]*?){/gim, |
+ cssContentNextSelectorRe = /polyfill-next-selector[^}]*content\:[\s]*'([^']*)'[^}]*}([^{]*?){/gim, |
+ // TODO(sorvell): remove either content or comment |
+ cssCommentRuleRe = /\/\*\s@polyfill-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim, |
+ cssContentRuleRe = /(polyfill-rule)[^}]*(content\:[\s]*'([^']*)'[^;]*;)[^}]*}/gim, |
+ // TODO(sorvell): remove either content or comment |
+ cssCommentUnscopedRuleRe = /\/\*\s@polyfill-unscoped-rule([^*]*\*+([^/*][^*]*\*+)*)\//gim, |
+ cssContentUnscopedRuleRe = /(polyfill-unscoped-rule)[^}]*(content\:[\s]*'([^']*)'[^;]*;)[^}]*}/gim, |
cssPseudoRe = /::(x-[^\s{,(]*)/gim, |
cssPartRe = /::part\(([^)]*)\)/gim, |
// note: :host pre-processed to -shadowcsshost. |
@@ -7595,7 +7705,7 @@ var selectorRe = /([^{]*)({[\s\S]*?})/gim, |
colonAncestorRe = /\:ancestor/gim, |
/* host name without combinator */ |
polyfillHostNoCombinator = polyfillHost + '-no-combinator', |
- polyfillHostRe = new RegExp(polyfillHost, 'gim'); |
+ polyfillHostRe = new RegExp(polyfillHost, 'gim'), |
polyfillAncestorRe = new RegExp(polyfillAncestor, 'gim'); |
function stylesToCssText(styles, preserveComments) { |
@@ -7700,6 +7810,7 @@ function addOwnSheet(cssText, name) { |
var SHIM_ATTRIBUTE = 'shim-shadowdom'; |
var SHIMMED_ATTRIBUTE = 'shim-shadowdom-css'; |
+var NO_SHIM_ATTRIBUTE = 'no-shim'; |
var sheet; |
function getSheet() { |
@@ -7754,8 +7865,7 @@ if (window.ShadowDOMPolyfill) { |
} else { |
urlResolver.resolveStyle(style); |
} |
- var styles = [style]; |
- style.textContent = ShadowCSS.stylesToShimmedCssText(styles, styles); |
+ style.textContent = ShadowCSS.shimStyle(style); |
style.removeAttribute(SHIM_ATTRIBUTE, ''); |
style.setAttribute(SHIMMED_ATTRIBUTE, ''); |
style[SHIMMED_ATTRIBUTE] = true; |
@@ -11712,6 +11822,7 @@ scope.styleResolver = styleResolver; |
var ev = new MouseEvent('click', {buttons: 1}); |
NEW_MOUSE_EVENT = true; |
HAS_BUTTONS = ev.buttons === 1; |
+ ev = null; |
} catch(e) { |
} |
@@ -11772,10 +11883,9 @@ scope.styleResolver = styleResolver; |
// is to call initMouseEvent with a buttonArg value of -1. |
// |
// This is fixed with DOM Level 4's use of buttons |
- var buttons; |
- if (inDict.buttons || HAS_BUTTONS) { |
- buttons = inDict.buttons; |
- } else { |
+ var buttons = inDict.buttons; |
+ // touch has two possible buttons state: 0 and 1, rely on being told the right one |
+ if (!HAS_BUTTONS && !buttons && inType !== 'touch') { |
switch (inDict.which) { |
case 1: buttons = 1; break; |
case 2: buttons = 4; break; |
@@ -12320,7 +12430,11 @@ scope.styleResolver = styleResolver; |
}, |
// register all touch-action = none nodes on document load |
installOnLoad: function() { |
- document.addEventListener('DOMContentLoaded', this.installNewSubtree.bind(this, document)); |
+ document.addEventListener('readystatechange', function() { |
+ if (document.readyState === 'complete') { |
+ this.installNewSubtree(document); |
+ } |
+ }.bind(this)); |
}, |
isElement: function(n) { |
return n.nodeType === Node.ELEMENT_NODE; |
@@ -12605,6 +12719,13 @@ scope.styleResolver = styleResolver; |
clearTimeout(this.resetId); |
} |
}, |
+ typeToButtons: function(type) { |
+ var ret = 0; |
+ if (type === 'touchstart' || type === 'touchmove') { |
+ ret = 1; |
+ } |
+ return ret; |
+ }, |
touchToPointer: function(inTouch) { |
var e = dispatcher.cloneEvent(inTouch); |
// Spec specifies that pointerId 1 is reserved for Mouse. |
@@ -12616,7 +12737,7 @@ scope.styleResolver = styleResolver; |
e.cancelable = true; |
e.detail = this.clickCount; |
e.button = 0; |
- e.buttons = 1; |
+ e.buttons = this.typeToButtons(this.currentTouchEvent); |
e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0; |
e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0; |
e.pressure = inTouch.webkitForce || inTouch.force || 0.5; |
@@ -12626,6 +12747,7 @@ scope.styleResolver = styleResolver; |
}, |
processTouches: function(inEvent, inFunction) { |
var tl = inEvent.changedTouches; |
+ this.currentTouchEvent = inEvent.type; |
var pointers = touchMap(tl, this.touchToPointer, this); |
// forward touch preventDefaults |
pointers.forEach(function(p) { |
@@ -13360,9 +13482,11 @@ PointerGestureEvent.prototype.preventTap = function() { |
} |
}; |
dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher); |
+ // recognizers call into the dispatcher and load later |
+ // solve the chicken and egg problem by having registerScopes module run last |
+ dispatcher.registerQueue = []; |
+ dispatcher.immediateRegister = false; |
scope.dispatcher = dispatcher; |
- var registerQueue = []; |
- var immediateRegister = false; |
/** |
* Enable gesture events for a given scope, typically |
* [ShadowRoots](https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#shadow-root-object). |
@@ -13373,22 +13497,17 @@ PointerGestureEvent.prototype.preventTap = function() { |
* support on. |
*/ |
scope.register = function(inScope) { |
- if (immediateRegister) { |
+ if (dispatcher.immediateRegister) { |
var pe = window.PointerEventsPolyfill; |
if (pe) { |
pe.register(inScope); |
} |
scope.dispatcher.registerTarget(inScope); |
} else { |
- registerQueue.push(inScope); |
+ dispatcher.registerQueue.push(inScope); |
} |
}; |
- // wait to register scopes until recognizers load |
- document.addEventListener('DOMContentLoaded', function() { |
- immediateRegister = true; |
- registerQueue.push(document); |
- registerQueue.forEach(scope.register); |
- }); |
+ scope.register(document); |
})(window.PointerGestures); |
/* |
@@ -14052,6 +14171,7 @@ PointerGestureEvent.prototype.preventTap = function() { |
if (inEvent.isPrimary && !inEvent.tapPrevented) { |
pointermap.set(inEvent.pointerId, { |
target: inEvent.target, |
+ buttons: inEvent.buttons, |
x: inEvent.clientX, |
y: inEvent.clientY |
}); |
@@ -14067,15 +14187,19 @@ PointerGestureEvent.prototype.preventTap = function() { |
} |
} |
}, |
- shouldTap: function(e) { |
+ shouldTap: function(e, downState) { |
if (!e.tapPrevented) { |
- // only allow left click to tap for mouse |
- return e.pointerType === 'mouse' ? e.buttons === 1 : true; |
+ if (e.pointerType === 'mouse') { |
+ // only allow left click to tap for mouse |
+ return downState.buttons === 1; |
+ } else { |
+ return true; |
+ } |
} |
}, |
pointerup: function(inEvent) { |
var start = pointermap.get(inEvent.pointerId); |
- if (start && this.shouldTap(inEvent)) { |
+ if (start && this.shouldTap(inEvent, start)) { |
var t = scope.findLCA(start.target, inEvent.target); |
if (t) { |
var e = dispatcher.makeEvent('tap', { |
@@ -14114,6 +14238,37 @@ PointerGestureEvent.prototype.preventTap = function() { |
dispatcher.registerRecognizer('tap', tap); |
})(window.PointerGestures); |
+/* |
+ * Copyright 2014 The Polymer Authors. All rights reserved. |
+ * Use of this source code is governed by a BSD-style |
+ * license that can be found in the LICENSE file. |
+ */ |
+ |
+/** |
+ * Because recognizers are loaded after dispatcher, we have to wait to register |
+ * scopes until after all the recognizers. |
+ */ |
+(function(scope) { |
+ var dispatcher = scope.dispatcher; |
+ function registerScopes() { |
+ dispatcher.immediateRegister = true; |
+ var rq = dispatcher.registerQueue; |
+ rq.forEach(scope.register); |
+ rq.length = 0; |
+ } |
+ if (document.readyState === 'complete') { |
+ registerScopes(); |
+ } else { |
+ // register scopes after a steadystate is reached |
+ // less MutationObserver churn |
+ document.addEventListener('readystatechange', function() { |
+ if (document.readyState === 'complete') { |
+ registerScopes(); |
+ } |
+ }); |
+ } |
+})(window.PointerGestures); |
+ |
// Copyright 2011 Google Inc. |
// |
// Licensed under the Apache License, Version 2.0 (the "License"); |
@@ -14141,33 +14296,6 @@ PointerGestureEvent.prototype.preventTap = function() { |
return typeof node.getElementById === 'function' ? node : null; |
} |
- // JScript does not have __proto__. We wrap all object literals with |
- // createObject which uses Object.create, Object.defineProperty and |
- // Object.getOwnPropertyDescriptor to create a new object that does the exact |
- // same thing. The main downside to this solution is that we have to extract |
- // all those property descriptors for IE. |
- var createObject = ('__proto__' in {}) ? |
- function(obj) { return obj; } : |
- function(obj) { |
- var proto = obj.__proto__; |
- if (!proto) |
- return obj; |
- var newObject = Object.create(proto); |
- Object.getOwnPropertyNames(obj).forEach(function(name) { |
- Object.defineProperty(newObject, name, |
- Object.getOwnPropertyDescriptor(obj, name)); |
- }); |
- return newObject; |
- }; |
- |
- // IE does not support have Document.prototype.contains. |
- if (typeof document.contains != 'function') { |
- Document.prototype.contains = function(node) { |
- if (node === this || node.parentNode === this) |
- return true; |
- return this.documentElement.contains(node); |
- } |
- } |
Node.prototype.bind = function(name, observable) { |
console.error('Unhandled binding to Node: ', this, name, observable); |