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 6fa2b7b4f1830554045bbb76fead092f966584bc..9fa19b5c3d0c77a98d5e5a12c59c39c79628b508 100644 |
--- a/pkg/web_components/lib/platform.concat.js |
+++ b/pkg/web_components/lib/platform.concat.js |
@@ -52,12 +52,6 @@ if (typeof WeakMap === 'undefined') { |
(function(global) { |
'use strict'; |
- var PROP_ADD_TYPE = 'add'; |
- var PROP_UPDATE_TYPE = 'update'; |
- var PROP_RECONFIGURE_TYPE = 'reconfigure'; |
- var PROP_DELETE_TYPE = 'delete'; |
- var ARRAY_SPLICE_TYPE = 'splice'; |
- |
// Detect and do basic sanity checking on Object/Array.observe. |
function detectObjectObserve() { |
if (typeof Object.observe !== 'function' || |
@@ -72,44 +66,29 @@ if (typeof WeakMap === 'undefined') { |
} |
var test = {}; |
+ var arr = []; |
Object.observe(test, callback); |
+ Array.observe(arr, callback); |
test.id = 1; |
test.id = 2; |
delete test.id; |
+ arr.push(1, 2); |
+ arr.length = 0; |
+ |
Object.deliverChangeRecords(callback); |
- if (records.length !== 3) |
+ if (records.length !== 5) |
return false; |
- // TODO(rafaelw): Remove this when new change record type names make it to |
- // chrome release. |
- if (records[0].type == 'new' && |
- records[1].type == 'updated' && |
- records[2].type == 'deleted') { |
- PROP_ADD_TYPE = 'new'; |
- PROP_UPDATE_TYPE = 'updated'; |
- PROP_RECONFIGURE_TYPE = 'reconfigured'; |
- PROP_DELETE_TYPE = 'deleted'; |
- } else if (records[0].type != 'add' || |
- records[1].type != 'update' || |
- records[2].type != 'delete') { |
- console.error('Unexpected change record names for Object.observe. ' + |
- 'Using dirty-checking instead'); |
+ if (records[0].type != 'add' || |
+ records[1].type != 'update' || |
+ records[2].type != 'delete' || |
+ records[3].type != 'splice' || |
+ records[4].type != 'splice') { |
return false; |
} |
- Object.unobserve(test, callback); |
- test = [0]; |
- Array.observe(test, callback); |
- test[1] = 1; |
- test.length = 0; |
- Object.deliverChangeRecords(callback); |
- if (records.length != 2) |
- return false; |
- if (records[0].type != ARRAY_SPLICE_TYPE || |
- records[1].type != ARRAY_SPLICE_TYPE) { |
- return false; |
- } |
- Array.unobserve(test, callback); |
+ Object.unobserve(test, callback); |
+ Array.unobserve(arr, callback); |
return true; |
} |
@@ -268,7 +247,7 @@ if (typeof WeakMap === 'undefined') { |
for (var i = 0; i < this.length; i++) { |
if (i) |
obj = obj[this[i - 1]]; |
- if (!obj) |
+ if (!isObject(obj)) |
return; |
observe(obj); |
} |
@@ -478,7 +457,7 @@ if (typeof WeakMap === 'undefined') { |
var resetScheduled = false; |
function observe(obj) { |
- if (!isObject(obj)) |
+ if (!obj) |
return; |
var index = toRemove.indexOf(obj); |
@@ -679,7 +658,14 @@ if (typeof WeakMap === 'undefined') { |
var runningMicrotaskCheckpoint = false; |
- var hasDebugForceFullDelivery = typeof Object.deliverAllChangeRecords == 'function'; |
+ var hasDebugForceFullDelivery = hasObserve && (function() { |
+ try { |
+ eval('%RunMicrotasks()'); |
+ return true; |
+ } catch (ex) { |
+ return false; |
+ } |
+ })(); |
global.Platform = global.Platform || {}; |
@@ -688,7 +674,7 @@ if (typeof WeakMap === 'undefined') { |
return; |
if (hasDebugForceFullDelivery) { |
- Object.deliverAllChangeRecords(); |
+ eval('%RunMicrotasks()'); |
return; |
} |
@@ -1118,10 +1104,11 @@ if (typeof WeakMap === 'undefined') { |
} |
} |
- var expectedRecordTypes = {}; |
- expectedRecordTypes[PROP_ADD_TYPE] = true; |
- expectedRecordTypes[PROP_UPDATE_TYPE] = true; |
- expectedRecordTypes[PROP_DELETE_TYPE] = true; |
+ var expectedRecordTypes = { |
+ add: true, |
+ update: true, |
+ delete: true |
+ }; |
function notifyFunction(object, name) { |
if (typeof Object.observe !== 'function') |
@@ -1145,7 +1132,7 @@ if (typeof WeakMap === 'undefined') { |
var value = observable.open(function(newValue, oldValue) { |
value = newValue; |
if (notify) |
- notify(PROP_UPDATE_TYPE, oldValue); |
+ notify('update', oldValue); |
}); |
Object.defineProperty(target, name, { |
@@ -1187,10 +1174,10 @@ if (typeof WeakMap === 'undefined') { |
if (!(record.name in oldValues)) |
oldValues[record.name] = record.oldValue; |
- if (record.type == PROP_UPDATE_TYPE) |
+ if (record.type == 'update') |
continue; |
- if (record.type == PROP_ADD_TYPE) { |
+ if (record.type == 'add') { |
if (record.name in removed) |
delete removed[record.name]; |
else |
@@ -1587,12 +1574,12 @@ if (typeof WeakMap === 'undefined') { |
for (var i = 0; i < changeRecords.length; i++) { |
var record = changeRecords[i]; |
switch(record.type) { |
- case ARRAY_SPLICE_TYPE: |
+ case 'splice': |
mergeSplice(splices, record.index, record.removed.slice(), record.addedCount); |
break; |
- case PROP_ADD_TYPE: |
- case PROP_UPDATE_TYPE: |
- case PROP_DELETE_TYPE: |
+ case 'add': |
+ case 'update': |
+ case 'delete': |
if (!isIndex(record.name)) |
continue; |
var index = toNumber(record.name); |
@@ -1641,16 +1628,6 @@ if (typeof WeakMap === 'undefined') { |
global.CompoundObserver = CompoundObserver; |
global.Path = Path; |
global.ObserverTransform = ObserverTransform; |
- |
- // TODO(rafaelw): Only needed for testing until new change record names |
- // make it to release. |
- global.Observer.changeRecordTypes = { |
- add: PROP_ADD_TYPE, |
- update: PROP_UPDATE_TYPE, |
- reconfigure: PROP_RECONFIGURE_TYPE, |
- 'delete': PROP_DELETE_TYPE, |
- splice: ARRAY_SPLICE_TYPE |
- }; |
})(typeof global !== 'undefined' && global && typeof module !== 'undefined' && module ? global : this || window); |
// prepoulate window.Platform.flags for default controls |
@@ -1666,7 +1643,8 @@ window.logFlags = window.logFlags || {}; |
o = o.split('='); |
o[0] && (flags[o[0]] = o[1] || true); |
}); |
- var entryPoint = document.currentScript || document.querySelector('script[src*="platform.js"]'); |
+ var entryPoint = document.currentScript || |
+ document.querySelector('script[src*="platform.js"]'); |
if (entryPoint) { |
var a = entryPoint.attributes; |
for (var i = 0, n; i < a.length; i++) { |
@@ -1684,13 +1662,19 @@ window.logFlags = window.logFlags || {}; |
// If any of these flags match 'native', then force native ShadowDOM; any |
// other truthy value, or failure to detect native |
// ShadowDOM, results in polyfill |
- flags.shadow = (flags.shadow || flags.shadowdom || flags.polyfill); |
+ flags.shadow = flags.shadow || flags.shadowdom || flags.polyfill; |
if (flags.shadow === 'native') { |
flags.shadow = false; |
} else { |
flags.shadow = flags.shadow || !HTMLElement.prototype.createShadowRoot; |
} |
+ if (flags.shadow && document.querySelectorAll('script').length > 1) { |
+ console.warn('platform.js is not the first script on the page. ' + |
+ 'See http://www.polymer-project.org/docs/start/platform.html#setup ' + |
+ 'for details.'); |
+ } |
+ |
// CustomElements polyfill flag |
if (flags.register) { |
window.CustomElements = window.CustomElements || {flags: {}}; |
@@ -2570,6 +2554,9 @@ window.ShadowDOMPolyfill = {}; |
function setTreeScope(node, treeScope) { |
if (node.treeScope_ !== treeScope) { |
node.treeScope_ = treeScope; |
+ for (var sr = node.shadowRoot; sr; sr = sr.olderShadowRoot) { |
+ sr.treeScope_.parent = treeScope; |
+ } |
for (var child = node.firstChild; child; child = child.nextSibling) { |
setTreeScope(child, treeScope); |
} |
@@ -2766,8 +2753,17 @@ window.ShadowDOMPolyfill = {}; |
if (handledEventsTable.get(originalEvent)) |
return; |
handledEventsTable.set(originalEvent, true); |
+ dispatchEvent(wrap(originalEvent), wrap(originalEvent.target)); |
+ } |
- return dispatchEvent(wrap(originalEvent), wrap(originalEvent.target)); |
+ function isLoadLikeEvent(event) { |
+ switch (event.type) { |
+ case 'beforeunload': |
+ case 'load': |
+ case 'unload': |
+ return true; |
+ } |
+ return false; |
} |
function dispatchEvent(event, originalWrapperTarget) { |
@@ -2779,15 +2775,15 @@ window.ShadowDOMPolyfill = {}; |
scope.renderAllPending(); |
var eventPath = retarget(originalWrapperTarget); |
- // For window load events the load event is dispatched at the window but |
+ // For window "load" events the "load" event is dispatched at the window but |
// the target is set to the document. |
// |
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#the-end |
// |
// TODO(arv): Find a less hacky way to do this. |
- if (event.type === 'load' && |
- eventPath.length === 2 && |
- eventPath[0].target instanceof wrappers.Document) { |
+ if (eventPath.length === 2 && |
+ eventPath[0].target instanceof wrappers.Document && |
+ isLoadLikeEvent(event)) { |
eventPath.shift(); |
} |
@@ -2857,17 +2853,26 @@ window.ShadowDOMPolyfill = {}; |
if ('relatedTarget' in event) { |
var originalEvent = unwrap(event); |
+ var unwrappedRelatedTarget = originalEvent.relatedTarget; |
+ |
// X-Tag sets relatedTarget on a CustomEvent. If they do that there is no |
// way to have relatedTarget return the adjusted target but worse is that |
// the originalEvent might not have a relatedTarget so we hit an assert |
// when we try to wrap it. |
- if (originalEvent.relatedTarget) { |
- var relatedTarget = wrap(originalEvent.relatedTarget); |
- |
- var adjusted = adjustRelatedTarget(currentTarget, relatedTarget); |
- if (adjusted === target) |
- return true; |
- |
+ if (unwrappedRelatedTarget) { |
+ // In IE we can get objects that are not EventTargets at this point. |
+ // Safari does not have an EventTarget interface so revert to checking |
+ // for addEventListener as an approximation. |
+ if (unwrappedRelatedTarget instanceof Object && |
+ unwrappedRelatedTarget.addEventListener) { |
+ var relatedTarget = wrap(unwrappedRelatedTarget); |
+ |
+ var adjusted = adjustRelatedTarget(currentTarget, relatedTarget); |
+ if (adjusted === target) |
+ return true; |
+ } else { |
+ adjusted = null; |
+ } |
relatedTargetTable.set(event, adjusted); |
} |
} |
@@ -2954,10 +2959,14 @@ window.ShadowDOMPolyfill = {}; |
* @constructor |
*/ |
function Event(type, options) { |
- if (type instanceof OriginalEvent) |
- this.impl = type; |
- else |
+ if (type instanceof OriginalEvent) { |
+ var impl = type; |
+ if (!OriginalBeforeUnloadEvent && impl.type === 'beforeunload') |
+ return new BeforeUnloadEvent(impl); |
+ this.impl = impl; |
+ } else { |
return wrap(constructEvent(OriginalEvent, 'Event', type, options)); |
+ } |
} |
Event.prototype = { |
get target() { |
@@ -3040,7 +3049,11 @@ window.ShadowDOMPolyfill = {}; |
var relatedTargetProto = { |
get relatedTarget() { |
- return relatedTargetTable.get(this) || wrap(unwrap(this).relatedTarget); |
+ var relatedTarget = relatedTargetTable.get(this); |
+ // relatedTarget can be null. |
+ if (relatedTarget !== undefined) |
+ return relatedTarget; |
+ return wrap(unwrap(this).relatedTarget); |
} |
}; |
@@ -3129,8 +3142,12 @@ window.ShadowDOMPolyfill = {}; |
configureEventConstructor('FocusEvent', {relatedTarget: null}, 'UIEvent'); |
} |
+ // Safari 7 does not yet have BeforeUnloadEvent. |
+ // https://bugs.webkit.org/show_bug.cgi?id=120849 |
+ var OriginalBeforeUnloadEvent = window.BeforeUnloadEvent; |
+ |
function BeforeUnloadEvent(impl) { |
- Event.call(this); |
+ Event.call(this, impl); |
} |
BeforeUnloadEvent.prototype = Object.create(Event.prototype); |
mixin(BeforeUnloadEvent.prototype, { |
@@ -3142,6 +3159,9 @@ window.ShadowDOMPolyfill = {}; |
} |
}); |
+ if (OriginalBeforeUnloadEvent) |
+ registerWrapper(OriginalBeforeUnloadEvent, BeforeUnloadEvent); |
+ |
function isValidListener(fun) { |
if (typeof fun === 'function') |
return true; |
@@ -3470,6 +3490,7 @@ window.ShadowDOMPolyfill = {}; |
var registerWrapper = scope.registerWrapper; |
var setTreeScope = scope.setTreeScope; |
var unwrap = scope.unwrap; |
+ var unwrapIfNeeded = scope.unwrapIfNeeded; |
var wrap = scope.wrap; |
var wrapIfNeeded = scope.wrapIfNeeded; |
var wrappers = scope.wrappers; |
@@ -4118,7 +4139,8 @@ window.ShadowDOMPolyfill = {}; |
compareDocumentPosition: function(otherNode) { |
// This only wraps, it therefore only operates on the composed DOM and not |
// the logical DOM. |
- return originalCompareDocumentPosition.call(this.impl, unwrap(otherNode)); |
+ return originalCompareDocumentPosition.call(this.impl, |
+ unwrapIfNeeded(otherNode)); |
}, |
normalize: function() { |
@@ -4305,6 +4327,12 @@ window.ShadowDOMPolyfill = {}; |
} |
wrapperList.length = i; |
return wrapperList; |
+ }, |
+ |
+ remove: function() { |
+ var p = this.parentNode; |
+ if (p) |
+ p.removeChild(this); |
} |
}; |
@@ -5247,8 +5275,14 @@ window.ShadowDOMPolyfill = {}; |
remove: function(indexOrNode) { |
// Spec only allows index but implementations allow index or node. |
// remove() is also allowed which is same as remove(undefined) |
+ if (indexOrNode === undefined) { |
+ HTMLElement.prototype.remove.call(this); |
+ return; |
+ } |
+ |
if (typeof indexOrNode === 'object') |
indexOrNode = unwrap(indexOrNode); |
+ |
unwrap(this).remove(indexOrNode); |
}, |
@@ -6768,7 +6802,15 @@ window.ShadowDOMPolyfill = {}; |
if (document.registerElement) { |
var originalRegisterElement = document.registerElement; |
Document.prototype.registerElement = function(tagName, object) { |
- var prototype = object.prototype; |
+ var prototype, extendsOption; |
+ if (object !== undefined) { |
+ prototype = object.prototype; |
+ extendsOption = object.extends; |
+ } |
+ |
+ if (!prototype) |
+ prototype = Object.create(HTMLElement.prototype); |
+ |
// If we already used the object as a prototype for another custom |
// element. |
@@ -6829,13 +6871,13 @@ window.ShadowDOMPolyfill = {}; |
}); |
var p = {prototype: newPrototype}; |
- if (object.extends) |
- p.extends = object.extends; |
+ if (extendsOption) |
+ p.extends = extendsOption; |
function CustomElementConstructor(node) { |
if (!node) { |
- if (object.extends) { |
- return document.createElement(object.extends, tagName); |
+ if (extendsOption) { |
+ return document.createElement(extendsOption, tagName); |
} else { |
return document.createElement(tagName); |
} |
@@ -7037,6 +7079,31 @@ 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'; |
+ |
+ var unwrap = scope.unwrap; |
+ |
+ // DataTransfer (Clipboard in old Blink/WebKit) has a single method that |
+ // requires wrapping. Since it is only a method we do not need a real wrapper, |
+ // we can just override the method. |
+ |
+ var OriginalDataTransfer = window.DataTransfer || window.Clipboard; |
+ var OriginalDataTransferSetDragImage = |
+ OriginalDataTransfer.prototype.setDragImage; |
+ |
+ OriginalDataTransfer.prototype.setDragImage = function(image, x, y) { |
+ OriginalDataTransferSetDragImage.call(this, unwrap(image), x, y); |
+ }; |
+ |
+})(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. |
@@ -7207,7 +7274,7 @@ window.ShadowDOMPolyfill = {}; |
Shimmed features: |
- * :host, :ancestor: ShadowDOM allows styling of the shadowRoot's host |
+ * :host, :host-context: ShadowDOM allows styling of the shadowRoot's host |
element using the :host rule. To shim this feature, the :host styles are |
reformatted and prefixed with a given scope name and promoted to a |
document level stylesheet. |
@@ -7381,7 +7448,7 @@ var ShadowCSS = { |
def.rootStyles = styles; |
def.scopeStyles = def.rootStyles; |
var extendee = this.registry[def.extendsName]; |
- if (extendee && (!root || root.querySelector('shadow'))) { |
+ if (extendee) { |
def.scopeStyles = extendee.scopeStyles.concat(def.scopeStyles); |
} |
return def; |
@@ -7476,7 +7543,7 @@ var ShadowCSS = { |
var unscoped = this.extractUnscopedRulesFromCssText(cssText); |
cssText = this.insertPolyfillHostInCssText(cssText); |
cssText = this.convertColonHost(cssText); |
- cssText = this.convertColonAncestor(cssText); |
+ cssText = this.convertColonHostContext(cssText); |
cssText = this.convertCombinators(cssText); |
if (scopeSelector) { |
var self = this, cssText; |
@@ -7525,7 +7592,7 @@ var ShadowCSS = { |
this.colonHostPartReplacer); |
}, |
/* |
- * convert a rule like :ancestor(.foo) > .bar { } |
+ * convert a rule like :host-context(.foo) > .bar { } |
* |
* to |
* |
@@ -7533,15 +7600,15 @@ var ShadowCSS = { |
* |
* and |
* |
- * :ancestor(.foo:host) .bar { ... } |
+ * :host-context(.foo:host) .bar { ... } |
* |
* to |
* |
* scopeName.foo .bar { ... } |
*/ |
- convertColonAncestor: function(cssText) { |
- return this.convertColonRule(cssText, cssColonAncestorRe, |
- this.colonAncestorPartReplacer); |
+ convertColonHostContext: function(cssText) { |
+ return this.convertColonRule(cssText, cssColonHostContextRe, |
+ this.colonHostContextPartReplacer); |
}, |
convertColonRule: function(cssText, regExp, partReplacer) { |
// p1 = :host, p2 = contents of (), p3 rest of rule |
@@ -7559,7 +7626,7 @@ var ShadowCSS = { |
} |
}); |
}, |
- colonAncestorPartReplacer: function(host, part, suffix) { |
+ colonHostContextPartReplacer: function(host, part, suffix) { |
if (part.match(polyfillHost)) { |
return this.colonHostPartReplacer(host, part, suffix); |
} else { |
@@ -7573,7 +7640,10 @@ var ShadowCSS = { |
* Convert ^ and ^^ combinators by replacing with space. |
*/ |
convertCombinators: function(cssText) { |
- return cssText.replace(/\^\^/g, ' ').replace(/\^/g, ' '); |
+ for (var i=0; i < combinatorsRe.length; i++) { |
+ cssText = cssText.replace(combinatorsRe[i], ' '); |
+ } |
+ return cssText; |
}, |
// change a selector like 'div' to 'name div' |
scopeRules: function(cssRules, scopeSelector) { |
@@ -7646,17 +7716,31 @@ var ShadowCSS = { |
return scoped; |
}, |
insertPolyfillHostInCssText: function(selector) { |
- return selector.replace(hostRe, polyfillHost).replace(colonHostRe, |
- polyfillHost).replace(colonAncestorRe, polyfillAncestor); |
+ return selector.replace(colonHostContextRe, polyfillHostContext).replace( |
+ colonHostRe, polyfillHost); |
}, |
propertiesFromRule: function(rule) { |
+ var cssText = rule.style.cssText; |
// TODO(sorvell): Safari cssom incorrectly removes quotes from the content |
// property. (https://bugs.webkit.org/show_bug.cgi?id=118045) |
- if (rule.style.content && !rule.style.content.match(/['"]+/)) { |
- return rule.style.cssText.replace(/content:[^;]*;/g, 'content: \'' + |
+ // don't replace attr rules |
+ if (rule.style.content && !rule.style.content.match(/['"]+|attr/)) { |
+ cssText = cssText.replace(/content:[^;]*;/g, 'content: \'' + |
rule.style.content + '\';'); |
} |
- return rule.style.cssText; |
+ // TODO(sorvell): we can workaround this issue here, but we need a list |
+ // of troublesome properties to fix https://github.com/Polymer/platform/issues/53 |
+ // |
+ // inherit rules can be omitted from cssText |
+ // TODO(sorvell): remove when Blink bug is fixed: |
+ // https://code.google.com/p/chromium/issues/detail?id=358273 |
+ var style = rule.style; |
+ for (var i in style) { |
+ if (style[i] === 'initial') { |
+ cssText += i + ': initial; '; |
+ } |
+ } |
+ return cssText; |
}, |
replaceTextInStyles: function(styles, action) { |
if (styles && action) { |
@@ -7692,21 +7776,28 @@ var selectorRe = /([^{]*)({[\s\S]*?})/gim, |
cssPartRe = /::part\(([^)]*)\)/gim, |
// note: :host pre-processed to -shadowcsshost. |
polyfillHost = '-shadowcsshost', |
- // note: :ancestor pre-processed to -shadowcssancestor. |
- polyfillAncestor = '-shadowcssancestor', |
+ // note: :host-context pre-processed to -shadowcsshostcontext. |
+ polyfillHostContext = '-shadowcsscontext', |
parenSuffix = ')(?:\\((' + |
'(?:\\([^)(]*\\)|[^)(]*)+?' + |
')\\))?([^,{]*)'; |
cssColonHostRe = new RegExp('(' + polyfillHost + parenSuffix, 'gim'), |
- cssColonAncestorRe = new RegExp('(' + polyfillAncestor + parenSuffix, 'gim'), |
+ cssColonHostContextRe = new RegExp('(' + polyfillHostContext + parenSuffix, 'gim'), |
selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$', |
- hostRe = /@host/gim, |
colonHostRe = /\:host/gim, |
- colonAncestorRe = /\:ancestor/gim, |
+ colonHostContextRe = /\:host-context/gim, |
/* host name without combinator */ |
polyfillHostNoCombinator = polyfillHost + '-no-combinator', |
polyfillHostRe = new RegExp(polyfillHost, 'gim'), |
- polyfillAncestorRe = new RegExp(polyfillAncestor, 'gim'); |
+ polyfillHostContextRe = new RegExp(polyfillHostContext, 'gim'), |
+ combinatorsRe = [ |
+ /\^\^/g, |
+ /\^/g, |
+ /\/shadow\//g, |
+ /\/shadow-deep\//g, |
+ /::shadow/g, |
+ /\/deep\//g |
+ ]; |
function stylesToCssText(styles, preserveComments) { |
var cssText = ''; |
@@ -7918,30 +8009,18 @@ scope.ShadowCSS = ShadowCSS; |
window.wrap = window.unwrap = function(n){ |
return n; |
} |
- |
- var originalCreateShadowRoot = Element.prototype.webkitCreateShadowRoot; |
- Element.prototype.webkitCreateShadowRoot = function() { |
- var elderRoot = this.webkitShadowRoot; |
- var root = originalCreateShadowRoot.call(this); |
- root.olderShadowRoot = elderRoot; |
- root.host = this; |
- CustomElements.watchShadow(this); |
- return root; |
- } |
- |
- Object.defineProperties(Element.prototype, { |
- shadowRoot: { |
- get: function() { |
- return this.webkitShadowRoot; |
- } |
- }, |
- createShadowRoot: { |
- value: function() { |
- return this.webkitCreateShadowRoot(); |
- } |
+ |
+ addEventListener('DOMContentLoaded', function() { |
+ if (CustomElements.useNative === false) { |
+ var originalCreateShadowRoot = Element.prototype.createShadowRoot; |
+ Element.prototype.createShadowRoot = function() { |
+ var root = originalCreateShadowRoot.call(this); |
+ CustomElements.watchShadow(this); |
+ return root; |
+ }; |
} |
}); |
- |
+ |
window.templateContent = function(inTemplate) { |
// if MDV exists, it may need to boostrap this template to reveal content |
if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) { |
@@ -9789,23 +9868,38 @@ window.HTMLImports = window.HTMLImports || {flags:{}}; |
}, |
fetch: function(url, elt) { |
flags.load && console.log('fetch', url, elt); |
- var receiveXhr = function(err, resource) { |
- this.receive(url, elt, err, resource); |
- }.bind(this); |
- xhr.load(url, receiveXhr); |
- // TODO(sorvell): blocked on |
- // https://code.google.com/p/chromium/issues/detail?id=257221 |
- // xhr'ing for a document makes scripts in imports runnable; otherwise |
- // they are not; however, it requires that we have doctype=html in |
- // the import which is unacceptable. This is only needed on Chrome |
- // to avoid the bug above. |
- /* |
- if (isDocumentLink(elt)) { |
- xhr.loadDocument(url, receiveXhr); |
+ if (url.match(/^data:/)) { |
+ // Handle Data URI Scheme |
+ var pieces = url.split(','); |
+ var header = pieces[0]; |
+ var body = pieces[1]; |
+ if(header.indexOf(';base64') > -1) { |
+ body = atob(body); |
+ } else { |
+ body = decodeURIComponent(body); |
+ } |
+ setTimeout(function() { |
+ this.receive(url, elt, null, body); |
+ }.bind(this), 0); |
} else { |
+ var receiveXhr = function(err, resource) { |
+ this.receive(url, elt, err, resource); |
+ }.bind(this); |
xhr.load(url, receiveXhr); |
+ // TODO(sorvell): blocked on) |
+ // https://code.google.com/p/chromium/issues/detail?id=257221 |
+ // xhr'ing for a document makes scripts in imports runnable; otherwise |
+ // they are not; however, it requires that we have doctype=html in |
+ // the import which is unacceptable. This is only needed on Chrome |
+ // to avoid the bug above. |
+ /* |
+ if (isDocumentLink(elt)) { |
+ xhr.loadDocument(url, receiveXhr); |
+ } else { |
+ xhr.load(url, receiveXhr); |
+ } |
+ */ |
} |
- */ |
}, |
receive: function(url, elt, err, resource) { |
this.cache[url] = resource; |
@@ -10930,6 +11024,7 @@ if (useNative) { |
scope.upgradeDocument = nop; |
scope.upgradeDocumentTree = nop; |
scope.takeRecords = nop; |
+ scope.reservedTagList = []; |
} else { |
@@ -10986,6 +11081,10 @@ if (useNative) { |
// offer guidance) |
throw new Error('document.registerElement: first argument (\'name\') must contain a dash (\'-\'). Argument provided was \'' + String(name) + '\'.'); |
} |
+ // prevent registering reserved names |
+ if (isReservedTag(name)) { |
+ throw new Error('Failed to execute \'registerElement\' on \'Document\': Registration failed for type \'' + String(name) + '\'. The type name is invalid.'); |
+ } |
// elements may only be registered once |
if (getRegisteredDefinition(name)) { |
throw new Error('DuplicateDefinitionError: a type with name \'' + String(name) + '\' is already registered'); |
@@ -11029,6 +11128,19 @@ if (useNative) { |
return definition.ctor; |
} |
+ function isReservedTag(name) { |
+ for (var i = 0; i < reservedTagList.length; i++) { |
+ if (name === reservedTagList[i]) { |
+ return true; |
+ } |
+ } |
+ } |
+ |
+ var reservedTagList = [ |
+ 'annotation-xml', 'color-profile', 'font-face', 'font-face-src', |
+ 'font-face-uri', 'font-face-format', 'font-face-name', 'missing-glyph' |
+ ]; |
+ |
function ancestry(extnds) { |
var extendee = getRegisteredDefinition(extnds); |
if (extendee) { |
@@ -11325,6 +11437,7 @@ if (!Object.__proto__ && !useNative) { |
// exports |
scope.instanceof = isInstance; |
+scope.reservedTagList = reservedTagList; |
// bc |
document.register = document.registerElement; |
@@ -11758,7 +11871,7 @@ scope.styleResolver = styleResolver; |
*/ |
(function() { |
function shadowSelector(v) { |
- return 'body ^^ ' + selector(v); |
+ return 'body /shadow-deep/ ' + selector(v); |
} |
function selector(v) { |
return '[touch-action="' + v + '"]'; |
@@ -11780,18 +11893,31 @@ scope.styleResolver = styleResolver; |
} |
]; |
var styles = ''; |
- attrib2css.forEach(function(r) { |
- if (String(r) === r) { |
- styles += selector(r) + rule(r) + '\n'; |
- styles += shadowSelector(r) + rule(r) + '\n'; |
- } else { |
- styles += r.selectors.map(selector) + rule(r.rule) + '\n'; |
- styles += r.selectors.map(shadowSelector) + rule(r.rule) + '\n'; |
- } |
- }); |
- var el = document.createElement('style'); |
- el.textContent = styles; |
- document.head.appendChild(el); |
+ // only install stylesheet if the browser has touch action support |
+ var head = document.head; |
+ var hasNativePE = window.PointerEvent || window.MSPointerEvent; |
+ // only add shadow selectors if shadowdom is supported |
+ var hasShadowRoot = !window.ShadowDOMPolyfill && document.head.createShadowRoot; |
+ |
+ if (hasNativePE) { |
+ attrib2css.forEach(function(r) { |
+ if (String(r) === r) { |
+ styles += selector(r) + rule(r) + '\n'; |
+ if (hasShadowRoot) { |
+ styles += shadowSelector(r) + rule(r) + '\n'; |
+ } |
+ } else { |
+ styles += r.selectors.map(selector) + rule(r.rule) + '\n'; |
+ if (hasShadowRoot) { |
+ styles += r.selectors.map(shadowSelector) + rule(r.rule) + '\n'; |
+ } |
+ } |
+ }); |
+ |
+ var el = document.createElement('style'); |
+ el.textContent = styles; |
+ document.head.appendChild(el); |
+ } |
})(); |
/* |
@@ -11815,16 +11941,6 @@ scope.styleResolver = styleResolver; |
* @return {Event} A new PointerEvent of type `inType` and initialized with properties from `inDict`. |
*/ |
(function(scope) { |
- // test for DOM Level 4 Events |
- var NEW_MOUSE_EVENT = false; |
- var HAS_BUTTONS = false; |
- try { |
- var ev = new MouseEvent('click', {buttons: 1}); |
- NEW_MOUSE_EVENT = true; |
- HAS_BUTTONS = ev.buttons === 1; |
- ev = null; |
- } catch(e) { |
- } |
var MOUSE_PROPS = [ |
'bubbles', |
@@ -11841,6 +11957,8 @@ scope.styleResolver = styleResolver; |
'metaKey', |
'button', |
'relatedTarget', |
+ 'pageX', |
+ 'pageY' |
]; |
var MOUSE_DEFAULTS = [ |
@@ -11857,73 +11975,23 @@ scope.styleResolver = styleResolver; |
false, |
false, |
0, |
- null |
+ null, |
+ 0, |
+ 0 |
]; |
function PointerEvent(inType, inDict) { |
- inDict = inDict || {}; |
- // According to the w3c spec, |
- // http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-button |
- // MouseEvent.button == 0 can mean either no mouse button depressed, or the |
- // left mouse button depressed. |
- // |
- // As of now, the only way to distinguish between the two states of |
- // MouseEvent.button is by using the deprecated MouseEvent.which property, as |
- // this maps mouse buttons to positive integers > 0, and uses 0 to mean that |
- // no mouse button is held. |
- // |
- // MouseEvent.which is derived from MouseEvent.button at MouseEvent creation, |
- // but initMouseEvent does not expose an argument with which to set |
- // MouseEvent.which. Calling initMouseEvent with a buttonArg of 0 will set |
- // MouseEvent.button == 0 and MouseEvent.which == 1, breaking the expectations |
- // of app developers. |
- // |
- // The only way to propagate the correct state of MouseEvent.which and |
- // MouseEvent.button to a new MouseEvent.button == 0 and MouseEvent.which == 0 |
- // is to call initMouseEvent with a buttonArg value of -1. |
- // |
- // This is fixed with DOM Level 4's use of buttons |
- 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; |
- case 3: buttons = 2; break; |
- default: buttons = 0; |
- } |
- } |
+ inDict = inDict || Object.create(null); |
- var e; |
- if (NEW_MOUSE_EVENT) { |
- e = new MouseEvent(inType, inDict); |
- } else { |
- e = document.createEvent('MouseEvent'); |
- |
- // import values from the given dictionary |
- var props = {}, p; |
- for(var i = 0; i < MOUSE_PROPS.length; i++) { |
- p = MOUSE_PROPS[i]; |
- props[p] = inDict[p] || MOUSE_DEFAULTS[i]; |
- } |
+ var e = document.createEvent('Event'); |
+ e.initEvent(inType, inDict.bubbles || false, inDict.cancelable || false); |
- // define the properties inherited from MouseEvent |
- e.initMouseEvent( |
- inType, props.bubbles, props.cancelable, props.view, props.detail, |
- props.screenX, props.screenY, props.clientX, props.clientY, props.ctrlKey, |
- props.altKey, props.shiftKey, props.metaKey, props.button, props.relatedTarget |
- ); |
- } |
- |
- // make the event pass instanceof checks |
- e.__proto__ = PointerEvent.prototype; |
- |
- // define the buttons property according to DOM Level 3 spec |
- if (!HAS_BUTTONS) { |
- // IE 10 has buttons on MouseEvent.prototype as a getter w/o any setting |
- // mechanism |
- Object.defineProperty(e, 'buttons', {get: function(){ return buttons; }, enumerable: true}); |
+ // define inherited MouseEvent properties |
+ for(var i = 0, p; i < MOUSE_PROPS.length; i++) { |
+ p = MOUSE_PROPS[i]; |
+ e[p] = inDict[p] || MOUSE_DEFAULTS[i]; |
} |
+ e.buttons = inDict.buttons || 0; |
// Spec requires that pointers without pressure specified use 0.5 for down |
// state and 0 for up state. |
@@ -11931,27 +11999,26 @@ scope.styleResolver = styleResolver; |
if (inDict.pressure) { |
pressure = inDict.pressure; |
} else { |
- pressure = buttons ? 0.5 : 0; |
+ pressure = e.buttons ? 0.5 : 0; |
} |
+ // add x/y properties aliased to clientX/Y |
+ e.x = e.clientX; |
+ e.y = e.clientY; |
+ |
// define the properties of the PointerEvent interface |
- Object.defineProperties(e, { |
- pointerId: { value: inDict.pointerId || 0, enumerable: true }, |
- width: { value: inDict.width || 0, enumerable: true }, |
- height: { value: inDict.height || 0, enumerable: true }, |
- pressure: { value: pressure, enumerable: true }, |
- tiltX: { value: inDict.tiltX || 0, enumerable: true }, |
- tiltY: { value: inDict.tiltY || 0, enumerable: true }, |
- pointerType: { value: inDict.pointerType || '', enumerable: true }, |
- hwTimestamp: { value: inDict.hwTimestamp || 0, enumerable: true }, |
- isPrimary: { value: inDict.isPrimary || false, enumerable: true } |
- }); |
+ e.pointerId = inDict.pointerId || 0; |
+ e.width = inDict.width || 0; |
+ e.height = inDict.height || 0; |
+ e.pressure = pressure; |
+ e.tiltX = inDict.tiltX || 0; |
+ e.tiltY = inDict.tiltY || 0; |
+ e.pointerType = inDict.pointerType || ''; |
+ e.hwTimestamp = inDict.hwTimestamp || 0; |
+ e.isPrimary = inDict.isPrimary || false; |
return e; |
} |
- // PointerEvent extends MouseEvent |
- PointerEvent.prototype = Object.create(MouseEvent.prototype); |
- |
// attach to window |
if (!scope.PointerEvent) { |
scope.PointerEvent = PointerEvent; |
@@ -12062,7 +12129,9 @@ scope.styleResolver = styleResolver; |
'type', |
'target', |
'currentTarget', |
- 'which' |
+ 'which', |
+ 'pageX', |
+ 'pageY' |
]; |
var CLONE_DEFAULTS = [ |
@@ -12097,6 +12166,8 @@ scope.styleResolver = styleResolver; |
'', |
null, |
null, |
+ 0, |
+ 0, |
0 |
]; |
@@ -12115,13 +12186,12 @@ scope.styleResolver = styleResolver; |
* - pointercancel: a pointer will no longer generate events |
*/ |
var dispatcher = { |
- targets: new WeakMap(), |
- handledEvents: new WeakMap(), |
pointermap: new scope.PointerMap(), |
- eventMap: {}, |
+ eventMap: Object.create(null), |
+ captureInfo: Object.create(null), |
// Scope objects for native events. |
// This exists for ease of testing. |
- eventSources: {}, |
+ eventSources: Object.create(null), |
eventSourceList: [], |
/** |
* Add a new event source that will generate pointer events. |
@@ -12211,7 +12281,7 @@ scope.styleResolver = styleResolver; |
// This is used to prevent multiple dispatch of pointerevents from |
// platform events. This can happen when two elements in different scopes |
// are set up to create pointer events, which is relevant to Shadow DOM. |
- if (this.handledEvents.get(inEvent)) { |
+ if (inEvent._handledByPE) { |
return; |
} |
var type = inEvent.type; |
@@ -12219,7 +12289,7 @@ scope.styleResolver = styleResolver; |
if (fn) { |
fn(inEvent); |
} |
- this.handledEvents.set(inEvent, true); |
+ inEvent._handledByPE = true; |
}, |
// set up event listeners |
listen: function(target, events) { |
@@ -12250,14 +12320,14 @@ scope.styleResolver = styleResolver; |
*/ |
makeEvent: function(inType, inEvent) { |
// relatedTarget must be null if pointer is captured |
- if (this.captureInfo) { |
+ if (this.captureInfo[inEvent.pointerId]) { |
inEvent.relatedTarget = null; |
} |
var e = new PointerEvent(inType, inEvent); |
if (inEvent.preventDefault) { |
e.preventDefault = inEvent.preventDefault; |
} |
- this.targets.set(e, this.targets.get(inEvent) || inEvent.target); |
+ e._target = e._target || inEvent.target; |
return e; |
}, |
// make and dispatch an event in one call |
@@ -12273,7 +12343,7 @@ scope.styleResolver = styleResolver; |
* properties. |
*/ |
cloneEvent: function(inEvent) { |
- var eventCopy = {}, p; |
+ var eventCopy = Object.create(null), p; |
for (var i = 0; i < CLONE_PROPS.length; i++) { |
p = CLONE_PROPS[i]; |
eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i]; |
@@ -12297,33 +12367,32 @@ scope.styleResolver = styleResolver; |
getTarget: function(inEvent) { |
// if pointer capture is set, route all events for the specified pointerId |
// to the capture target |
- if (this.captureInfo) { |
- if (this.captureInfo.id === inEvent.pointerId) { |
- return this.captureInfo.target; |
- } |
- } |
- return this.targets.get(inEvent); |
+ return this.captureInfo[inEvent.pointerId] || inEvent._target; |
}, |
setCapture: function(inPointerId, inTarget) { |
- if (this.captureInfo) { |
- this.releaseCapture(this.captureInfo.id); |
+ if (this.captureInfo[inPointerId]) { |
+ this.releaseCapture(inPointerId); |
} |
- this.captureInfo = {id: inPointerId, target: inTarget}; |
- var e = new PointerEvent('gotpointercapture', { bubbles: true }); |
+ this.captureInfo[inPointerId] = inTarget; |
+ var e = document.createEvent('Event'); |
+ e.initEvent('gotpointercapture', true, false); |
+ e.pointerId = inPointerId; |
this.implicitRelease = this.releaseCapture.bind(this, inPointerId); |
document.addEventListener('pointerup', this.implicitRelease); |
document.addEventListener('pointercancel', this.implicitRelease); |
- this.targets.set(e, inTarget); |
+ e._target = inTarget; |
this.asyncDispatchEvent(e); |
}, |
releaseCapture: function(inPointerId) { |
- if (this.captureInfo && this.captureInfo.id === inPointerId) { |
- var e = new PointerEvent('lostpointercapture', { bubbles: true }); |
- var t = this.captureInfo.target; |
- this.captureInfo = null; |
+ var t = this.captureInfo[inPointerId]; |
+ if (t) { |
+ var e = document.createEvent('Event'); |
+ e.initEvent('lostpointercapture', true, false); |
+ e.pointerId = inPointerId; |
+ this.captureInfo[inPointerId] = undefined; |
document.removeEventListener('pointerup', this.implicitRelease); |
document.removeEventListener('pointercancel', this.implicitRelease); |
- this.targets.set(e, t); |
+ e._target = t; |
this.asyncDispatchEvent(e); |
} |
}, |
@@ -12340,7 +12409,7 @@ scope.styleResolver = styleResolver; |
} |
}, |
asyncDispatchEvent: function(inEvent) { |
- setTimeout(this.dispatchEvent.bind(this, inEvent), 0); |
+ requestAnimationFrame(this.dispatchEvent.bind(this, inEvent)); |
} |
}; |
dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher); |
@@ -12483,6 +12552,13 @@ scope.styleResolver = styleResolver; |
// radius around touchend that swallows mouse events |
var DEDUP_DIST = 25; |
+ var WHICH_TO_BUTTONS = [0, 1, 4, 2]; |
+ |
+ var HAS_BUTTONS = false; |
+ try { |
+ HAS_BUTTONS = new MouseEvent('test', {buttons: 1}).buttons === 1; |
+ } catch (e) {} |
+ |
// handler block for native mouse events |
var mouseEvents = { |
POINTER_ID: 1, |
@@ -12524,6 +12600,9 @@ scope.styleResolver = styleResolver; |
e.pointerId = this.POINTER_ID; |
e.isPrimary = true; |
e.pointerType = this.POINTER_TYPE; |
+ if (!HAS_BUTTONS) { |
+ e.buttons = WHICH_TO_BUTTONS[e.which] || 0; |
+ } |
return e; |
}, |
mousedown: function(inEvent) { |
@@ -12588,6 +12667,7 @@ scope.styleResolver = styleResolver; |
(function(scope) { |
var dispatcher = scope.dispatcher; |
+ var captureInfo = dispatcher.captureInfo; |
var findTarget = scope.findTarget; |
var allShadows = scope.targetFinding.allShadows.bind(scope.targetFinding); |
var pointermap = dispatcher.pointermap; |
@@ -12606,7 +12686,6 @@ scope.styleResolver = styleResolver; |
// handler block for native touch events |
var touchEvents = { |
- scrollType: new WeakMap(), |
events: [ |
'touchstart', |
'touchmove', |
@@ -12631,21 +12710,21 @@ scope.styleResolver = styleResolver; |
var a = el.getAttribute(ATTRIB); |
var st = this.touchActionToScrollType(a); |
if (st) { |
- this.scrollType.set(el, st); |
+ el._scrollType = st; |
dispatcher.listen(el, this.events); |
// set touch-action on shadows as well |
allShadows(el).forEach(function(s) { |
- this.scrollType.set(s, st); |
+ s._scrollType = st; |
dispatcher.listen(s, this.events); |
}, this); |
} |
}, |
elementRemoved: function(el) { |
- this.scrollType['delete'](el); |
+ el._scrollType = undefined; |
dispatcher.unlisten(el, this.events); |
// remove touch-action from shadow |
allShadows(el).forEach(function(s) { |
- this.scrollType['delete'](s); |
+ s._scrollType = undefined; |
dispatcher.unlisten(s, this.events); |
}, this); |
}, |
@@ -12655,9 +12734,9 @@ scope.styleResolver = styleResolver; |
var oldSt = this.touchActionToScrollType(oldValue); |
// simply update scrollType if listeners are already established |
if (st && oldSt) { |
- this.scrollType.set(el, st); |
+ el._scrollType = st; |
allShadows(el).forEach(function(s) { |
- this.scrollType.set(s, st); |
+ s._scrollType = st; |
}, this); |
} else if (oldSt) { |
this.elementRemoved(el); |
@@ -12727,44 +12806,46 @@ scope.styleResolver = styleResolver; |
return ret; |
}, |
touchToPointer: function(inTouch) { |
+ var cte = this.currentTouchEvent; |
var e = dispatcher.cloneEvent(inTouch); |
// Spec specifies that pointerId 1 is reserved for Mouse. |
// Touch identifiers can start at 0. |
// Add 2 to the touch identifier for compatibility. |
- e.pointerId = inTouch.identifier + 2; |
- e.target = findTarget(e); |
+ var id = e.pointerId = inTouch.identifier + 2; |
+ e.target = captureInfo[id] || findTarget(e); |
e.bubbles = true; |
e.cancelable = true; |
e.detail = this.clickCount; |
e.button = 0; |
- e.buttons = this.typeToButtons(this.currentTouchEvent); |
+ e.buttons = this.typeToButtons(cte.type); |
e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0; |
e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0; |
e.pressure = inTouch.webkitForce || inTouch.force || 0.5; |
e.isPrimary = this.isPrimaryTouch(inTouch); |
e.pointerType = this.POINTER_TYPE; |
+ // forward touch preventDefaults |
+ var self = this; |
+ e.preventDefault = function() { |
+ self.scrolling = false; |
+ self.firstXY = null; |
+ cte.preventDefault(); |
+ }; |
return e; |
}, |
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) { |
- p.preventDefault = function() { |
- this.scrolling = false; |
- this.firstXY = null; |
- inEvent.preventDefault(); |
- }; |
- }, this); |
- pointers.forEach(inFunction, this); |
+ this.currentTouchEvent = inEvent; |
+ for (var i = 0, t; i < tl.length; i++) { |
+ t = tl[i]; |
+ inFunction.call(this, this.touchToPointer(t)); |
+ } |
}, |
// For single axis scrollers, determines whether the element should emit |
// pointer events or behave as a scroller |
shouldScroll: function(inEvent) { |
if (this.firstXY) { |
var ret; |
- var scrollAxis = this.scrollType.get(inEvent.currentTarget); |
+ var scrollAxis = inEvent.currentTarget._scrollType; |
if (scrollAxis === 'none') { |
// this element is a touch-action: none, should never scroll |
ret = false; |
@@ -12811,7 +12892,7 @@ scope.styleResolver = styleResolver; |
// index in pointermap. |
if (key !== 1 && !this.findTouch(tl, key - 2)) { |
var p = value.out; |
- d.push(this.touchToPointer(p)); |
+ d.push(p); |
} |
}, this); |
d.forEach(this.cancelOut, this); |
@@ -13027,8 +13108,7 @@ scope.styleResolver = styleResolver; |
var dispatcher = scope.dispatcher; |
// only activate if this platform does not have pointer events |
- if (window.navigator.pointerEnabled === undefined) { |
- Object.defineProperty(window.navigator, 'pointerEnabled', {value: true, enumerable: true}); |
+ if (window.PointerEvent !== scope.PointerEvent) { |
if (window.navigator.msPointerEnabled) { |
var tp = window.navigator.msMaxTouchPoints; |
@@ -13408,7 +13488,7 @@ PointerGestureEvent.prototype.preventTap = function() { |
// must clone events to keep the (possibly shadowed) target correct for |
// async dispatching |
var e = this.cloneEvent(inEvent); |
- setTimeout(this.runQueue.bind(this, inHandlerFns, e), 0); |
+ requestAnimationFrame(this.runQueue.bind(this, inHandlerFns, e)); |
}, |
// Dispatch the queued events |
runQueue: function(inHandlers, inEvent) { |
@@ -13469,10 +13549,7 @@ PointerGestureEvent.prototype.preventTap = function() { |
} |
}, |
asyncDispatchEvent: function(inEvent, inTarget) { |
- var fn = function() { |
- this.dispatchEvent(inEvent, inTarget); |
- }.bind(this); |
- setTimeout(fn, 0); |
+ requestAnimationFrame(this.dispatchEvent.bind(this, inEvent, inTarget)); |
}, |
preventTap: function(inPointerId) { |
var t = this.recognizers.tap; |
@@ -14091,7 +14168,8 @@ PointerGestureEvent.prototype.preventTap = function() { |
pointers.push(p); |
}); |
var dist = 0; |
- var points = {}; |
+ // start with at least two pointers |
+ var points = {a: pointers[0], b: pointers[1]}; |
var x, y, d; |
for (var i = 0; i < pointers.length; i++) { |
var a = pointers[i]; |
@@ -14269,19 +14347,12 @@ PointerGestureEvent.prototype.preventTap = function() { |
} |
})(window.PointerGestures); |
-// Copyright 2011 Google Inc. |
-// |
-// Licensed under the Apache License, Version 2.0 (the "License"); |
-// you may not use this file except in compliance with the License. |
-// You may obtain a copy of the License at |
-// |
-// http://www.apache.org/licenses/LICENSE-2.0 |
-// |
-// Unless required by applicable law or agreed to in writing, software |
-// distributed under the License is distributed on an "AS IS" BASIS, |
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-// See the License for the specific language governing permissions and |
-// limitations under the License. |
+// Copyright (c) 2014 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 |
(function(global) { |
'use strict'; |
@@ -14296,42 +14367,24 @@ PointerGestureEvent.prototype.preventTap = function() { |
return typeof node.getElementById === 'function' ? node : null; |
} |
- |
Node.prototype.bind = function(name, observable) { |
console.error('Unhandled binding to Node: ', this, name, observable); |
}; |
- function unbind(node, name) { |
- var bindings = node.bindings; |
- if (!bindings) { |
- node.bindings = {}; |
- return; |
- } |
+ function updateBindings(node, name, binding) { |
+ var bindings = node.bindings_; |
+ if (!bindings) |
+ bindings = node.bindings_ = {}; |
- var binding = bindings[name]; |
- if (!binding) |
- return; |
+ if (bindings[name]) |
+ binding[name].close(); |
- binding.close(); |
- bindings[name] = undefined; |
+ return bindings[name] = binding; |
} |
- Node.prototype.unbind = function(name) { |
- unbind(this, name); |
- }; |
- |
- Node.prototype.unbindAll = function() { |
- if (!this.bindings) |
- return; |
- var names = Object.keys(this.bindings); |
- for (var i = 0; i < names.length; i++) { |
- var binding = this.bindings[names[i]]; |
- if (binding) |
- binding.close(); |
- } |
- |
- this.bindings = {}; |
- }; |
+ function returnBinding(node, name, binding) { |
+ return binding; |
+ } |
function sanitizeValue(value) { |
return value == null ? '' : value; |
@@ -14347,6 +14400,19 @@ PointerGestureEvent.prototype.preventTap = function() { |
}; |
} |
+ var maybeUpdateBindings = returnBinding; |
+ |
+ Object.defineProperty(Platform, 'enableBindingsReflection', { |
+ get: function() { |
+ return maybeUpdateBindings === updateBindings; |
+ }, |
+ set: function(enable) { |
+ maybeUpdateBindings = enable ? updateBindings : returnBinding; |
+ return enable; |
+ }, |
+ configurable: true |
+ }); |
+ |
Text.prototype.bind = function(name, value, oneTime) { |
if (name !== 'textContent') |
return Node.prototype.bind.call(this, name, value, oneTime); |
@@ -14354,9 +14420,9 @@ PointerGestureEvent.prototype.preventTap = function() { |
if (oneTime) |
return updateText(this, value); |
- unbind(this, 'textContent'); |
- updateText(this, value.open(textBinding(this))); |
- return this.bindings.textContent = value; |
+ var observable = value; |
+ updateText(this, observable.open(textBinding(this))); |
+ return maybeUpdateBindings(this, name, observable); |
} |
function updateAttribute(el, name, conditional, value) { |
@@ -14387,11 +14453,12 @@ PointerGestureEvent.prototype.preventTap = function() { |
if (oneTime) |
return updateAttribute(this, name, conditional, value); |
- unbind(this, name); |
+ |
+ var observable = value; |
updateAttribute(this, name, conditional, |
- value.open(attributeBinding(this, name, conditional))); |
+ observable.open(attributeBinding(this, name, conditional))); |
- return this.bindings[name] = value; |
+ return maybeUpdateBindings(this, name, observable); |
}; |
var checkboxEventType; |
@@ -14460,15 +14527,13 @@ PointerGestureEvent.prototype.preventTap = function() { |
} |
input.addEventListener(eventType, eventHandler); |
- var capturedClose = observable.close; |
- observable.close = function() { |
- if (!capturedClose) |
- return; |
- input.removeEventListener(eventType, eventHandler); |
+ return { |
+ close: function() { |
+ input.removeEventListener(eventType, eventHandler); |
+ observable.close(); |
+ }, |
- observable.close = capturedClose; |
- observable.close(); |
- capturedClose = undefined; |
+ observable_: observable |
} |
} |
@@ -14512,10 +14577,10 @@ PointerGestureEvent.prototype.preventTap = function() { |
if (input.tagName === 'INPUT' && |
input.type === 'radio') { |
getAssociatedRadioButtons(input).forEach(function(radio) { |
- var checkedBinding = radio.bindings.checked; |
+ var checkedBinding = radio.bindings_.checked; |
if (checkedBinding) { |
// Set the value directly to avoid an infinite call stack. |
- checkedBinding.setValue(false); |
+ checkedBinding.observable_.setValue(false); |
} |
}); |
} |
@@ -14525,7 +14590,6 @@ PointerGestureEvent.prototype.preventTap = function() { |
if (name !== 'value' && name !== 'checked') |
return HTMLElement.prototype.bind.call(this, name, value, oneTime); |
- |
this.removeAttribute(name); |
var sanitizeFn = name == 'checked' ? booleanSanitize : sanitizeValue; |
var postEventFn = name == 'checked' ? checkedPostEvent : noop; |
@@ -14533,13 +14597,15 @@ PointerGestureEvent.prototype.preventTap = function() { |
if (oneTime) |
return updateInput(this, name, value, sanitizeFn); |
- unbind(this, name); |
- bindInputEvent(this, name, value, postEventFn); |
+ |
+ var observable = value; |
+ var binding = bindInputEvent(this, name, observable, postEventFn); |
updateInput(this, name, |
- value.open(inputBinding(this, name, sanitizeFn)), |
+ observable.open(inputBinding(this, name, sanitizeFn)), |
sanitizeFn); |
- return this.bindings[name] = value; |
+ // Checkboxes may need to update bindings of other checkboxes. |
+ return updateBindings(this, name, binding); |
} |
HTMLTextAreaElement.prototype.bind = function(name, value, oneTime) { |
@@ -14551,12 +14617,11 @@ PointerGestureEvent.prototype.preventTap = function() { |
if (oneTime) |
return updateInput(this, 'value', value); |
- unbind(this, 'value'); |
- bindInputEvent(this, 'value', value); |
+ var observable = value; |
+ var binding = bindInputEvent(this, 'value', observable); |
updateInput(this, 'value', |
- value.open(inputBinding(this, 'value', sanitizeValue))); |
- |
- return this.bindings.value = value; |
+ observable.open(inputBinding(this, 'value', sanitizeValue))); |
+ return maybeUpdateBindings(this, name, binding); |
} |
function updateOption(option, value) { |
@@ -14565,18 +14630,18 @@ PointerGestureEvent.prototype.preventTap = function() { |
var selectBinding; |
var oldValue; |
if (parentNode instanceof HTMLSelectElement && |
- parentNode.bindings && |
- parentNode.bindings.value) { |
+ parentNode.bindings_ && |
+ parentNode.bindings_.value) { |
select = parentNode; |
- selectBinding = select.bindings.value; |
+ selectBinding = select.bindings_.value; |
oldValue = select.value; |
} |
option.value = sanitizeValue(value); |
if (select && select.value != oldValue) { |
- selectBinding.setValue(select.value); |
- selectBinding.discardChanges(); |
+ selectBinding.observable_.setValue(select.value); |
+ selectBinding.observable_.discardChanges(); |
Platform.performMicrotaskCheckpoint(); |
} |
} |
@@ -14596,10 +14661,10 @@ PointerGestureEvent.prototype.preventTap = function() { |
if (oneTime) |
return updateOption(this, value); |
- unbind(this, 'value'); |
- bindInputEvent(this, 'value', value); |
- updateOption(this, value.open(optionBinding(this))); |
- return this.bindings.value = value; |
+ var observable = value; |
+ var binding = bindInputEvent(this, 'value', observable); |
+ updateOption(this, observable.open(optionBinding(this))); |
+ return maybeUpdateBindings(this, name, binding); |
} |
HTMLSelectElement.prototype.bind = function(name, value, oneTime) { |
@@ -14614,27 +14679,22 @@ PointerGestureEvent.prototype.preventTap = function() { |
if (oneTime) |
return updateInput(this, name, value); |
- unbind(this, name); |
- bindInputEvent(this, name, value); |
+ var observable = value; |
+ var binding = bindInputEvent(this, name, observable); |
updateInput(this, name, |
- value.open(inputBinding(this, name))); |
- return this.bindings[name] = value; |
+ observable.open(inputBinding(this, name))); |
+ |
+ // Option update events may need to access select bindings. |
+ return updateBindings(this, name, binding); |
} |
})(this); |
-// Copyright 2011 Google Inc. |
-// |
-// Licensed under the Apache License, Version 2.0 (the "License"); |
-// you may not use this file except in compliance with the License. |
-// You may obtain a copy of the License at |
-// |
-// http://www.apache.org/licenses/LICENSE-2.0 |
-// |
-// Unless required by applicable law or agreed to in writing, software |
-// distributed under the License is distributed on an "AS IS" BASIS, |
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-// See the License for the specific language governing permissions and |
-// limitations under the License. |
+// Copyright (c) 2014 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 |
(function(global) { |
'use strict'; |
@@ -15099,8 +15159,13 @@ PointerGestureEvent.prototype.preventTap = function() { |
if (oneTime) |
return; |
- this.unbind('ref'); |
- return this.bindings.ref = value; |
+ if (!this.bindings_) { |
+ this.bindings_ = { ref: value }; |
+ } else { |
+ this.bindings_.ref = value; |
+ } |
+ |
+ return value; |
}, |
processBindingDirectives_: function(directives) { |
@@ -15111,7 +15176,6 @@ PointerGestureEvent.prototype.preventTap = function() { |
if (this.iterator_) { |
this.iterator_.close(); |
this.iterator_ = undefined; |
- this.bindings.iterator = undefined; |
} |
return; |
@@ -15119,8 +15183,6 @@ PointerGestureEvent.prototype.preventTap = function() { |
if (!this.iterator_) { |
this.iterator_ = new TemplateIterator(this); |
- this.bindings = this.bindings || {}; |
- this.bindings.iterator = this.iterator_; |
} |
this.iterator_.updateDependencies(directives, this.model_); |
@@ -15133,14 +15195,16 @@ PointerGestureEvent.prototype.preventTap = function() { |
return this.iterator_; |
}, |
- createInstance: function(model, bindingDelegate, delegate_, |
- instanceBindings_) { |
+ createInstance: function(model, bindingDelegate, delegate_) { |
if (bindingDelegate) |
delegate_ = this.newDelegate_(bindingDelegate); |
if (!this.refContent_) |
this.refContent_ = this.ref_.content; |
var content = this.refContent_; |
+ if (content.firstChild === null) |
+ return emptyInstance; |
+ |
var map = this.bindingMap_; |
if (!map || map.content !== content) { |
// TODO(rafaelw): Setup a MutationObserver on content to detect |
@@ -15155,21 +15219,32 @@ PointerGestureEvent.prototype.preventTap = function() { |
var instance = stagingDocument.createDocumentFragment(); |
instance.templateCreator_ = this; |
instance.protoContent_ = content; |
- |
- var instanceRecord = { |
+ instance.bindings_ = []; |
+ instance.terminator_ = null; |
+ var instanceRecord = instance.templateInstance_ = { |
firstNode: null, |
lastNode: null, |
model: model |
}; |
var i = 0; |
+ var collectTerminator = false; |
for (var child = content.firstChild; child; child = child.nextSibling) { |
+ // The terminator of the instance is the clone of the last child of the |
+ // content. If the last child is an active template, it may produce |
+ // instances as a result of production, so simply collecting the last |
+ // child of the instance after it has finished producing may be wrong. |
+ if (child.nextSibling === null) |
+ collectTerminator = true; |
+ |
var clone = cloneAndBindInstance(child, instance, stagingDocument, |
map.children[i++], |
model, |
delegate_, |
- instanceBindings_); |
+ instance.bindings_); |
clone.templateInstance_ = instanceRecord; |
+ if (collectTerminator) |
+ instance.terminator_ = clone; |
} |
instanceRecord.firstNode = instance.firstChild; |
@@ -15204,7 +15279,8 @@ PointerGestureEvent.prototype.preventTap = function() { |
clear: function() { |
this.model_ = undefined; |
this.delegate_ = undefined; |
- this.bindings_ = undefined; |
+ if (this.bindings_ && this.bindings_.ref) |
+ this.bindings_.ref.close() |
this.refContent_ = undefined; |
if (!this.iterator_) |
return; |
@@ -15309,9 +15385,14 @@ PointerGestureEvent.prototype.preventTap = function() { |
var pathString = s.slice(startIndex + 2, endIndex).trim(); |
tokens.push(oneTime); // ONE_TIME? |
onlyOneTime = onlyOneTime && oneTime; |
- tokens.push(Path.get(pathString)); // PATH |
var delegateFn = prepareBindingFn && |
prepareBindingFn(pathString, name, node); |
+ // Don't try to parse the expression if there's a prepareBinding function |
+ if (delegateFn == null) { |
+ tokens.push(Path.get(pathString)); // PATH |
+ } else { |
+ tokens.push(null); |
+ } |
tokens.push(delegateFn); // DELEGATE_FN |
lastIndex = endIndex + 2; |
} |
@@ -15529,14 +15610,14 @@ PointerGestureEvent.prototype.preventTap = function() { |
} |
}); |
+ var emptyInstance = document.createDocumentFragment(); |
+ emptyInstance.bindings_ = []; |
+ emptyInstance.terminator_ = null; |
+ |
function TemplateIterator(templateElement) { |
this.closed = false; |
this.templateElement_ = templateElement; |
- |
- // Flattened array of tuples: |
- // <instanceTerminatorNode, [bindingsSetupByInstance]> |
- this.terminators = []; |
- |
+ this.instances = []; |
this.deps = undefined; |
this.iteratedValue = []; |
this.presentValue = undefined; |
@@ -15631,63 +15712,53 @@ PointerGestureEvent.prototype.preventTap = function() { |
this.iteratedValue)); |
}, |
- getTerminatorAt: function(index) { |
+ getLastInstanceNode: function(index) { |
if (index == -1) |
return this.templateElement_; |
- var terminator = this.terminators[index*2]; |
+ var instance = this.instances[index]; |
+ var terminator = instance.terminator_; |
+ if (!terminator) |
+ return this.getLastInstanceNode(index - 1); |
+ |
if (terminator.nodeType !== Node.ELEMENT_NODE || |
this.templateElement_ === terminator) { |
return terminator; |
} |
- var subIterator = terminator.iterator_; |
- if (!subIterator) |
+ var subtemplateIterator = terminator.iterator_; |
+ if (!subtemplateIterator) |
return terminator; |
- return subIterator.getTerminatorAt(subIterator.terminators.length/2 - 1); |
+ return subtemplateIterator.getLastTemplateNode(); |
}, |
- // TODO(rafaelw): If we inserting sequences of instances we can probably |
- // avoid lots of calls to getTerminatorAt(), or cache its result. |
- insertInstanceAt: function(index, fragment, instanceNodes, |
- instanceBindings) { |
- var previousTerminator = this.getTerminatorAt(index - 1); |
- var terminator = previousTerminator; |
- if (fragment) |
- terminator = fragment.lastChild || terminator; |
- else if (instanceNodes) |
- terminator = instanceNodes[instanceNodes.length - 1] || terminator; |
+ getLastTemplateNode: function() { |
+ return this.getLastInstanceNode(this.instances.length - 1); |
+ }, |
- this.terminators.splice(index*2, 0, terminator, instanceBindings); |
+ insertInstanceAt: function(index, fragment) { |
+ var previousInstanceLast = this.getLastInstanceNode(index - 1); |
var parent = this.templateElement_.parentNode; |
- var insertBeforeNode = previousTerminator.nextSibling; |
+ this.instances.splice(index, 0, fragment); |
- if (fragment) { |
- parent.insertBefore(fragment, insertBeforeNode); |
- } else if (instanceNodes) { |
- for (var i = 0; i < instanceNodes.length; i++) |
- parent.insertBefore(instanceNodes[i], insertBeforeNode); |
- } |
+ parent.insertBefore(fragment, previousInstanceLast.nextSibling); |
}, |
extractInstanceAt: function(index) { |
- var instanceNodes = []; |
- var previousTerminator = this.getTerminatorAt(index - 1); |
- var terminator = this.getTerminatorAt(index); |
- instanceNodes.instanceBindings = this.terminators[index*2 + 1]; |
- this.terminators.splice(index*2, 2); |
- |
+ var previousInstanceLast = this.getLastInstanceNode(index - 1); |
+ var lastNode = this.getLastInstanceNode(index); |
var parent = this.templateElement_.parentNode; |
- while (terminator !== previousTerminator) { |
- var node = previousTerminator.nextSibling; |
- if (node == terminator) |
- terminator = previousTerminator; |
+ var instance = this.instances.splice(index, 1)[0]; |
+ |
+ while (lastNode !== previousInstanceLast) { |
+ var node = previousInstanceLast.nextSibling; |
+ if (node == lastNode) |
+ lastNode = previousInstanceLast; |
- parent.removeChild(node); |
- instanceNodes.push(node); |
+ instance.appendChild(parent.removeChild(node)); |
} |
- return instanceNodes; |
+ return instance; |
}, |
getDelegateFn: function(fn) { |
@@ -15721,46 +15792,50 @@ PointerGestureEvent.prototype.preventTap = function() { |
delegate.prepareInstancePositionChanged); |
} |
+ // Instance Removals |
var instanceCache = new Map; |
var removeDelta = 0; |
- splices.forEach(function(splice) { |
- splice.removed.forEach(function(model) { |
- var instanceNodes = |
- this.extractInstanceAt(splice.index + removeDelta); |
- instanceCache.set(model, instanceNodes); |
- }, this); |
+ for (var i = 0; i < splices.length; i++) { |
+ var splice = splices[i]; |
+ var removed = splice.removed; |
+ for (var j = 0; j < removed.length; j++) { |
+ var model = removed[j]; |
+ var instance = this.extractInstanceAt(splice.index + removeDelta); |
+ if (instance !== emptyInstance) { |
+ instanceCache.set(model, instance); |
+ } |
+ } |
removeDelta -= splice.addedCount; |
- }, this); |
+ } |
- splices.forEach(function(splice) { |
+ // Instance Insertions |
+ for (var i = 0; i < splices.length; i++) { |
+ var splice = splices[i]; |
var addIndex = splice.index; |
for (; addIndex < splice.index + splice.addedCount; addIndex++) { |
var model = this.iteratedValue[addIndex]; |
- var fragment = undefined; |
- var instanceNodes = instanceCache.get(model); |
- var instanceBindings; |
- if (instanceNodes) { |
+ var instance = instanceCache.get(model); |
+ if (instance) { |
instanceCache.delete(model); |
- instanceBindings = instanceNodes.instanceBindings; |
} else { |
- instanceBindings = []; |
- if (this.instanceModelFn_) |
+ if (this.instanceModelFn_) { |
model = this.instanceModelFn_(model); |
+ } |
- if (model !== undefined) { |
- fragment = template.createInstance(model, undefined, delegate, |
- instanceBindings); |
+ if (model === undefined) { |
+ instance = emptyInstance; |
+ } else { |
+ instance = template.createInstance(model, undefined, delegate); |
} |
} |
- this.insertInstanceAt(addIndex, fragment, instanceNodes, |
- instanceBindings); |
+ this.insertInstanceAt(addIndex, instance); |
} |
- }, this); |
+ } |
- instanceCache.forEach(function(instanceNodes) { |
- this.closeInstanceBindings(instanceNodes.instanceBindings); |
+ instanceCache.forEach(function(instance) { |
+ this.closeInstanceBindings(instance); |
}, this); |
if (this.instancePositionChangedFn_) |
@@ -15768,17 +15843,11 @@ PointerGestureEvent.prototype.preventTap = function() { |
}, |
reportInstanceMoved: function(index) { |
- var previousTerminator = this.getTerminatorAt(index - 1); |
- var terminator = this.getTerminatorAt(index); |
- if (previousTerminator === terminator) |
- return; // instance has zero nodes. |
+ var instance = this.instances[index]; |
+ if (instance === emptyInstance) |
+ return; |
- // We must use the first node of the instance, because any subsequent |
- // nodes may have been generated by sub-templates. |
- // TODO(rafaelw): This is brittle WRT instance mutation -- e.g. if the |
- // first node was removed by script. |
- var templateInstance = previousTerminator.nextSibling.templateInstance; |
- this.instancePositionChangedFn_(templateInstance, index); |
+ this.instancePositionChangedFn_(instance.templateInstance_, index); |
}, |
reportInstancesMoved: function(splices) { |
@@ -15806,16 +15875,17 @@ PointerGestureEvent.prototype.preventTap = function() { |
if (offset == 0) |
return; |
- var length = this.terminators.length / 2; |
+ var length = this.instances.length; |
while (index < length) { |
this.reportInstanceMoved(index); |
index++; |
} |
}, |
- closeInstanceBindings: function(instanceBindings) { |
- for (var i = 0; i < instanceBindings.length; i++) { |
- instanceBindings[i].close(); |
+ closeInstanceBindings: function(instance) { |
+ var bindings = instance.bindings_; |
+ for (var i = 0; i < bindings.length; i++) { |
+ bindings[i].close(); |
} |
}, |
@@ -15831,11 +15901,11 @@ PointerGestureEvent.prototype.preventTap = function() { |
if (this.closed) |
return; |
this.unobserve(); |
- for (var i = 1; i < this.terminators.length; i += 2) { |
- this.closeInstanceBindings(this.terminators[i]); |
+ for (var i = 0; i < this.instances.length; i++) { |
+ this.closeInstanceBindings(this.instances[i]); |
} |
- this.terminators.length = 0; |
+ this.instances.length = 0; |
this.closeDeps(); |
this.templateElement_.iterator_ = undefined; |
this.closed = true; |
@@ -16884,19 +16954,12 @@ PointerGestureEvent.prototype.preventTap = function() { |
}; |
})(this); |
-// Copyright 2013 Google Inc. |
-// |
-// Licensed under the Apache License, Version 2.0 (the "License"); |
-// you may not use this file except in compliance with the License. |
-// You may obtain a copy of the License at |
-// |
-// http://www.apache.org/licenses/LICENSE-2.0 |
-// |
-// Unless required by applicable law or agreed to in writing, software |
-// distributed under the License is distributed on an "AS IS" BASIS, |
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-// See the License for the specific language governing permissions and |
-// limitations under the License. |
+// Copyright (c) 2014 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 |
(function (global) { |
'use strict'; |
@@ -17323,10 +17386,18 @@ PointerGestureEvent.prototype.preventTap = function() { |
return this.getValue(model, undefined, filterRegistry); |
var observer = new CompoundObserver(); |
- this.getValue(model, observer, filterRegistry); // captures deps. |
+ // captures deps. |
+ var firstValue = this.getValue(model, observer, filterRegistry); |
+ var firstTime = true; |
var self = this; |
function valueFn() { |
+ // deps cannot have changed on first value retrieval. |
+ if (firstTime) { |
+ firstTime = false; |
+ return firstValue; |
+ } |
+ |
if (self.dynamicDeps) |
observer.startReset(); |
@@ -17470,6 +17541,23 @@ PointerGestureEvent.prototype.preventTap = function() { |
} |
} |
+ function isLiteralExpression(pathString) { |
+ switch (pathString) { |
+ case '': |
+ return false; |
+ |
+ case 'false': |
+ case 'null': |
+ case 'true': |
+ return true; |
+ } |
+ |
+ if (!isNaN(Number(pathString))) |
+ return true; |
+ |
+ return false; |
+ }; |
+ |
function PolymerExpressions() {} |
PolymerExpressions.prototype = { |
@@ -17513,7 +17601,7 @@ PointerGestureEvent.prototype.preventTap = function() { |
return prepareEventBinding(path, name, this); |
} |
- if (path.valid) { |
+ if (!isLiteralExpression(pathString) && path.valid) { |
if (path.length == 1) { |
return function(model, node, oneTime) { |
if (oneTime) |
@@ -17521,9 +17609,8 @@ PointerGestureEvent.prototype.preventTap = function() { |
var scope = findScope(model, path[0]); |
return new PathObserver(scope, path); |
- } |
+ }; |
} |
- |
return; // bail out early if pathString is simple path. |
} |
@@ -17586,14 +17673,17 @@ function flush() { |
}; |
// polling dirty checker |
-var FLUSH_POLL_INTERVAL = 125; |
-window.addEventListener('WebComponentsReady', function() { |
- flush(); |
- // flush periodically if platform does not have object observe. |
- if (!Observer.hasObjectObserve) { |
+// flush periodically if platform does not have object observe. |
+if (!Observer.hasObjectObserve) { |
+ var FLUSH_POLL_INTERVAL = 125; |
+ window.addEventListener('WebComponentsReady', function() { |
+ flush(); |
scope.flushPoll = setInterval(flush, FLUSH_POLL_INTERVAL); |
- } |
-}); |
+ }); |
+} else { |
+ // make flush a no-op when we have Object.observe |
+ flush = function() {}; |
+} |
if (window.CustomElements && !CustomElements.useNative) { |
var originalImportNode = Document.prototype.importNode; |