| 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.
|
|
|