Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1134)

Unified Diff: pkg/polymer/lib/src/js/polymer/polymer.concat.js

Issue 558673004: update polymer js to 0.4.0 (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: review updates Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/polymer/lib/src/js/polymer/polymer.js ('k') | pkg/polymer/lib/src/js/polymer/polymer.concat.js.map » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/polymer/lib/src/js/polymer/polymer.concat.js
diff --git a/pkg/polymer/lib/src/js/polymer/polymer.concat.js b/pkg/polymer/lib/src/js/polymer/polymer.concat.js
index 65d22b94d5be066be10de506f2182b3ef454c6a4..0d97f534bc59706470301d6e993dc36f9d6a5dee 100644
--- a/pkg/polymer/lib/src/js/polymer/polymer.concat.js
+++ b/pkg/polymer/lib/src/js/polymer/polymer.concat.js
@@ -591,6 +591,9 @@ window.PolymerGestures = {};
var eventFactory = scope.eventFactory;
+ // set of recognizers to run for the currently handled event
+ var currentGestures;
+
/**
* This module is for normalizing events. Mouse and Touch events will be
* collected here, and fire PointerEvents that have the same semantics, no
@@ -605,6 +608,7 @@ window.PolymerGestures = {};
*/
var dispatcher = {
pointermap: new scope.PointerMap(),
+ requiredGestures: new scope.PointerMap(),
eventMap: Object.create(null),
// Scope objects for native events.
// This exists for ease of testing.
@@ -665,6 +669,7 @@ window.PolymerGestures = {};
},
// EVENTS
down: function(inEvent) {
+ this.requiredGestures.set(inEvent.pointerId, currentGestures);
this.fireEvent('down', inEvent);
},
move: function(inEvent) {
@@ -674,10 +679,12 @@ window.PolymerGestures = {};
},
up: function(inEvent) {
this.fireEvent('up', inEvent);
+ this.requiredGestures.delete(inEvent.pointerId);
},
cancel: function(inEvent) {
inEvent.tapPrevented = true;
this.fireEvent('up', inEvent);
+ this.requiredGestures.delete(inEvent.pointerId);
},
// LISTENER LOGIC
eventHandler: function(inEvent) {
@@ -685,12 +692,34 @@ window.PolymerGestures = {};
// platform events. This can happen when two elements in different scopes
// are set up to create pointer events, which is relevant to Shadow DOM.
- // TODO(dfreedm): make this check more granular, allow for minimal event generation
- // e.g inEvent._handledByPG['tap'] and inEvent._handledByPG['track'], etc
+ var type = inEvent.type;
+
+ // only generate the list of desired events on "down"
+ if (type === 'touchstart' || type === 'mousedown' || type === 'pointerdown' || type === 'MSPointerDown') {
+ if (!inEvent._handledByPG) {
+ currentGestures = {};
+ }
+ // map gesture names to ordered set of recognizers
+ var gesturesWanted = inEvent.currentTarget._pgEvents;
+ if (gesturesWanted) {
+ var gk = Object.keys(gesturesWanted);
+ for (var i = 0, r, ri, g; i < gk.length; i++) {
+ // gesture
+ g = gk[i];
+ if (gesturesWanted[g] > 0) {
+ // lookup gesture recognizer
+ r = this.dependencyMap[g];
+ // recognizer index
+ ri = r ? r.index : -1;
+ currentGestures[ri] = true;
+ }
+ }
+ }
+ }
+
if (inEvent._handledByPG) {
return;
}
- var type = inEvent.type;
var fn = this.eventMap && this.eventMap[type];
if (fn) {
fn(inEvent);
@@ -782,13 +811,17 @@ window.PolymerGestures = {};
},
gestureTrigger: function() {
// process the gesture queue
- for (var i = 0, e; i < this.gestureQueue.length; i++) {
+ for (var i = 0, e, rg; i < this.gestureQueue.length; i++) {
e = this.gestureQueue[i];
+ rg = e._requiredGestures;
for (var j = 0, g, fn; j < this.gestures.length; j++) {
- g = this.gestures[j];
- fn = g[e.type];
- if (g.enabled && fn) {
- fn.call(g, e);
+ // only run recognizer if an element in the source event's path is listening for those gestures
+ if (rg[j]) {
+ g = this.gestures[j];
+ fn = g[e.type];
+ if (fn) {
+ fn.call(g, e);
+ }
}
}
}
@@ -799,6 +832,7 @@ window.PolymerGestures = {};
if (!this.gestureQueue.length) {
requestAnimationFrame(this.boundGestureTrigger);
}
+ ev._requiredGestures = this.requiredGestures.get(ev.pointerId);
this.gestureQueue.push(ev);
}
};
@@ -820,12 +854,6 @@ window.PolymerGestures = {};
var dep = dispatcher.dependencyMap[g];
if (dep) {
var recognizer = dispatcher.gestures[dep.index];
- if (dep.listeners === 0) {
- if (recognizer) {
- recognizer.enabled = true;
- }
- }
- dep.listeners++;
if (!node._pgListeners) {
dispatcher.register(node);
node._pgListeners = 0;
@@ -849,6 +877,10 @@ window.PolymerGestures = {};
actionNode.setAttribute('touch-action', touchAction);
}
}
+ if (!node._pgEvents) {
+ node._pgEvents = {};
+ }
+ node._pgEvents[g] = (node._pgEvents[g] || 0) + 1;
node._pgListeners++;
}
return Boolean(dep);
@@ -883,21 +915,19 @@ window.PolymerGestures = {};
var g = gesture.toLowerCase();
var dep = dispatcher.dependencyMap[g];
if (dep) {
- if (dep.listeners > 0) {
- dep.listeners--;
- }
- if (dep.listeners === 0) {
- var recognizer = dispatcher.gestures[dep.index];
- if (recognizer) {
- recognizer.enabled = false;
- }
- }
if (node._pgListeners > 0) {
node._pgListeners--;
}
if (node._pgListeners === 0) {
dispatcher.unregister(node);
}
+ if (node._pgEvents) {
+ if (node._pgEvents[g] > 0) {
+ node._pgEvents[g]--;
+ } else {
+ node._pgEvents[g] = 0;
+ }
+ }
}
return Boolean(dep);
};
@@ -1365,9 +1395,6 @@ window.PolymerGestures = {};
'MSPointerCancel',
],
register: function(target) {
- if (target !== document) {
- return;
- }
dispatcher.listen(target, this.events);
},
unregister: function(target) {
@@ -1399,9 +1426,12 @@ window.PolymerGestures = {};
dispatcher.down(e);
},
MSPointerMove: function(inEvent) {
- var e = this.prepareEvent(inEvent);
- e.target = pointermap.get(e.pointerId);
- dispatcher.move(e);
+ var target = pointermap.get(inEvent.pointerId);
+ if (target) {
+ var e = this.prepareEvent(inEvent);
+ e.target = target;
+ dispatcher.move(e);
+ }
},
MSPointerUp: function(inEvent) {
var e = this.prepareEvent(inEvent);
@@ -1447,9 +1477,6 @@ window.PolymerGestures = {};
return e;
},
register: function(target) {
- if (target !== document) {
- return;
- }
dispatcher.listen(target, this.events);
},
unregister: function(target) {
@@ -1465,9 +1492,12 @@ window.PolymerGestures = {};
dispatcher.down(e);
},
pointermove: function(inEvent) {
- var e = this.prepareEvent(inEvent);
- e.target = pointermap.get(e.pointerId);
- dispatcher.move(e);
+ var target = pointermap.get(inEvent.pointerId);
+ if (target) {
+ var e = this.prepareEvent(inEvent);
+ e.target = target;
+ dispatcher.move(e);
+ }
},
pointerup: function(inEvent) {
var e = this.prepareEvent(inEvent);
@@ -3668,7 +3698,7 @@ window.PolymerGestures = {};
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
Polymer = {
- version: '0.3.5-5d00e4b'
+ version: '0.4.0-d66a86e'
};
/*
@@ -3697,143 +3727,46 @@ if (typeof window.Polymer === 'function') {
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
-(function(scope) {
-
- // copy own properties from 'api' to 'prototype, with name hinting for 'super'
- function extend(prototype, api) {
- if (prototype && api) {
- // use only own properties of 'api'
- Object.getOwnPropertyNames(api).forEach(function(n) {
- // acquire property descriptor
- var pd = Object.getOwnPropertyDescriptor(api, n);
- if (pd) {
- // clone property via descriptor
- Object.defineProperty(prototype, n, pd);
- // cache name-of-method for 'super' engine
- if (typeof pd.value == 'function') {
- // hint the 'super' engine
- pd.value.nom = n;
- }
- }
- });
- }
- return prototype;
- }
-
- // exports
-
- scope.extend = extend;
-
-})(Polymer);
-
-/*
- * 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
+ /*
+ On supported platforms, platform.js is not needed. To retain compatibility
+ with the polyfills, we stub out minimal functionality.
*/
+if (!window.Platform) {
+ logFlags = window.logFlags || {};
-(function(scope) {
-
- // usage
-
- // invoke cb.call(this) in 100ms, unless the job is re-registered,
- // which resets the timer
- //
- // this.myJob = this.job(this.myJob, cb, 100)
- //
- // returns a job handle which can be used to re-register a job
- var Job = function(inContext) {
- this.context = inContext;
- this.boundComplete = this.complete.bind(this)
+ Platform = {
+ flush: function() {}
};
- Job.prototype = {
- go: function(callback, wait) {
- this.callback = callback;
- var h;
- if (!wait) {
- h = requestAnimationFrame(this.boundComplete);
- this.handle = function() {
- cancelAnimationFrame(h);
- }
- } else {
- h = setTimeout(this.boundComplete, wait);
- this.handle = function() {
- clearTimeout(h);
- }
- }
- },
- stop: function() {
- if (this.handle) {
- this.handle();
- this.handle = null;
- }
- },
- complete: function() {
- if (this.handle) {
- this.stop();
- this.callback.call(this.context);
- }
+
+ CustomElements = {
+ useNative: true,
+ ready: true,
+ takeRecords: function() {},
+ instanceof: function(obj, base) {
+ return obj instanceof base;
}
};
- function job(job, callback, wait) {
- if (job) {
- job.stop();
- } else {
- job = new Job(this);
- }
- job.go(callback, wait);
- return job;
- }
-
- // exports
+ HTMLImports = {
+ useNative: true
+ };
- scope.job = job;
-})(Polymer);
-
-/*
- * 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(scope) {
-
- var registry = {};
+ addEventListener('HTMLImportsLoaded', function() {
+ document.dispatchEvent(
+ new CustomEvent('WebComponentsReady', {bubbles: true})
+ );
+ });
- HTMLElement.register = function(tag, prototype) {
- registry[tag] = prototype;
- }
- // get prototype mapped to node <tag>
- HTMLElement.getPrototypeForTag = function(tag) {
- var prototype = !tag ? HTMLElement.prototype : registry[tag];
- // TODO(sjmiles): creating <tag> is likely to have wasteful side-effects
- return prototype || Object.getPrototypeOf(document.createElement(tag));
+ // ShadowDOM
+ ShadowDOMPolyfill = null;
+ wrap = unwrap = function(n){
+ return n;
};
- // we have to flag propagation stoppage for the event dispatcher
- var originalStopPropagation = Event.prototype.stopPropagation;
- Event.prototype.stopPropagation = function() {
- this.cancelBubble = true;
- originalStopPropagation.apply(this, arguments);
- };
-
- // TODO(sorvell): remove when we're sure imports does not need
- // to load stylesheets
- /*
- HTMLImports.importer.preloadSelectors +=
- ', polymer-element link[rel=stylesheet]';
- */
-})(Polymer);
+}
/*
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
@@ -3844,108 +3777,4402 @@ if (typeof window.Polymer === 'function') {
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
- (function(scope) {
- // super
+(function(scope) {
- // `arrayOfArgs` is an optional array of args like one might pass
- // to `Function.apply`
+var hasNative = ('import' in document.createElement('link'));
+var useNative = hasNative;
- // TODO(sjmiles):
- // $super must be installed on an instance or prototype chain
- // as `super`, and invoked via `this`, e.g.
- // `this.super();`
+isIE = /Trident/.test(navigator.userAgent);
- // will not work if function objects are not unique, for example,
- // when using mixins.
- // The memoization strategy assumes each function exists on only one
- // prototype chain i.e. we use the function object for memoizing)
- // perhaps we can bookkeep on the prototype itself instead
- function $super(arrayOfArgs) {
- // since we are thunking a method call, performance is important here:
- // memoize all lookups, once memoized the fast path calls no other
- // functions
- //
- // find the caller (cannot be `strict` because of 'caller')
- var caller = $super.caller;
- // memoized 'name of method'
- var nom = caller.nom;
- // memoized next implementation prototype
- var _super = caller._super;
- if (!_super) {
- if (!nom) {
- nom = caller.nom = nameInThis.call(this, caller);
- }
- if (!nom) {
- console.warn('called super() on a method not installed declaratively (has no .nom property)');
- }
- // super prototype is either cached or we have to find it
- // by searching __proto__ (at the 'top')
- // invariant: because we cache _super on fn below, we never reach
- // here from inside a series of calls to super(), so it's ok to
- // start searching from the prototype of 'this' (at the 'top')
- // we must never memoize a null super for this reason
- _super = memoizeSuper(caller, nom, getPrototypeOf(this));
+// TODO(sorvell): SD polyfill intrusion
+var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill);
+var wrap = function(node) {
+ return hasShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) : node;
+};
+var mainDoc = wrap(document);
+
+// NOTE: We cannot polyfill document.currentScript because it's not possible
+// both to override and maintain the ability to capture the native value;
+// therefore we choose to expose _currentScript both when native imports
+// and the polyfill are in use.
+var currentScriptDescriptor = {
+ get: function() {
+ var script = HTMLImports.currentScript || document.currentScript ||
+ // NOTE: only works when called in synchronously executing code.
+ // readyState should check if `loading` but IE10 is
+ // interactive when scripts run so we cheat.
+ (document.readyState !== 'complete' ?
+ document.scripts[document.scripts.length - 1] : null);
+ return wrap(script);
+ },
+ configurable: true
+};
+
+Object.defineProperty(document, '_currentScript', currentScriptDescriptor);
+Object.defineProperty(mainDoc, '_currentScript', currentScriptDescriptor);
+
+// call a callback when all HTMLImports in the document at call (or at least
+// document ready) time have loaded.
+// 1. ensure the document is in a ready state (has dom), then
+// 2. watch for loading of imports and call callback when done
+function whenImportsReady(callback, doc) {
+ doc = doc || mainDoc;
+ // if document is loading, wait and try again
+ whenDocumentReady(function() {
+ watchImportsLoad(callback, doc);
+ }, doc);
+}
+
+// call the callback when the document is in a ready state (has dom)
+var requiredReadyState = isIE ? 'complete' : 'interactive';
+var READY_EVENT = 'readystatechange';
+function isDocumentReady(doc) {
+ return (doc.readyState === 'complete' ||
+ doc.readyState === requiredReadyState);
+}
+
+// call <callback> when we ensure the document is in a ready state
+function whenDocumentReady(callback, doc) {
+ if (!isDocumentReady(doc)) {
+ var checkReady = function() {
+ if (doc.readyState === 'complete' ||
+ doc.readyState === requiredReadyState) {
+ doc.removeEventListener(READY_EVENT, checkReady);
+ whenDocumentReady(callback, doc);
}
- // our super function
- var fn = _super[nom];
- if (fn) {
- // memoize information so 'fn' can call 'super'
- if (!fn._super) {
- // must not memoize null, or we lose our invariant above
- memoizeSuper(fn, nom, _super);
- }
- // invoke the inherited method
- // if 'fn' is not function valued, this will throw
- return fn.apply(this, arrayOfArgs || []);
+ };
+ doc.addEventListener(READY_EVENT, checkReady);
+ } else if (callback) {
+ callback();
+ }
+}
+
+function markTargetLoaded(event) {
+ event.target.__loaded = true;
+}
+
+// call <callback> when we ensure all imports have loaded
+function watchImportsLoad(callback, doc) {
+ var imports = doc.querySelectorAll('link[rel=import]');
+ var loaded = 0, l = imports.length;
+ function checkDone(d) {
+ if (loaded == l) {
+ callback && callback();
+ }
+ }
+ function loadedImport(e) {
+ markTargetLoaded(e);
+ loaded++;
+ checkDone();
+ }
+ if (l) {
+ for (var i=0, imp; (i<l) && (imp=imports[i]); i++) {
+ if (isImportLoaded(imp)) {
+ loadedImport.call(imp, {target: imp});
+ } else {
+ imp.addEventListener('load', loadedImport);
+ imp.addEventListener('error', loadedImport);
}
}
+ } else {
+ checkDone();
+ }
+}
- function nameInThis(value) {
- var p = this.__proto__;
- while (p && p !== HTMLElement.prototype) {
- // TODO(sjmiles): getOwnPropertyNames is absurdly expensive
- var n$ = Object.getOwnPropertyNames(p);
- for (var i=0, l=n$.length, n; i<l && (n=n$[i]); i++) {
- var d = Object.getOwnPropertyDescriptor(p, n);
- if (typeof d.value === 'function' && d.value === value) {
- return n;
- }
- }
- p = p.__proto__;
+// NOTE: test for native imports loading is based on explicitly watching
+// all imports (see below).
+function isImportLoaded(link) {
+ return useNative ? link.__loaded : link.__importParsed;
+}
+
+// TODO(sorvell): Workaround for
+// https://www.w3.org/Bugs/Public/show_bug.cgi?id=25007, should be removed when
+// this bug is addressed.
+// (1) Install a mutation observer to see when HTMLImports have loaded
+// (2) if this script is run during document load it will watch any existing
+// imports for loading.
+//
+// NOTE: The workaround has restricted functionality: (1) it's only compatible
+// with imports that are added to document.head since the mutation observer
+// watches only head for perf reasons, (2) it requires this script
+// to run before any imports have completed loading.
+if (useNative) {
+ new MutationObserver(function(mxns) {
+ for (var i=0, l=mxns.length, m; (i < l) && (m=mxns[i]); i++) {
+ if (m.addedNodes) {
+ handleImports(m.addedNodes);
}
}
+ }).observe(document.head, {childList: true});
- function memoizeSuper(method, name, proto) {
- // find and cache next prototype containing `name`
- // we need the prototype so we can do another lookup
- // from here
- var s = nextSuper(proto, name, method);
- if (s[name]) {
- // `s` is a prototype, the actual method is `s[name]`
- // tag super method with it's name for quicker lookups
- s[name].nom = name;
+ function handleImports(nodes) {
+ for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) {
+ if (isImport(n)) {
+ handleImport(n);
}
- return method._super = s;
}
+ }
- function nextSuper(proto, name, caller) {
- // look for an inherited prototype that implements name
- while (proto) {
- if ((proto[name] !== caller) && proto[name]) {
- return proto;
- }
- proto = getPrototypeOf(proto);
+ function isImport(element) {
+ return element.localName === 'link' && element.rel === 'import';
+ }
+
+ function handleImport(element) {
+ var loaded = element.import;
+ if (loaded) {
+ markTargetLoaded({target: element});
+ } else {
+ element.addEventListener('load', markTargetLoaded);
+ element.addEventListener('error', markTargetLoaded);
+ }
+ }
+
+ // make sure to catch any imports that are in the process of loading
+ // when this script is run.
+ (function() {
+ if (document.readyState === 'loading') {
+ var imports = document.querySelectorAll('link[rel=import]');
+ for (var i=0, l=imports.length, imp; (i<l) && (imp=imports[i]); i++) {
+ handleImport(imp);
}
- // must not return null, or we lose our invariant above
- // in this case, a super() call was invoked where no superclass
- // method exists
- // TODO(sjmiles): thow an exception?
- return Object;
}
+ })();
- // NOTE: In some platforms (IE10) the prototype chain is faked via
- // __proto__. Therefore, always get prototype via __proto__ instead of
- // the more standard Object.getPrototypeOf.
+}
+
+// Fire the 'HTMLImportsLoaded' event when imports in document at load time
+// have loaded. This event is required to simulate the script blocking
+// behavior of native imports. A main document script that needs to be sure
+// imports have loaded should wait for this event.
+whenImportsReady(function() {
+ HTMLImports.ready = true;
+ HTMLImports.readyTime = new Date().getTime();
+ mainDoc.dispatchEvent(
+ new CustomEvent('HTMLImportsLoaded', {bubbles: true})
+ );
+});
+
+// exports
+scope.useNative = useNative;
+scope.isImportLoaded = isImportLoaded;
+scope.whenReady = whenImportsReady;
+scope.isIE = isIE;
+
+// deprecated
+scope.whenImportsReady = whenImportsReady;
+
+})(window.HTMLImports);
+/*
+ * 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(scope) {
+
+ function withDependencies(task, depends) {
+ depends = depends || [];
+ if (!depends.map) {
+ depends = [depends];
+ }
+ return task.apply(this, depends.map(marshal));
+ }
+
+ function module(name, dependsOrFactory, moduleFactory) {
+ var module;
+ switch (arguments.length) {
+ case 0:
+ return;
+ case 1:
+ 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;
+ }
+ modules[name] = module;
+ };
+
+ function marshal(name) {
+ return modules[name];
+ }
+
+ var modules = {};
+
+ function using(depends, task) {
+ HTMLImports.whenImportsReady(function() {
+ withDependencies(task, depends);
+ });
+ };
+
+ // exports
+
+ scope.marshal = marshal;
+ // `module` confuses commonjs detectors
+ scope.modularize = module;
+ scope.using = using;
+
+})(window);
+
+/*
+ * 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(scope) {
+
+ // TODO(sorvell): It's desireable to provide a default stylesheet
+ // that's convenient for styling unresolved elements, but
+ // it's cumbersome to have to include this manually in every page.
+ // It would make sense to put inside some HTMLImport but
+ // the HTMLImports polyfill does not allow loading of stylesheets
+ // that block rendering. Therefore this injection is tolerated here.
+ var style = document.createElement('style');
+ style.textContent = ''
+ + 'body {'
+ + 'transition: opacity ease-in 0.2s;'
+ + ' } \n'
+ + 'body[unresolved] {'
+ + 'opacity: 0; display: block; overflow: hidden;'
+ + ' } \n'
+ ;
+ var head = document.querySelector('head');
+ head.insertBefore(style, head.firstChild);
+
+})(Platform);
+
+// Copyright 2012 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.
+
+(function(global) {
+ 'use strict';
+
+ var testingExposeCycleCount = global.testingExposeCycleCount;
+
+ // Detect and do basic sanity checking on Object/Array.observe.
+ function detectObjectObserve() {
+ if (typeof Object.observe !== 'function' ||
+ typeof Array.observe !== 'function') {
+ return false;
+ }
+
+ var records = [];
+
+ function callback(recs) {
+ records = recs;
+ }
+
+ 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 !== 5)
+ return false;
+
+ 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);
+ Array.unobserve(arr, callback);
+
+ return true;
+ }
+
+ var hasObserve = detectObjectObserve();
+
+ function detectEval() {
+ // Don't test for eval if we're running in a Chrome App environment.
+ // We check for APIs set that only exist in a Chrome App context.
+ if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) {
+ return false;
+ }
+
+ // Firefox OS Apps do not allow eval. This feature detection is very hacky
+ // but even if some other platform adds support for this function this code
+ // will continue to work.
+ if (navigator.getDeviceStorage) {
+ return false;
+ }
+
+ try {
+ var f = new Function('', 'return true;');
+ return f();
+ } catch (ex) {
+ return false;
+ }
+ }
+
+ var hasEval = detectEval();
+
+ function isIndex(s) {
+ return +s === s >>> 0;
+ }
+
+ function toNumber(s) {
+ return +s;
+ }
+
+ function isObject(obj) {
+ return obj === Object(obj);
+ }
+
+ var numberIsNaN = global.Number.isNaN || function(value) {
+ return typeof value === 'number' && global.isNaN(value);
+ }
+
+ function areSameValue(left, right) {
+ if (left === right)
+ return left !== 0 || 1 / left === 1 / right;
+ if (numberIsNaN(left) && numberIsNaN(right))
+ return true;
+
+ return left !== left && right !== right;
+ }
+
+ var createObject = ('__proto__' in {}) ?
+ function(obj) { return obj; } :
+ function(obj) {
+ var proto = obj.__proto__;
+ if (!proto)
+ return obj;
+ var newObject = Object.create(proto);
+ Object.getOwnPropertyNames(obj).forEach(function(name) {
+ Object.defineProperty(newObject, name,
+ Object.getOwnPropertyDescriptor(obj, name));
+ });
+ return newObject;
+ };
+
+ var identStart = '[\$_a-zA-Z]';
+ var identPart = '[\$_a-zA-Z0-9]';
+ var identRegExp = new RegExp('^' + identStart + '+' + identPart + '*' + '$');
+
+ function getPathCharType(char) {
+ if (char === undefined)
+ return 'eof';
+
+ 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';
+
+ // 1-9
+ if (0x31 <= code && code <= 0x39)
+ return 'number';
+
+ return 'else';
+ }
+
+ var pathStateMachine = {
+ 'beforePath': {
+ 'ws': ['beforePath'],
+ 'ident': ['inIdent', 'append'],
+ '[': ['beforeElement'],
+ 'eof': ['afterPath']
+ },
+
+ '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']
+ },
+
+ 'inIndex': {
+ '0': ['inIndex', 'append'],
+ 'number': ['inIndex', 'append'],
+ 'ws': ['afterElement'],
+ ']': ['inPath', 'push']
+ },
+
+ 'inSingleQuote': {
+ "'": ['afterElement'],
+ 'eof': ['error'],
+ 'else': ['inSingleQuote', 'append']
+ },
+
+ 'inDoubleQuote': {
+ '"': ['afterElement'],
+ 'eof': ['error'],
+ 'else': ['inDoubleQuote', 'append']
+ },
+
+ 'afterElement': {
+ 'ws': ['afterElement'],
+ ']': ['inPath', 'push']
+ }
+ }
+
+ 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');
+
+ for (var i = 0; i < parts.length; i++) {
+ this.push(String(parts[i]));
+ }
+
+ if (hasEval && this.length) {
+ this.getValueFrom = this.compiledGetValueFromFn();
+ }
+ }
+
+ // TODO(rafaelw): Make simple LRU cache
+ var pathCache = {};
+
+ function getPath(pathString) {
+ if (pathString instanceof Path)
+ return pathString;
+
+ if (pathString == null || pathString.length == 0)
+ pathString = '';
+
+ 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;
+
+ var parts = parsePath(pathString);
+ if (!parts)
+ return invalidPath;
+
+ 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() {
+ 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) {
+ for (var i = 0; i < this.length; i++) {
+ if (obj == null)
+ return;
+ obj = obj[this[i]];
+ }
+ return obj;
+ },
+
+ iterateObjects: function(obj, observe) {
+ for (var i = 0; i < this.length; i++) {
+ if (i)
+ obj = obj[this[i - 1]];
+ if (!isObject(obj))
+ return;
+ observe(obj, this[0]);
+ }
+ },
+
+ compiledGetValueFromFn: function() {
+ var str = '';
+ var pathString = 'obj';
+ str += 'if (obj != null';
+ var i = 0;
+ var key;
+ for (; i < (this.length - 1); i++) {
+ key = this[i];
+ pathString += isIdent(key) ? '.' + key : formatAccessor(key);
+ str += ' &&\n ' + pathString + ' != null';
+ }
+ str += ')\n';
+
+ var key = this[i];
+ pathString += isIdent(key) ? '.' + key : formatAccessor(key);
+
+ str += ' return ' + pathString + ';\nelse\n return undefined;';
+ return new Function('obj', str);
+ },
+
+ setValueFrom: function(obj, value) {
+ if (!this.length)
+ return false;
+
+ for (var i = 0; i < this.length - 1; i++) {
+ if (!isObject(obj))
+ return false;
+ obj = obj[this[i]];
+ }
+
+ if (!isObject(obj))
+ return false;
+
+ obj[this[i]] = value;
+ return true;
+ }
+ });
+
+ var invalidPath = new Path('', constructorIsPrivate);
+ invalidPath.valid = false;
+ invalidPath.getValueFrom = invalidPath.setValueFrom = function() {};
+
+ var MAX_DIRTY_CHECK_CYCLES = 1000;
+
+ function dirtyCheck(observer) {
+ var cycles = 0;
+ while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check_()) {
+ cycles++;
+ }
+ if (testingExposeCycleCount)
+ global.dirtyCheckCycleCount = cycles;
+
+ return cycles > 0;
+ }
+
+ function objectIsEmpty(object) {
+ for (var prop in object)
+ return false;
+ return true;
+ }
+
+ function diffIsEmpty(diff) {
+ return objectIsEmpty(diff.added) &&
+ objectIsEmpty(diff.removed) &&
+ objectIsEmpty(diff.changed);
+ }
+
+ function diffObjectFromOldObject(object, oldObject) {
+ var added = {};
+ var removed = {};
+ var changed = {};
+
+ for (var prop in oldObject) {
+ var newValue = object[prop];
+
+ if (newValue !== undefined && newValue === oldObject[prop])
+ continue;
+
+ if (!(prop in object)) {
+ removed[prop] = undefined;
+ continue;
+ }
+
+ if (newValue !== oldObject[prop])
+ changed[prop] = newValue;
+ }
+
+ for (var prop in object) {
+ if (prop in oldObject)
+ continue;
+
+ added[prop] = object[prop];
+ }
+
+ if (Array.isArray(object) && object.length !== oldObject.length)
+ changed.length = object.length;
+
+ return {
+ added: added,
+ removed: removed,
+ changed: changed
+ };
+ }
+
+ var eomTasks = [];
+ function runEOMTasks() {
+ if (!eomTasks.length)
+ return false;
+
+ for (var i = 0; i < eomTasks.length; i++) {
+ eomTasks[i]();
+ }
+ eomTasks.length = 0;
+ return true;
+ }
+
+ var runEOM = hasObserve ? (function(){
+ var eomObj = { pingPong: true };
+ var eomRunScheduled = false;
+
+ Object.observe(eomObj, function() {
+ runEOMTasks();
+ eomRunScheduled = false;
+ });
+
+ return function(fn) {
+ eomTasks.push(fn);
+ if (!eomRunScheduled) {
+ eomRunScheduled = true;
+ eomObj.pingPong = !eomObj.pingPong;
+ }
+ };
+ })() :
+ (function() {
+ return function(fn) {
+ eomTasks.push(fn);
+ };
+ })();
+
+ var observedObjectCache = [];
+
+ function newObservedObject() {
+ var observer;
+ var object;
+ var discardRecords = false;
+ var first = true;
+
+ function callback(records) {
+ if (observer && observer.state_ === OPENED && !discardRecords)
+ observer.check_(records);
+ }
+
+ return {
+ open: function(obs) {
+ if (observer)
+ throw Error('ObservedObject in use');
+
+ if (!first)
+ Object.deliverChangeRecords(callback);
+
+ observer = obs;
+ first = false;
+ },
+ observe: function(obj, arrayObserve) {
+ object = obj;
+ if (arrayObserve)
+ Array.observe(object, callback);
+ else
+ Object.observe(object, callback);
+ },
+ deliver: function(discard) {
+ discardRecords = discard;
+ Object.deliverChangeRecords(callback);
+ discardRecords = false;
+ },
+ close: function() {
+ observer = undefined;
+ Object.unobserve(object, callback);
+ observedObjectCache.push(this);
+ }
+ };
+ }
+
+ /*
+ * The observedSet abstraction is a perf optimization which reduces the total
+ * number of Object.observe observations of a set of objects. The idea is that
+ * groups of Observers will have some object dependencies in common and this
+ * observed set ensures that each object in the transitive closure of
+ * dependencies is only observed once. The observedSet acts as a write barrier
+ * such that whenever any change comes through, all Observers are checked for
+ * changed values.
+ *
+ * Note that this optimization is explicitly moving work from setup-time to
+ * change-time.
+ *
+ * TODO(rafaelw): Implement "garbage collection". In order to move work off
+ * the critical path, when Observers are closed, their observed objects are
+ * not Object.unobserve(d). As a result, it's possible that if the observedSet
+ * is kept open, but some Observers have been closed, it could cause "leaks"
+ * (prevent otherwise collectable objects from being collected). At some
+ * point, we should implement incremental "gc" which keeps a list of
+ * observedSets which may need clean-up and does small amounts of cleanup on a
+ * timeout until all is clean.
+ */
+
+ function getObservedObject(observer, object, arrayObserve) {
+ var dir = observedObjectCache.pop() || newObservedObject();
+ dir.open(observer);
+ dir.observe(object, arrayObserve);
+ return dir;
+ }
+
+ var observedSetCache = [];
+
+ function newObservedSet() {
+ var observerCount = 0;
+ var observers = [];
+ var objects = [];
+ var rootObj;
+ var rootObjProps;
+
+ function observe(obj, prop) {
+ if (!obj)
+ return;
+
+ if (obj === rootObj)
+ rootObjProps[prop] = true;
+
+ if (objects.indexOf(obj) < 0) {
+ objects.push(obj);
+ Object.observe(obj, callback);
+ }
+
+ observe(Object.getPrototypeOf(obj), prop);
+ }
+
+ function allRootObjNonObservedProps(recs) {
+ for (var i = 0; i < recs.length; i++) {
+ var rec = recs[i];
+ if (rec.object !== rootObj ||
+ rootObjProps[rec.name] ||
+ rec.type === 'setPrototype') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function callback(recs) {
+ if (allRootObjNonObservedProps(recs))
+ return;
+
+ var observer;
+ for (var i = 0; i < observers.length; i++) {
+ observer = observers[i];
+ if (observer.state_ == OPENED) {
+ observer.iterateObjects_(observe);
+ }
+ }
+
+ for (var i = 0; i < observers.length; i++) {
+ observer = observers[i];
+ if (observer.state_ == OPENED) {
+ observer.check_();
+ }
+ }
+ }
+
+ var record = {
+ object: undefined,
+ objects: objects,
+ open: function(obs, object) {
+ if (!rootObj) {
+ rootObj = object;
+ rootObjProps = {};
+ }
+
+ observers.push(obs);
+ observerCount++;
+ obs.iterateObjects_(observe);
+ },
+ close: function(obs) {
+ observerCount--;
+ if (observerCount > 0) {
+ return;
+ }
+
+ for (var i = 0; i < objects.length; i++) {
+ Object.unobserve(objects[i], callback);
+ Observer.unobservedCount++;
+ }
+
+ observers.length = 0;
+ objects.length = 0;
+ rootObj = undefined;
+ rootObjProps = undefined;
+ observedSetCache.push(this);
+ }
+ };
+
+ return record;
+ }
+
+ var lastObservedSet;
+
+ function getObservedSet(observer, obj) {
+ if (!lastObservedSet || lastObservedSet.object !== obj) {
+ lastObservedSet = observedSetCache.pop() || newObservedSet();
+ lastObservedSet.object = obj;
+ }
+ lastObservedSet.open(observer, obj);
+ return lastObservedSet;
+ }
+
+ var UNOPENED = 0;
+ var OPENED = 1;
+ var CLOSED = 2;
+ var RESETTING = 3;
+
+ var nextObserverId = 1;
+
+ function Observer() {
+ this.state_ = UNOPENED;
+ this.callback_ = undefined;
+ this.target_ = undefined; // TODO(rafaelw): Should be WeakRef
+ this.directObserver_ = undefined;
+ this.value_ = undefined;
+ this.id_ = nextObserverId++;
+ }
+
+ Observer.prototype = {
+ open: function(callback, target) {
+ if (this.state_ != UNOPENED)
+ throw Error('Observer has already been opened.');
+
+ addToAll(this);
+ this.callback_ = callback;
+ this.target_ = target;
+ this.connect_();
+ this.state_ = OPENED;
+ return this.value_;
+ },
+
+ close: function() {
+ if (this.state_ != OPENED)
+ return;
+
+ removeFromAll(this);
+ this.disconnect_();
+ this.value_ = undefined;
+ this.callback_ = undefined;
+ this.target_ = undefined;
+ this.state_ = CLOSED;
+ },
+
+ deliver: function() {
+ if (this.state_ != OPENED)
+ return;
+
+ dirtyCheck(this);
+ },
+
+ report_: function(changes) {
+ try {
+ this.callback_.apply(this.target_, changes);
+ } catch (ex) {
+ Observer._errorThrownDuringCallback = true;
+ console.error('Exception caught during observer callback: ' +
+ (ex.stack || ex));
+ }
+ },
+
+ discardChanges: function() {
+ this.check_(undefined, true);
+ return this.value_;
+ }
+ }
+
+ var collectObservers = !hasObserve;
+ var allObservers;
+ Observer._allObserversCount = 0;
+
+ if (collectObservers) {
+ allObservers = [];
+ }
+
+ function addToAll(observer) {
+ Observer._allObserversCount++;
+ if (!collectObservers)
+ return;
+
+ allObservers.push(observer);
+ }
+
+ function removeFromAll(observer) {
+ Observer._allObserversCount--;
+ }
+
+ var runningMicrotaskCheckpoint = false;
+
+ var hasDebugForceFullDelivery = hasObserve && hasEval && (function() {
+ try {
+ eval('%RunMicrotasks()');
+ return true;
+ } catch (ex) {
+ return false;
+ }
+ })();
+
+ global.Platform = global.Platform || {};
+
+ global.Platform.performMicrotaskCheckpoint = function() {
+ if (runningMicrotaskCheckpoint)
+ return;
+
+ if (hasDebugForceFullDelivery) {
+ eval('%RunMicrotasks()');
+ return;
+ }
+
+ if (!collectObservers)
+ return;
+
+ runningMicrotaskCheckpoint = true;
+
+ var cycles = 0;
+ var anyChanged, toCheck;
+
+ do {
+ cycles++;
+ toCheck = allObservers;
+ allObservers = [];
+ anyChanged = false;
+
+ for (var i = 0; i < toCheck.length; i++) {
+ var observer = toCheck[i];
+ if (observer.state_ != OPENED)
+ continue;
+
+ if (observer.check_())
+ anyChanged = true;
+
+ allObservers.push(observer);
+ }
+ if (runEOMTasks())
+ anyChanged = true;
+ } while (cycles < MAX_DIRTY_CHECK_CYCLES && anyChanged);
+
+ if (testingExposeCycleCount)
+ global.dirtyCheckCycleCount = cycles;
+
+ runningMicrotaskCheckpoint = false;
+ };
+
+ if (collectObservers) {
+ global.Platform.clearObservers = function() {
+ allObservers = [];
+ };
+ }
+
+ function ObjectObserver(object) {
+ Observer.call(this);
+ this.value_ = object;
+ this.oldObject_ = undefined;
+ }
+
+ ObjectObserver.prototype = createObject({
+ __proto__: Observer.prototype,
+
+ arrayObserve: false,
+
+ connect_: function(callback, target) {
+ if (hasObserve) {
+ this.directObserver_ = getObservedObject(this, this.value_,
+ this.arrayObserve);
+ } else {
+ this.oldObject_ = this.copyObject(this.value_);
+ }
+
+ },
+
+ copyObject: function(object) {
+ var copy = Array.isArray(object) ? [] : {};
+ for (var prop in object) {
+ copy[prop] = object[prop];
+ };
+ if (Array.isArray(object))
+ copy.length = object.length;
+ return copy;
+ },
+
+ check_: function(changeRecords, skipChanges) {
+ var diff;
+ var oldValues;
+ if (hasObserve) {
+ if (!changeRecords)
+ return false;
+
+ oldValues = {};
+ diff = diffObjectFromChangeRecords(this.value_, changeRecords,
+ oldValues);
+ } else {
+ oldValues = this.oldObject_;
+ diff = diffObjectFromOldObject(this.value_, this.oldObject_);
+ }
+
+ if (diffIsEmpty(diff))
+ return false;
+
+ if (!hasObserve)
+ this.oldObject_ = this.copyObject(this.value_);
+
+ this.report_([
+ diff.added || {},
+ diff.removed || {},
+ diff.changed || {},
+ function(property) {
+ return oldValues[property];
+ }
+ ]);
+
+ return true;
+ },
+
+ disconnect_: function() {
+ if (hasObserve) {
+ this.directObserver_.close();
+ this.directObserver_ = undefined;
+ } else {
+ this.oldObject_ = undefined;
+ }
+ },
+
+ deliver: function() {
+ if (this.state_ != OPENED)
+ return;
+
+ if (hasObserve)
+ this.directObserver_.deliver(false);
+ else
+ dirtyCheck(this);
+ },
+
+ discardChanges: function() {
+ if (this.directObserver_)
+ this.directObserver_.deliver(true);
+ else
+ this.oldObject_ = this.copyObject(this.value_);
+
+ return this.value_;
+ }
+ });
+
+ function ArrayObserver(array) {
+ if (!Array.isArray(array))
+ throw Error('Provided object is not an Array');
+ ObjectObserver.call(this, array);
+ }
+
+ ArrayObserver.prototype = createObject({
+
+ __proto__: ObjectObserver.prototype,
+
+ arrayObserve: true,
+
+ copyObject: function(arr) {
+ return arr.slice();
+ },
+
+ check_: function(changeRecords) {
+ var splices;
+ if (hasObserve) {
+ if (!changeRecords)
+ return false;
+ splices = projectArraySplices(this.value_, changeRecords);
+ } else {
+ splices = calcSplices(this.value_, 0, this.value_.length,
+ this.oldObject_, 0, this.oldObject_.length);
+ }
+
+ if (!splices || !splices.length)
+ return false;
+
+ if (!hasObserve)
+ this.oldObject_ = this.copyObject(this.value_);
+
+ this.report_([splices]);
+ return true;
+ }
+ });
+
+ ArrayObserver.applySplices = function(previous, current, splices) {
+ splices.forEach(function(splice) {
+ var spliceArgs = [splice.index, splice.removed.length];
+ var addIndex = splice.index;
+ while (addIndex < splice.index + splice.addedCount) {
+ spliceArgs.push(current[addIndex]);
+ addIndex++;
+ }
+
+ Array.prototype.splice.apply(previous, spliceArgs);
+ });
+ };
+
+ function PathObserver(object, path) {
+ Observer.call(this);
+
+ this.object_ = object;
+ this.path_ = getPath(path);
+ this.directObserver_ = undefined;
+ }
+
+ PathObserver.prototype = createObject({
+ __proto__: Observer.prototype,
+
+ get path() {
+ return this.path_;
+ },
+
+ connect_: function() {
+ if (hasObserve)
+ this.directObserver_ = getObservedSet(this, this.object_);
+
+ this.check_(undefined, true);
+ },
+
+ disconnect_: function() {
+ this.value_ = undefined;
+
+ if (this.directObserver_) {
+ this.directObserver_.close(this);
+ this.directObserver_ = undefined;
+ }
+ },
+
+ iterateObjects_: function(observe) {
+ this.path_.iterateObjects(this.object_, observe);
+ },
+
+ check_: function(changeRecords, skipChanges) {
+ var oldValue = this.value_;
+ this.value_ = this.path_.getValueFrom(this.object_);
+ if (skipChanges || areSameValue(this.value_, oldValue))
+ return false;
+
+ this.report_([this.value_, oldValue, this]);
+ return true;
+ },
+
+ setValue: function(newValue) {
+ if (this.path_)
+ this.path_.setValueFrom(this.object_, newValue);
+ }
+ });
+
+ function CompoundObserver(reportChangesOnOpen) {
+ Observer.call(this);
+
+ this.reportChangesOnOpen_ = reportChangesOnOpen;
+ this.value_ = [];
+ this.directObserver_ = undefined;
+ this.observed_ = [];
+ }
+
+ var observerSentinel = {};
+
+ CompoundObserver.prototype = createObject({
+ __proto__: Observer.prototype,
+
+ connect_: function() {
+ if (hasObserve) {
+ var object;
+ var needsDirectObserver = false;
+ for (var i = 0; i < this.observed_.length; i += 2) {
+ object = this.observed_[i]
+ if (object !== observerSentinel) {
+ needsDirectObserver = true;
+ break;
+ }
+ }
+
+ if (needsDirectObserver)
+ this.directObserver_ = getObservedSet(this, object);
+ }
+
+ this.check_(undefined, !this.reportChangesOnOpen_);
+ },
+
+ disconnect_: function() {
+ for (var i = 0; i < this.observed_.length; i += 2) {
+ if (this.observed_[i] === observerSentinel)
+ this.observed_[i + 1].close();
+ }
+ this.observed_.length = 0;
+ this.value_.length = 0;
+
+ if (this.directObserver_) {
+ this.directObserver_.close(this);
+ this.directObserver_ = undefined;
+ }
+ },
+
+ addPath: function(object, path) {
+ if (this.state_ != UNOPENED && this.state_ != RESETTING)
+ throw Error('Cannot add paths once started.');
+
+ var path = getPath(path);
+ this.observed_.push(object, path);
+ if (!this.reportChangesOnOpen_)
+ return;
+ var index = this.observed_.length / 2 - 1;
+ this.value_[index] = path.getValueFrom(object);
+ },
+
+ addObserver: function(observer) {
+ if (this.state_ != UNOPENED && this.state_ != RESETTING)
+ throw Error('Cannot add observers once started.');
+
+ this.observed_.push(observerSentinel, observer);
+ if (!this.reportChangesOnOpen_)
+ return;
+ var index = this.observed_.length / 2 - 1;
+ this.value_[index] = observer.open(this.deliver, this);
+ },
+
+ startReset: function() {
+ if (this.state_ != OPENED)
+ throw Error('Can only reset while open');
+
+ this.state_ = RESETTING;
+ this.disconnect_();
+ },
+
+ finishReset: function() {
+ if (this.state_ != RESETTING)
+ throw Error('Can only finishReset after startReset');
+ this.state_ = OPENED;
+ this.connect_();
+
+ return this.value_;
+ },
+
+ iterateObjects_: function(observe) {
+ var object;
+ for (var i = 0; i < this.observed_.length; i += 2) {
+ object = this.observed_[i]
+ if (object !== observerSentinel)
+ this.observed_[i + 1].iterateObjects(object, observe)
+ }
+ },
+
+ check_: function(changeRecords, skipChanges) {
+ var oldValues;
+ for (var i = 0; i < this.observed_.length; i += 2) {
+ var object = this.observed_[i];
+ var path = this.observed_[i+1];
+ var value;
+ if (object === observerSentinel) {
+ var observable = path;
+ value = this.state_ === UNOPENED ?
+ observable.open(this.deliver, this) :
+ observable.discardChanges();
+ } else {
+ value = path.getValueFrom(object);
+ }
+
+ if (skipChanges) {
+ this.value_[i / 2] = value;
+ continue;
+ }
+
+ if (areSameValue(value, this.value_[i / 2]))
+ continue;
+
+ oldValues = oldValues || [];
+ oldValues[i / 2] = this.value_[i / 2];
+ this.value_[i / 2] = value;
+ }
+
+ if (!oldValues)
+ return false;
+
+ // TODO(rafaelw): Having observed_ as the third callback arg here is
+ // pretty lame API. Fix.
+ this.report_([this.value_, oldValues, this.observed_]);
+ return true;
+ }
+ });
+
+ function identFn(value) { return value; }
+
+ function ObserverTransform(observable, getValueFn, setValueFn,
+ dontPassThroughSet) {
+ this.callback_ = undefined;
+ this.target_ = undefined;
+ this.value_ = undefined;
+ this.observable_ = observable;
+ this.getValueFn_ = getValueFn || identFn;
+ this.setValueFn_ = setValueFn || identFn;
+ // TODO(rafaelw): This is a temporary hack. PolymerExpressions needs this
+ // at the moment because of a bug in it's dependency tracking.
+ this.dontPassThroughSet_ = dontPassThroughSet;
+ }
+
+ ObserverTransform.prototype = {
+ open: function(callback, target) {
+ this.callback_ = callback;
+ this.target_ = target;
+ this.value_ =
+ this.getValueFn_(this.observable_.open(this.observedCallback_, this));
+ return this.value_;
+ },
+
+ observedCallback_: function(value) {
+ value = this.getValueFn_(value);
+ if (areSameValue(value, this.value_))
+ return;
+ var oldValue = this.value_;
+ this.value_ = value;
+ this.callback_.call(this.target_, this.value_, oldValue);
+ },
+
+ discardChanges: function() {
+ this.value_ = this.getValueFn_(this.observable_.discardChanges());
+ return this.value_;
+ },
+
+ deliver: function() {
+ return this.observable_.deliver();
+ },
+
+ setValue: function(value) {
+ value = this.setValueFn_(value);
+ if (!this.dontPassThroughSet_ && this.observable_.setValue)
+ return this.observable_.setValue(value);
+ },
+
+ close: function() {
+ if (this.observable_)
+ this.observable_.close();
+ this.callback_ = undefined;
+ this.target_ = undefined;
+ this.observable_ = undefined;
+ this.value_ = undefined;
+ this.getValueFn_ = undefined;
+ this.setValueFn_ = undefined;
+ }
+ }
+
+ var expectedRecordTypes = {
+ add: true,
+ update: true,
+ delete: true
+ };
+
+ function diffObjectFromChangeRecords(object, changeRecords, oldValues) {
+ var added = {};
+ var removed = {};
+
+ for (var i = 0; i < changeRecords.length; i++) {
+ var record = changeRecords[i];
+ if (!expectedRecordTypes[record.type]) {
+ console.error('Unknown changeRecord type: ' + record.type);
+ console.error(record);
+ continue;
+ }
+
+ if (!(record.name in oldValues))
+ oldValues[record.name] = record.oldValue;
+
+ if (record.type == 'update')
+ continue;
+
+ if (record.type == 'add') {
+ if (record.name in removed)
+ delete removed[record.name];
+ else
+ added[record.name] = true;
+
+ continue;
+ }
+
+ // type = 'delete'
+ if (record.name in added) {
+ delete added[record.name];
+ delete oldValues[record.name];
+ } else {
+ removed[record.name] = true;
+ }
+ }
+
+ for (var prop in added)
+ added[prop] = object[prop];
+
+ for (var prop in removed)
+ removed[prop] = undefined;
+
+ var changed = {};
+ for (var prop in oldValues) {
+ if (prop in added || prop in removed)
+ continue;
+
+ var newValue = object[prop];
+ if (oldValues[prop] !== newValue)
+ changed[prop] = newValue;
+ }
+
+ return {
+ added: added,
+ removed: removed,
+ changed: changed
+ };
+ }
+
+ function newSplice(index, removed, addedCount) {
+ return {
+ index: index,
+ removed: removed,
+ addedCount: addedCount
+ };
+ }
+
+ var EDIT_LEAVE = 0;
+ var EDIT_UPDATE = 1;
+ var EDIT_ADD = 2;
+ var EDIT_DELETE = 3;
+
+ function ArraySplice() {}
+
+ ArraySplice.prototype = {
+
+ // Note: This function is *based* on the computation of the Levenshtein
+ // "edit" distance. The one change is that "updates" are treated as two
+ // edits - not one. With Array splices, an update is really a delete
+ // followed by an add. By retaining this, we optimize for "keeping" the
+ // maximum array items in the original array. For example:
+ //
+ // 'xxxx123' -> '123yyyy'
+ //
+ // With 1-edit updates, the shortest path would be just to update all seven
+ // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
+ // leaves the substring '123' intact.
+ calcEditDistances: function(current, currentStart, currentEnd,
+ old, oldStart, oldEnd) {
+ // "Deletion" columns
+ var rowCount = oldEnd - oldStart + 1;
+ var columnCount = currentEnd - currentStart + 1;
+ var distances = new Array(rowCount);
+
+ // "Addition" rows. Initialize null column.
+ for (var i = 0; i < rowCount; i++) {
+ distances[i] = new Array(columnCount);
+ distances[i][0] = i;
+ }
+
+ // Initialize null row
+ for (var j = 0; j < columnCount; j++)
+ distances[0][j] = j;
+
+ for (var i = 1; i < rowCount; i++) {
+ for (var j = 1; j < columnCount; j++) {
+ if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
+ distances[i][j] = distances[i - 1][j - 1];
+ else {
+ var north = distances[i - 1][j] + 1;
+ var west = distances[i][j - 1] + 1;
+ distances[i][j] = north < west ? north : west;
+ }
+ }
+ }
+
+ return distances;
+ },
+
+ // This starts at the final weight, and walks "backward" by finding
+ // the minimum previous weight recursively until the origin of the weight
+ // matrix.
+ spliceOperationsFromEditDistances: function(distances) {
+ var i = distances.length - 1;
+ var j = distances[0].length - 1;
+ var current = distances[i][j];
+ var edits = [];
+ while (i > 0 || j > 0) {
+ if (i == 0) {
+ edits.push(EDIT_ADD);
+ j--;
+ continue;
+ }
+ if (j == 0) {
+ edits.push(EDIT_DELETE);
+ i--;
+ continue;
+ }
+ var northWest = distances[i - 1][j - 1];
+ var west = distances[i - 1][j];
+ var north = distances[i][j - 1];
+
+ var min;
+ if (west < north)
+ min = west < northWest ? west : northWest;
+ else
+ min = north < northWest ? north : northWest;
+
+ if (min == northWest) {
+ if (northWest == current) {
+ edits.push(EDIT_LEAVE);
+ } else {
+ edits.push(EDIT_UPDATE);
+ current = northWest;
+ }
+ i--;
+ j--;
+ } else if (min == west) {
+ edits.push(EDIT_DELETE);
+ i--;
+ current = west;
+ } else {
+ edits.push(EDIT_ADD);
+ j--;
+ current = north;
+ }
+ }
+
+ edits.reverse();
+ return edits;
+ },
+
+ /**
+ * Splice Projection functions:
+ *
+ * A splice map is a representation of how a previous array of items
+ * was transformed into a new array of items. Conceptually it is a list of
+ * tuples of
+ *
+ * <index, removed, addedCount>
+ *
+ * which are kept in ascending index order of. The tuple represents that at
+ * the |index|, |removed| sequence of items were removed, and counting forward
+ * from |index|, |addedCount| items were added.
+ */
+
+ /**
+ * Lacking individual splice mutation information, the minimal set of
+ * splices can be synthesized given the previous state and final state of an
+ * array. The basic approach is to calculate the edit distance matrix and
+ * choose the shortest path through it.
+ *
+ * Complexity: O(l * p)
+ * l: The length of the current array
+ * p: The length of the old array
+ */
+ calcSplices: function(current, currentStart, currentEnd,
+ old, oldStart, oldEnd) {
+ var prefixCount = 0;
+ var suffixCount = 0;
+
+ var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
+ if (currentStart == 0 && oldStart == 0)
+ prefixCount = this.sharedPrefix(current, old, minLength);
+
+ if (currentEnd == current.length && oldEnd == old.length)
+ suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
+
+ currentStart += prefixCount;
+ oldStart += prefixCount;
+ currentEnd -= suffixCount;
+ oldEnd -= suffixCount;
+
+ if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
+ return [];
+
+ if (currentStart == currentEnd) {
+ var splice = newSplice(currentStart, [], 0);
+ while (oldStart < oldEnd)
+ splice.removed.push(old[oldStart++]);
+
+ return [ splice ];
+ } else if (oldStart == oldEnd)
+ return [ newSplice(currentStart, [], currentEnd - currentStart) ];
+
+ var ops = this.spliceOperationsFromEditDistances(
+ this.calcEditDistances(current, currentStart, currentEnd,
+ old, oldStart, oldEnd));
+
+ var splice = undefined;
+ var splices = [];
+ var index = currentStart;
+ var oldIndex = oldStart;
+ for (var i = 0; i < ops.length; i++) {
+ switch(ops[i]) {
+ case EDIT_LEAVE:
+ if (splice) {
+ splices.push(splice);
+ splice = undefined;
+ }
+
+ index++;
+ oldIndex++;
+ break;
+ case EDIT_UPDATE:
+ if (!splice)
+ splice = newSplice(index, [], 0);
+
+ splice.addedCount++;
+ index++;
+
+ splice.removed.push(old[oldIndex]);
+ oldIndex++;
+ break;
+ case EDIT_ADD:
+ if (!splice)
+ splice = newSplice(index, [], 0);
+
+ splice.addedCount++;
+ index++;
+ break;
+ case EDIT_DELETE:
+ if (!splice)
+ splice = newSplice(index, [], 0);
+
+ splice.removed.push(old[oldIndex]);
+ oldIndex++;
+ break;
+ }
+ }
+
+ if (splice) {
+ splices.push(splice);
+ }
+ return splices;
+ },
+
+ sharedPrefix: function(current, old, searchLength) {
+ for (var i = 0; i < searchLength; i++)
+ if (!this.equals(current[i], old[i]))
+ return i;
+ return searchLength;
+ },
+
+ sharedSuffix: function(current, old, searchLength) {
+ var index1 = current.length;
+ var index2 = old.length;
+ var count = 0;
+ while (count < searchLength && this.equals(current[--index1], old[--index2]))
+ count++;
+
+ return count;
+ },
+
+ calculateSplices: function(current, previous) {
+ return this.calcSplices(current, 0, current.length, previous, 0,
+ previous.length);
+ },
+
+ equals: function(currentValue, previousValue) {
+ return currentValue === previousValue;
+ }
+ };
+
+ var arraySplice = new ArraySplice();
+
+ function calcSplices(current, currentStart, currentEnd,
+ old, oldStart, oldEnd) {
+ return arraySplice.calcSplices(current, currentStart, currentEnd,
+ old, oldStart, oldEnd);
+ }
+
+ function intersect(start1, end1, start2, end2) {
+ // Disjoint
+ if (end1 < start2 || end2 < start1)
+ return -1;
+
+ // Adjacent
+ if (end1 == start2 || end2 == start1)
+ return 0;
+
+ // Non-zero intersect, span1 first
+ if (start1 < start2) {
+ if (end1 < end2)
+ return end1 - start2; // Overlap
+ else
+ return end2 - start2; // Contained
+ } else {
+ // Non-zero intersect, span2 first
+ if (end2 < end1)
+ return end2 - start1; // Overlap
+ else
+ return end1 - start1; // Contained
+ }
+ }
+
+ function mergeSplice(splices, index, removed, addedCount) {
+
+ var splice = newSplice(index, removed, addedCount);
+
+ var inserted = false;
+ var insertionOffset = 0;
+
+ for (var i = 0; i < splices.length; i++) {
+ var current = splices[i];
+ current.index += insertionOffset;
+
+ if (inserted)
+ continue;
+
+ var intersectCount = intersect(splice.index,
+ splice.index + splice.removed.length,
+ current.index,
+ current.index + current.addedCount);
+
+ if (intersectCount >= 0) {
+ // Merge the two splices
+
+ splices.splice(i, 1);
+ i--;
+
+ insertionOffset -= current.addedCount - current.removed.length;
+
+ splice.addedCount += current.addedCount - intersectCount;
+ var deleteCount = splice.removed.length +
+ current.removed.length - intersectCount;
+
+ if (!splice.addedCount && !deleteCount) {
+ // merged splice is a noop. discard.
+ inserted = true;
+ } else {
+ var removed = current.removed;
+
+ if (splice.index < current.index) {
+ // some prefix of splice.removed is prepended to current.removed.
+ var prepend = splice.removed.slice(0, current.index - splice.index);
+ Array.prototype.push.apply(prepend, removed);
+ removed = prepend;
+ }
+
+ if (splice.index + splice.removed.length > current.index + current.addedCount) {
+ // some suffix of splice.removed is appended to current.removed.
+ var append = splice.removed.slice(current.index + current.addedCount - splice.index);
+ Array.prototype.push.apply(removed, append);
+ }
+
+ splice.removed = removed;
+ if (current.index < splice.index) {
+ splice.index = current.index;
+ }
+ }
+ } else if (splice.index < current.index) {
+ // Insert splice here.
+
+ inserted = true;
+
+ splices.splice(i, 0, splice);
+ i++;
+
+ var offset = splice.addedCount - splice.removed.length
+ current.index += offset;
+ insertionOffset += offset;
+ }
+ }
+
+ if (!inserted)
+ splices.push(splice);
+ }
+
+ function createInitialSplices(array, changeRecords) {
+ var splices = [];
+
+ for (var i = 0; i < changeRecords.length; i++) {
+ var record = changeRecords[i];
+ switch(record.type) {
+ case 'splice':
+ mergeSplice(splices, record.index, record.removed.slice(), record.addedCount);
+ break;
+ case 'add':
+ case 'update':
+ case 'delete':
+ if (!isIndex(record.name))
+ continue;
+ var index = toNumber(record.name);
+ if (index < 0)
+ continue;
+ mergeSplice(splices, index, [record.oldValue], 1);
+ break;
+ default:
+ console.error('Unexpected record type: ' + JSON.stringify(record));
+ break;
+ }
+ }
+
+ return splices;
+ }
+
+ function projectArraySplices(array, changeRecords) {
+ var splices = [];
+
+ createInitialSplices(array, changeRecords).forEach(function(splice) {
+ if (splice.addedCount == 1 && splice.removed.length == 1) {
+ if (splice.removed[0] !== array[splice.index])
+ splices.push(splice);
+
+ return
+ };
+
+ splices = splices.concat(calcSplices(array, splice.index, splice.index + splice.addedCount,
+ splice.removed, 0, splice.removed.length));
+ });
+
+ return splices;
+ }
+
+ global.Observer = Observer;
+ global.Observer.runEOM_ = runEOM;
+ global.Observer.observerSentinel_ = observerSentinel; // for testing.
+ global.Observer.hasObjectObserve = hasObserve;
+ global.ArrayObserver = ArrayObserver;
+ global.ArrayObserver.calculateSplices = function(current, previous) {
+ return arraySplice.calculateSplices(current, previous);
+ };
+
+ global.ArraySplice = ArraySplice;
+ global.ObjectObserver = ObjectObserver;
+ global.PathObserver = PathObserver;
+ global.CompoundObserver = CompoundObserver;
+ global.Path = Path;
+ global.ObserverTransform = ObserverTransform;
+})(typeof global !== 'undefined' && global && typeof module !== 'undefined' && module ? global : this || window);
+
+// 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';
+
+ var filter = Array.prototype.filter.call.bind(Array.prototype.filter);
+
+ function getTreeScope(node) {
+ while (node.parentNode) {
+ node = node.parentNode;
+ }
+
+ return typeof node.getElementById === 'function' ? node : null;
+ }
+
+ Node.prototype.bind = function(name, observable) {
+ console.error('Unhandled binding to Node: ', this, name, observable);
+ };
+
+ Node.prototype.bindFinished = function() {};
+
+ function updateBindings(node, name, binding) {
+ var bindings = node.bindings_;
+ if (!bindings)
+ bindings = node.bindings_ = {};
+
+ if (bindings[name])
+ binding[name].close();
+
+ return bindings[name] = binding;
+ }
+
+ function returnBinding(node, name, binding) {
+ return binding;
+ }
+
+ function sanitizeValue(value) {
+ return value == null ? '' : value;
+ }
+
+ function updateText(node, value) {
+ node.data = sanitizeValue(value);
+ }
+
+ function textBinding(node) {
+ return function(value) {
+ return updateText(node, value);
+ };
+ }
+
+ 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);
+
+ if (oneTime)
+ return updateText(this, value);
+
+ var observable = value;
+ updateText(this, observable.open(textBinding(this)));
+ return maybeUpdateBindings(this, name, observable);
+ }
+
+ function updateAttribute(el, name, conditional, value) {
+ if (conditional) {
+ if (value)
+ el.setAttribute(name, '');
+ else
+ el.removeAttribute(name);
+ return;
+ }
+
+ el.setAttribute(name, sanitizeValue(value));
+ }
+
+ function attributeBinding(el, name, conditional) {
+ return function(value) {
+ updateAttribute(el, name, conditional, value);
+ };
+ }
+
+ Element.prototype.bind = function(name, value, oneTime) {
+ var conditional = name[name.length - 1] == '?';
+ if (conditional) {
+ this.removeAttribute(name);
+ name = name.slice(0, -1);
+ }
+
+ if (oneTime)
+ return updateAttribute(this, name, conditional, value);
+
+
+ var observable = value;
+ updateAttribute(this, name, conditional,
+ observable.open(attributeBinding(this, name, conditional)));
+
+ return maybeUpdateBindings(this, name, observable);
+ };
+
+ var checkboxEventType;
+ (function() {
+ // Attempt to feature-detect which event (change or click) is fired first
+ // for checkboxes.
+ var div = document.createElement('div');
+ var checkbox = div.appendChild(document.createElement('input'));
+ checkbox.setAttribute('type', 'checkbox');
+ var first;
+ var count = 0;
+ checkbox.addEventListener('click', function(e) {
+ count++;
+ first = first || 'click';
+ });
+ checkbox.addEventListener('change', function() {
+ count++;
+ first = first || 'change';
+ });
+
+ var event = document.createEvent('MouseEvent');
+ event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false,
+ false, false, false, 0, null);
+ checkbox.dispatchEvent(event);
+ // WebKit/Blink don't fire the change event if the element is outside the
+ // document, so assume 'change' for that case.
+ checkboxEventType = count == 1 ? 'change' : first;
+ })();
+
+ function getEventForInputType(element) {
+ switch (element.type) {
+ case 'checkbox':
+ return checkboxEventType;
+ case 'radio':
+ case 'select-multiple':
+ case 'select-one':
+ return 'change';
+ case 'range':
+ if (/Trident|MSIE/.test(navigator.userAgent))
+ return 'change';
+ default:
+ return 'input';
+ }
+ }
+
+ function updateInput(input, property, value, santizeFn) {
+ input[property] = (santizeFn || sanitizeValue)(value);
+ }
+
+ function inputBinding(input, property, santizeFn) {
+ return function(value) {
+ return updateInput(input, property, value, santizeFn);
+ }
+ }
+
+ function noop() {}
+
+ function bindInputEvent(input, property, observable, postEventFn) {
+ var eventType = getEventForInputType(input);
+
+ function eventHandler() {
+ observable.setValue(input[property]);
+ observable.discardChanges();
+ (postEventFn || noop)(input);
+ Platform.performMicrotaskCheckpoint();
+ }
+ input.addEventListener(eventType, eventHandler);
+
+ return {
+ close: function() {
+ input.removeEventListener(eventType, eventHandler);
+ observable.close();
+ },
+
+ observable_: observable
+ }
+ }
+
+ function booleanSanitize(value) {
+ return Boolean(value);
+ }
+
+ // |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
+ // Returns an array containing all radio buttons other than |element| that
+ // have the same |name|, either in the form that |element| belongs to or,
+ // if no form, in the document tree to which |element| belongs.
+ //
+ // This implementation is based upon the HTML spec definition of a
+ // "radio button group":
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state.html#radio-button-group
+ //
+ function getAssociatedRadioButtons(element) {
+ if (element.form) {
+ return filter(element.form.elements, function(el) {
+ return el != element &&
+ el.tagName == 'INPUT' &&
+ el.type == 'radio' &&
+ el.name == element.name;
+ });
+ } else {
+ var treeScope = getTreeScope(element);
+ if (!treeScope)
+ return [];
+ var radios = treeScope.querySelectorAll(
+ 'input[type="radio"][name="' + element.name + '"]');
+ return filter(radios, function(el) {
+ return el != element && !el.form;
+ });
+ }
+ }
+
+ function checkedPostEvent(input) {
+ // Only the radio button that is getting checked gets an event. We
+ // therefore find all the associated radio buttons and update their
+ // check binding manually.
+ if (input.tagName === 'INPUT' &&
+ input.type === 'radio') {
+ getAssociatedRadioButtons(input).forEach(function(radio) {
+ var checkedBinding = radio.bindings_.checked;
+ if (checkedBinding) {
+ // Set the value directly to avoid an infinite call stack.
+ checkedBinding.observable_.setValue(false);
+ }
+ });
+ }
+ }
+
+ HTMLInputElement.prototype.bind = function(name, value, oneTime) {
+ 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;
+
+ if (oneTime)
+ return updateInput(this, name, value, sanitizeFn);
+
+
+ var observable = value;
+ var binding = bindInputEvent(this, name, observable, postEventFn);
+ updateInput(this, name,
+ observable.open(inputBinding(this, name, sanitizeFn)),
+ sanitizeFn);
+
+ // Checkboxes may need to update bindings of other checkboxes.
+ return updateBindings(this, name, binding);
+ }
+
+ HTMLTextAreaElement.prototype.bind = function(name, value, oneTime) {
+ if (name !== 'value')
+ return HTMLElement.prototype.bind.call(this, name, value, oneTime);
+
+ this.removeAttribute('value');
+
+ if (oneTime)
+ return updateInput(this, 'value', value);
+
+ var observable = value;
+ var binding = bindInputEvent(this, 'value', observable);
+ updateInput(this, 'value',
+ observable.open(inputBinding(this, 'value', sanitizeValue)));
+ return maybeUpdateBindings(this, name, binding);
+ }
+
+ function updateOption(option, value) {
+ var parentNode = option.parentNode;;
+ var select;
+ var selectBinding;
+ var oldValue;
+ if (parentNode instanceof HTMLSelectElement &&
+ parentNode.bindings_ &&
+ parentNode.bindings_.value) {
+ select = parentNode;
+ selectBinding = select.bindings_.value;
+ oldValue = select.value;
+ }
+
+ option.value = sanitizeValue(value);
+
+ if (select && select.value != oldValue) {
+ selectBinding.observable_.setValue(select.value);
+ selectBinding.observable_.discardChanges();
+ Platform.performMicrotaskCheckpoint();
+ }
+ }
+
+ function optionBinding(option) {
+ return function(value) {
+ updateOption(option, value);
+ }
+ }
+
+ HTMLOptionElement.prototype.bind = function(name, value, oneTime) {
+ if (name !== 'value')
+ return HTMLElement.prototype.bind.call(this, name, value, oneTime);
+
+ this.removeAttribute('value');
+
+ if (oneTime)
+ return updateOption(this, 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) {
+ if (name === 'selectedindex')
+ name = 'selectedIndex';
+
+ if (name !== 'selectedIndex' && name !== 'value')
+ return HTMLElement.prototype.bind.call(this, name, value, oneTime);
+
+ this.removeAttribute(name);
+
+ if (oneTime)
+ return updateInput(this, name, value);
+
+ var observable = value;
+ var binding = bindInputEvent(this, name, observable);
+ updateInput(this, name,
+ observable.open(inputBinding(this, name)));
+
+ // Option update events may need to access select bindings.
+ return updateBindings(this, name, binding);
+ }
+})(this);
+
+// 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';
+
+ function assert(v) {
+ if (!v)
+ throw new Error('Assertion failed');
+ }
+
+ var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
+
+ function getFragmentRoot(node) {
+ var p;
+ while (p = node.parentNode) {
+ node = p;
+ }
+
+ return node;
+ }
+
+ function searchRefId(node, id) {
+ if (!id)
+ return;
+
+ var ref;
+ var selector = '#' + id;
+ while (!ref) {
+ node = getFragmentRoot(node);
+
+ if (node.protoContent_)
+ ref = node.protoContent_.querySelector(selector);
+ else if (node.getElementById)
+ ref = node.getElementById(id);
+
+ if (ref || !node.templateCreator_)
+ break
+
+ node = node.templateCreator_;
+ }
+
+ return ref;
+ }
+
+ function getInstanceRoot(node) {
+ while (node.parentNode) {
+ node = node.parentNode;
+ }
+ return node.templateCreator_ ? node : null;
+ }
+
+ var Map;
+ if (global.Map && typeof global.Map.prototype.forEach === 'function') {
+ Map = global.Map;
+ } else {
+ Map = function() {
+ this.keys = [];
+ this.values = [];
+ };
+
+ Map.prototype = {
+ set: function(key, value) {
+ var index = this.keys.indexOf(key);
+ if (index < 0) {
+ this.keys.push(key);
+ this.values.push(value);
+ } else {
+ this.values[index] = value;
+ }
+ },
+
+ get: function(key) {
+ var index = this.keys.indexOf(key);
+ if (index < 0)
+ return;
+
+ return this.values[index];
+ },
+
+ delete: function(key, value) {
+ var index = this.keys.indexOf(key);
+ if (index < 0)
+ return false;
+
+ this.keys.splice(index, 1);
+ this.values.splice(index, 1);
+ return true;
+ },
+
+ forEach: function(f, opt_this) {
+ for (var i = 0; i < this.keys.length; i++)
+ f.call(opt_this || this, this.values[i], this.keys[i], this);
+ }
+ };
+ }
+
+ // JScript does not have __proto__. We wrap all object literals with
+ // createObject which uses Object.create, Object.defineProperty and
+ // Object.getOwnPropertyDescriptor to create a new object that does the exact
+ // same thing. The main downside to this solution is that we have to extract
+ // all those property descriptors for IE.
+ var createObject = ('__proto__' in {}) ?
+ function(obj) { return obj; } :
+ function(obj) {
+ var proto = obj.__proto__;
+ if (!proto)
+ return obj;
+ var newObject = Object.create(proto);
+ Object.getOwnPropertyNames(obj).forEach(function(name) {
+ Object.defineProperty(newObject, name,
+ Object.getOwnPropertyDescriptor(obj, name));
+ });
+ return newObject;
+ };
+
+ // IE does not support have Document.prototype.contains.
+ if (typeof document.contains != 'function') {
+ Document.prototype.contains = function(node) {
+ if (node === this || node.parentNode === this)
+ return true;
+ return this.documentElement.contains(node);
+ }
+ }
+
+ var BIND = 'bind';
+ var REPEAT = 'repeat';
+ var IF = 'if';
+
+ var templateAttributeDirectives = {
+ 'template': true,
+ 'repeat': true,
+ 'bind': true,
+ 'ref': true
+ };
+
+ var semanticTemplateElements = {
+ 'THEAD': true,
+ 'TBODY': true,
+ 'TFOOT': true,
+ 'TH': true,
+ 'TR': true,
+ 'TD': true,
+ 'COLGROUP': true,
+ 'COL': true,
+ 'CAPTION': true,
+ 'OPTION': true,
+ 'OPTGROUP': true
+ };
+
+ var hasTemplateElement = typeof HTMLTemplateElement !== 'undefined';
+ if (hasTemplateElement) {
+ // TODO(rafaelw): Remove when fix for
+ // https://codereview.chromium.org/164803002/
+ // makes it to Chrome release.
+ (function() {
+ var t = document.createElement('template');
+ var d = t.content.ownerDocument;
+ var html = d.appendChild(d.createElement('html'));
+ var head = html.appendChild(d.createElement('head'));
+ var base = d.createElement('base');
+ base.href = document.baseURI;
+ head.appendChild(base);
+ })();
+ }
+
+ var allTemplatesSelectors = 'template, ' +
+ Object.keys(semanticTemplateElements).map(function(tagName) {
+ return tagName.toLowerCase() + '[template]';
+ }).join(', ');
+
+ function isSVGTemplate(el) {
+ return el.tagName == 'template' &&
+ el.namespaceURI == 'http://www.w3.org/2000/svg';
+ }
+
+ function isHTMLTemplate(el) {
+ return el.tagName == 'TEMPLATE' &&
+ el.namespaceURI == 'http://www.w3.org/1999/xhtml';
+ }
+
+ function isAttributeTemplate(el) {
+ return Boolean(semanticTemplateElements[el.tagName] &&
+ el.hasAttribute('template'));
+ }
+
+ function isTemplate(el) {
+ if (el.isTemplate_ === undefined)
+ el.isTemplate_ = el.tagName == 'TEMPLATE' || isAttributeTemplate(el);
+
+ return el.isTemplate_;
+ }
+
+ // FIXME: Observe templates being added/removed from documents
+ // FIXME: Expose imperative API to decorate and observe templates in
+ // "disconnected tress" (e.g. ShadowRoot)
+ document.addEventListener('DOMContentLoaded', function(e) {
+ bootstrapTemplatesRecursivelyFrom(document);
+ // FIXME: Is this needed? Seems like it shouldn't be.
+ Platform.performMicrotaskCheckpoint();
+ }, false);
+
+ function forAllTemplatesFrom(node, fn) {
+ var subTemplates = node.querySelectorAll(allTemplatesSelectors);
+
+ if (isTemplate(node))
+ fn(node)
+ forEach(subTemplates, fn);
+ }
+
+ function bootstrapTemplatesRecursivelyFrom(node) {
+ function bootstrap(template) {
+ if (!HTMLTemplateElement.decorate(template))
+ bootstrapTemplatesRecursivelyFrom(template.content);
+ }
+
+ forAllTemplatesFrom(node, bootstrap);
+ }
+
+ if (!hasTemplateElement) {
+ /**
+ * This represents a <template> element.
+ * @constructor
+ * @extends {HTMLElement}
+ */
+ global.HTMLTemplateElement = function() {
+ throw TypeError('Illegal constructor');
+ };
+ }
+
+ var hasProto = '__proto__' in {};
+
+ function mixin(to, from) {
+ Object.getOwnPropertyNames(from).forEach(function(name) {
+ Object.defineProperty(to, name,
+ Object.getOwnPropertyDescriptor(from, name));
+ });
+ }
+
+ // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#dfn-template-contents-owner
+ function getOrCreateTemplateContentsOwner(template) {
+ var doc = template.ownerDocument
+ if (!doc.defaultView)
+ return doc;
+ var d = doc.templateContentsOwner_;
+ if (!d) {
+ // TODO(arv): This should either be a Document or HTMLDocument depending
+ // on doc.
+ d = doc.implementation.createHTMLDocument('');
+ while (d.lastChild) {
+ d.removeChild(d.lastChild);
+ }
+ doc.templateContentsOwner_ = d;
+ }
+ return d;
+ }
+
+ function getTemplateStagingDocument(template) {
+ if (!template.stagingDocument_) {
+ var owner = template.ownerDocument;
+ if (!owner.stagingDocument_) {
+ owner.stagingDocument_ = owner.implementation.createHTMLDocument('');
+ owner.stagingDocument_.isStagingDocument = true;
+ // TODO(rafaelw): Remove when fix for
+ // https://codereview.chromium.org/164803002/
+ // makes it to Chrome release.
+ var base = owner.stagingDocument_.createElement('base');
+ base.href = document.baseURI;
+ owner.stagingDocument_.head.appendChild(base);
+
+ owner.stagingDocument_.stagingDocument_ = owner.stagingDocument_;
+ }
+
+ template.stagingDocument_ = owner.stagingDocument_;
+ }
+
+ return template.stagingDocument_;
+ }
+
+ // For non-template browsers, the parser will disallow <template> in certain
+ // locations, so we allow "attribute templates" which combine the template
+ // element with the top-level container node of the content, e.g.
+ //
+ // <tr template repeat="{{ foo }}"" class="bar"><td>Bar</td></tr>
+ //
+ // becomes
+ //
+ // <template repeat="{{ foo }}">
+ // + #document-fragment
+ // + <tr class="bar">
+ // + <td>Bar</td>
+ //
+ function extractTemplateFromAttributeTemplate(el) {
+ var template = el.ownerDocument.createElement('template');
+ el.parentNode.insertBefore(template, el);
+
+ var attribs = el.attributes;
+ var count = attribs.length;
+ while (count-- > 0) {
+ var attrib = attribs[count];
+ if (templateAttributeDirectives[attrib.name]) {
+ if (attrib.name !== 'template')
+ template.setAttribute(attrib.name, attrib.value);
+ el.removeAttribute(attrib.name);
+ }
+ }
+
+ return template;
+ }
+
+ function extractTemplateFromSVGTemplate(el) {
+ var template = el.ownerDocument.createElement('template');
+ el.parentNode.insertBefore(template, el);
+
+ var attribs = el.attributes;
+ var count = attribs.length;
+ while (count-- > 0) {
+ var attrib = attribs[count];
+ template.setAttribute(attrib.name, attrib.value);
+ el.removeAttribute(attrib.name);
+ }
+
+ el.parentNode.removeChild(el);
+ return template;
+ }
+
+ function liftNonNativeTemplateChildrenIntoContent(template, el, useRoot) {
+ var content = template.content;
+ if (useRoot) {
+ content.appendChild(el);
+ return;
+ }
+
+ var child;
+ while (child = el.firstChild) {
+ content.appendChild(child);
+ }
+ }
+
+ var templateObserver;
+ if (typeof MutationObserver == 'function') {
+ templateObserver = new MutationObserver(function(records) {
+ for (var i = 0; i < records.length; i++) {
+ records[i].target.refChanged_();
+ }
+ });
+ }
+
+ /**
+ * Ensures proper API and content model for template elements.
+ * @param {HTMLTemplateElement} opt_instanceRef The template element which
+ * |el| template element will return as the value of its ref(), and whose
+ * content will be used as source when createInstance() is invoked.
+ */
+ HTMLTemplateElement.decorate = function(el, opt_instanceRef) {
+ if (el.templateIsDecorated_)
+ return false;
+
+ var templateElement = el;
+ templateElement.templateIsDecorated_ = true;
+
+ var isNativeHTMLTemplate = isHTMLTemplate(templateElement) &&
+ hasTemplateElement;
+ var bootstrapContents = isNativeHTMLTemplate;
+ var liftContents = !isNativeHTMLTemplate;
+ var liftRoot = false;
+
+ if (!isNativeHTMLTemplate) {
+ if (isAttributeTemplate(templateElement)) {
+ assert(!opt_instanceRef);
+ templateElement = extractTemplateFromAttributeTemplate(el);
+ templateElement.templateIsDecorated_ = true;
+ isNativeHTMLTemplate = hasTemplateElement;
+ liftRoot = true;
+ } else if (isSVGTemplate(templateElement)) {
+ templateElement = extractTemplateFromSVGTemplate(el);
+ templateElement.templateIsDecorated_ = true;
+ isNativeHTMLTemplate = hasTemplateElement;
+ }
+ }
+
+ if (!isNativeHTMLTemplate) {
+ fixTemplateElementPrototype(templateElement);
+ var doc = getOrCreateTemplateContentsOwner(templateElement);
+ templateElement.content_ = doc.createDocumentFragment();
+ }
+
+ if (opt_instanceRef) {
+ // template is contained within an instance, its direct content must be
+ // empty
+ templateElement.instanceRef_ = opt_instanceRef;
+ } else if (liftContents) {
+ liftNonNativeTemplateChildrenIntoContent(templateElement,
+ el,
+ liftRoot);
+ } else if (bootstrapContents) {
+ bootstrapTemplatesRecursivelyFrom(templateElement.content);
+ }
+
+ return true;
+ };
+
+ // TODO(rafaelw): This used to decorate recursively all templates from a given
+ // node. This happens by default on 'DOMContentLoaded', but may be needed
+ // in subtrees not descendent from document (e.g. ShadowRoot).
+ // Review whether this is the right public API.
+ HTMLTemplateElement.bootstrap = bootstrapTemplatesRecursivelyFrom;
+
+ var htmlElement = global.HTMLUnknownElement || HTMLElement;
+
+ var contentDescriptor = {
+ get: function() {
+ return this.content_;
+ },
+ enumerable: true,
+ configurable: true
+ };
+
+ if (!hasTemplateElement) {
+ // Gecko is more picky with the prototype than WebKit. Make sure to use the
+ // same prototype as created in the constructor.
+ HTMLTemplateElement.prototype = Object.create(htmlElement.prototype);
+
+ Object.defineProperty(HTMLTemplateElement.prototype, 'content',
+ contentDescriptor);
+ }
+
+ function fixTemplateElementPrototype(el) {
+ if (hasProto)
+ el.__proto__ = HTMLTemplateElement.prototype;
+ else
+ mixin(el, HTMLTemplateElement.prototype);
+ }
+
+ function ensureSetModelScheduled(template) {
+ if (!template.setModelFn_) {
+ template.setModelFn_ = function() {
+ template.setModelFnScheduled_ = false;
+ var map = getBindings(template,
+ template.delegate_ && template.delegate_.prepareBinding);
+ processBindings(template, map, template.model_);
+ };
+ }
+
+ if (!template.setModelFnScheduled_) {
+ template.setModelFnScheduled_ = true;
+ Observer.runEOM_(template.setModelFn_);
+ }
+ }
+
+ mixin(HTMLTemplateElement.prototype, {
+ bind: function(name, value, oneTime) {
+ if (name != 'ref')
+ return Element.prototype.bind.call(this, name, value, oneTime);
+
+ var self = this;
+ var ref = oneTime ? value : value.open(function(ref) {
+ self.setAttribute('ref', ref);
+ self.refChanged_();
+ });
+
+ this.setAttribute('ref', ref);
+ this.refChanged_();
+ if (oneTime)
+ return;
+
+ if (!this.bindings_) {
+ this.bindings_ = { ref: value };
+ } else {
+ this.bindings_.ref = value;
+ }
+
+ return value;
+ },
+
+ processBindingDirectives_: function(directives) {
+ if (this.iterator_)
+ this.iterator_.closeDeps();
+
+ if (!directives.if && !directives.bind && !directives.repeat) {
+ if (this.iterator_) {
+ this.iterator_.close();
+ this.iterator_ = undefined;
+ }
+
+ return;
+ }
+
+ if (!this.iterator_) {
+ this.iterator_ = new TemplateIterator(this);
+ }
+
+ this.iterator_.updateDependencies(directives, this.model_);
+
+ if (templateObserver) {
+ templateObserver.observe(this, { attributes: true,
+ attributeFilter: ['ref'] });
+ }
+
+ return this.iterator_;
+ },
+
+ createInstance: function(model, bindingDelegate, delegate_) {
+ if (bindingDelegate)
+ delegate_ = this.newDelegate_(bindingDelegate);
+ else if (!delegate_)
+ delegate_ = this.delegate_;
+
+ if (!this.refContent_)
+ this.refContent_ = this.ref_.content;
+ var content = this.refContent_;
+ if (content.firstChild === null)
+ return emptyInstance;
+
+ var map = getInstanceBindingMap(content, delegate_);
+ var stagingDocument = getTemplateStagingDocument(this);
+ var instance = stagingDocument.createDocumentFragment();
+ instance.templateCreator_ = this;
+ instance.protoContent_ = content;
+ 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_,
+ instance.bindings_);
+ clone.templateInstance_ = instanceRecord;
+ if (collectTerminator)
+ instance.terminator_ = clone;
+ }
+
+ instanceRecord.firstNode = instance.firstChild;
+ instanceRecord.lastNode = instance.lastChild;
+ instance.templateCreator_ = undefined;
+ instance.protoContent_ = undefined;
+ return instance;
+ },
+
+ get model() {
+ return this.model_;
+ },
+
+ set model(model) {
+ this.model_ = model;
+ ensureSetModelScheduled(this);
+ },
+
+ get bindingDelegate() {
+ return this.delegate_ && this.delegate_.raw;
+ },
+
+ refChanged_: function() {
+ if (!this.iterator_ || this.refContent_ === this.ref_.content)
+ return;
+
+ this.refContent_ = undefined;
+ this.iterator_.valueChanged();
+ this.iterator_.updateIteratedValue(this.iterator_.getUpdatedValue());
+ },
+
+ clear: function() {
+ this.model_ = undefined;
+ this.delegate_ = undefined;
+ if (this.bindings_ && this.bindings_.ref)
+ this.bindings_.ref.close()
+ this.refContent_ = undefined;
+ if (!this.iterator_)
+ return;
+ this.iterator_.valueChanged();
+ this.iterator_.close()
+ this.iterator_ = undefined;
+ },
+
+ setDelegate_: function(delegate) {
+ this.delegate_ = delegate;
+ this.bindingMap_ = undefined;
+ if (this.iterator_) {
+ this.iterator_.instancePositionChangedFn_ = undefined;
+ this.iterator_.instanceModelFn_ = undefined;
+ }
+ },
+
+ newDelegate_: function(bindingDelegate) {
+ if (!bindingDelegate)
+ return;
+
+ function delegateFn(name) {
+ var fn = bindingDelegate && bindingDelegate[name];
+ if (typeof fn != 'function')
+ return;
+
+ return function() {
+ return fn.apply(bindingDelegate, arguments);
+ };
+ }
+
+ return {
+ bindingMaps: {},
+ raw: bindingDelegate,
+ prepareBinding: delegateFn('prepareBinding'),
+ prepareInstanceModel: delegateFn('prepareInstanceModel'),
+ prepareInstancePositionChanged:
+ delegateFn('prepareInstancePositionChanged')
+ };
+ },
+
+ set bindingDelegate(bindingDelegate) {
+ if (this.delegate_) {
+ throw Error('Template must be cleared before a new bindingDelegate ' +
+ 'can be assigned');
+ }
+
+ this.setDelegate_(this.newDelegate_(bindingDelegate));
+ },
+
+ get ref_() {
+ var ref = searchRefId(this, this.getAttribute('ref'));
+ if (!ref)
+ ref = this.instanceRef_;
+
+ if (!ref)
+ return this;
+
+ var nextRef = ref.ref_;
+ return nextRef ? nextRef : ref;
+ }
+ });
+
+ // Returns
+ // a) undefined if there are no mustaches.
+ // b) [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+] if there is at least one mustache.
+ function parseMustaches(s, name, node, prepareBindingFn) {
+ if (!s || !s.length)
+ return;
+
+ var tokens;
+ var length = s.length;
+ var startIndex = 0, lastIndex = 0, endIndex = 0;
+ var onlyOneTime = true;
+ while (lastIndex < length) {
+ var startIndex = s.indexOf('{{', lastIndex);
+ var oneTimeStart = s.indexOf('[[', lastIndex);
+ var oneTime = false;
+ var terminator = '}}';
+
+ if (oneTimeStart >= 0 &&
+ (startIndex < 0 || oneTimeStart < startIndex)) {
+ startIndex = oneTimeStart;
+ oneTime = true;
+ terminator = ']]';
+ }
+
+ endIndex = startIndex < 0 ? -1 : s.indexOf(terminator, startIndex + 2);
+
+ if (endIndex < 0) {
+ if (!tokens)
+ return;
+
+ tokens.push(s.slice(lastIndex)); // TEXT
+ break;
+ }
+
+ tokens = tokens || [];
+ tokens.push(s.slice(lastIndex, startIndex)); // TEXT
+ var pathString = s.slice(startIndex + 2, endIndex).trim();
+ tokens.push(oneTime); // ONE_TIME?
+ onlyOneTime = onlyOneTime && oneTime;
+ 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;
+ }
+
+ if (lastIndex === length)
+ tokens.push(''); // TEXT
+
+ tokens.hasOnePath = tokens.length === 5;
+ tokens.isSimplePath = tokens.hasOnePath &&
+ tokens[0] == '' &&
+ tokens[4] == '';
+ tokens.onlyOneTime = onlyOneTime;
+
+ tokens.combinator = function(values) {
+ var newValue = tokens[0];
+
+ for (var i = 1; i < tokens.length; i += 4) {
+ var value = tokens.hasOnePath ? values : values[(i - 1) / 4];
+ if (value !== undefined)
+ newValue += value;
+ newValue += tokens[i + 3];
+ }
+
+ return newValue;
+ }
+
+ return tokens;
+ };
+
+ function processOneTimeBinding(name, tokens, node, model) {
+ if (tokens.hasOnePath) {
+ var delegateFn = tokens[3];
+ var value = delegateFn ? delegateFn(model, node, true) :
+ tokens[2].getValueFrom(model);
+ return tokens.isSimplePath ? value : tokens.combinator(value);
+ }
+
+ var values = [];
+ for (var i = 1; i < tokens.length; i += 4) {
+ var delegateFn = tokens[i + 2];
+ values[(i - 1) / 4] = delegateFn ? delegateFn(model, node) :
+ tokens[i + 1].getValueFrom(model);
+ }
+
+ return tokens.combinator(values);
+ }
+
+ function processSinglePathBinding(name, tokens, node, model) {
+ var delegateFn = tokens[3];
+ var observer = delegateFn ? delegateFn(model, node, false) :
+ new PathObserver(model, tokens[2]);
+
+ return tokens.isSimplePath ? observer :
+ new ObserverTransform(observer, tokens.combinator);
+ }
+
+ function processBinding(name, tokens, node, model) {
+ if (tokens.onlyOneTime)
+ return processOneTimeBinding(name, tokens, node, model);
+
+ if (tokens.hasOnePath)
+ return processSinglePathBinding(name, tokens, node, model);
+
+ var observer = new CompoundObserver();
+
+ for (var i = 1; i < tokens.length; i += 4) {
+ var oneTime = tokens[i];
+ var delegateFn = tokens[i + 2];
+
+ if (delegateFn) {
+ var value = delegateFn(model, node, oneTime);
+ if (oneTime)
+ observer.addPath(value)
+ else
+ observer.addObserver(value);
+ continue;
+ }
+
+ var path = tokens[i + 1];
+ if (oneTime)
+ observer.addPath(path.getValueFrom(model))
+ else
+ observer.addPath(model, path);
+ }
+
+ return new ObserverTransform(observer, tokens.combinator);
+ }
+
+ function processBindings(node, bindings, model, instanceBindings) {
+ for (var i = 0; i < bindings.length; i += 2) {
+ var name = bindings[i]
+ var tokens = bindings[i + 1];
+ var value = processBinding(name, tokens, node, model);
+ var binding = node.bind(name, value, tokens.onlyOneTime);
+ if (binding && instanceBindings)
+ instanceBindings.push(binding);
+ }
+
+ node.bindFinished();
+ if (!bindings.isTemplate)
+ return;
+
+ node.model_ = model;
+ var iter = node.processBindingDirectives_(bindings);
+ if (instanceBindings && iter)
+ instanceBindings.push(iter);
+ }
+
+ function parseWithDefault(el, name, prepareBindingFn) {
+ var v = el.getAttribute(name);
+ return parseMustaches(v == '' ? '{{}}' : v, name, el, prepareBindingFn);
+ }
+
+ function parseAttributeBindings(element, prepareBindingFn) {
+ assert(element);
+
+ var bindings = [];
+ var ifFound = false;
+ var bindFound = false;
+
+ for (var i = 0; i < element.attributes.length; i++) {
+ var attr = element.attributes[i];
+ var name = attr.name;
+ var value = attr.value;
+
+ // Allow bindings expressed in attributes to be prefixed with underbars.
+ // We do this to allow correct semantics for browsers that don't implement
+ // <template> where certain attributes might trigger side-effects -- and
+ // for IE which sanitizes certain attributes, disallowing mustache
+ // replacements in their text.
+ while (name[0] === '_') {
+ name = name.substring(1);
+ }
+
+ if (isTemplate(element) &&
+ (name === IF || name === BIND || name === REPEAT)) {
+ continue;
+ }
+
+ var tokens = parseMustaches(value, name, element,
+ prepareBindingFn);
+ if (!tokens)
+ continue;
+
+ bindings.push(name, tokens);
+ }
+
+ if (isTemplate(element)) {
+ bindings.isTemplate = true;
+ bindings.if = parseWithDefault(element, IF, prepareBindingFn);
+ bindings.bind = parseWithDefault(element, BIND, prepareBindingFn);
+ bindings.repeat = parseWithDefault(element, REPEAT, prepareBindingFn);
+
+ if (bindings.if && !bindings.bind && !bindings.repeat)
+ bindings.bind = parseMustaches('{{}}', BIND, element, prepareBindingFn);
+ }
+
+ return bindings;
+ }
+
+ function getBindings(node, prepareBindingFn) {
+ if (node.nodeType === Node.ELEMENT_NODE)
+ return parseAttributeBindings(node, prepareBindingFn);
+
+ if (node.nodeType === Node.TEXT_NODE) {
+ var tokens = parseMustaches(node.data, 'textContent', node,
+ prepareBindingFn);
+ if (tokens)
+ return ['textContent', tokens];
+ }
+
+ return [];
+ }
+
+ function cloneAndBindInstance(node, parent, stagingDocument, bindings, model,
+ delegate,
+ instanceBindings,
+ instanceRecord) {
+ var clone = parent.appendChild(stagingDocument.importNode(node, false));
+
+ var i = 0;
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ cloneAndBindInstance(child, clone, stagingDocument,
+ bindings.children[i++],
+ model,
+ delegate,
+ instanceBindings);
+ }
+
+ if (bindings.isTemplate) {
+ HTMLTemplateElement.decorate(clone, node);
+ if (delegate)
+ clone.setDelegate_(delegate);
+ }
+
+ processBindings(clone, bindings, model, instanceBindings);
+ return clone;
+ }
+
+ function createInstanceBindingMap(node, prepareBindingFn) {
+ var map = getBindings(node, prepareBindingFn);
+ map.children = {};
+ var index = 0;
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ map.children[index++] = createInstanceBindingMap(child, prepareBindingFn);
+ }
+
+ return map;
+ }
+
+ var contentUidCounter = 1;
+
+ // TODO(rafaelw): Setup a MutationObserver on content which clears the id
+ // so that bindingMaps regenerate when the template.content changes.
+ function getContentUid(content) {
+ var id = content.id_;
+ if (!id)
+ id = content.id_ = contentUidCounter++;
+ return id;
+ }
+
+ // Each delegate is associated with a set of bindingMaps, one for each
+ // content which may be used by a template. The intent is that each binding
+ // delegate gets the opportunity to prepare the instance (via the prepare*
+ // delegate calls) once across all uses.
+ // TODO(rafaelw): Separate out the parse map from the binding map. In the
+ // current implementation, if two delegates need a binding map for the same
+ // content, the second will have to reparse.
+ function getInstanceBindingMap(content, delegate_) {
+ var contentId = getContentUid(content);
+ if (delegate_) {
+ var map = delegate_.bindingMaps[contentId];
+ if (!map) {
+ map = delegate_.bindingMaps[contentId] =
+ createInstanceBindingMap(content, delegate_.prepareBinding) || [];
+ }
+ return map;
+ }
+
+ var map = content.bindingMap_;
+ if (!map) {
+ map = content.bindingMap_ =
+ createInstanceBindingMap(content, undefined) || [];
+ }
+ return map;
+ }
+
+ Object.defineProperty(Node.prototype, 'templateInstance', {
+ get: function() {
+ var instance = this.templateInstance_;
+ return instance ? instance :
+ (this.parentNode ? this.parentNode.templateInstance : undefined);
+ }
+ });
+
+ var emptyInstance = document.createDocumentFragment();
+ emptyInstance.bindings_ = [];
+ emptyInstance.terminator_ = null;
+
+ function TemplateIterator(templateElement) {
+ this.closed = false;
+ this.templateElement_ = templateElement;
+ this.instances = [];
+ this.deps = undefined;
+ this.iteratedValue = [];
+ this.presentValue = undefined;
+ this.arrayObserver = undefined;
+ }
+
+ TemplateIterator.prototype = {
+ closeDeps: function() {
+ var deps = this.deps;
+ if (deps) {
+ if (deps.ifOneTime === false)
+ deps.ifValue.close();
+ if (deps.oneTime === false)
+ deps.value.close();
+ }
+ },
+
+ updateDependencies: function(directives, model) {
+ this.closeDeps();
+
+ var deps = this.deps = {};
+ var template = this.templateElement_;
+
+ var ifValue = true;
+ if (directives.if) {
+ deps.hasIf = true;
+ deps.ifOneTime = directives.if.onlyOneTime;
+ deps.ifValue = processBinding(IF, directives.if, template, model);
+
+ ifValue = deps.ifValue;
+
+ // oneTime if & predicate is false. nothing else to do.
+ if (deps.ifOneTime && !ifValue) {
+ this.valueChanged();
+ return;
+ }
+
+ if (!deps.ifOneTime)
+ ifValue = ifValue.open(this.updateIfValue, this);
+ }
+
+ if (directives.repeat) {
+ deps.repeat = true;
+ deps.oneTime = directives.repeat.onlyOneTime;
+ deps.value = processBinding(REPEAT, directives.repeat, template, model);
+ } else {
+ deps.repeat = false;
+ deps.oneTime = directives.bind.onlyOneTime;
+ deps.value = processBinding(BIND, directives.bind, template, model);
+ }
+
+ var value = deps.value;
+ if (!deps.oneTime)
+ value = value.open(this.updateIteratedValue, this);
+
+ if (!ifValue) {
+ this.valueChanged();
+ return;
+ }
+
+ this.updateValue(value);
+ },
+
+ /**
+ * Gets the updated value of the bind/repeat. This can potentially call
+ * user code (if a bindingDelegate is set up) so we try to avoid it if we
+ * already have the value in hand (from Observer.open).
+ */
+ getUpdatedValue: function() {
+ var value = this.deps.value;
+ if (!this.deps.oneTime)
+ value = value.discardChanges();
+ return value;
+ },
+
+ updateIfValue: function(ifValue) {
+ if (!ifValue) {
+ this.valueChanged();
+ return;
+ }
+
+ this.updateValue(this.getUpdatedValue());
+ },
+
+ updateIteratedValue: function(value) {
+ if (this.deps.hasIf) {
+ var ifValue = this.deps.ifValue;
+ if (!this.deps.ifOneTime)
+ ifValue = ifValue.discardChanges();
+ if (!ifValue) {
+ this.valueChanged();
+ return;
+ }
+ }
+
+ this.updateValue(value);
+ },
+
+ updateValue: function(value) {
+ if (!this.deps.repeat)
+ value = [value];
+ var observe = this.deps.repeat &&
+ !this.deps.oneTime &&
+ Array.isArray(value);
+ this.valueChanged(value, observe);
+ },
+
+ valueChanged: function(value, observeValue) {
+ if (!Array.isArray(value))
+ value = [];
+
+ if (value === this.iteratedValue)
+ return;
+
+ this.unobserve();
+ this.presentValue = value;
+ if (observeValue) {
+ this.arrayObserver = new ArrayObserver(this.presentValue);
+ this.arrayObserver.open(this.handleSplices, this);
+ }
+
+ this.handleSplices(ArrayObserver.calculateSplices(this.presentValue,
+ this.iteratedValue));
+ },
+
+ getLastInstanceNode: function(index) {
+ if (index == -1)
+ return this.templateElement_;
+ 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 subtemplateIterator = terminator.iterator_;
+ if (!subtemplateIterator)
+ return terminator;
+
+ return subtemplateIterator.getLastTemplateNode();
+ },
+
+ getLastTemplateNode: function() {
+ return this.getLastInstanceNode(this.instances.length - 1);
+ },
+
+ insertInstanceAt: function(index, fragment) {
+ var previousInstanceLast = this.getLastInstanceNode(index - 1);
+ var parent = this.templateElement_.parentNode;
+ this.instances.splice(index, 0, fragment);
+
+ parent.insertBefore(fragment, previousInstanceLast.nextSibling);
+ },
+
+ extractInstanceAt: function(index) {
+ var previousInstanceLast = this.getLastInstanceNode(index - 1);
+ var lastNode = this.getLastInstanceNode(index);
+ var parent = this.templateElement_.parentNode;
+ var instance = this.instances.splice(index, 1)[0];
+
+ while (lastNode !== previousInstanceLast) {
+ var node = previousInstanceLast.nextSibling;
+ if (node == lastNode)
+ lastNode = previousInstanceLast;
+
+ instance.appendChild(parent.removeChild(node));
+ }
+
+ return instance;
+ },
+
+ getDelegateFn: function(fn) {
+ fn = fn && fn(this.templateElement_);
+ return typeof fn === 'function' ? fn : null;
+ },
+
+ handleSplices: function(splices) {
+ if (this.closed || !splices.length)
+ return;
+
+ var template = this.templateElement_;
+
+ if (!template.parentNode) {
+ this.close();
+ return;
+ }
+
+ ArrayObserver.applySplices(this.iteratedValue, this.presentValue,
+ splices);
+
+ var delegate = template.delegate_;
+ if (this.instanceModelFn_ === undefined) {
+ this.instanceModelFn_ =
+ this.getDelegateFn(delegate && delegate.prepareInstanceModel);
+ }
+
+ if (this.instancePositionChangedFn_ === undefined) {
+ this.instancePositionChangedFn_ =
+ this.getDelegateFn(delegate &&
+ delegate.prepareInstancePositionChanged);
+ }
+
+ // Instance Removals
+ var instanceCache = new Map;
+ var removeDelta = 0;
+ 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;
+ }
+
+ // 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 instance = instanceCache.get(model);
+ if (instance) {
+ instanceCache.delete(model);
+ } else {
+ if (this.instanceModelFn_) {
+ model = this.instanceModelFn_(model);
+ }
+
+ if (model === undefined) {
+ instance = emptyInstance;
+ } else {
+ instance = template.createInstance(model, undefined, delegate);
+ }
+ }
+
+ this.insertInstanceAt(addIndex, instance);
+ }
+ }
+
+ instanceCache.forEach(function(instance) {
+ this.closeInstanceBindings(instance);
+ }, this);
+
+ if (this.instancePositionChangedFn_)
+ this.reportInstancesMoved(splices);
+ },
+
+ reportInstanceMoved: function(index) {
+ var instance = this.instances[index];
+ if (instance === emptyInstance)
+ return;
+
+ this.instancePositionChangedFn_(instance.templateInstance_, index);
+ },
+
+ reportInstancesMoved: function(splices) {
+ var index = 0;
+ var offset = 0;
+ for (var i = 0; i < splices.length; i++) {
+ var splice = splices[i];
+ if (offset != 0) {
+ while (index < splice.index) {
+ this.reportInstanceMoved(index);
+ index++;
+ }
+ } else {
+ index = splice.index;
+ }
+
+ while (index < splice.index + splice.addedCount) {
+ this.reportInstanceMoved(index);
+ index++;
+ }
+
+ offset += splice.addedCount - splice.removed.length;
+ }
+
+ if (offset == 0)
+ return;
+
+ var length = this.instances.length;
+ while (index < length) {
+ this.reportInstanceMoved(index);
+ index++;
+ }
+ },
+
+ closeInstanceBindings: function(instance) {
+ var bindings = instance.bindings_;
+ for (var i = 0; i < bindings.length; i++) {
+ bindings[i].close();
+ }
+ },
+
+ unobserve: function() {
+ if (!this.arrayObserver)
+ return;
+
+ this.arrayObserver.close();
+ this.arrayObserver = undefined;
+ },
+
+ close: function() {
+ if (this.closed)
+ return;
+ this.unobserve();
+ for (var i = 0; i < this.instances.length; i++) {
+ this.closeInstanceBindings(this.instances[i]);
+ }
+
+ this.instances.length = 0;
+ this.closeDeps();
+ this.templateElement_.iterator_ = undefined;
+ this.closed = true;
+ }
+ };
+
+ // Polyfill-specific API.
+ HTMLTemplateElement.forAllTemplatesFrom_ = forAllTemplatesFrom;
+})(this);
+
+/*
+ * 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(scope) {
+
+var iterations = 0;
+var callbacks = [];
+var twiddle = document.createTextNode('');
+
+function endOfMicrotask(callback) {
+ twiddle.textContent = iterations++;
+ callbacks.push(callback);
+}
+
+function atEndOfMicrotask() {
+ while (callbacks.length) {
+ callbacks.shift()();
+ }
+}
+
+new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask)
+ .observe(twiddle, {characterData: true})
+ ;
+
+// exports
+
+scope.endOfMicrotask = endOfMicrotask;
+
+})(Platform);
+
+
+/*
+ * 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(scope) {
+
+// inject style sheet
+var style = document.createElement('style');
+style.textContent = 'template {display: none !important;} /* injected by platform.js */';
+var head = document.querySelector('head');
+head.insertBefore(style, head.firstChild);
+
+// flush (with logging)
+var flushing;
+function flush() {
+ if (!flushing) {
+ flushing = true;
+ scope.endOfMicrotask(function() {
+ flushing = false;
+ logFlags.data && console.group('Platform.flush()');
+ scope.performMicrotaskCheckpoint();
+ logFlags.data && console.groupEnd();
+ });
+ }
+};
+
+// polling dirty checker
+// 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;
+ Document.prototype.importNode = function(node, deep) {
+ var imported = originalImportNode.call(this, node, deep);
+ CustomElements.upgradeAll(imported);
+ return imported;
+ }
+}
+
+// exports
+scope.flush = flush;
+
+})(window.Platform);
+
+
+/*
+ * 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(scope) {
+
+var urlResolver = {
+ resolveDom: function(root, url) {
+ url = url || root.ownerDocument.baseURI;
+ this.resolveAttributes(root, url);
+ this.resolveStyles(root, url);
+ // handle template.content
+ var templates = root.querySelectorAll('template');
+ if (templates) {
+ for (var i = 0, l = templates.length, t; (i < l) && (t = templates[i]); i++) {
+ if (t.content) {
+ this.resolveDom(t.content, url);
+ }
+ }
+ }
+ },
+ resolveTemplate: function(template) {
+ this.resolveDom(template.content, template.ownerDocument.baseURI);
+ },
+ resolveStyles: function(root, url) {
+ var styles = root.querySelectorAll('style');
+ if (styles) {
+ for (var i = 0, l = styles.length, s; (i < l) && (s = styles[i]); i++) {
+ this.resolveStyle(s, url);
+ }
+ }
+ },
+ resolveStyle: function(style, url) {
+ url = url || style.ownerDocument.baseURI;
+ style.textContent = this.resolveCssText(style.textContent, url);
+ },
+ resolveCssText: function(cssText, baseUrl, keepAbsolute) {
+ cssText = replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_URL_REGEXP);
+ return replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_IMPORT_REGEXP);
+ },
+ resolveAttributes: function(root, url) {
+ if (root.hasAttributes && root.hasAttributes()) {
+ this.resolveElementAttributes(root, url);
+ }
+ // search for attributes that host urls
+ var nodes = root && root.querySelectorAll(URL_ATTRS_SELECTOR);
+ if (nodes) {
+ for (var i = 0, l = nodes.length, n; (i < l) && (n = nodes[i]); i++) {
+ this.resolveElementAttributes(n, url);
+ }
+ }
+ },
+ resolveElementAttributes: function(node, url) {
+ url = url || node.ownerDocument.baseURI;
+ URL_ATTRS.forEach(function(v) {
+ var attr = node.attributes[v];
+ var value = attr && attr.value;
+ var replacement;
+ if (value && value.search(URL_TEMPLATE_SEARCH) < 0) {
+ if (v === 'style') {
+ replacement = replaceUrlsInCssText(value, url, false, CSS_URL_REGEXP);
+ } else {
+ replacement = resolveRelativeUrl(url, value);
+ }
+ attr.value = replacement;
+ }
+ });
+ }
+};
+
+var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g;
+var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
+var URL_ATTRS = ['href', 'src', 'action', 'style', 'url'];
+var URL_ATTRS_SELECTOR = '[' + URL_ATTRS.join('],[') + ']';
+var URL_TEMPLATE_SEARCH = '{{.*}}';
+
+function replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, regexp) {
+ return cssText.replace(regexp, function(m, pre, url, post) {
+ var urlPath = url.replace(/["']/g, '');
+ urlPath = resolveRelativeUrl(baseUrl, urlPath, keepAbsolute);
+ return pre + '\'' + urlPath + '\'' + post;
+ });
+}
+
+function resolveRelativeUrl(baseUrl, url, keepAbsolute) {
+ // do not resolve '/' absolute urls
+ if (url && url[0] === '/') {
+ return url;
+ }
+ var u = new URL(url, baseUrl);
+ return keepAbsolute ? u.href : makeDocumentRelPath(u.href);
+}
+
+function makeDocumentRelPath(url) {
+ var root = new URL(document.baseURI);
+ var u = new URL(url, root);
+ if (u.host === root.host && u.port === root.port &&
+ u.protocol === root.protocol) {
+ return makeRelPath(root, u);
+ } else {
+ return url;
+ }
+}
+
+// make a relative path from source to target
+function makeRelPath(sourceUrl, targetUrl) {
+ var source = sourceUrl.pathname;
+ var target = targetUrl.pathname;
+ var s = source.split('/');
+ var t = target.split('/');
+ while (s.length && s[0] === t[0]){
+ s.shift();
+ t.shift();
+ }
+ for (var i = 0, l = s.length - 1; i < l; i++) {
+ t.unshift('..');
+ }
+ return t.join('/') + targetUrl.search + targetUrl.hash;
+}
+
+// exports
+scope.urlResolver = urlResolver;
+
+})(Polymer);
+
+/*
+ * 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(scope) {
+ var endOfMicrotask = Platform.endOfMicrotask;
+
+ // Generic url loader
+ function Loader(regex) {
+ this.cache = Object.create(null);
+ this.map = Object.create(null);
+ this.requests = 0;
+ this.regex = regex;
+ }
+ Loader.prototype = {
+
+ // TODO(dfreedm): there may be a better factoring here
+ // extract absolute urls from the text (full of relative urls)
+ extractUrls: function(text, base) {
+ var matches = [];
+ var matched, u;
+ while ((matched = this.regex.exec(text))) {
+ u = new URL(matched[1], base);
+ matches.push({matched: matched[0], url: u.href});
+ }
+ return matches;
+ },
+ // take a text blob, a root url, and a callback and load all the urls found within the text
+ // returns a map of absolute url to text
+ process: function(text, root, callback) {
+ var matches = this.extractUrls(text, root);
+
+ // every call to process returns all the text this loader has ever received
+ var done = callback.bind(null, this.map);
+ this.fetch(matches, done);
+ },
+ // build a mapping of url -> text from matches
+ fetch: function(matches, callback) {
+ var inflight = matches.length;
+
+ // return early if there is no fetching to be done
+ if (!inflight) {
+ return callback();
+ }
+
+ // wait for all subrequests to return
+ var done = function() {
+ if (--inflight === 0) {
+ callback();
+ }
+ };
+
+ // start fetching all subrequests
+ var m, req, url;
+ for (var i = 0; i < inflight; i++) {
+ m = matches[i];
+ url = m.url;
+ req = this.cache[url];
+ // if this url has already been requested, skip requesting it again
+ if (!req) {
+ req = this.xhr(url);
+ req.match = m;
+ this.cache[url] = req;
+ }
+ // wait for the request to process its subrequests
+ req.wait(done);
+ }
+ },
+ handleXhr: function(request) {
+ var match = request.match;
+ var url = match.url;
+
+ // handle errors with an empty string
+ var response = request.response || request.responseText || '';
+ this.map[url] = response;
+ this.fetch(this.extractUrls(response, url), request.resolve);
+ },
+ xhr: function(url) {
+ this.requests++;
+ var request = new XMLHttpRequest();
+ request.open('GET', url, true);
+ request.send();
+ request.onerror = request.onload = this.handleXhr.bind(this, request);
+
+ // queue of tasks to run after XHR returns
+ request.pending = [];
+ request.resolve = function() {
+ var pending = request.pending;
+ for(var i = 0; i < pending.length; i++) {
+ pending[i]();
+ }
+ request.pending = null;
+ };
+
+ // if we have already resolved, pending is null, async call the callback
+ request.wait = function(fn) {
+ if (request.pending) {
+ request.pending.push(fn);
+ } else {
+ endOfMicrotask(fn);
+ }
+ };
+
+ return request;
+ }
+ };
+
+ scope.Loader = Loader;
+})(Polymer);
+
+/*
+ * 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(scope) {
+
+var urlResolver = scope.urlResolver;
+var Loader = scope.Loader;
+
+function StyleResolver() {
+ this.loader = new Loader(this.regex);
+}
+StyleResolver.prototype = {
+ regex: /@import\s+(?:url)?["'\(]*([^'"\)]*)['"\)]*;/g,
+ // Recursively replace @imports with the text at that url
+ resolve: function(text, url, callback) {
+ var done = function(map) {
+ callback(this.flatten(text, url, map));
+ }.bind(this);
+ this.loader.process(text, url, done);
+ },
+ // resolve the textContent of a style node
+ resolveNode: function(style, url, callback) {
+ var text = style.textContent;
+ var done = function(text) {
+ style.textContent = text;
+ callback(style);
+ };
+ this.resolve(text, url, done);
+ },
+ // flatten all the @imports to text
+ flatten: function(text, base, map) {
+ var matches = this.loader.extractUrls(text, base);
+ var match, url, intermediate;
+ for (var i = 0; i < matches.length; i++) {
+ match = matches[i];
+ url = match.url;
+ // resolve any css text to be relative to the importer, keep absolute url
+ intermediate = urlResolver.resolveCssText(map[url], url, true);
+ // flatten intermediate @imports
+ intermediate = this.flatten(intermediate, base, map);
+ text = text.replace(match.matched, intermediate);
+ }
+ return text;
+ },
+ loadStyles: function(styles, base, callback) {
+ var loaded=0, l = styles.length;
+ // called in the context of the style
+ function loadedStyle(style) {
+ loaded++;
+ if (loaded === l && callback) {
+ callback();
+ }
+ }
+ for (var i=0, s; (i<l) && (s=styles[i]); i++) {
+ this.resolveNode(s, base, loadedStyle);
+ }
+ }
+};
+
+var styleResolver = new StyleResolver();
+
+// exports
+scope.styleResolver = styleResolver;
+
+})(Polymer);
+
+/*
+ * 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(scope) {
+
+ // copy own properties from 'api' to 'prototype, with name hinting for 'super'
+ function extend(prototype, api) {
+ if (prototype && api) {
+ // use only own properties of 'api'
+ Object.getOwnPropertyNames(api).forEach(function(n) {
+ // acquire property descriptor
+ var pd = Object.getOwnPropertyDescriptor(api, n);
+ if (pd) {
+ // clone property via descriptor
+ Object.defineProperty(prototype, n, pd);
+ // cache name-of-method for 'super' engine
+ if (typeof pd.value == 'function') {
+ // hint the 'super' engine
+ pd.value.nom = n;
+ }
+ }
+ });
+ }
+ return prototype;
+ }
+
+
+ // mixin
+
+ // copy all properties from inProps (et al) to inObj
+ function mixin(inObj/*, inProps, inMoreProps, ...*/) {
+ var obj = inObj || {};
+ for (var i = 1; i < arguments.length; i++) {
+ var p = arguments[i];
+ try {
+ for (var n in p) {
+ copyProperty(n, p, obj);
+ }
+ } catch(x) {
+ }
+ }
+ return obj;
+ }
+
+ // copy property inName from inSource object to inTarget object
+ function copyProperty(inName, inSource, inTarget) {
+ var pd = getPropertyDescriptor(inSource, inName);
+ Object.defineProperty(inTarget, inName, pd);
+ }
+
+ // get property descriptor for inName on inObject, even if
+ // inName exists on some link in inObject's prototype chain
+ function getPropertyDescriptor(inObject, inName) {
+ if (inObject) {
+ var pd = Object.getOwnPropertyDescriptor(inObject, inName);
+ return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName);
+ }
+ }
+
+ // exports
+
+ scope.extend = extend;
+ scope.mixin = mixin;
+
+ // for bc
+ Platform.mixin = mixin;
+
+})(Polymer);
+
+/*
+ * 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(scope) {
+
+ // usage
+
+ // invoke cb.call(this) in 100ms, unless the job is re-registered,
+ // which resets the timer
+ //
+ // this.myJob = this.job(this.myJob, cb, 100)
+ //
+ // returns a job handle which can be used to re-register a job
+
+ var Job = function(inContext) {
+ this.context = inContext;
+ this.boundComplete = this.complete.bind(this)
+ };
+ Job.prototype = {
+ go: function(callback, wait) {
+ this.callback = callback;
+ var h;
+ if (!wait) {
+ h = requestAnimationFrame(this.boundComplete);
+ this.handle = function() {
+ cancelAnimationFrame(h);
+ }
+ } else {
+ h = setTimeout(this.boundComplete, wait);
+ this.handle = function() {
+ clearTimeout(h);
+ }
+ }
+ },
+ stop: function() {
+ if (this.handle) {
+ this.handle();
+ this.handle = null;
+ }
+ },
+ complete: function() {
+ if (this.handle) {
+ this.stop();
+ this.callback.call(this.context);
+ }
+ }
+ };
+
+ function job(job, callback, wait) {
+ if (job) {
+ job.stop();
+ } else {
+ job = new Job(this);
+ }
+ job.go(callback, wait);
+ return job;
+ }
+
+ // exports
+
+ scope.job = job;
+
+})(Polymer);
+
+/*
+ * 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(scope) {
+
+ var registry = {};
+
+ HTMLElement.register = function(tag, prototype) {
+ registry[tag] = prototype;
+ }
+
+ // get prototype mapped to node <tag>
+ HTMLElement.getPrototypeForTag = function(tag) {
+ var prototype = !tag ? HTMLElement.prototype : registry[tag];
+ // TODO(sjmiles): creating <tag> is likely to have wasteful side-effects
+ return prototype || Object.getPrototypeOf(document.createElement(tag));
+ };
+
+ // we have to flag propagation stoppage for the event dispatcher
+ var originalStopPropagation = Event.prototype.stopPropagation;
+ Event.prototype.stopPropagation = function() {
+ this.cancelBubble = true;
+ originalStopPropagation.apply(this, arguments);
+ };
+
+
+ // polyfill DOMTokenList
+ // * add/remove: allow these methods to take multiple classNames
+ // * toggle: add a 2nd argument which forces the given state rather
+ // than toggling.
+
+ var add = DOMTokenList.prototype.add;
+ var remove = DOMTokenList.prototype.remove;
+ DOMTokenList.prototype.add = function() {
+ for (var i = 0; i < arguments.length; i++) {
+ add.call(this, arguments[i]);
+ }
+ };
+ DOMTokenList.prototype.remove = function() {
+ for (var i = 0; i < arguments.length; i++) {
+ remove.call(this, arguments[i]);
+ }
+ };
+ DOMTokenList.prototype.toggle = function(name, bool) {
+ if (arguments.length == 1) {
+ bool = !this.contains(name);
+ }
+ bool ? this.add(name) : this.remove(name);
+ };
+ DOMTokenList.prototype.switch = function(oldName, newName) {
+ oldName && this.remove(oldName);
+ newName && this.add(newName);
+ };
+
+ // add array() to NodeList, NamedNodeMap, HTMLCollection
+
+ var ArraySlice = function() {
+ return Array.prototype.slice.call(this);
+ };
+
+ var namedNodeMap = (window.NamedNodeMap || window.MozNamedAttrMap || {});
+
+ NodeList.prototype.array = ArraySlice;
+ namedNodeMap.prototype.array = ArraySlice;
+ HTMLCollection.prototype.array = ArraySlice;
+
+ // utility
+
+ function createDOM(inTagOrNode, inHTML, inAttrs) {
+ var dom = typeof inTagOrNode == 'string' ?
+ document.createElement(inTagOrNode) : inTagOrNode.cloneNode(true);
+ dom.innerHTML = inHTML;
+ if (inAttrs) {
+ for (var n in inAttrs) {
+ dom.setAttribute(n, inAttrs[n]);
+ }
+ }
+ return dom;
+ }
+
+ // exports
+
+ scope.createDOM = createDOM;
+
+})(Polymer);
+
+/*
+ * 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(scope) {
+ // super
+
+ // `arrayOfArgs` is an optional array of args like one might pass
+ // to `Function.apply`
+
+ // TODO(sjmiles):
+ // $super must be installed on an instance or prototype chain
+ // as `super`, and invoked via `this`, e.g.
+ // `this.super();`
+
+ // will not work if function objects are not unique, for example,
+ // when using mixins.
+ // The memoization strategy assumes each function exists on only one
+ // prototype chain i.e. we use the function object for memoizing)
+ // perhaps we can bookkeep on the prototype itself instead
+ function $super(arrayOfArgs) {
+ // since we are thunking a method call, performance is important here:
+ // memoize all lookups, once memoized the fast path calls no other
+ // functions
+ //
+ // find the caller (cannot be `strict` because of 'caller')
+ var caller = $super.caller;
+ // memoized 'name of method'
+ var nom = caller.nom;
+ // memoized next implementation prototype
+ var _super = caller._super;
+ if (!_super) {
+ if (!nom) {
+ nom = caller.nom = nameInThis.call(this, caller);
+ }
+ if (!nom) {
+ console.warn('called super() on a method not installed declaratively (has no .nom property)');
+ }
+ // super prototype is either cached or we have to find it
+ // by searching __proto__ (at the 'top')
+ // invariant: because we cache _super on fn below, we never reach
+ // here from inside a series of calls to super(), so it's ok to
+ // start searching from the prototype of 'this' (at the 'top')
+ // we must never memoize a null super for this reason
+ _super = memoizeSuper(caller, nom, getPrototypeOf(this));
+ }
+ // our super function
+ var fn = _super[nom];
+ if (fn) {
+ // memoize information so 'fn' can call 'super'
+ if (!fn._super) {
+ // must not memoize null, or we lose our invariant above
+ memoizeSuper(fn, nom, _super);
+ }
+ // invoke the inherited method
+ // if 'fn' is not function valued, this will throw
+ return fn.apply(this, arrayOfArgs || []);
+ }
+ }
+
+ function nameInThis(value) {
+ var p = this.__proto__;
+ while (p && p !== HTMLElement.prototype) {
+ // TODO(sjmiles): getOwnPropertyNames is absurdly expensive
+ var n$ = Object.getOwnPropertyNames(p);
+ for (var i=0, l=n$.length, n; i<l && (n=n$[i]); i++) {
+ var d = Object.getOwnPropertyDescriptor(p, n);
+ if (typeof d.value === 'function' && d.value === value) {
+ return n;
+ }
+ }
+ p = p.__proto__;
+ }
+ }
+
+ function memoizeSuper(method, name, proto) {
+ // find and cache next prototype containing `name`
+ // we need the prototype so we can do another lookup
+ // from here
+ var s = nextSuper(proto, name, method);
+ if (s[name]) {
+ // `s` is a prototype, the actual method is `s[name]`
+ // tag super method with it's name for quicker lookups
+ s[name].nom = name;
+ }
+ return method._super = s;
+ }
+
+ function nextSuper(proto, name, caller) {
+ // look for an inherited prototype that implements name
+ while (proto) {
+ if ((proto[name] !== caller) && proto[name]) {
+ return proto;
+ }
+ proto = getPrototypeOf(proto);
+ }
+ // must not return null, or we lose our invariant above
+ // in this case, a super() call was invoked where no superclass
+ // method exists
+ // TODO(sjmiles): thow an exception?
+ return Object;
+ }
+
+ // NOTE: In some platforms (IE10) the prototype chain is faked via
+ // __proto__. Therefore, always get prototype via __proto__ instead of
+ // the more standard Object.getPrototypeOf.
function getPrototypeOf(prototype) {
return prototype.__proto__;
}
@@ -4477,8 +8704,8 @@ if (typeof window.Polymer === 'function') {
if (Array.isArray(value)) {
log.observe && console.log('[%s] observeArrayValue: register observer [%s]', this.localName, name, value);
var observer = new ArrayObserver(value);
- observer.open(function(value, old) {
- this.invokeMethod(callbackName, [old]);
+ observer.open(function(splices) {
+ this.invokeMethod(callbackName, [splices]);
}, this);
this.registerNamedObserver(name + '__array', observer);
}
@@ -4644,6 +8871,8 @@ if (typeof window.Polymer === 'function') {
// element api supporting mdv
var mdv = {
instanceTemplate: function(template) {
+ // ensure template is decorated (lets' things like <tr template ...> work)
+ HTMLTemplateElement.decorate(template);
// ensure a default bindingDelegate
var syntax = this.syntax || (!template.bindingDelegate &&
this.element.syntax);
@@ -5116,9 +9345,9 @@ if (typeof window.Polymer === 'function') {
// specify an 'own' prototype for tag `name`
function element(name, prototype) {
- if (arguments.length === 1 && typeof arguments[0] !== 'string') {
+ if (typeof name !== 'string') {
+ var script = prototype || document._currentScript;
prototype = name;
- var script = document._currentScript;
name = script && script.parentNode && script.parentNode.getAttribute ?
script.parentNode.getAttribute('name') : '';
if (!name) {
@@ -5191,11 +9420,14 @@ if (typeof window.Polymer === 'function') {
// document. Platform collects those calls until we can process them, which
// we do here.
- var declarations = Platform.deliverDeclarations();
- if (declarations) {
- for (var i=0, l=declarations.length, d; (i<l) && (d=declarations[i]); i++) {
- element.apply(null, d);
- }
+ if (Platform.consumeDeclarations) {
+ Platform.consumeDeclarations(function(declarations) {;
+ if (declarations) {
+ for (var i=0, l=declarations.length, d; (i<l) && (d=declarations[i]); i++) {
+ element.apply(null, d);
+ }
+ }
+ });
}
})(Polymer);
@@ -5213,7 +9445,7 @@ if (typeof window.Polymer === 'function') {
var path = {
resolveElementPaths: function(node) {
- Platform.urlResolver.resolveDom(node);
+ Polymer.urlResolver.resolveDom(node);
},
addResolvePathApi: function() {
// let assetpath attribute modify the resolve path
@@ -5268,7 +9500,7 @@ scope.api.declaration.path = path;
var styles = this.findLoadableStyles(content);
if (styles.length) {
var templateUrl = template.ownerDocument.baseURI;
- return Platform.styleResolver.loadStyles(styles, templateUrl, callback);
+ return Polymer.styleResolver.loadStyles(styles, templateUrl, callback);
}
}
if (callback) {
@@ -5901,7 +10133,7 @@ scope.api.declaration.path = path;
},
templateContent: function() {
var template = this.fetchTemplate();
- return template && Platform.templateContent(template);
+ return template && template.content;
},
installBindingDelegate: function(template) {
if (template) {
@@ -6301,17 +10533,19 @@ scope.api.declaration.path = path;
},
ready: function() {
- this.flush();
// TODO(sorvell): As an optimization, turn off CE polyfill upgrading
// while registering. This way we avoid having to upgrade each document
// piecemeal per registration and can instead register all elements
// and upgrade once in a batch. Without this optimization, upgrade time
// degrades significantly when SD polyfill is used. This is mainly because
// querying the document tree for elements is slow under the SD polyfill.
- if (CustomElements.ready === false) {
+ var polyfillWasReady = CustomElements.ready;
+ CustomElements.ready = false;
+ this.flush();
+ if (!CustomElements.useNative) {
CustomElements.upgradeDocumentTree(document);
- CustomElements.ready = true;
}
+ CustomElements.ready = polyfillWasReady;
Platform.flush();
requestAnimationFrame(this.flushReadyCallbacks);
},
@@ -6350,15 +10584,8 @@ scope.api.declaration.path = path;
return importQueue.length ? importQueue[0] : mainQueue[0];
}
- var polymerReadied = false;
-
- document.addEventListener('WebComponentsReady', function() {
- CustomElements.ready = false;
- });
-
- function whenPolymerReady(callback) {
+ function whenReady(callback) {
queue.waitToReady = true;
- CustomElements.ready = false;
HTMLImports.whenImportsReady(function() {
queue.addReadyCallback(callback);
queue.waitToReady = false;
@@ -6369,50 +10596,7 @@ scope.api.declaration.path = path;
// exports
scope.elements = elements;
scope.queue = queue;
- scope.whenReady = scope.whenPolymerReady = whenPolymerReady;
-})(Polymer);
-
-/*
- * 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(scope) {
-
- var whenPolymerReady = scope.whenPolymerReady;
-
- function importElements(elementOrFragment, callback) {
- if (elementOrFragment) {
- document.head.appendChild(elementOrFragment);
- whenPolymerReady(callback);
- } else if (callback) {
- callback();
- }
- }
-
- function importUrls(urls, callback) {
- if (urls && urls.length) {
- var frag = document.createDocumentFragment();
- for (var i=0, l=urls.length, url, link; (i<l) && (url=urls[i]); i++) {
- link = document.createElement('link');
- link.rel = 'import';
- link.href = url;
- frag.appendChild(link);
- }
- importElements(frag, callback);
- } else if (callback) {
- callback();
- }
- }
-
- // exports
- scope.import = importUrls;
- scope.importElements = importElements;
-
+ scope.whenReady = scope.whenPolymerReady = whenReady;
})(Polymer);
/*
@@ -6431,7 +10615,7 @@ scope.api.declaration.path = path;
var extend = scope.extend;
var api = scope.api;
var queue = scope.queue;
- var whenPolymerReady = scope.whenPolymerReady;
+ var whenReady = scope.whenReady;
var getRegisteredPrototype = scope.getRegisteredPrototype;
var waitingForPrototype = scope.waitingForPrototype;
@@ -6541,7 +10725,7 @@ scope.api.declaration.path = path;
// boot tasks
- whenPolymerReady(function() {
+ whenReady(function() {
document.body.removeAttribute('unresolved');
document.dispatchEvent(
new CustomEvent('polymer-ready', {bubbles: true})
@@ -6563,6 +10747,49 @@ scope.api.declaration.path = path;
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
+(function(scope) {
+
+ var whenPolymerReady = scope.whenPolymerReady;
+
+ function importElements(elementOrFragment, callback) {
+ if (elementOrFragment) {
+ document.head.appendChild(elementOrFragment);
+ whenPolymerReady(callback);
+ } else if (callback) {
+ callback();
+ }
+ }
+
+ function importUrls(urls, callback) {
+ if (urls && urls.length) {
+ var frag = document.createDocumentFragment();
+ for (var i=0, l=urls.length, url, link; (i<l) && (url=urls[i]); i++) {
+ link = document.createElement('link');
+ link.rel = 'import';
+ link.href = url;
+ frag.appendChild(link);
+ }
+ importElements(frag, callback);
+ } else if (callback) {
+ callback();
+ }
+ }
+
+ // exports
+ scope.import = importUrls;
+ scope.importElements = importElements;
+
+})(Polymer);
+
+/*
+ * 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
+ */
+
/**
* The `auto-binding` element extends the template element. It provides a quick
* and easy way to do data binding without the need to setup a model.
« no previous file with comments | « pkg/polymer/lib/src/js/polymer/polymer.js ('k') | pkg/polymer/lib/src/js/polymer/polymer.concat.js.map » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698