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 be0db90657c39e80d29d1498ad277a8f1db78489..2044ee351a452fa27b35c88e93128acf2c446804 100644 |
--- a/pkg/web_components/lib/platform.concat.js |
+++ b/pkg/web_components/lib/platform.concat.js |
@@ -192,7 +192,7 @@ if (typeof WeakMap === 'undefined') { |
return obj === Object(obj); |
} |
- var numberIsNaN = global.Number.isNaN || function isNaN(value) { |
+ var numberIsNaN = global.Number.isNaN || function(value) { |
return typeof value === 'number' && global.isNaN(value); |
} |
@@ -221,45 +221,194 @@ if (typeof WeakMap === 'undefined') { |
var identStart = '[\$_a-zA-Z]'; |
var identPart = '[\$_a-zA-Z0-9]'; |
- var ident = identStart + '+' + identPart + '*'; |
- var elementIndex = '(?:[0-9]|[1-9]+[0-9]+)'; |
- var identOrElementIndex = '(?:' + ident + '|' + elementIndex + ')'; |
- var path = '(?:' + identOrElementIndex + ')(?:\\s*\\.\\s*' + identOrElementIndex + ')*'; |
- var pathRegExp = new RegExp('^' + path + '$'); |
- |
- function isPathValid(s) { |
- if (typeof s != 'string') |
- return false; |
- s = s.trim(); |
+ var identRegExp = new RegExp('^' + identStart + '+' + identPart + '*' + '$'); |
- if (s == '') |
- return true; |
+ function getPathCharType(char) { |
+ if (char === undefined) |
+ return 'eof'; |
- if (s[0] == '.') |
- return false; |
+ var code = char.charCodeAt(0); |
+ |
+ switch(code) { |
+ case 0x5B: // [ |
+ case 0x5D: // ] |
+ case 0x2E: // . |
+ case 0x22: // " |
+ case 0x27: // ' |
+ case 0x30: // 0 |
+ return char; |
+ |
+ case 0x5F: // _ |
+ case 0x24: // $ |
+ return 'ident'; |
+ |
+ case 0x20: // Space |
+ case 0x09: // Tab |
+ case 0x0A: // Newline |
+ case 0x0D: // Return |
+ case 0xA0: // No-break space |
+ case 0xFEFF: // Byte Order Mark |
+ case 0x2028: // Line Separator |
+ case 0x2029: // Paragraph Separator |
+ return 'ws'; |
+ } |
+ |
+ // a-z, A-Z |
+ if ((0x61 <= code && code <= 0x7A) || (0x41 <= code && code <= 0x5A)) |
+ return 'ident'; |
- return pathRegExp.test(s); |
+ // 1-9 |
+ if (0x31 <= code && code <= 0x39) |
+ return 'number'; |
+ |
+ return 'else'; |
} |
- var constructorIsPrivate = {}; |
+ var pathStateMachine = { |
+ 'beforePath': { |
+ 'ws': ['beforePath'], |
+ 'ident': ['inIdent', 'append'], |
+ '[': ['beforeElement'], |
+ 'eof': ['afterPath'] |
+ }, |
- function Path(s, privateToken) { |
- if (privateToken !== constructorIsPrivate) |
- throw Error('Use Path.get to retrieve path objects'); |
+ 'inPath': { |
+ 'ws': ['inPath'], |
+ '.': ['beforeIdent'], |
+ '[': ['beforeElement'], |
+ 'eof': ['afterPath'] |
+ }, |
+ |
+ 'beforeIdent': { |
+ 'ws': ['beforeIdent'], |
+ 'ident': ['inIdent', 'append'] |
+ }, |
+ |
+ 'inIdent': { |
+ 'ident': ['inIdent', 'append'], |
+ '0': ['inIdent', 'append'], |
+ 'number': ['inIdent', 'append'], |
+ 'ws': ['inPath', 'push'], |
+ '.': ['beforeIdent', 'push'], |
+ '[': ['beforeElement', 'push'], |
+ 'eof': ['afterPath', 'push'] |
+ }, |
+ |
+ 'beforeElement': { |
+ 'ws': ['beforeElement'], |
+ '0': ['afterZero', 'append'], |
+ 'number': ['inIndex', 'append'], |
+ "'": ['inSingleQuote', 'append', ''], |
+ '"': ['inDoubleQuote', 'append', ''] |
+ }, |
+ |
+ 'afterZero': { |
+ 'ws': ['afterElement', 'push'], |
+ ']': ['inPath', 'push'] |
+ }, |
- if (s.trim() == '') |
- return this; |
+ 'inIndex': { |
+ '0': ['inIndex', 'append'], |
+ 'number': ['inIndex', 'append'], |
+ 'ws': ['afterElement'], |
+ ']': ['inPath', 'push'] |
+ }, |
- if (isIndex(s)) { |
- this.push(s); |
- return this; |
+ 'inSingleQuote': { |
+ "'": ['afterElement'], |
+ 'eof': ['error'], |
+ 'else': ['inSingleQuote', 'append'] |
+ }, |
+ |
+ 'inDoubleQuote': { |
+ '"': ['afterElement'], |
+ 'eof': ['error'], |
+ 'else': ['inDoubleQuote', 'append'] |
+ }, |
+ |
+ 'afterElement': { |
+ 'ws': ['afterElement'], |
+ ']': ['inPath', 'push'] |
} |
+ } |
- s.split(/\s*\.\s*/).filter(function(part) { |
- return part; |
- }).forEach(function(part) { |
- this.push(part); |
- }, this); |
+ function noop() {} |
+ |
+ function parsePath(path) { |
+ var keys = []; |
+ var index = -1; |
+ var c, newChar, key, type, transition, action, typeMap, mode = 'beforePath'; |
+ |
+ var actions = { |
+ push: function() { |
+ if (key === undefined) |
+ return; |
+ |
+ keys.push(key); |
+ key = undefined; |
+ }, |
+ |
+ append: function() { |
+ if (key === undefined) |
+ key = newChar |
+ else |
+ key += newChar; |
+ } |
+ }; |
+ |
+ function maybeUnescapeQuote() { |
+ if (index >= path.length) |
+ return; |
+ |
+ var nextChar = path[index + 1]; |
+ if ((mode == 'inSingleQuote' && nextChar == "'") || |
+ (mode == 'inDoubleQuote' && nextChar == '"')) { |
+ index++; |
+ newChar = nextChar; |
+ actions.append(); |
+ return true; |
+ } |
+ } |
+ |
+ while (mode) { |
+ index++; |
+ c = path[index]; |
+ |
+ if (c == '\\' && maybeUnescapeQuote(mode)) |
+ continue; |
+ |
+ type = getPathCharType(c); |
+ typeMap = pathStateMachine[mode]; |
+ transition = typeMap[type] || typeMap['else'] || 'error'; |
+ |
+ if (transition == 'error') |
+ return; // parse error; |
+ |
+ mode = transition[0]; |
+ action = actions[transition[1]] || noop; |
+ newChar = transition[2] === undefined ? c : transition[2]; |
+ action(); |
+ |
+ if (mode === 'afterPath') { |
+ return keys; |
+ } |
+ } |
+ |
+ return; // parse error |
+ } |
+ |
+ function isIdent(s) { |
+ return identRegExp.test(s); |
+ } |
+ |
+ var constructorIsPrivate = {}; |
+ |
+ function Path(parts, privateToken) { |
+ if (privateToken !== constructorIsPrivate) |
+ throw Error('Use Path.get to retrieve path objects'); |
+ |
+ if (parts.length) |
+ Array.prototype.push.apply(this, parts.slice()); |
if (hasEval && this.length) { |
this.getValueFrom = this.compiledGetValueFromFn(); |
@@ -273,30 +422,57 @@ if (typeof WeakMap === 'undefined') { |
if (pathString instanceof Path) |
return pathString; |
- if (pathString == null) |
+ if (pathString == null || pathString.length == 0) |
pathString = ''; |
- if (typeof pathString !== 'string') |
+ if (typeof pathString != 'string') { |
+ if (isIndex(pathString.length)) { |
+ // Constructed with array-like (pre-parsed) keys |
+ return new Path(pathString, constructorIsPrivate); |
+ } |
+ |
pathString = String(pathString); |
+ } |
var path = pathCache[pathString]; |
if (path) |
return path; |
- if (!isPathValid(pathString)) |
+ |
+ var parts = parsePath(pathString); |
+ if (!parts) |
return invalidPath; |
- var path = new Path(pathString, constructorIsPrivate); |
+ |
+ var path = new Path(parts, constructorIsPrivate); |
pathCache[pathString] = path; |
return path; |
} |
Path.get = getPath; |
+ function formatAccessor(key) { |
+ if (isIndex(key)) { |
+ return '[' + key + ']'; |
+ } else { |
+ return '["' + key.replace(/"/g, '\\"') + '"]'; |
+ } |
+ } |
+ |
Path.prototype = createObject({ |
__proto__: [], |
valid: true, |
toString: function() { |
- return this.join('.'); |
+ var pathString = ''; |
+ for (var i = 0; i < this.length; i++) { |
+ var key = this[i]; |
+ if (isIdent(key)) { |
+ pathString += i ? '.' + key : key; |
+ } else { |
+ pathString += formatAccessor(key); |
+ } |
+ } |
+ |
+ return pathString; |
}, |
getValueFrom: function(obj, directObserver) { |
@@ -319,22 +495,20 @@ if (typeof WeakMap === 'undefined') { |
}, |
compiledGetValueFromFn: function() { |
- var accessors = this.map(function(ident) { |
- return isIndex(ident) ? '["' + ident + '"]' : '.' + ident; |
- }); |
- |
var str = ''; |
var pathString = 'obj'; |
str += 'if (obj != null'; |
var i = 0; |
+ var key; |
for (; i < (this.length - 1); i++) { |
- var ident = this[i]; |
- pathString += accessors[i]; |
+ key = this[i]; |
+ pathString += isIdent(key) ? '.' + key : formatAccessor(key); |
str += ' &&\n ' + pathString + ' != null'; |
} |
str += ')\n'; |
- pathString += accessors[i]; |
+ var key = this[i]; |
+ pathString += isIdent(key) ? '.' + key : formatAccessor(key); |
str += ' return ' + pathString + ';\nelse\n return undefined;'; |
return new Function('obj', str); |
@@ -935,6 +1109,10 @@ if (typeof WeakMap === 'undefined') { |
PathObserver.prototype = createObject({ |
__proto__: Observer.prototype, |
+ get path() { |
+ return this.path_; |
+ }, |
+ |
connect_: function() { |
if (hasObserve) |
this.directObserver_ = getObservedSet(this, this.object_); |
@@ -961,7 +1139,7 @@ if (typeof WeakMap === 'undefined') { |
if (skipChanges || areSameValue(this.value_, oldValue)) |
return false; |
- this.report_([this.value_, oldValue]); |
+ this.report_([this.value_, oldValue, this]); |
return true; |
}, |
@@ -1171,98 +1349,6 @@ if (typeof WeakMap === 'undefined') { |
delete: true |
}; |
- var updateRecord = { |
- object: undefined, |
- type: 'update', |
- name: undefined, |
- oldValue: undefined |
- }; |
- |
- function notify(object, name, value, oldValue) { |
- if (areSameValue(value, oldValue)) |
- return; |
- |
- // TODO(rafaelw): Hack hack hack. This entire code really needs to move |
- // out of observe-js into polymer. |
- if (typeof object.propertyChanged_ == 'function') |
- object.propertyChanged_(name, value, oldValue); |
- |
- if (!hasObserve) |
- return; |
- |
- var notifier = object.notifier_; |
- if (!notifier) |
- notifier = object.notifier_ = Object.getNotifier(object); |
- |
- updateRecord.object = object; |
- updateRecord.name = name; |
- updateRecord.oldValue = oldValue; |
- |
- notifier.notify(updateRecord); |
- } |
- |
- Observer.createBindablePrototypeAccessor = function(proto, name) { |
- var privateName = name + '_'; |
- var privateObservable = name + 'Observable_'; |
- |
- proto[privateName] = proto[name]; |
- |
- Object.defineProperty(proto, name, { |
- get: function() { |
- var observable = this[privateObservable]; |
- if (observable) |
- observable.deliver(); |
- |
- return this[privateName]; |
- }, |
- set: function(value) { |
- var observable = this[privateObservable]; |
- if (observable) { |
- observable.setValue(value); |
- return; |
- } |
- |
- var oldValue = this[privateName]; |
- this[privateName] = value; |
- notify(this, name, value, oldValue); |
- |
- return value; |
- }, |
- configurable: true |
- }); |
- } |
- |
- Observer.bindToInstance = function(instance, name, observable, resolveFn) { |
- var privateName = name + '_'; |
- var privateObservable = name + 'Observable_'; |
- |
- instance[privateObservable] = observable; |
- var oldValue = instance[privateName]; |
- var value = observable.open(function(value, oldValue) { |
- instance[privateName] = value; |
- notify(instance, name, value, oldValue); |
- }); |
- |
- if (resolveFn && !areSameValue(oldValue, value)) { |
- var resolvedValue = resolveFn(oldValue, value); |
- if (!areSameValue(value, resolvedValue)) { |
- value = resolvedValue; |
- if (observable.setValue) |
- observable.setValue(value); |
- } |
- } |
- |
- instance[privateName] = value; |
- notify(instance, name, value, oldValue); |
- |
- return { |
- close: function() { |
- observable.close(); |
- instance[privateObservable] = undefined; |
- } |
- }; |
- } |
- |
function diffObjectFromChangeRecords(object, changeRecords, oldValues) { |
var added = {}; |
var removed = {}; |
@@ -3044,7 +3130,11 @@ window.ShadowDOMPolyfill = {}; |
targetTable.set(event, target); |
currentTargetTable.set(event, currentTarget); |
- for (var i = 0; i < listeners.length; i++) { |
+ // Keep track of the invoke depth so that we only clean up the removed |
+ // listeners if we are in the outermost invoke. |
+ listeners.depth++; |
+ |
+ for (var i = 0, len = listeners.length; i < len; i++) { |
var listener = listeners[i]; |
if (listener.removed) { |
anyRemoved = true; |
@@ -3072,7 +3162,9 @@ window.ShadowDOMPolyfill = {}; |
} |
} |
- if (anyRemoved) { |
+ listeners.depth--; |
+ |
+ if (anyRemoved && listeners.depth === 0) { |
var copy = listeners.slice(); |
listeners.length = 0; |
for (var i = 0; i < copy.length; i++) { |
@@ -3137,25 +3229,11 @@ window.ShadowDOMPolyfill = {}; |
return eventPhaseTable.get(this); |
}, |
get path() { |
- var nodeList = new wrappers.NodeList(); |
var eventPath = eventPathTable.get(this); |
- if (eventPath) { |
- var index = 0; |
- var lastIndex = eventPath.length - 1; |
- var baseRoot = getTreeScope(currentTargetTable.get(this)); |
- |
- for (var i = 0; i <= lastIndex; i++) { |
- var currentTarget = eventPath[i]; |
- 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; |
- } |
- } |
- nodeList.length = index; |
- } |
- return nodeList; |
+ if (!eventPath) |
+ return []; |
+ // TODO(arv): Event path should contain window. |
+ return eventPath.slice(); |
}, |
stopPropagation: function() { |
stopPropagationTable.set(this, true); |
@@ -3383,6 +3461,7 @@ window.ShadowDOMPolyfill = {}; |
var listeners = listenersTable.get(this); |
if (!listeners) { |
listeners = []; |
+ listeners.depth = 0; |
listenersTable.set(this, listeners); |
} else { |
// Might have a duplicate. |
@@ -3491,6 +3570,15 @@ window.ShadowDOMPolyfill = {}; |
if (!element) |
return null; |
var path = getEventPath(element, null); |
+ |
+ // scope the path to this TreeScope |
+ var idx = path.lastIndexOf(self); |
+ if (idx == -1) |
+ return null; |
+ else |
+ path = path.slice(0, idx); |
+ |
+ // TODO(dfreedm): pass idx to eventRetargetting to avoid array copy |
return eventRetargetting(path, self); |
} |
@@ -4448,7 +4536,7 @@ window.ShadowDOMPolyfill = {}; |
} else { |
if (modNode && remNodes.length) { |
modNode.data += s; |
- cleanUpNodes(remNodes); |
+ cleanupNodes(remNodes); |
} |
remNodes = []; |
s = ''; |
@@ -4766,6 +4854,53 @@ window.ShadowDOMPolyfill = {}; |
scope.wrappers.Text = Text; |
})(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'; |
+ |
+ function invalidateClass(el) { |
+ scope.invalidateRendererBasedOnAttribute(el, 'class'); |
+ } |
+ |
+ function DOMTokenList(impl, ownerElement) { |
+ this.impl = impl; |
+ this.ownerElement_ = ownerElement; |
+ } |
+ |
+ DOMTokenList.prototype = { |
+ get length() { |
+ return this.impl.length; |
+ }, |
+ item: function(index) { |
+ return this.impl.item(index); |
+ }, |
+ contains: function(token) { |
+ return this.impl.contains(token); |
+ }, |
+ add: function() { |
+ this.impl.add.apply(this.impl, arguments); |
+ invalidateClass(this.ownerElement_); |
+ }, |
+ remove: function() { |
+ this.impl.remove.apply(this.impl, arguments); |
+ invalidateClass(this.ownerElement_); |
+ }, |
+ toggle: function(token) { |
+ var rv = this.impl.toggle.apply(this.impl, arguments); |
+ invalidateClass(this.ownerElement_); |
+ return rv; |
+ }, |
+ toString: function() { |
+ return this.impl.toString(); |
+ } |
+ }; |
+ |
+ scope.wrappers.DOMTokenList = DOMTokenList; |
+})(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. |
@@ -4776,6 +4911,7 @@ window.ShadowDOMPolyfill = {}; |
var ChildNodeInterface = scope.ChildNodeInterface; |
var GetElementsByInterface = scope.GetElementsByInterface; |
var Node = scope.wrappers.Node; |
+ var DOMTokenList = scope.wrappers.DOMTokenList; |
var ParentNodeInterface = scope.ParentNodeInterface; |
var SelectorsInterface = scope.SelectorsInterface; |
var addWrapNodeListMethod = scope.addWrapNodeListMethod; |
@@ -4783,6 +4919,7 @@ window.ShadowDOMPolyfill = {}; |
var mixin = scope.mixin; |
var oneOf = scope.oneOf; |
var registerWrapper = scope.registerWrapper; |
+ var unwrap = scope.unwrap; |
var wrappers = scope.wrappers; |
var OriginalElement = window.Element; |
@@ -4822,6 +4959,8 @@ window.ShadowDOMPolyfill = {}; |
}); |
} |
+ var classListTable = new WeakMap(); |
+ |
function Element(node) { |
Node.call(this, node); |
} |
@@ -4859,6 +4998,31 @@ window.ShadowDOMPolyfill = {}; |
matches: function(selector) { |
return originalMatches.call(this.impl, selector); |
+ }, |
+ |
+ get classList() { |
+ var list = classListTable.get(this); |
+ if (!list) { |
+ classListTable.set(this, |
+ list = new DOMTokenList(unwrap(this).classList, this)); |
+ } |
+ return list; |
+ }, |
+ |
+ get className() { |
+ return unwrap(this).className; |
+ }, |
+ |
+ set className(v) { |
+ this.setAttribute('class', v); |
+ }, |
+ |
+ get id() { |
+ return unwrap(this).id; |
+ }, |
+ |
+ set id(v) { |
+ this.setAttribute('id', v); |
} |
}); |
@@ -4875,28 +5039,6 @@ window.ShadowDOMPolyfill = {}; |
Element.prototype.createShadowRoot; |
} |
- /** |
- * Useful for generating the accessor pair for a property that reflects an |
- * attribute. |
- */ |
- function setterDirtiesAttribute(prototype, propertyName, opt_attrName) { |
- var attrName = opt_attrName || propertyName; |
- Object.defineProperty(prototype, propertyName, { |
- get: function() { |
- return this.impl[propertyName]; |
- }, |
- set: function(v) { |
- this.impl[propertyName] = v; |
- invalidateRendererBasedOnAttribute(this, attrName); |
- }, |
- configurable: true, |
- enumerable: true |
- }); |
- } |
- |
- setterDirtiesAttribute(Element.prototype, 'id'); |
- setterDirtiesAttribute(Element.prototype, 'className', 'class'); |
- |
mixin(Element.prototype, ChildNodeInterface); |
mixin(Element.prototype, GetElementsByInterface); |
mixin(Element.prototype, ParentNodeInterface); |
@@ -4905,8 +5047,7 @@ window.ShadowDOMPolyfill = {}; |
registerWrapper(OriginalElement, Element, |
document.createElementNS(null, 'x')); |
- // TODO(arv): Export setterDirtiesAttribute and apply it to more bindings |
- // that reflect attributes. |
+ scope.invalidateRendererBasedOnAttribute = invalidateRendererBasedOnAttribute; |
scope.matchesNames = matchesNames; |
scope.wrappers.Element = Element; |
})(window.ShadowDOMPolyfill); |
@@ -5802,6 +5943,8 @@ window.ShadowDOMPolyfill = {}; |
(function(scope) { |
'use strict'; |
+ var Element = scope.wrappers.Element; |
+ var HTMLElement = scope.wrappers.HTMLElement; |
var registerObject = scope.registerObject; |
var SVG_NS = 'http://www.w3.org/2000/svg'; |
@@ -5809,6 +5952,16 @@ window.ShadowDOMPolyfill = {}; |
var SVGTitleElement = registerObject(svgTitleElement); |
var SVGElement = Object.getPrototypeOf(SVGTitleElement.prototype).constructor; |
+ // IE11 does not have classList for SVG elements. The spec says that classList |
+ // is an accessor on Element, but IE11 puts classList on HTMLElement, leaving |
+ // SVGElement without a classList property. We therefore move the accessor for |
+ // IE11. |
+ if (!('classList' in svgTitleElement)) { |
+ var descr = Object.getOwnPropertyDescriptor(Element.prototype, 'classList'); |
+ Object.defineProperty(HTMLElement.prototype, 'classList', descr); |
+ delete Element.prototype.classList; |
+ } |
+ |
scope.wrappers.SVGElement = SVGElement; |
})(window.ShadowDOMPolyfill); |
@@ -7100,6 +7253,10 @@ window.ShadowDOMPolyfill = {}; |
getSelection: function() { |
renderAllPending(); |
return new Selection(originalGetSelection.call(unwrap(this))); |
+ }, |
+ getElementsByName: function(name) { |
+ return SelectorsInterface.querySelectorAll.call(this, |
+ '[name=' + JSON.stringify(String(name)) + ']'); |
} |
}); |
@@ -7244,6 +7401,7 @@ window.ShadowDOMPolyfill = {}; |
'createTextNode', |
'elementFromPoint', |
'getElementById', |
+ 'getElementsByName', |
'getSelection', |
]); |
@@ -7534,7 +7692,7 @@ window.ShadowDOMPolyfill = {}; |
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt |
*/ |
-(function() { |
+(function(scope) { |
// convenient global |
window.wrap = ShadowDOMPolyfill.wrapIfNeeded; |
@@ -7567,7 +7725,62 @@ window.ShadowDOMPolyfill = {}; |
}; |
Element.prototype.webkitCreateShadowRoot = Element.prototype.createShadowRoot; |
-})(); |
+ |
+ function queryShadow(node, selector) { |
+ var m, el = node.firstElementChild; |
+ var shadows, sr, i; |
+ shadows = []; |
+ sr = node.shadowRoot; |
+ while(sr) { |
+ shadows.push(sr); |
+ sr = sr.olderShadowRoot; |
+ } |
+ for(i = shadows.length - 1; i >= 0; i--) { |
+ m = shadows[i].querySelector(selector); |
+ if (m) { |
+ return m; |
+ } |
+ } |
+ while(el) { |
+ m = queryShadow(el, selector); |
+ if (m) { |
+ return m; |
+ } |
+ el = el.nextElementSibling; |
+ } |
+ return null; |
+ } |
+ |
+ function queryAllShadows(node, selector, results) { |
+ var el = node.firstElementChild; |
+ var temp, sr, shadows, i, j; |
+ shadows = []; |
+ sr = node.shadowRoot; |
+ while(sr) { |
+ shadows.push(sr); |
+ sr = sr.olderShadowRoot; |
+ } |
+ for (i = shadows.length - 1; i >= 0; i--) { |
+ temp = shadows[i].querySelectorAll(selector); |
+ for(j = 0; j < temp.length; j++) { |
+ results.push(temp[j]); |
+ } |
+ } |
+ while (el) { |
+ queryAllShadows(el, selector, results); |
+ el = el.nextElementSibling; |
+ } |
+ return results; |
+ } |
+ |
+ scope.queryAllShadows = function(node, selector, all) { |
+ if (all) { |
+ return queryAllShadows(node, selector, []); |
+ } else { |
+ return queryShadow(node, selector); |
+ } |
+ }; |
+})(window.Platform); |
/* |
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
@@ -7969,7 +8182,7 @@ var ShadowCSS = { |
var cssText = ''; |
if (cssRules) { |
Array.prototype.forEach.call(cssRules, function(rule) { |
- if (rule.selectorText && (rule.style && rule.style.cssText)) { |
+ if (rule.selectorText && (rule.style && rule.style.cssText !== undefined)) { |
cssText += this.scopeSelector(rule.selectorText, scopeSelector, |
this.strictStyling) + ' {\n\t'; |
cssText += this.propertiesFromRule(rule) + '\n}\n\n'; |
@@ -7977,8 +8190,23 @@ var ShadowCSS = { |
cssText += '@media ' + rule.media.mediaText + ' {\n'; |
cssText += this.scopeRules(rule.cssRules, scopeSelector); |
cssText += '\n}\n\n'; |
- } else if (rule.cssText) { |
- cssText += rule.cssText + '\n\n'; |
+ } else { |
+ // TODO(sjmiles): KEYFRAMES_RULE in IE11 throws when we query cssText |
+ // 'cssText' in rule returns true, but rule.cssText throws anyway |
+ // We can test the rule type, e.g. |
+ // else if (rule.type !== CSSRule.KEYFRAMES_RULE && rule.cssText) { |
+ // but this will prevent cssText propagation in other browsers which |
+ // support it. |
+ // KEYFRAMES_RULE has a CSSRuleSet, so the text can probably be reconstructed |
+ // from that collection; this would be a proper fix. |
+ // For now, I'm trapping the exception so IE11 is unblocked in other areas. |
+ try { |
+ if (rule.cssText) { |
+ cssText += rule.cssText + '\n\n'; |
+ } |
+ } catch(x) { |
+ // squelch |
+ } |
} |
}, this); |
} |
@@ -8306,6 +8534,7 @@ if (window.ShadowDOMPolyfill) { |
} |
style.__importParsed = true; |
this.markParsingComplete(elt); |
+ this.parseNext(); |
} |
var hasResource = HTMLImports.parser.hasResource; |
@@ -9420,9 +9649,11 @@ scope.mixin = mixin; |
module = null; |
break; |
case 2: |
+ // dependsOrFactory is `factory` in this case |
module = dependsOrFactory.apply(this); |
break; |
default: |
+ // dependsOrFactory is `depends` in this case |
module = withDependencies(moduleFactory, dependsOrFactory); |
break; |
} |
@@ -9444,7 +9675,8 @@ scope.mixin = mixin; |
// exports |
scope.marshal = marshal; |
- scope.module = module; |
+ // `module` confuses commonjs detectors |
+ scope.modularize = module; |
scope.using = using; |
})(window); |
@@ -9551,7 +9783,7 @@ var urlResolver = { |
var replacement; |
if (value && value.search(URL_TEMPLATE_SEARCH) < 0) { |
if (v === 'style') { |
- replacement = replaceUrlsInCssText(value, url, CSS_URL_REGEXP); |
+ replacement = replaceUrlsInCssText(value, url, false, CSS_URL_REGEXP); |
} else { |
replacement = resolveRelativeUrl(url, value); |
} |
@@ -9563,7 +9795,7 @@ var urlResolver = { |
var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g; |
var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g; |
-var URL_ATTRS = ['href', 'src', 'action', 'style']; |
+var URL_ATTRS = ['href', 'src', 'action', 'style', 'url']; |
var URL_ATTRS_SELECTOR = '[' + URL_ATTRS.join('],[') + ']'; |
var URL_TEMPLATE_SEARCH = '{{.*}}'; |
@@ -10261,8 +10493,8 @@ window.HTMLImports = window.HTMLImports || {flags:{}}; |
this.receive(url, elt, null, body); |
}.bind(this), 0); |
} else { |
- var receiveXhr = function(err, resource) { |
- this.receive(url, elt, err, resource); |
+ var receiveXhr = function(err, resource, redirectedUrl) { |
+ this.receive(url, elt, err, resource, redirectedUrl); |
}.bind(this); |
xhr.load(url, receiveXhr); |
// TODO(sorvell): blocked on) |
@@ -10280,16 +10512,25 @@ window.HTMLImports = window.HTMLImports || {flags:{}}; |
*/ |
} |
}, |
- receive: function(url, elt, err, resource) { |
+ receive: function(url, elt, err, resource, redirectedUrl) { |
this.cache[url] = resource; |
var $p = this.pending[url]; |
+ if ( redirectedUrl && redirectedUrl !== url ) { |
+ this.cache[redirectedUrl] = resource; |
+ $p = $p.concat(this.pending[redirectedUrl]); |
+ } |
for (var i=0, l=$p.length, p; (i<l) && (p=$p[i]); i++) { |
//if (!err) { |
- this.onload(url, p, resource); |
+ // If url was redirected, use the redirected location so paths are |
+ // calculated relative to that. |
+ this.onload(redirectedUrl || url, p, resource); |
//} |
this.tail(); |
} |
this.pending[url] = null; |
+ if ( redirectedUrl && redirectedUrl !== url ) { |
+ this.pending[redirectedUrl] = null; |
+ } |
}, |
tail: function() { |
--this.inflight; |
@@ -10317,8 +10558,17 @@ window.HTMLImports = window.HTMLImports || {flags:{}}; |
request.open('GET', url, xhr.async); |
request.addEventListener('readystatechange', function(e) { |
if (request.readyState === 4) { |
+ // Servers redirecting an import can add a Location header to help us |
+ // polyfill correctly. |
+ var locationHeader = request.getResponseHeader("Location"); |
+ var redirectedUrl = null; |
+ if (locationHeader) { |
+ var redirectedUrl = (locationHeader.substr( 0, 1 ) === "/") |
+ ? location.origin + locationHeader // Location is a relative path |
+ : redirectedUrl; // Full path |
+ } |
next.call(nextContext, !xhr.ok(request) && request, |
- request.response || request.responseText, url); |
+ request.response || request.responseText, redirectedUrl); |
} |
}); |
request.send(); |
@@ -10392,9 +10642,14 @@ var importParser = { |
fn.call(this, elt); |
} |
}, |
- // only 1 element may be parsed at a time; parsing is async so, each |
+ // only 1 element may be parsed at a time; parsing is async so each |
// parsing implementation must inform the system that parsing is complete |
// via markParsingComplete. |
+ // To prompt the system to parse the next element, parseNext should then be |
+ // called. |
+ // Note, parseNext used to be included at the end of markParsingComplete, but |
+ // we must not do this so that, for example, we can (1) mark parsing complete |
+ // then (2) fire an import load event, and then (3) parse the next resource. |
markParsing: function(elt) { |
flags.parse && console.log('parsing', elt); |
this.parsingElement = elt; |
@@ -10406,16 +10661,31 @@ var importParser = { |
} |
this.parsingElement = null; |
flags.parse && console.log('completed', elt); |
- this.parseNext(); |
+ }, |
+ invalidateParse: function(doc) { |
+ if (doc && doc.__importLink) { |
+ doc.__importParsed = doc.__importLink.__importParsed = false; |
+ this.parseSoon(); |
+ } |
+ }, |
+ parseSoon: function() { |
+ if (this._parseSoon) { |
+ cancelAnimationFrame(this._parseDelay); |
+ } |
+ var parser = this; |
+ this._parseSoon = requestAnimationFrame(function() { |
+ parser.parseNext(); |
+ }); |
}, |
parseImport: function(elt) { |
- elt.import.__importParsed = true; |
// TODO(sorvell): consider if there's a better way to do this; |
// expose an imports parsing hook; this is needed, for example, by the |
// CustomElements polyfill. |
if (HTMLImports.__importsParsingHook) { |
HTMLImports.__importsParsingHook(elt); |
} |
+ elt.import.__importParsed = true; |
+ this.markParsingComplete(elt); |
// fire load event |
if (elt.__resource) { |
elt.dispatchEvent(new CustomEvent('load', {bubbles: false})); |
@@ -10433,7 +10703,7 @@ var importParser = { |
} |
} |
} |
- this.markParsingComplete(elt); |
+ this.parseNext(); |
}, |
parseLink: function(linkElt) { |
if (nodeIsImport(linkElt)) { |
@@ -10463,6 +10733,7 @@ var importParser = { |
callback(e); |
} |
self.markParsingComplete(elt); |
+ self.parseNext(); |
}; |
elt.addEventListener('load', done); |
elt.addEventListener('error', done); |
@@ -10934,6 +11205,7 @@ license that can be found in the LICENSE file. |
var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE; |
var importSelector = 'link[rel=' + IMPORT_LINK_TYPE + ']'; |
var importer = scope.importer; |
+var parser = scope.parser; |
// we track mutations for addedNodes, looking for imports |
function handler(mutations) { |
@@ -10946,7 +11218,9 @@ function handler(mutations) { |
// find loadable elements and add them to the importer |
function addedNodes(nodes) { |
+ var owner; |
for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) { |
+ owner = owner || n.ownerDocument; |
if (shouldLoadNode(n)) { |
importer.loadNode(n); |
} |
@@ -10954,6 +11228,14 @@ function addedNodes(nodes) { |
addedNodes(n.children); |
} |
} |
+ // TODO(sorvell): This is not the right approach here. We shouldn't need to |
+ // invalidate parsing when an element is added. Disabling this code |
+ // until a better approach is found. |
+ /* |
+ if (owner) { |
+ parser.invalidateParse(owner); |
+ } |
+ */ |
} |
function shouldLoadNode(node) { |
@@ -11418,12 +11700,9 @@ var flags = scope.flags; |
// native document.registerElement? |
var hasNative = Boolean(document.registerElement); |
-// TODO(sorvell): See https://github.com/Polymer/polymer/issues/399 |
-// we'll address this by defaulting to CE polyfill in the presence of the SD |
-// polyfill. This will avoid spamming excess attached/detached callbacks. |
-// If there is a compelling need to run CE native with SD polyfill, |
-// we'll need to fix this issue. |
-var useNative = !flags.register && hasNative && !window.ShadowDOMPolyfill; |
+// For consistent timing, use native custom elements only when not polyfilling |
+// other key related web components features. |
+var useNative = !flags.register && hasNative && !window.ShadowDOMPolyfill && (!window.HTMLImports || HTMLImports.useNative); |
if (useNative) { |
@@ -11713,6 +11992,7 @@ if (useNative) { |
// https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/ |
// index.html#dfn-attribute-changed-callback |
function changeAttribute(name, value, operation) { |
+ name = name.toLowerCase(); |
var oldValue = this.getAttribute(name); |
operation.apply(this, arguments); |
var newValue = this.getAttribute(name); |
@@ -13193,9 +13473,6 @@ scope.styleResolver = styleResolver; |
}; |
}, |
- // TODO(rafaelw): Assigning .bindingDelegate always succeeds. It may |
- // make sense to issue a warning or even throw if the template is already |
- // "activated", since this would be a strange thing to do. |
set bindingDelegate(bindingDelegate) { |
if (this.delegate_) { |
throw Error('Template must be cleared before a new bindingDelegate ' + |