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

Unified Diff: packages/polymer_interop/lib/src/js/polymer.js

Issue 2312183003: Removed Polymer from Observatory deps (Closed)
Patch Set: Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: packages/polymer_interop/lib/src/js/polymer.js
diff --git a/packages/polymer_interop/lib/src/js/polymer.js b/packages/polymer_interop/lib/src/js/polymer.js
deleted file mode 100644
index f3df2e836fc3cd13b03bded4c23b55f4c9a39d5c..0000000000000000000000000000000000000000
--- a/packages/polymer_interop/lib/src/js/polymer.js
+++ /dev/null
@@ -1,11859 +0,0 @@
-/**
- * @license
- * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
- * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
- * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
- * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
- * Code distributed by Google as part of the polymer project is also
- * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
- */
-// @version 0.5.5
-window.PolymerGestures = {};
-
-(function(scope) {
- var hasFullPath = false;
-
- // test for full event path support
- var pathTest = document.createElement('meta');
- if (pathTest.createShadowRoot) {
- var sr = pathTest.createShadowRoot();
- var s = document.createElement('span');
- sr.appendChild(s);
- pathTest.addEventListener('testpath', function(ev) {
- if (ev.path) {
- // if the span is in the event path, then path[0] is the real source for all events
- hasFullPath = ev.path[0] === s;
- }
- ev.stopPropagation();
- });
- var ev = new CustomEvent('testpath', {bubbles: true});
- // must add node to DOM to trigger event listener
- document.head.appendChild(pathTest);
- s.dispatchEvent(ev);
- pathTest.parentNode.removeChild(pathTest);
- sr = s = null;
- }
- pathTest = null;
-
- var target = {
- shadow: function(inEl) {
- if (inEl) {
- return inEl.shadowRoot || inEl.webkitShadowRoot;
- }
- },
- canTarget: function(shadow) {
- return shadow && Boolean(shadow.elementFromPoint);
- },
- targetingShadow: function(inEl) {
- var s = this.shadow(inEl);
- if (this.canTarget(s)) {
- return s;
- }
- },
- olderShadow: function(shadow) {
- var os = shadow.olderShadowRoot;
- if (!os) {
- var se = shadow.querySelector('shadow');
- if (se) {
- os = se.olderShadowRoot;
- }
- }
- return os;
- },
- allShadows: function(element) {
- var shadows = [], s = this.shadow(element);
- while(s) {
- shadows.push(s);
- s = this.olderShadow(s);
- }
- return shadows;
- },
- searchRoot: function(inRoot, x, y) {
- var t, st, sr, os;
- if (inRoot) {
- t = inRoot.elementFromPoint(x, y);
- if (t) {
- // found element, check if it has a ShadowRoot
- sr = this.targetingShadow(t);
- } else if (inRoot !== document) {
- // check for sibling roots
- sr = this.olderShadow(inRoot);
- }
- // search other roots, fall back to light dom element
- return this.searchRoot(sr, x, y) || t;
- }
- },
- owner: function(element) {
- if (!element) {
- return document;
- }
- var s = element;
- // walk up until you hit the shadow root or document
- while (s.parentNode) {
- s = s.parentNode;
- }
- // the owner element is expected to be a Document or ShadowRoot
- if (s.nodeType != Node.DOCUMENT_NODE && s.nodeType != Node.DOCUMENT_FRAGMENT_NODE) {
- s = document;
- }
- return s;
- },
- findTarget: function(inEvent) {
- if (hasFullPath && inEvent.path && inEvent.path.length) {
- return inEvent.path[0];
- }
- var x = inEvent.clientX, y = inEvent.clientY;
- // if the listener is in the shadow root, it is much faster to start there
- var s = this.owner(inEvent.target);
- // if x, y is not in this root, fall back to document search
- if (!s.elementFromPoint(x, y)) {
- s = document;
- }
- return this.searchRoot(s, x, y);
- },
- findTouchAction: function(inEvent) {
- var n;
- if (hasFullPath && inEvent.path && inEvent.path.length) {
- var path = inEvent.path;
- for (var i = 0; i < path.length; i++) {
- n = path[i];
- if (n.nodeType === Node.ELEMENT_NODE && n.hasAttribute('touch-action')) {
- return n.getAttribute('touch-action');
- }
- }
- } else {
- n = inEvent.target;
- while(n) {
- if (n.nodeType === Node.ELEMENT_NODE && n.hasAttribute('touch-action')) {
- return n.getAttribute('touch-action');
- }
- n = n.parentNode || n.host;
- }
- }
- // auto is default
- return "auto";
- },
- LCA: function(a, b) {
- if (a === b) {
- return a;
- }
- if (a && !b) {
- return a;
- }
- if (b && !a) {
- return b;
- }
- if (!b && !a) {
- return document;
- }
- // fast case, a is a direct descendant of b or vice versa
- if (a.contains && a.contains(b)) {
- return a;
- }
- if (b.contains && b.contains(a)) {
- return b;
- }
- var adepth = this.depth(a);
- var bdepth = this.depth(b);
- var d = adepth - bdepth;
- if (d >= 0) {
- a = this.walk(a, d);
- } else {
- b = this.walk(b, -d);
- }
- while (a && b && a !== b) {
- a = a.parentNode || a.host;
- b = b.parentNode || b.host;
- }
- return a;
- },
- walk: function(n, u) {
- for (var i = 0; n && (i < u); i++) {
- n = n.parentNode || n.host;
- }
- return n;
- },
- depth: function(n) {
- var d = 0;
- while(n) {
- d++;
- n = n.parentNode || n.host;
- }
- return d;
- },
- deepContains: function(a, b) {
- var common = this.LCA(a, b);
- // if a is the common ancestor, it must "deeply" contain b
- return common === a;
- },
- insideNode: function(node, x, y) {
- var rect = node.getBoundingClientRect();
- return (rect.left <= x) && (x <= rect.right) && (rect.top <= y) && (y <= rect.bottom);
- },
- path: function(event) {
- var p;
- if (hasFullPath && event.path && event.path.length) {
- p = event.path;
- } else {
- p = [];
- var n = this.findTarget(event);
- while (n) {
- p.push(n);
- n = n.parentNode || n.host;
- }
- }
- return p;
- }
- };
- scope.targetFinding = target;
- /**
- * Given an event, finds the "deepest" node that could have been the original target before ShadowDOM retargetting
- *
- * @param {Event} Event An event object with clientX and clientY properties
- * @return {Element} The probable event origninator
- */
- scope.findTarget = target.findTarget.bind(target);
- /**
- * Determines if the "container" node deeply contains the "containee" node, including situations where the "containee" is contained by one or more ShadowDOM
- * roots.
- *
- * @param {Node} container
- * @param {Node} containee
- * @return {Boolean}
- */
- scope.deepContains = target.deepContains.bind(target);
-
- /**
- * Determines if the x/y position is inside the given node.
- *
- * Example:
- *
- * function upHandler(event) {
- * var innode = PolymerGestures.insideNode(event.target, event.clientX, event.clientY);
- * if (innode) {
- * // wait for tap?
- * } else {
- * // tap will never happen
- * }
- * }
- *
- * @param {Node} node
- * @param {Number} x Screen X position
- * @param {Number} y screen Y position
- * @return {Boolean}
- */
- scope.insideNode = target.insideNode;
-
-})(window.PolymerGestures);
-
-(function() {
- function shadowSelector(v) {
- return 'html /deep/ ' + selector(v);
- }
- function selector(v) {
- return '[touch-action="' + v + '"]';
- }
- function rule(v) {
- return '{ -ms-touch-action: ' + v + '; touch-action: ' + v + ';}';
- }
- var attrib2css = [
- 'none',
- 'auto',
- 'pan-x',
- 'pan-y',
- {
- rule: 'pan-x pan-y',
- selectors: [
- 'pan-x pan-y',
- 'pan-y pan-x'
- ]
- },
- 'manipulation'
- ];
- var styles = '';
- // only install stylesheet if the browser has touch action support
- var hasTouchAction = typeof document.head.style.touchAction === 'string';
- // only add shadow selectors if shadowdom is supported
- var hasShadowRoot = !window.ShadowDOMPolyfill && document.head.createShadowRoot;
-
- if (hasTouchAction) {
- attrib2css.forEach(function(r) {
- if (String(r) === r) {
- styles += selector(r) + rule(r) + '\n';
- if (hasShadowRoot) {
- styles += shadowSelector(r) + rule(r) + '\n';
- }
- } else {
- styles += r.selectors.map(selector) + rule(r.rule) + '\n';
- if (hasShadowRoot) {
- styles += r.selectors.map(shadowSelector) + rule(r.rule) + '\n';
- }
- }
- });
-
- var el = document.createElement('style');
- el.textContent = styles;
- document.head.appendChild(el);
- }
-})();
-
-/**
- * This is the constructor for new PointerEvents.
- *
- * New Pointer Events must be given a type, and an optional dictionary of
- * initialization properties.
- *
- * Due to certain platform requirements, events returned from the constructor
- * identify as MouseEvents.
- *
- * @constructor
- * @param {String} inType The type of the event to create.
- * @param {Object} [inDict] An optional dictionary of initial event properties.
- * @return {Event} A new PointerEvent of type `inType` and initialized with properties from `inDict`.
- */
-(function(scope) {
-
- var MOUSE_PROPS = [
- 'bubbles',
- 'cancelable',
- 'view',
- 'detail',
- 'screenX',
- 'screenY',
- 'clientX',
- 'clientY',
- 'ctrlKey',
- 'altKey',
- 'shiftKey',
- 'metaKey',
- 'button',
- 'relatedTarget',
- 'pageX',
- 'pageY'
- ];
-
- var MOUSE_DEFAULTS = [
- false,
- false,
- null,
- null,
- 0,
- 0,
- 0,
- 0,
- false,
- false,
- false,
- false,
- 0,
- null,
- 0,
- 0
- ];
-
- var NOP_FACTORY = function(){ return function(){}; };
-
- var eventFactory = {
- // TODO(dfreedm): this is overridden by tap recognizer, needs review
- preventTap: NOP_FACTORY,
- makeBaseEvent: function(inType, inDict) {
- var e = document.createEvent('Event');
- e.initEvent(inType, inDict.bubbles || false, inDict.cancelable || false);
- e.preventTap = eventFactory.preventTap(e);
- return e;
- },
- makeGestureEvent: function(inType, inDict) {
- inDict = inDict || Object.create(null);
-
- var e = this.makeBaseEvent(inType, inDict);
- for (var i = 0, keys = Object.keys(inDict), k; i < keys.length; i++) {
- k = keys[i];
- if( k !== 'bubbles' && k !== 'cancelable' ) {
- e[k] = inDict[k];
- }
- }
- return e;
- },
- makePointerEvent: function(inType, inDict) {
- inDict = inDict || Object.create(null);
-
- var e = this.makeBaseEvent(inType, inDict);
- // define inherited MouseEvent properties
- for(var i = 2, p; i < MOUSE_PROPS.length; i++) {
- p = MOUSE_PROPS[i];
- e[p] = inDict[p] || MOUSE_DEFAULTS[i];
- }
- e.buttons = inDict.buttons || 0;
-
- // Spec requires that pointers without pressure specified use 0.5 for down
- // state and 0 for up state.
- var pressure = 0;
- if (inDict.pressure) {
- pressure = inDict.pressure;
- } else {
- pressure = e.buttons ? 0.5 : 0;
- }
-
- // add x/y properties aliased to clientX/Y
- e.x = e.clientX;
- e.y = e.clientY;
-
- // define the properties of the PointerEvent interface
- e.pointerId = inDict.pointerId || 0;
- e.width = inDict.width || 0;
- e.height = inDict.height || 0;
- e.pressure = pressure;
- e.tiltX = inDict.tiltX || 0;
- e.tiltY = inDict.tiltY || 0;
- e.pointerType = inDict.pointerType || '';
- e.hwTimestamp = inDict.hwTimestamp || 0;
- e.isPrimary = inDict.isPrimary || false;
- e._source = inDict._source || '';
- return e;
- }
- };
-
- scope.eventFactory = eventFactory;
-})(window.PolymerGestures);
-
-/**
- * This module implements an map of pointer states
- */
-(function(scope) {
- var USE_MAP = window.Map && window.Map.prototype.forEach;
- var POINTERS_FN = function(){ return this.size; };
- function PointerMap() {
- if (USE_MAP) {
- var m = new Map();
- m.pointers = POINTERS_FN;
- return m;
- } else {
- this.keys = [];
- this.values = [];
- }
- }
-
- PointerMap.prototype = {
- set: function(inId, inEvent) {
- var i = this.keys.indexOf(inId);
- if (i > -1) {
- this.values[i] = inEvent;
- } else {
- this.keys.push(inId);
- this.values.push(inEvent);
- }
- },
- has: function(inId) {
- return this.keys.indexOf(inId) > -1;
- },
- 'delete': function(inId) {
- var i = this.keys.indexOf(inId);
- if (i > -1) {
- this.keys.splice(i, 1);
- this.values.splice(i, 1);
- }
- },
- get: function(inId) {
- var i = this.keys.indexOf(inId);
- return this.values[i];
- },
- clear: function() {
- this.keys.length = 0;
- this.values.length = 0;
- },
- // return value, key, map
- forEach: function(callback, thisArg) {
- this.values.forEach(function(v, i) {
- callback.call(thisArg, v, this.keys[i], this);
- }, this);
- },
- pointers: function() {
- return this.keys.length;
- }
- };
-
- scope.PointerMap = PointerMap;
-})(window.PolymerGestures);
-
-(function(scope) {
- var CLONE_PROPS = [
- // MouseEvent
- 'bubbles',
- 'cancelable',
- 'view',
- 'detail',
- 'screenX',
- 'screenY',
- 'clientX',
- 'clientY',
- 'ctrlKey',
- 'altKey',
- 'shiftKey',
- 'metaKey',
- 'button',
- 'relatedTarget',
- // DOM Level 3
- 'buttons',
- // PointerEvent
- 'pointerId',
- 'width',
- 'height',
- 'pressure',
- 'tiltX',
- 'tiltY',
- 'pointerType',
- 'hwTimestamp',
- 'isPrimary',
- // event instance
- 'type',
- 'target',
- 'currentTarget',
- 'which',
- 'pageX',
- 'pageY',
- 'timeStamp',
- // gesture addons
- 'preventTap',
- 'tapPrevented',
- '_source'
- ];
-
- var CLONE_DEFAULTS = [
- // MouseEvent
- false,
- false,
- null,
- null,
- 0,
- 0,
- 0,
- 0,
- false,
- false,
- false,
- false,
- 0,
- null,
- // DOM Level 3
- 0,
- // PointerEvent
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- '',
- 0,
- false,
- // event instance
- '',
- null,
- null,
- 0,
- 0,
- 0,
- 0,
- function(){},
- false
- ];
-
- var HAS_SVG_INSTANCE = (typeof SVGElementInstance !== 'undefined');
-
- 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
- * matter the source.
- * Events fired:
- * - pointerdown: a pointing is added
- * - pointerup: a pointer is removed
- * - pointermove: a pointer is moved
- * - pointerover: a pointer crosses into an element
- * - pointerout: a pointer leaves an element
- * - pointercancel: a pointer will no longer generate events
- */
- var dispatcher = {
- IS_IOS: false,
- pointermap: new scope.PointerMap(),
- requiredGestures: new scope.PointerMap(),
- eventMap: Object.create(null),
- // Scope objects for native events.
- // This exists for ease of testing.
- eventSources: Object.create(null),
- eventSourceList: [],
- gestures: [],
- // map gesture event -> {listeners: int, index: gestures[int]}
- dependencyMap: {
- // make sure down and up are in the map to trigger "register"
- down: {listeners: 0, index: -1},
- up: {listeners: 0, index: -1}
- },
- gestureQueue: [],
- /**
- * Add a new event source that will generate pointer events.
- *
- * `inSource` must contain an array of event names named `events`, and
- * functions with the names specified in the `events` array.
- * @param {string} name A name for the event source
- * @param {Object} source A new source of platform events.
- */
- registerSource: function(name, source) {
- var s = source;
- var newEvents = s.events;
- if (newEvents) {
- newEvents.forEach(function(e) {
- if (s[e]) {
- this.eventMap[e] = s[e].bind(s);
- }
- }, this);
- this.eventSources[name] = s;
- this.eventSourceList.push(s);
- }
- },
- registerGesture: function(name, source) {
- var obj = Object.create(null);
- obj.listeners = 0;
- obj.index = this.gestures.length;
- for (var i = 0, g; i < source.exposes.length; i++) {
- g = source.exposes[i].toLowerCase();
- this.dependencyMap[g] = obj;
- }
- this.gestures.push(source);
- },
- register: function(element, initial) {
- var l = this.eventSourceList.length;
- for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
- // call eventsource register
- es.register.call(es, element, initial);
- }
- },
- unregister: function(element) {
- var l = this.eventSourceList.length;
- for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
- // call eventsource register
- es.unregister.call(es, element);
- }
- },
- // EVENTS
- down: function(inEvent) {
- this.requiredGestures.set(inEvent.pointerId, currentGestures);
- this.fireEvent('down', inEvent);
- },
- move: function(inEvent) {
- // pipe move events into gesture queue directly
- inEvent.type = 'move';
- this.fillGestureQueue(inEvent);
- },
- 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);
- },
- addGestureDependency: function(node, currentGestures) {
- var gesturesWanted = node._pgEvents;
- if (gesturesWanted && currentGestures) {
- 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;
- }
- }
- }
- },
- // LISTENER LOGIC
- eventHandler: function(inEvent) {
- // This is used to prevent multiple dispatch of events from
- // platform events. This can happen when two elements in different scopes
- // are set up to create pointer events, which is relevant to Shadow DOM.
-
- 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 = {};
- }
-
- // in IOS mode, there is only a listener on the document, so this is not re-entrant
- if (this.IS_IOS) {
- var ev = inEvent;
- if (type === 'touchstart') {
- var ct = inEvent.changedTouches[0];
- // set up a fake event to give to the path builder
- ev = {target: inEvent.target, clientX: ct.clientX, clientY: ct.clientY, path: inEvent.path};
- }
- // use event path if available, otherwise build a path from target finding
- var nodes = inEvent.path || scope.targetFinding.path(ev);
- for (var i = 0, n; i < nodes.length; i++) {
- n = nodes[i];
- this.addGestureDependency(n, currentGestures);
- }
- } else {
- this.addGestureDependency(inEvent.currentTarget, currentGestures);
- }
- }
-
- if (inEvent._handledByPG) {
- return;
- }
- var fn = this.eventMap && this.eventMap[type];
- if (fn) {
- fn(inEvent);
- }
- inEvent._handledByPG = true;
- },
- // set up event listeners
- listen: function(target, events) {
- for (var i = 0, l = events.length, e; (i < l) && (e = events[i]); i++) {
- this.addEvent(target, e);
- }
- },
- // remove event listeners
- unlisten: function(target, events) {
- for (var i = 0, l = events.length, e; (i < l) && (e = events[i]); i++) {
- this.removeEvent(target, e);
- }
- },
- addEvent: function(target, eventName) {
- target.addEventListener(eventName, this.boundHandler);
- },
- removeEvent: function(target, eventName) {
- target.removeEventListener(eventName, this.boundHandler);
- },
- // EVENT CREATION AND TRACKING
- /**
- * Creates a new Event of type `inType`, based on the information in
- * `inEvent`.
- *
- * @param {string} inType A string representing the type of event to create
- * @param {Event} inEvent A platform event with a target
- * @return {Event} A PointerEvent of type `inType`
- */
- makeEvent: function(inType, inEvent) {
- var e = eventFactory.makePointerEvent(inType, inEvent);
- e.preventDefault = inEvent.preventDefault;
- e.tapPrevented = inEvent.tapPrevented;
- e._target = e._target || inEvent.target;
- return e;
- },
- // make and dispatch an event in one call
- fireEvent: function(inType, inEvent) {
- var e = this.makeEvent(inType, inEvent);
- return this.dispatchEvent(e);
- },
- /**
- * Returns a snapshot of inEvent, with writable properties.
- *
- * @param {Event} inEvent An event that contains properties to copy.
- * @return {Object} An object containing shallow copies of `inEvent`'s
- * properties.
- */
- cloneEvent: function(inEvent) {
- var eventCopy = Object.create(null), p;
- for (var i = 0; i < CLONE_PROPS.length; i++) {
- p = CLONE_PROPS[i];
- eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i];
- // Work around SVGInstanceElement shadow tree
- // Return the <use> element that is represented by the instance for Safari, Chrome, IE.
- // This is the behavior implemented by Firefox.
- if (p === 'target' || p === 'relatedTarget') {
- if (HAS_SVG_INSTANCE && eventCopy[p] instanceof SVGElementInstance) {
- eventCopy[p] = eventCopy[p].correspondingUseElement;
- }
- }
- }
- // keep the semantics of preventDefault
- eventCopy.preventDefault = function() {
- inEvent.preventDefault();
- };
- return eventCopy;
- },
- /**
- * Dispatches the event to its target.
- *
- * @param {Event} inEvent The event to be dispatched.
- * @return {Boolean} True if an event handler returns true, false otherwise.
- */
- dispatchEvent: function(inEvent) {
- var t = inEvent._target;
- if (t) {
- t.dispatchEvent(inEvent);
- // clone the event for the gesture system to process
- // clone after dispatch to pick up gesture prevention code
- var clone = this.cloneEvent(inEvent);
- clone.target = t;
- this.fillGestureQueue(clone);
- }
- },
- gestureTrigger: function() {
- // process the gesture queue
- for (var i = 0, e, rg; i < this.gestureQueue.length; i++) {
- e = this.gestureQueue[i];
- rg = e._requiredGestures;
- if (rg) {
- for (var j = 0, g, fn; j < this.gestures.length; j++) {
- // 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);
- }
- }
- }
- }
- }
- this.gestureQueue.length = 0;
- },
- fillGestureQueue: function(ev) {
- // only trigger the gesture queue once
- if (!this.gestureQueue.length) {
- requestAnimationFrame(this.boundGestureTrigger);
- }
- ev._requiredGestures = this.requiredGestures.get(ev.pointerId);
- this.gestureQueue.push(ev);
- }
- };
- dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher);
- dispatcher.boundGestureTrigger = dispatcher.gestureTrigger.bind(dispatcher);
- scope.dispatcher = dispatcher;
-
- /**
- * Listen for `gesture` on `node` with the `handler` function
- *
- * If `handler` is the first listener for `gesture`, the underlying gesture recognizer is then enabled.
- *
- * @param {Element} node
- * @param {string} gesture
- * @return Boolean `gesture` is a valid gesture
- */
- scope.activateGesture = function(node, gesture) {
- var g = gesture.toLowerCase();
- var dep = dispatcher.dependencyMap[g];
- if (dep) {
- var recognizer = dispatcher.gestures[dep.index];
- if (!node._pgListeners) {
- dispatcher.register(node);
- node._pgListeners = 0;
- }
- // TODO(dfreedm): re-evaluate bookkeeping to avoid using attributes
- if (recognizer) {
- var touchAction = recognizer.defaultActions && recognizer.defaultActions[g];
- var actionNode;
- switch(node.nodeType) {
- case Node.ELEMENT_NODE:
- actionNode = node;
- break;
- case Node.DOCUMENT_FRAGMENT_NODE:
- actionNode = node.host;
- break;
- default:
- actionNode = null;
- break;
- }
- if (touchAction && actionNode && !actionNode.hasAttribute('touch-action')) {
- actionNode.setAttribute('touch-action', touchAction);
- }
- }
- if (!node._pgEvents) {
- node._pgEvents = {};
- }
- node._pgEvents[g] = (node._pgEvents[g] || 0) + 1;
- node._pgListeners++;
- }
- return Boolean(dep);
- };
-
- /**
- *
- * Listen for `gesture` from `node` with `handler` function.
- *
- * @param {Element} node
- * @param {string} gesture
- * @param {Function} handler
- * @param {Boolean} capture
- */
- scope.addEventListener = function(node, gesture, handler, capture) {
- if (handler) {
- scope.activateGesture(node, gesture);
- node.addEventListener(gesture, handler, capture);
- }
- };
-
- /**
- * Tears down the gesture configuration for `node`
- *
- * If `handler` is the last listener for `gesture`, the underlying gesture recognizer is disabled.
- *
- * @param {Element} node
- * @param {string} gesture
- * @return Boolean `gesture` is a valid gesture
- */
- scope.deactivateGesture = function(node, gesture) {
- var g = gesture.toLowerCase();
- var dep = dispatcher.dependencyMap[g];
- if (dep) {
- 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);
- };
-
- /**
- * Stop listening for `gesture` from `node` with `handler` function.
- *
- * @param {Element} node
- * @param {string} gesture
- * @param {Function} handler
- * @param {Boolean} capture
- */
- scope.removeEventListener = function(node, gesture, handler, capture) {
- if (handler) {
- scope.deactivateGesture(node, gesture);
- node.removeEventListener(gesture, handler, capture);
- }
- };
-})(window.PolymerGestures);
-
-(function(scope) {
- var dispatcher = scope.dispatcher;
- var pointermap = dispatcher.pointermap;
- // radius around touchend that swallows mouse events
- var DEDUP_DIST = 25;
-
- var WHICH_TO_BUTTONS = [0, 1, 4, 2];
-
- var currentButtons = 0;
-
- var FIREFOX_LINUX = /Linux.*Firefox\//i;
-
- var HAS_BUTTONS = (function() {
- // firefox on linux returns spec-incorrect values for mouseup.buttons
- // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.buttons#See_also
- // https://codereview.chromium.org/727593003/#msg16
- if (FIREFOX_LINUX.test(navigator.userAgent)) {
- return false;
- }
- try {
- return new MouseEvent('test', {buttons: 1}).buttons === 1;
- } catch (e) {
- return false;
- }
- })();
-
- // handler block for native mouse events
- var mouseEvents = {
- POINTER_ID: 1,
- POINTER_TYPE: 'mouse',
- events: [
- 'mousedown',
- 'mousemove',
- 'mouseup'
- ],
- exposes: [
- 'down',
- 'up',
- 'move'
- ],
- register: function(target) {
- dispatcher.listen(target, this.events);
- },
- unregister: function(target) {
- if (target.nodeType === Node.DOCUMENT_NODE) {
- return;
- }
- dispatcher.unlisten(target, this.events);
- },
- lastTouches: [],
- // collide with the global mouse listener
- isEventSimulatedFromTouch: function(inEvent) {
- var lts = this.lastTouches;
- var x = inEvent.clientX, y = inEvent.clientY;
- for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
- // simulated mouse events will be swallowed near a primary touchend
- var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
- if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) {
- return true;
- }
- }
- },
- prepareEvent: function(inEvent) {
- var e = dispatcher.cloneEvent(inEvent);
- e.pointerId = this.POINTER_ID;
- e.isPrimary = true;
- e.pointerType = this.POINTER_TYPE;
- e._source = 'mouse';
- if (!HAS_BUTTONS) {
- var type = inEvent.type;
- var bit = WHICH_TO_BUTTONS[inEvent.which] || 0;
- if (type === 'mousedown') {
- currentButtons |= bit;
- } else if (type === 'mouseup') {
- currentButtons &= ~bit;
- }
- e.buttons = currentButtons;
- }
- return e;
- },
- mousedown: function(inEvent) {
- if (!this.isEventSimulatedFromTouch(inEvent)) {
- var p = pointermap.has(this.POINTER_ID);
- var e = this.prepareEvent(inEvent);
- e.target = scope.findTarget(inEvent);
- pointermap.set(this.POINTER_ID, e.target);
- dispatcher.down(e);
- }
- },
- mousemove: function(inEvent) {
- if (!this.isEventSimulatedFromTouch(inEvent)) {
- var target = pointermap.get(this.POINTER_ID);
- if (target) {
- var e = this.prepareEvent(inEvent);
- e.target = target;
- // handle case where we missed a mouseup
- if ((HAS_BUTTONS ? e.buttons : e.which) === 0) {
- if (!HAS_BUTTONS) {
- currentButtons = e.buttons = 0;
- }
- dispatcher.cancel(e);
- this.cleanupMouse(e.buttons);
- } else {
- dispatcher.move(e);
- }
- }
- }
- },
- mouseup: function(inEvent) {
- if (!this.isEventSimulatedFromTouch(inEvent)) {
- var e = this.prepareEvent(inEvent);
- e.relatedTarget = scope.findTarget(inEvent);
- e.target = pointermap.get(this.POINTER_ID);
- dispatcher.up(e);
- this.cleanupMouse(e.buttons);
- }
- },
- cleanupMouse: function(buttons) {
- if (buttons === 0) {
- pointermap.delete(this.POINTER_ID);
- }
- }
- };
-
- scope.mouseEvents = mouseEvents;
-})(window.PolymerGestures);
-
-(function(scope) {
- var dispatcher = scope.dispatcher;
- var allShadows = scope.targetFinding.allShadows.bind(scope.targetFinding);
- var pointermap = dispatcher.pointermap;
- var touchMap = Array.prototype.map.call.bind(Array.prototype.map);
- // This should be long enough to ignore compat mouse events made by touch
- var DEDUP_TIMEOUT = 2500;
- var DEDUP_DIST = 25;
- var CLICK_COUNT_TIMEOUT = 200;
- var HYSTERESIS = 20;
- var ATTRIB = 'touch-action';
- // TODO(dfreedm): disable until http://crbug.com/399765 is resolved
- // var HAS_TOUCH_ACTION = ATTRIB in document.head.style;
- var HAS_TOUCH_ACTION = false;
-
- // handler block for native touch events
- var touchEvents = {
- IS_IOS: false,
- events: [
- 'touchstart',
- 'touchmove',
- 'touchend',
- 'touchcancel'
- ],
- exposes: [
- 'down',
- 'up',
- 'move'
- ],
- register: function(target, initial) {
- if (this.IS_IOS ? initial : !initial) {
- dispatcher.listen(target, this.events);
- }
- },
- unregister: function(target) {
- if (!this.IS_IOS) {
- dispatcher.unlisten(target, this.events);
- }
- },
- scrollTypes: {
- EMITTER: 'none',
- XSCROLLER: 'pan-x',
- YSCROLLER: 'pan-y',
- },
- touchActionToScrollType: function(touchAction) {
- var t = touchAction;
- var st = this.scrollTypes;
- if (t === st.EMITTER) {
- return 'none';
- } else if (t === st.XSCROLLER) {
- return 'X';
- } else if (t === st.YSCROLLER) {
- return 'Y';
- } else {
- return 'XY';
- }
- },
- POINTER_TYPE: 'touch',
- firstTouch: null,
- isPrimaryTouch: function(inTouch) {
- return this.firstTouch === inTouch.identifier;
- },
- setPrimaryTouch: function(inTouch) {
- // set primary touch if there no pointers, or the only pointer is the mouse
- if (pointermap.pointers() === 0 || (pointermap.pointers() === 1 && pointermap.has(1))) {
- this.firstTouch = inTouch.identifier;
- this.firstXY = {X: inTouch.clientX, Y: inTouch.clientY};
- this.firstTarget = inTouch.target;
- this.scrolling = null;
- this.cancelResetClickCount();
- }
- },
- removePrimaryPointer: function(inPointer) {
- if (inPointer.isPrimary) {
- this.firstTouch = null;
- this.firstXY = null;
- this.resetClickCount();
- }
- },
- clickCount: 0,
- resetId: null,
- resetClickCount: function() {
- var fn = function() {
- this.clickCount = 0;
- this.resetId = null;
- }.bind(this);
- this.resetId = setTimeout(fn, CLICK_COUNT_TIMEOUT);
- },
- cancelResetClickCount: function() {
- if (this.resetId) {
- clearTimeout(this.resetId);
- }
- },
- typeToButtons: function(type) {
- var ret = 0;
- if (type === 'touchstart' || type === 'touchmove') {
- ret = 1;
- }
- return ret;
- },
- findTarget: function(touch, id) {
- if (this.currentTouchEvent.type === 'touchstart') {
- if (this.isPrimaryTouch(touch)) {
- var fastPath = {
- clientX: touch.clientX,
- clientY: touch.clientY,
- path: this.currentTouchEvent.path,
- target: this.currentTouchEvent.target
- };
- return scope.findTarget(fastPath);
- } else {
- return scope.findTarget(touch);
- }
- }
- // reuse target we found in touchstart
- return pointermap.get(id);
- },
- touchToPointer: function(inTouch) {
- var cte = this.currentTouchEvent;
- var e = dispatcher.cloneEvent(inTouch);
- // Spec specifies that pointerId 1 is reserved for Mouse.
- // Touch identifiers can start at 0.
- // Add 2 to the touch identifier for compatibility.
- var id = e.pointerId = inTouch.identifier + 2;
- e.target = this.findTarget(inTouch, id);
- e.bubbles = true;
- e.cancelable = true;
- e.detail = this.clickCount;
- e.buttons = this.typeToButtons(cte.type);
- e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0;
- e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0;
- e.pressure = inTouch.webkitForce || inTouch.force || 0.5;
- e.isPrimary = this.isPrimaryTouch(inTouch);
- e.pointerType = this.POINTER_TYPE;
- e._source = 'touch';
- // forward touch preventDefaults
- var self = this;
- e.preventDefault = function() {
- self.scrolling = false;
- self.firstXY = null;
- cte.preventDefault();
- };
- return e;
- },
- processTouches: function(inEvent, inFunction) {
- var tl = inEvent.changedTouches;
- this.currentTouchEvent = inEvent;
- for (var i = 0, t, p; i < tl.length; i++) {
- t = tl[i];
- p = this.touchToPointer(t);
- if (inEvent.type === 'touchstart') {
- pointermap.set(p.pointerId, p.target);
- }
- if (pointermap.has(p.pointerId)) {
- inFunction.call(this, p);
- }
- if (inEvent.type === 'touchend' || inEvent._cancel) {
- this.cleanUpPointer(p);
- }
- }
- },
- // For single axis scrollers, determines whether the element should emit
- // pointer events or behave as a scroller
- shouldScroll: function(inEvent) {
- if (this.firstXY) {
- var ret;
- var touchAction = scope.targetFinding.findTouchAction(inEvent);
- var scrollAxis = this.touchActionToScrollType(touchAction);
- if (scrollAxis === 'none') {
- // this element is a touch-action: none, should never scroll
- ret = false;
- } else if (scrollAxis === 'XY') {
- // this element should always scroll
- ret = true;
- } else {
- var t = inEvent.changedTouches[0];
- // check the intended scroll axis, and other axis
- var a = scrollAxis;
- var oa = scrollAxis === 'Y' ? 'X' : 'Y';
- var da = Math.abs(t['client' + a] - this.firstXY[a]);
- var doa = Math.abs(t['client' + oa] - this.firstXY[oa]);
- // if delta in the scroll axis > delta other axis, scroll instead of
- // making events
- ret = da >= doa;
- }
- return ret;
- }
- },
- findTouch: function(inTL, inId) {
- for (var i = 0, l = inTL.length, t; i < l && (t = inTL[i]); i++) {
- if (t.identifier === inId) {
- return true;
- }
- }
- },
- // In some instances, a touchstart can happen without a touchend. This
- // leaves the pointermap in a broken state.
- // Therefore, on every touchstart, we remove the touches that did not fire a
- // touchend event.
- // To keep state globally consistent, we fire a
- // pointercancel for this "abandoned" touch
- vacuumTouches: function(inEvent) {
- var tl = inEvent.touches;
- // pointermap.pointers() should be < tl.length here, as the touchstart has not
- // been processed yet.
- if (pointermap.pointers() >= tl.length) {
- var d = [];
- pointermap.forEach(function(value, key) {
- // Never remove pointerId == 1, which is mouse.
- // Touch identifiers are 2 smaller than their pointerId, which is the
- // index in pointermap.
- if (key !== 1 && !this.findTouch(tl, key - 2)) {
- var p = value;
- d.push(p);
- }
- }, this);
- d.forEach(function(p) {
- this.cancel(p);
- pointermap.delete(p.pointerId);
- }, this);
- }
- },
- touchstart: function(inEvent) {
- this.vacuumTouches(inEvent);
- this.setPrimaryTouch(inEvent.changedTouches[0]);
- this.dedupSynthMouse(inEvent);
- if (!this.scrolling) {
- this.clickCount++;
- this.processTouches(inEvent, this.down);
- }
- },
- down: function(inPointer) {
- dispatcher.down(inPointer);
- },
- touchmove: function(inEvent) {
- if (HAS_TOUCH_ACTION) {
- // touchevent.cancelable == false is sent when the page is scrolling under native Touch Action in Chrome 36
- // https://groups.google.com/a/chromium.org/d/msg/input-dev/wHnyukcYBcA/b9kmtwM1jJQJ
- if (inEvent.cancelable) {
- this.processTouches(inEvent, this.move);
- }
- } else {
- if (!this.scrolling) {
- if (this.scrolling === null && this.shouldScroll(inEvent)) {
- this.scrolling = true;
- } else {
- this.scrolling = false;
- inEvent.preventDefault();
- this.processTouches(inEvent, this.move);
- }
- } else if (this.firstXY) {
- var t = inEvent.changedTouches[0];
- var dx = t.clientX - this.firstXY.X;
- var dy = t.clientY - this.firstXY.Y;
- var dd = Math.sqrt(dx * dx + dy * dy);
- if (dd >= HYSTERESIS) {
- this.touchcancel(inEvent);
- this.scrolling = true;
- this.firstXY = null;
- }
- }
- }
- },
- move: function(inPointer) {
- dispatcher.move(inPointer);
- },
- touchend: function(inEvent) {
- this.dedupSynthMouse(inEvent);
- this.processTouches(inEvent, this.up);
- },
- up: function(inPointer) {
- inPointer.relatedTarget = scope.findTarget(inPointer);
- dispatcher.up(inPointer);
- },
- cancel: function(inPointer) {
- dispatcher.cancel(inPointer);
- },
- touchcancel: function(inEvent) {
- inEvent._cancel = true;
- this.processTouches(inEvent, this.cancel);
- },
- cleanUpPointer: function(inPointer) {
- pointermap['delete'](inPointer.pointerId);
- this.removePrimaryPointer(inPointer);
- },
- // prevent synth mouse events from creating pointer events
- dedupSynthMouse: function(inEvent) {
- var lts = scope.mouseEvents.lastTouches;
- var t = inEvent.changedTouches[0];
- // only the primary finger will synth mouse events
- if (this.isPrimaryTouch(t)) {
- // remember x/y of last touch
- var lt = {x: t.clientX, y: t.clientY};
- lts.push(lt);
- var fn = (function(lts, lt){
- var i = lts.indexOf(lt);
- if (i > -1) {
- lts.splice(i, 1);
- }
- }).bind(null, lts, lt);
- setTimeout(fn, DEDUP_TIMEOUT);
- }
- }
- };
-
- // prevent "ghost clicks" that come from elements that were removed in a touch handler
- var STOP_PROP_FN = Event.prototype.stopImmediatePropagation || Event.prototype.stopPropagation;
- document.addEventListener('click', function(ev) {
- var x = ev.clientX, y = ev.clientY;
- // check if a click is within DEDUP_DIST px radius of the touchstart
- var closeTo = function(touch) {
- var dx = Math.abs(x - touch.x), dy = Math.abs(y - touch.y);
- return (dx <= DEDUP_DIST && dy <= DEDUP_DIST);
- };
- // if click coordinates are close to touch coordinates, assume the click came from a touch
- var wasTouched = scope.mouseEvents.lastTouches.some(closeTo);
- // if the click came from touch, and the touchstart target is not in the path of the click event,
- // then the touchstart target was probably removed, and the click should be "busted"
- var path = scope.targetFinding.path(ev);
- if (wasTouched) {
- for (var i = 0; i < path.length; i++) {
- if (path[i] === touchEvents.firstTarget) {
- return;
- }
- }
- ev.preventDefault();
- STOP_PROP_FN.call(ev);
- }
- }, true);
-
- scope.touchEvents = touchEvents;
-})(window.PolymerGestures);
-
-(function(scope) {
- var dispatcher = scope.dispatcher;
- var pointermap = dispatcher.pointermap;
- var HAS_BITMAP_TYPE = window.MSPointerEvent && typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE === 'number';
- var msEvents = {
- events: [
- 'MSPointerDown',
- 'MSPointerMove',
- 'MSPointerUp',
- 'MSPointerCancel',
- ],
- register: function(target) {
- dispatcher.listen(target, this.events);
- },
- unregister: function(target) {
- if (target.nodeType === Node.DOCUMENT_NODE) {
- return;
- }
- dispatcher.unlisten(target, this.events);
- },
- POINTER_TYPES: [
- '',
- 'unavailable',
- 'touch',
- 'pen',
- 'mouse'
- ],
- prepareEvent: function(inEvent) {
- var e = inEvent;
- e = dispatcher.cloneEvent(inEvent);
- if (HAS_BITMAP_TYPE) {
- e.pointerType = this.POINTER_TYPES[inEvent.pointerType];
- }
- e._source = 'ms';
- return e;
- },
- cleanup: function(id) {
- pointermap['delete'](id);
- },
- MSPointerDown: function(inEvent) {
- var e = this.prepareEvent(inEvent);
- e.target = scope.findTarget(inEvent);
- pointermap.set(inEvent.pointerId, e.target);
- dispatcher.down(e);
- },
- MSPointerMove: function(inEvent) {
- 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);
- e.relatedTarget = scope.findTarget(inEvent);
- e.target = pointermap.get(e.pointerId);
- dispatcher.up(e);
- this.cleanup(inEvent.pointerId);
- },
- MSPointerCancel: function(inEvent) {
- var e = this.prepareEvent(inEvent);
- e.relatedTarget = scope.findTarget(inEvent);
- e.target = pointermap.get(e.pointerId);
- dispatcher.cancel(e);
- this.cleanup(inEvent.pointerId);
- }
- };
-
- scope.msEvents = msEvents;
-})(window.PolymerGestures);
-
-(function(scope) {
- var dispatcher = scope.dispatcher;
- var pointermap = dispatcher.pointermap;
- var pointerEvents = {
- events: [
- 'pointerdown',
- 'pointermove',
- 'pointerup',
- 'pointercancel'
- ],
- prepareEvent: function(inEvent) {
- var e = dispatcher.cloneEvent(inEvent);
- e._source = 'pointer';
- return e;
- },
- register: function(target) {
- dispatcher.listen(target, this.events);
- },
- unregister: function(target) {
- if (target.nodeType === Node.DOCUMENT_NODE) {
- return;
- }
- dispatcher.unlisten(target, this.events);
- },
- cleanup: function(id) {
- pointermap['delete'](id);
- },
- pointerdown: function(inEvent) {
- var e = this.prepareEvent(inEvent);
- e.target = scope.findTarget(inEvent);
- pointermap.set(e.pointerId, e.target);
- dispatcher.down(e);
- },
- pointermove: function(inEvent) {
- 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);
- e.relatedTarget = scope.findTarget(inEvent);
- e.target = pointermap.get(e.pointerId);
- dispatcher.up(e);
- this.cleanup(inEvent.pointerId);
- },
- pointercancel: function(inEvent) {
- var e = this.prepareEvent(inEvent);
- e.relatedTarget = scope.findTarget(inEvent);
- e.target = pointermap.get(e.pointerId);
- dispatcher.cancel(e);
- this.cleanup(inEvent.pointerId);
- }
- };
-
- scope.pointerEvents = pointerEvents;
-})(window.PolymerGestures);
-
-/**
- * This module contains the handlers for native platform events.
- * From here, the dispatcher is called to create unified pointer events.
- * Included are touch events (v1), mouse events, and MSPointerEvents.
- */
-(function(scope) {
-
- var dispatcher = scope.dispatcher;
- var nav = window.navigator;
-
- if (window.PointerEvent) {
- dispatcher.registerSource('pointer', scope.pointerEvents);
- } else if (nav.msPointerEnabled) {
- dispatcher.registerSource('ms', scope.msEvents);
- } else {
- dispatcher.registerSource('mouse', scope.mouseEvents);
- if (window.ontouchstart !== undefined) {
- dispatcher.registerSource('touch', scope.touchEvents);
- }
- }
-
- // Work around iOS bugs https://bugs.webkit.org/show_bug.cgi?id=135628 and https://bugs.webkit.org/show_bug.cgi?id=136506
- var ua = navigator.userAgent;
- var IS_IOS = ua.match(/iPad|iPhone|iPod/) && 'ontouchstart' in window;
-
- dispatcher.IS_IOS = IS_IOS;
- scope.touchEvents.IS_IOS = IS_IOS;
-
- dispatcher.register(document, true);
-})(window.PolymerGestures);
-
-/**
- * This event denotes the beginning of a series of tracking events.
- *
- * @module PointerGestures
- * @submodule Events
- * @class trackstart
- */
-/**
- * Pixels moved in the x direction since trackstart.
- * @type Number
- * @property dx
- */
-/**
- * Pixes moved in the y direction since trackstart.
- * @type Number
- * @property dy
- */
-/**
- * Pixels moved in the x direction since the last track.
- * @type Number
- * @property ddx
- */
-/**
- * Pixles moved in the y direction since the last track.
- * @type Number
- * @property ddy
- */
-/**
- * The clientX position of the track gesture.
- * @type Number
- * @property clientX
- */
-/**
- * The clientY position of the track gesture.
- * @type Number
- * @property clientY
- */
-/**
- * The pageX position of the track gesture.
- * @type Number
- * @property pageX
- */
-/**
- * The pageY position of the track gesture.
- * @type Number
- * @property pageY
- */
-/**
- * The screenX position of the track gesture.
- * @type Number
- * @property screenX
- */
-/**
- * The screenY position of the track gesture.
- * @type Number
- * @property screenY
- */
-/**
- * The last x axis direction of the pointer.
- * @type Number
- * @property xDirection
- */
-/**
- * The last y axis direction of the pointer.
- * @type Number
- * @property yDirection
- */
-/**
- * A shared object between all tracking events.
- * @type Object
- * @property trackInfo
- */
-/**
- * The element currently under the pointer.
- * @type Element
- * @property relatedTarget
- */
-/**
- * The type of pointer that make the track gesture.
- * @type String
- * @property pointerType
- */
-/**
- *
- * This event fires for all pointer movement being tracked.
- *
- * @class track
- * @extends trackstart
- */
-/**
- * This event fires when the pointer is no longer being tracked.
- *
- * @class trackend
- * @extends trackstart
- */
-
- (function(scope) {
- var dispatcher = scope.dispatcher;
- var eventFactory = scope.eventFactory;
- var pointermap = new scope.PointerMap();
- var track = {
- events: [
- 'down',
- 'move',
- 'up',
- ],
- exposes: [
- 'trackstart',
- 'track',
- 'trackx',
- 'tracky',
- 'trackend'
- ],
- defaultActions: {
- 'track': 'none',
- 'trackx': 'pan-y',
- 'tracky': 'pan-x'
- },
- WIGGLE_THRESHOLD: 4,
- clampDir: function(inDelta) {
- return inDelta > 0 ? 1 : -1;
- },
- calcPositionDelta: function(inA, inB) {
- var x = 0, y = 0;
- if (inA && inB) {
- x = inB.pageX - inA.pageX;
- y = inB.pageY - inA.pageY;
- }
- return {x: x, y: y};
- },
- fireTrack: function(inType, inEvent, inTrackingData) {
- var t = inTrackingData;
- var d = this.calcPositionDelta(t.downEvent, inEvent);
- var dd = this.calcPositionDelta(t.lastMoveEvent, inEvent);
- if (dd.x) {
- t.xDirection = this.clampDir(dd.x);
- } else if (inType === 'trackx') {
- return;
- }
- if (dd.y) {
- t.yDirection = this.clampDir(dd.y);
- } else if (inType === 'tracky') {
- return;
- }
- var gestureProto = {
- bubbles: true,
- cancelable: true,
- trackInfo: t.trackInfo,
- relatedTarget: inEvent.relatedTarget,
- pointerType: inEvent.pointerType,
- pointerId: inEvent.pointerId,
- _source: 'track'
- };
- if (inType !== 'tracky') {
- gestureProto.x = inEvent.x;
- gestureProto.dx = d.x;
- gestureProto.ddx = dd.x;
- gestureProto.clientX = inEvent.clientX;
- gestureProto.pageX = inEvent.pageX;
- gestureProto.screenX = inEvent.screenX;
- gestureProto.xDirection = t.xDirection;
- }
- if (inType !== 'trackx') {
- gestureProto.dy = d.y;
- gestureProto.ddy = dd.y;
- gestureProto.y = inEvent.y;
- gestureProto.clientY = inEvent.clientY;
- gestureProto.pageY = inEvent.pageY;
- gestureProto.screenY = inEvent.screenY;
- gestureProto.yDirection = t.yDirection;
- }
- var e = eventFactory.makeGestureEvent(inType, gestureProto);
- t.downTarget.dispatchEvent(e);
- },
- down: function(inEvent) {
- if (inEvent.isPrimary && (inEvent.pointerType === 'mouse' ? inEvent.buttons === 1 : true)) {
- var p = {
- downEvent: inEvent,
- downTarget: inEvent.target,
- trackInfo: {},
- lastMoveEvent: null,
- xDirection: 0,
- yDirection: 0,
- tracking: false
- };
- pointermap.set(inEvent.pointerId, p);
- }
- },
- move: function(inEvent) {
- var p = pointermap.get(inEvent.pointerId);
- if (p) {
- if (!p.tracking) {
- var d = this.calcPositionDelta(p.downEvent, inEvent);
- var move = d.x * d.x + d.y * d.y;
- // start tracking only if finger moves more than WIGGLE_THRESHOLD
- if (move > this.WIGGLE_THRESHOLD) {
- p.tracking = true;
- p.lastMoveEvent = p.downEvent;
- this.fireTrack('trackstart', inEvent, p);
- }
- }
- if (p.tracking) {
- this.fireTrack('track', inEvent, p);
- this.fireTrack('trackx', inEvent, p);
- this.fireTrack('tracky', inEvent, p);
- }
- p.lastMoveEvent = inEvent;
- }
- },
- up: function(inEvent) {
- var p = pointermap.get(inEvent.pointerId);
- if (p) {
- if (p.tracking) {
- this.fireTrack('trackend', inEvent, p);
- }
- pointermap.delete(inEvent.pointerId);
- }
- }
- };
- dispatcher.registerGesture('track', track);
- })(window.PolymerGestures);
-
-/**
- * This event is fired when a pointer is held down for 200ms.
- *
- * @module PointerGestures
- * @submodule Events
- * @class hold
- */
-/**
- * Type of pointer that made the holding event.
- * @type String
- * @property pointerType
- */
-/**
- * Screen X axis position of the held pointer
- * @type Number
- * @property clientX
- */
-/**
- * Screen Y axis position of the held pointer
- * @type Number
- * @property clientY
- */
-/**
- * Type of pointer that made the holding event.
- * @type String
- * @property pointerType
- */
-/**
- * This event is fired every 200ms while a pointer is held down.
- *
- * @class holdpulse
- * @extends hold
- */
-/**
- * Milliseconds pointer has been held down.
- * @type Number
- * @property holdTime
- */
-/**
- * This event is fired when a held pointer is released or moved.
- *
- * @class release
- */
-
-(function(scope) {
- var dispatcher = scope.dispatcher;
- var eventFactory = scope.eventFactory;
- var hold = {
- // wait at least HOLD_DELAY ms between hold and pulse events
- HOLD_DELAY: 200,
- // pointer can move WIGGLE_THRESHOLD pixels before not counting as a hold
- WIGGLE_THRESHOLD: 16,
- events: [
- 'down',
- 'move',
- 'up',
- ],
- exposes: [
- 'hold',
- 'holdpulse',
- 'release'
- ],
- heldPointer: null,
- holdJob: null,
- pulse: function() {
- var hold = Date.now() - this.heldPointer.timeStamp;
- var type = this.held ? 'holdpulse' : 'hold';
- this.fireHold(type, hold);
- this.held = true;
- },
- cancel: function() {
- clearInterval(this.holdJob);
- if (this.held) {
- this.fireHold('release');
- }
- this.held = false;
- this.heldPointer = null;
- this.target = null;
- this.holdJob = null;
- },
- down: function(inEvent) {
- if (inEvent.isPrimary && !this.heldPointer) {
- this.heldPointer = inEvent;
- this.target = inEvent.target;
- this.holdJob = setInterval(this.pulse.bind(this), this.HOLD_DELAY);
- }
- },
- up: function(inEvent) {
- if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) {
- this.cancel();
- }
- },
- move: function(inEvent) {
- if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) {
- var x = inEvent.clientX - this.heldPointer.clientX;
- var y = inEvent.clientY - this.heldPointer.clientY;
- if ((x * x + y * y) > this.WIGGLE_THRESHOLD) {
- this.cancel();
- }
- }
- },
- fireHold: function(inType, inHoldTime) {
- var p = {
- bubbles: true,
- cancelable: true,
- pointerType: this.heldPointer.pointerType,
- pointerId: this.heldPointer.pointerId,
- x: this.heldPointer.clientX,
- y: this.heldPointer.clientY,
- _source: 'hold'
- };
- if (inHoldTime) {
- p.holdTime = inHoldTime;
- }
- var e = eventFactory.makeGestureEvent(inType, p);
- this.target.dispatchEvent(e);
- }
- };
- dispatcher.registerGesture('hold', hold);
-})(window.PolymerGestures);
-
-/**
- * This event is fired when a pointer quickly goes down and up, and is used to
- * denote activation.
- *
- * Any gesture event can prevent the tap event from being created by calling
- * `event.preventTap`.
- *
- * Any pointer event can prevent the tap by setting the `tapPrevented` property
- * on itself.
- *
- * @module PointerGestures
- * @submodule Events
- * @class tap
- */
-/**
- * X axis position of the tap.
- * @property x
- * @type Number
- */
-/**
- * Y axis position of the tap.
- * @property y
- * @type Number
- */
-/**
- * Type of the pointer that made the tap.
- * @property pointerType
- * @type String
- */
-(function(scope) {
- var dispatcher = scope.dispatcher;
- var eventFactory = scope.eventFactory;
- var pointermap = new scope.PointerMap();
- var tap = {
- events: [
- 'down',
- 'up'
- ],
- exposes: [
- 'tap'
- ],
- down: function(inEvent) {
- if (inEvent.isPrimary && !inEvent.tapPrevented) {
- pointermap.set(inEvent.pointerId, {
- target: inEvent.target,
- buttons: inEvent.buttons,
- x: inEvent.clientX,
- y: inEvent.clientY
- });
- }
- },
- shouldTap: function(e, downState) {
- var tap = true;
- if (e.pointerType === 'mouse') {
- // only allow left click to tap for mouse
- tap = (e.buttons ^ 1) && (downState.buttons & 1);
- }
- return tap && !e.tapPrevented;
- },
- up: function(inEvent) {
- var start = pointermap.get(inEvent.pointerId);
- if (start && this.shouldTap(inEvent, start)) {
- // up.relatedTarget is target currently under finger
- var t = scope.targetFinding.LCA(start.target, inEvent.relatedTarget);
- if (t) {
- var e = eventFactory.makeGestureEvent('tap', {
- bubbles: true,
- cancelable: true,
- x: inEvent.clientX,
- y: inEvent.clientY,
- detail: inEvent.detail,
- pointerType: inEvent.pointerType,
- pointerId: inEvent.pointerId,
- altKey: inEvent.altKey,
- ctrlKey: inEvent.ctrlKey,
- metaKey: inEvent.metaKey,
- shiftKey: inEvent.shiftKey,
- _source: 'tap'
- });
- t.dispatchEvent(e);
- }
- }
- pointermap.delete(inEvent.pointerId);
- }
- };
- // patch eventFactory to remove id from tap's pointermap for preventTap calls
- eventFactory.preventTap = function(e) {
- return function() {
- e.tapPrevented = true;
- pointermap.delete(e.pointerId);
- };
- };
- dispatcher.registerGesture('tap', tap);
-})(window.PolymerGestures);
-
-/*
- * Basic strategy: find the farthest apart points, use as diameter of circle
- * react to size change and rotation of the chord
- */
-
-/**
- * @module pointer-gestures
- * @submodule Events
- * @class pinch
- */
-/**
- * Scale of the pinch zoom gesture
- * @property scale
- * @type Number
- */
-/**
- * Center X position of pointers causing pinch
- * @property centerX
- * @type Number
- */
-/**
- * Center Y position of pointers causing pinch
- * @property centerY
- * @type Number
- */
-
-/**
- * @module pointer-gestures
- * @submodule Events
- * @class rotate
- */
-/**
- * Angle (in degrees) of rotation. Measured from starting positions of pointers.
- * @property angle
- * @type Number
- */
-/**
- * Center X position of pointers causing rotation
- * @property centerX
- * @type Number
- */
-/**
- * Center Y position of pointers causing rotation
- * @property centerY
- * @type Number
- */
-(function(scope) {
- var dispatcher = scope.dispatcher;
- var eventFactory = scope.eventFactory;
- var pointermap = new scope.PointerMap();
- var RAD_TO_DEG = 180 / Math.PI;
- var pinch = {
- events: [
- 'down',
- 'up',
- 'move',
- 'cancel'
- ],
- exposes: [
- 'pinchstart',
- 'pinch',
- 'pinchend',
- 'rotate'
- ],
- defaultActions: {
- 'pinch': 'none',
- 'rotate': 'none'
- },
- reference: {},
- down: function(inEvent) {
- pointermap.set(inEvent.pointerId, inEvent);
- if (pointermap.pointers() == 2) {
- var points = this.calcChord();
- var angle = this.calcAngle(points);
- this.reference = {
- angle: angle,
- diameter: points.diameter,
- target: scope.targetFinding.LCA(points.a.target, points.b.target)
- };
-
- this.firePinch('pinchstart', points.diameter, points);
- }
- },
- up: function(inEvent) {
- var p = pointermap.get(inEvent.pointerId);
- var num = pointermap.pointers();
- if (p) {
- if (num === 2) {
- // fire 'pinchend' before deleting pointer
- var points = this.calcChord();
- this.firePinch('pinchend', points.diameter, points);
- }
- pointermap.delete(inEvent.pointerId);
- }
- },
- move: function(inEvent) {
- if (pointermap.has(inEvent.pointerId)) {
- pointermap.set(inEvent.pointerId, inEvent);
- if (pointermap.pointers() > 1) {
- this.calcPinchRotate();
- }
- }
- },
- cancel: function(inEvent) {
- this.up(inEvent);
- },
- firePinch: function(type, diameter, points) {
- var zoom = diameter / this.reference.diameter;
- var e = eventFactory.makeGestureEvent(type, {
- bubbles: true,
- cancelable: true,
- scale: zoom,
- centerX: points.center.x,
- centerY: points.center.y,
- _source: 'pinch'
- });
- this.reference.target.dispatchEvent(e);
- },
- fireRotate: function(angle, points) {
- var diff = Math.round((angle - this.reference.angle) % 360);
- var e = eventFactory.makeGestureEvent('rotate', {
- bubbles: true,
- cancelable: true,
- angle: diff,
- centerX: points.center.x,
- centerY: points.center.y,
- _source: 'pinch'
- });
- this.reference.target.dispatchEvent(e);
- },
- calcPinchRotate: function() {
- var points = this.calcChord();
- var diameter = points.diameter;
- var angle = this.calcAngle(points);
- if (diameter != this.reference.diameter) {
- this.firePinch('pinch', diameter, points);
- }
- if (angle != this.reference.angle) {
- this.fireRotate(angle, points);
- }
- },
- calcChord: function() {
- var pointers = [];
- pointermap.forEach(function(p) {
- pointers.push(p);
- });
- var dist = 0;
- // start with at least two pointers
- var points = {a: pointers[0], b: pointers[1]};
- var x, y, d;
- for (var i = 0; i < pointers.length; i++) {
- var a = pointers[i];
- for (var j = i + 1; j < pointers.length; j++) {
- var b = pointers[j];
- x = Math.abs(a.clientX - b.clientX);
- y = Math.abs(a.clientY - b.clientY);
- d = x + y;
- if (d > dist) {
- dist = d;
- points = {a: a, b: b};
- }
- }
- }
- x = Math.abs(points.a.clientX + points.b.clientX) / 2;
- y = Math.abs(points.a.clientY + points.b.clientY) / 2;
- points.center = { x: x, y: y };
- points.diameter = dist;
- return points;
- },
- calcAngle: function(points) {
- var x = points.a.clientX - points.b.clientX;
- var y = points.a.clientY - points.b.clientY;
- return (360 + Math.atan2(y, x) * RAD_TO_DEG) % 360;
- }
- };
- dispatcher.registerGesture('pinch', pinch);
-})(window.PolymerGestures);
-
-(function (global) {
- 'use strict';
-
- var Token,
- TokenName,
- Syntax,
- Messages,
- source,
- index,
- length,
- delegate,
- lookahead,
- state;
-
- Token = {
- BooleanLiteral: 1,
- EOF: 2,
- Identifier: 3,
- Keyword: 4,
- NullLiteral: 5,
- NumericLiteral: 6,
- Punctuator: 7,
- StringLiteral: 8
- };
-
- TokenName = {};
- TokenName[Token.BooleanLiteral] = 'Boolean';
- TokenName[Token.EOF] = '<end>';
- TokenName[Token.Identifier] = 'Identifier';
- TokenName[Token.Keyword] = 'Keyword';
- TokenName[Token.NullLiteral] = 'Null';
- TokenName[Token.NumericLiteral] = 'Numeric';
- TokenName[Token.Punctuator] = 'Punctuator';
- TokenName[Token.StringLiteral] = 'String';
-
- Syntax = {
- ArrayExpression: 'ArrayExpression',
- BinaryExpression: 'BinaryExpression',
- CallExpression: 'CallExpression',
- ConditionalExpression: 'ConditionalExpression',
- EmptyStatement: 'EmptyStatement',
- ExpressionStatement: 'ExpressionStatement',
- Identifier: 'Identifier',
- Literal: 'Literal',
- LabeledStatement: 'LabeledStatement',
- LogicalExpression: 'LogicalExpression',
- MemberExpression: 'MemberExpression',
- ObjectExpression: 'ObjectExpression',
- Program: 'Program',
- Property: 'Property',
- ThisExpression: 'ThisExpression',
- UnaryExpression: 'UnaryExpression'
- };
-
- // Error messages should be identical to V8.
- Messages = {
- UnexpectedToken: 'Unexpected token %0',
- UnknownLabel: 'Undefined label \'%0\'',
- Redeclaration: '%0 \'%1\' has already been declared'
- };
-
- // Ensure the condition is true, otherwise throw an error.
- // This is only to have a better contract semantic, i.e. another safety net
- // to catch a logic error. The condition shall be fulfilled in normal case.
- // Do NOT use this to enforce a certain condition on any user input.
-
- function assert(condition, message) {
- if (!condition) {
- throw new Error('ASSERT: ' + message);
- }
- }
-
- function isDecimalDigit(ch) {
- return (ch >= 48 && ch <= 57); // 0..9
- }
-
-
- // 7.2 White Space
-
- function isWhiteSpace(ch) {
- return (ch === 32) || // space
- (ch === 9) || // tab
- (ch === 0xB) ||
- (ch === 0xC) ||
- (ch === 0xA0) ||
- (ch >= 0x1680 && '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(String.fromCharCode(ch)) > 0);
- }
-
- // 7.3 Line Terminators
-
- function isLineTerminator(ch) {
- return (ch === 10) || (ch === 13) || (ch === 0x2028) || (ch === 0x2029);
- }
-
- // 7.6 Identifier Names and Identifiers
-
- function isIdentifierStart(ch) {
- return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore)
- (ch >= 65 && ch <= 90) || // A..Z
- (ch >= 97 && ch <= 122); // a..z
- }
-
- function isIdentifierPart(ch) {
- return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore)
- (ch >= 65 && ch <= 90) || // A..Z
- (ch >= 97 && ch <= 122) || // a..z
- (ch >= 48 && ch <= 57); // 0..9
- }
-
- // 7.6.1.1 Keywords
-
- function isKeyword(id) {
- return (id === 'this')
- }
-
- // 7.4 Comments
-
- function skipWhitespace() {
- while (index < length && isWhiteSpace(source.charCodeAt(index))) {
- ++index;
- }
- }
-
- function getIdentifier() {
- var start, ch;
-
- start = index++;
- while (index < length) {
- ch = source.charCodeAt(index);
- if (isIdentifierPart(ch)) {
- ++index;
- } else {
- break;
- }
- }
-
- return source.slice(start, index);
- }
-
- function scanIdentifier() {
- var start, id, type;
-
- start = index;
-
- id = getIdentifier();
-
- // There is no keyword or literal with only one character.
- // Thus, it must be an identifier.
- if (id.length === 1) {
- type = Token.Identifier;
- } else if (isKeyword(id)) {
- type = Token.Keyword;
- } else if (id === 'null') {
- type = Token.NullLiteral;
- } else if (id === 'true' || id === 'false') {
- type = Token.BooleanLiteral;
- } else {
- type = Token.Identifier;
- }
-
- return {
- type: type,
- value: id,
- range: [start, index]
- };
- }
-
-
- // 7.7 Punctuators
-
- function scanPunctuator() {
- var start = index,
- code = source.charCodeAt(index),
- code2,
- ch1 = source[index],
- ch2;
-
- switch (code) {
-
- // Check for most common single-character punctuators.
- case 46: // . dot
- case 40: // ( open bracket
- case 41: // ) close bracket
- case 59: // ; semicolon
- case 44: // , comma
- case 123: // { open curly brace
- case 125: // } close curly brace
- case 91: // [
- case 93: // ]
- case 58: // :
- case 63: // ?
- ++index;
- return {
- type: Token.Punctuator,
- value: String.fromCharCode(code),
- range: [start, index]
- };
-
- default:
- code2 = source.charCodeAt(index + 1);
-
- // '=' (char #61) marks an assignment or comparison operator.
- if (code2 === 61) {
- switch (code) {
- case 37: // %
- case 38: // &
- case 42: // *:
- case 43: // +
- case 45: // -
- case 47: // /
- case 60: // <
- case 62: // >
- case 124: // |
- index += 2;
- return {
- type: Token.Punctuator,
- value: String.fromCharCode(code) + String.fromCharCode(code2),
- range: [start, index]
- };
-
- case 33: // !
- case 61: // =
- index += 2;
-
- // !== and ===
- if (source.charCodeAt(index) === 61) {
- ++index;
- }
- return {
- type: Token.Punctuator,
- value: source.slice(start, index),
- range: [start, index]
- };
- default:
- break;
- }
- }
- break;
- }
-
- // Peek more characters.
-
- ch2 = source[index + 1];
-
- // Other 2-character punctuators: && ||
-
- if (ch1 === ch2 && ('&|'.indexOf(ch1) >= 0)) {
- index += 2;
- return {
- type: Token.Punctuator,
- value: ch1 + ch2,
- range: [start, index]
- };
- }
-
- if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
- ++index;
- return {
- type: Token.Punctuator,
- value: ch1,
- range: [start, index]
- };
- }
-
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
-
- // 7.8.3 Numeric Literals
- function scanNumericLiteral() {
- var number, start, ch;
-
- ch = source[index];
- assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'),
- 'Numeric literal must start with a decimal digit or a decimal point');
-
- start = index;
- number = '';
- if (ch !== '.') {
- number = source[index++];
- ch = source[index];
-
- // Hex number starts with '0x'.
- // Octal number starts with '0'.
- if (number === '0') {
- // decimal number starts with '0' such as '09' is illegal.
- if (ch && isDecimalDigit(ch.charCodeAt(0))) {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
- }
-
- while (isDecimalDigit(source.charCodeAt(index))) {
- number += source[index++];
- }
- ch = source[index];
- }
-
- if (ch === '.') {
- number += source[index++];
- while (isDecimalDigit(source.charCodeAt(index))) {
- number += source[index++];
- }
- ch = source[index];
- }
-
- if (ch === 'e' || ch === 'E') {
- number += source[index++];
-
- ch = source[index];
- if (ch === '+' || ch === '-') {
- number += source[index++];
- }
- if (isDecimalDigit(source.charCodeAt(index))) {
- while (isDecimalDigit(source.charCodeAt(index))) {
- number += source[index++];
- }
- } else {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
- }
-
- if (isIdentifierStart(source.charCodeAt(index))) {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
-
- return {
- type: Token.NumericLiteral,
- value: parseFloat(number),
- range: [start, index]
- };
- }
-
- // 7.8.4 String Literals
-
- function scanStringLiteral() {
- var str = '', quote, start, ch, octal = false;
-
- quote = source[index];
- assert((quote === '\'' || quote === '"'),
- 'String literal must starts with a quote');
-
- start = index;
- ++index;
-
- while (index < length) {
- ch = source[index++];
-
- if (ch === quote) {
- quote = '';
- break;
- } else if (ch === '\\') {
- ch = source[index++];
- if (!ch || !isLineTerminator(ch.charCodeAt(0))) {
- switch (ch) {
- case 'n':
- str += '\n';
- break;
- case 'r':
- str += '\r';
- break;
- case 't':
- str += '\t';
- break;
- case 'b':
- str += '\b';
- break;
- case 'f':
- str += '\f';
- break;
- case 'v':
- str += '\x0B';
- break;
-
- default:
- str += ch;
- break;
- }
- } else {
- if (ch === '\r' && source[index] === '\n') {
- ++index;
- }
- }
- } else if (isLineTerminator(ch.charCodeAt(0))) {
- break;
- } else {
- str += ch;
- }
- }
-
- if (quote !== '') {
- throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
- }
-
- return {
- type: Token.StringLiteral,
- value: str,
- octal: octal,
- range: [start, index]
- };
- }
-
- function isIdentifierName(token) {
- return token.type === Token.Identifier ||
- token.type === Token.Keyword ||
- token.type === Token.BooleanLiteral ||
- token.type === Token.NullLiteral;
- }
-
- function advance() {
- var ch;
-
- skipWhitespace();
-
- if (index >= length) {
- return {
- type: Token.EOF,
- range: [index, index]
- };
- }
-
- ch = source.charCodeAt(index);
-
- // Very common: ( and ) and ;
- if (ch === 40 || ch === 41 || ch === 58) {
- return scanPunctuator();
- }
-
- // String literal starts with single quote (#39) or double quote (#34).
- if (ch === 39 || ch === 34) {
- return scanStringLiteral();
- }
-
- if (isIdentifierStart(ch)) {
- return scanIdentifier();
- }
-
- // Dot (.) char #46 can also start a floating-point number, hence the need
- // to check the next character.
- if (ch === 46) {
- if (isDecimalDigit(source.charCodeAt(index + 1))) {
- return scanNumericLiteral();
- }
- return scanPunctuator();
- }
-
- if (isDecimalDigit(ch)) {
- return scanNumericLiteral();
- }
-
- return scanPunctuator();
- }
-
- function lex() {
- var token;
-
- token = lookahead;
- index = token.range[1];
-
- lookahead = advance();
-
- index = token.range[1];
-
- return token;
- }
-
- function peek() {
- var pos;
-
- pos = index;
- lookahead = advance();
- index = pos;
- }
-
- // Throw an exception
-
- function throwError(token, messageFormat) {
- var error,
- args = Array.prototype.slice.call(arguments, 2),
- msg = messageFormat.replace(
- /%(\d)/g,
- function (whole, index) {
- assert(index < args.length, 'Message reference must be in range');
- return args[index];
- }
- );
-
- error = new Error(msg);
- error.index = index;
- error.description = msg;
- throw error;
- }
-
- // Throw an exception because of the token.
-
- function throwUnexpected(token) {
- throwError(token, Messages.UnexpectedToken, token.value);
- }
-
- // Expect the next token to match the specified punctuator.
- // If not, an exception will be thrown.
-
- function expect(value) {
- var token = lex();
- if (token.type !== Token.Punctuator || token.value !== value) {
- throwUnexpected(token);
- }
- }
-
- // Return true if the next token matches the specified punctuator.
-
- function match(value) {
- return lookahead.type === Token.Punctuator && lookahead.value === value;
- }
-
- // Return true if the next token matches the specified keyword
-
- function matchKeyword(keyword) {
- return lookahead.type === Token.Keyword && lookahead.value === keyword;
- }
-
- function consumeSemicolon() {
- // Catch the very common case first: immediately a semicolon (char #59).
- if (source.charCodeAt(index) === 59) {
- lex();
- return;
- }
-
- skipWhitespace();
-
- if (match(';')) {
- lex();
- return;
- }
-
- if (lookahead.type !== Token.EOF && !match('}')) {
- throwUnexpected(lookahead);
- }
- }
-
- // 11.1.4 Array Initialiser
-
- function parseArrayInitialiser() {
- var elements = [];
-
- expect('[');
-
- while (!match(']')) {
- if (match(',')) {
- lex();
- elements.push(null);
- } else {
- elements.push(parseExpression());
-
- if (!match(']')) {
- expect(',');
- }
- }
- }
-
- expect(']');
-
- return delegate.createArrayExpression(elements);
- }
-
- // 11.1.5 Object Initialiser
-
- function parseObjectPropertyKey() {
- var token;
-
- skipWhitespace();
- token = lex();
-
- // Note: This function is called only from parseObjectProperty(), where
- // EOF and Punctuator tokens are already filtered out.
- if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) {
- return delegate.createLiteral(token);
- }
-
- return delegate.createIdentifier(token.value);
- }
-
- function parseObjectProperty() {
- var token, key;
-
- token = lookahead;
- skipWhitespace();
-
- if (token.type === Token.EOF || token.type === Token.Punctuator) {
- throwUnexpected(token);
- }
-
- key = parseObjectPropertyKey();
- expect(':');
- return delegate.createProperty('init', key, parseExpression());
- }
-
- function parseObjectInitialiser() {
- var properties = [];
-
- expect('{');
-
- while (!match('}')) {
- properties.push(parseObjectProperty());
-
- if (!match('}')) {
- expect(',');
- }
- }
-
- expect('}');
-
- return delegate.createObjectExpression(properties);
- }
-
- // 11.1.6 The Grouping Operator
-
- function parseGroupExpression() {
- var expr;
-
- expect('(');
-
- expr = parseExpression();
-
- expect(')');
-
- return expr;
- }
-
-
- // 11.1 Primary Expressions
-
- function parsePrimaryExpression() {
- var type, token, expr;
-
- if (match('(')) {
- return parseGroupExpression();
- }
-
- type = lookahead.type;
-
- if (type === Token.Identifier) {
- expr = delegate.createIdentifier(lex().value);
- } else if (type === Token.StringLiteral || type === Token.NumericLiteral) {
- expr = delegate.createLiteral(lex());
- } else if (type === Token.Keyword) {
- if (matchKeyword('this')) {
- lex();
- expr = delegate.createThisExpression();
- }
- } else if (type === Token.BooleanLiteral) {
- token = lex();
- token.value = (token.value === 'true');
- expr = delegate.createLiteral(token);
- } else if (type === Token.NullLiteral) {
- token = lex();
- token.value = null;
- expr = delegate.createLiteral(token);
- } else if (match('[')) {
- expr = parseArrayInitialiser();
- } else if (match('{')) {
- expr = parseObjectInitialiser();
- }
-
- if (expr) {
- return expr;
- }
-
- throwUnexpected(lex());
- }
-
- // 11.2 Left-Hand-Side Expressions
-
- function parseArguments() {
- var args = [];
-
- expect('(');
-
- if (!match(')')) {
- while (index < length) {
- args.push(parseExpression());
- if (match(')')) {
- break;
- }
- expect(',');
- }
- }
-
- expect(')');
-
- return args;
- }
-
- function parseNonComputedProperty() {
- var token;
-
- token = lex();
-
- if (!isIdentifierName(token)) {
- throwUnexpected(token);
- }
-
- return delegate.createIdentifier(token.value);
- }
-
- function parseNonComputedMember() {
- expect('.');
-
- return parseNonComputedProperty();
- }
-
- function parseComputedMember() {
- var expr;
-
- expect('[');
-
- expr = parseExpression();
-
- expect(']');
-
- return expr;
- }
-
- function parseLeftHandSideExpression() {
- var expr, args, property;
-
- expr = parsePrimaryExpression();
-
- while (true) {
- if (match('[')) {
- property = parseComputedMember();
- expr = delegate.createMemberExpression('[', expr, property);
- } else if (match('.')) {
- property = parseNonComputedMember();
- expr = delegate.createMemberExpression('.', expr, property);
- } else if (match('(')) {
- args = parseArguments();
- expr = delegate.createCallExpression(expr, args);
- } else {
- break;
- }
- }
-
- return expr;
- }
-
- // 11.3 Postfix Expressions
-
- var parsePostfixExpression = parseLeftHandSideExpression;
-
- // 11.4 Unary Operators
-
- function parseUnaryExpression() {
- var token, expr;
-
- if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) {
- expr = parsePostfixExpression();
- } else if (match('+') || match('-') || match('!')) {
- token = lex();
- expr = parseUnaryExpression();
- expr = delegate.createUnaryExpression(token.value, expr);
- } else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
- throwError({}, Messages.UnexpectedToken);
- } else {
- expr = parsePostfixExpression();
- }
-
- return expr;
- }
-
- function binaryPrecedence(token) {
- var prec = 0;
-
- if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
- return 0;
- }
-
- switch (token.value) {
- case '||':
- prec = 1;
- break;
-
- case '&&':
- prec = 2;
- break;
-
- case '==':
- case '!=':
- case '===':
- case '!==':
- prec = 6;
- break;
-
- case '<':
- case '>':
- case '<=':
- case '>=':
- case 'instanceof':
- prec = 7;
- break;
-
- case 'in':
- prec = 7;
- break;
-
- case '+':
- case '-':
- prec = 9;
- break;
-
- case '*':
- case '/':
- case '%':
- prec = 11;
- break;
-
- default:
- break;
- }
-
- return prec;
- }
-
- // 11.5 Multiplicative Operators
- // 11.6 Additive Operators
- // 11.7 Bitwise Shift Operators
- // 11.8 Relational Operators
- // 11.9 Equality Operators
- // 11.10 Binary Bitwise Operators
- // 11.11 Binary Logical Operators
-
- function parseBinaryExpression() {
- var expr, token, prec, stack, right, operator, left, i;
-
- left = parseUnaryExpression();
-
- token = lookahead;
- prec = binaryPrecedence(token);
- if (prec === 0) {
- return left;
- }
- token.prec = prec;
- lex();
-
- right = parseUnaryExpression();
-
- stack = [left, token, right];
-
- while ((prec = binaryPrecedence(lookahead)) > 0) {
-
- // Reduce: make a binary expression from the three topmost entries.
- while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) {
- right = stack.pop();
- operator = stack.pop().value;
- left = stack.pop();
- expr = delegate.createBinaryExpression(operator, left, right);
- stack.push(expr);
- }
-
- // Shift.
- token = lex();
- token.prec = prec;
- stack.push(token);
- expr = parseUnaryExpression();
- stack.push(expr);
- }
-
- // Final reduce to clean-up the stack.
- i = stack.length - 1;
- expr = stack[i];
- while (i > 1) {
- expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr);
- i -= 2;
- }
-
- return expr;
- }
-
-
- // 11.12 Conditional Operator
-
- function parseConditionalExpression() {
- var expr, consequent, alternate;
-
- expr = parseBinaryExpression();
-
- if (match('?')) {
- lex();
- consequent = parseConditionalExpression();
- expect(':');
- alternate = parseConditionalExpression();
-
- expr = delegate.createConditionalExpression(expr, consequent, alternate);
- }
-
- return expr;
- }
-
- // Simplification since we do not support AssignmentExpression.
- var parseExpression = parseConditionalExpression;
-
- // Polymer Syntax extensions
-
- // Filter ::
- // Identifier
- // Identifier "(" ")"
- // Identifier "(" FilterArguments ")"
-
- function parseFilter() {
- var identifier, args;
-
- identifier = lex();
-
- if (identifier.type !== Token.Identifier) {
- throwUnexpected(identifier);
- }
-
- args = match('(') ? parseArguments() : [];
-
- return delegate.createFilter(identifier.value, args);
- }
-
- // Filters ::
- // "|" Filter
- // Filters "|" Filter
-
- function parseFilters() {
- while (match('|')) {
- lex();
- parseFilter();
- }
- }
-
- // TopLevel ::
- // LabelledExpressions
- // AsExpression
- // InExpression
- // FilterExpression
-
- // AsExpression ::
- // FilterExpression as Identifier
-
- // InExpression ::
- // Identifier, Identifier in FilterExpression
- // Identifier in FilterExpression
-
- // FilterExpression ::
- // Expression
- // Expression Filters
-
- function parseTopLevel() {
- skipWhitespace();
- peek();
-
- var expr = parseExpression();
- if (expr) {
- if (lookahead.value === ',' || lookahead.value == 'in' &&
- expr.type === Syntax.Identifier) {
- parseInExpression(expr);
- } else {
- parseFilters();
- if (lookahead.value === 'as') {
- parseAsExpression(expr);
- } else {
- delegate.createTopLevel(expr);
- }
- }
- }
-
- if (lookahead.type !== Token.EOF) {
- throwUnexpected(lookahead);
- }
- }
-
- function parseAsExpression(expr) {
- lex(); // as
- var identifier = lex().value;
- delegate.createAsExpression(expr, identifier);
- }
-
- function parseInExpression(identifier) {
- var indexName;
- if (lookahead.value === ',') {
- lex();
- if (lookahead.type !== Token.Identifier)
- throwUnexpected(lookahead);
- indexName = lex().value;
- }
-
- lex(); // in
- var expr = parseExpression();
- parseFilters();
- delegate.createInExpression(identifier.name, indexName, expr);
- }
-
- function parse(code, inDelegate) {
- delegate = inDelegate;
- source = code;
- index = 0;
- length = source.length;
- lookahead = null;
- state = {
- labelSet: {}
- };
-
- return parseTopLevel();
- }
-
- global.esprima = {
- parse: parse
- };
-})(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 prepareBinding(expressionText, name, node, filterRegistry) {
- var expression;
- try {
- expression = getExpression(expressionText);
- if (expression.scopeIdent &&
- (node.nodeType !== Node.ELEMENT_NODE ||
- node.tagName !== 'TEMPLATE' ||
- (name !== 'bind' && name !== 'repeat'))) {
- throw Error('as and in can only be used within <template bind/repeat>');
- }
- } catch (ex) {
- console.error('Invalid expression syntax: ' + expressionText, ex);
- return;
- }
-
- return function(model, node, oneTime) {
- var binding = expression.getBinding(model, filterRegistry, oneTime);
- if (expression.scopeIdent && binding) {
- node.polymerExpressionScopeIdent_ = expression.scopeIdent;
- if (expression.indexIdent)
- node.polymerExpressionIndexIdent_ = expression.indexIdent;
- }
-
- return binding;
- }
- }
-
- // TODO(rafaelw): Implement simple LRU.
- var expressionParseCache = Object.create(null);
-
- function getExpression(expressionText) {
- var expression = expressionParseCache[expressionText];
- if (!expression) {
- var delegate = new ASTDelegate();
- esprima.parse(expressionText, delegate);
- expression = new Expression(delegate);
- expressionParseCache[expressionText] = expression;
- }
- return expression;
- }
-
- function Literal(value) {
- this.value = value;
- this.valueFn_ = undefined;
- }
-
- Literal.prototype = {
- valueFn: function() {
- if (!this.valueFn_) {
- var value = this.value;
- this.valueFn_ = function() {
- return value;
- }
- }
-
- return this.valueFn_;
- }
- }
-
- function IdentPath(name) {
- this.name = name;
- this.path = Path.get(name);
- }
-
- IdentPath.prototype = {
- valueFn: function() {
- if (!this.valueFn_) {
- var name = this.name;
- var path = this.path;
- this.valueFn_ = function(model, observer) {
- if (observer)
- observer.addPath(model, path);
-
- return path.getValueFrom(model);
- }
- }
-
- return this.valueFn_;
- },
-
- setValue: function(model, newValue) {
- if (this.path.length == 1)
- model = findScope(model, this.path[0]);
-
- return this.path.setValueFrom(model, newValue);
- }
- };
-
- function MemberExpression(object, property, accessor) {
- this.computed = accessor == '[';
-
- this.dynamicDeps = typeof object == 'function' ||
- object.dynamicDeps ||
- (this.computed && !(property instanceof Literal));
-
- this.simplePath =
- !this.dynamicDeps &&
- (property instanceof IdentPath || property instanceof Literal) &&
- (object instanceof MemberExpression || object instanceof IdentPath);
-
- this.object = this.simplePath ? object : getFn(object);
- this.property = !this.computed || this.simplePath ?
- property : getFn(property);
- }
-
- MemberExpression.prototype = {
- get fullPath() {
- if (!this.fullPath_) {
-
- var parts = this.object instanceof MemberExpression ?
- this.object.fullPath.slice() : [this.object.name];
- parts.push(this.property instanceof IdentPath ?
- this.property.name : this.property.value);
- this.fullPath_ = Path.get(parts);
- }
-
- return this.fullPath_;
- },
-
- valueFn: function() {
- if (!this.valueFn_) {
- var object = this.object;
-
- if (this.simplePath) {
- var path = this.fullPath;
-
- this.valueFn_ = function(model, observer) {
- if (observer)
- observer.addPath(model, path);
-
- return path.getValueFrom(model);
- };
- } else if (!this.computed) {
- var path = Path.get(this.property.name);
-
- this.valueFn_ = function(model, observer, filterRegistry) {
- var context = object(model, observer, filterRegistry);
-
- if (observer)
- observer.addPath(context, path);
-
- return path.getValueFrom(context);
- }
- } else {
- // Computed property.
- var property = this.property;
-
- this.valueFn_ = function(model, observer, filterRegistry) {
- var context = object(model, observer, filterRegistry);
- var propName = property(model, observer, filterRegistry);
- if (observer)
- observer.addPath(context, [propName]);
-
- return context ? context[propName] : undefined;
- };
- }
- }
- return this.valueFn_;
- },
-
- setValue: function(model, newValue) {
- if (this.simplePath) {
- this.fullPath.setValueFrom(model, newValue);
- return newValue;
- }
-
- var object = this.object(model);
- var propName = this.property instanceof IdentPath ? this.property.name :
- this.property(model);
- return object[propName] = newValue;
- }
- };
-
- function Filter(name, args) {
- this.name = name;
- this.args = [];
- for (var i = 0; i < args.length; i++) {
- this.args[i] = getFn(args[i]);
- }
- }
-
- Filter.prototype = {
- transform: function(model, observer, filterRegistry, toModelDirection,
- initialArgs) {
- var context = model;
- var fn = context[this.name];
-
- if (!fn) {
- fn = filterRegistry[this.name];
- if (!fn) {
- console.error('Cannot find function or filter: ' + this.name);
- return;
- }
- }
-
- // If toModelDirection is falsey, then the "normal" (dom-bound) direction
- // is used. Otherwise, it looks for a 'toModel' property function on the
- // object.
- if (toModelDirection) {
- fn = fn.toModel;
- } else if (typeof fn.toDOM == 'function') {
- fn = fn.toDOM;
- }
-
- if (typeof fn != 'function') {
- console.error('Cannot find function or filter: ' + this.name);
- return;
- }
-
- var args = initialArgs || [];
- for (var i = 0; i < this.args.length; i++) {
- args.push(getFn(this.args[i])(model, observer, filterRegistry));
- }
-
- return fn.apply(context, args);
- }
- };
-
- function notImplemented() { throw Error('Not Implemented'); }
-
- var unaryOperators = {
- '+': function(v) { return +v; },
- '-': function(v) { return -v; },
- '!': function(v) { return !v; }
- };
-
- var binaryOperators = {
- '+': function(l, r) { return l+r; },
- '-': function(l, r) { return l-r; },
- '*': function(l, r) { return l*r; },
- '/': function(l, r) { return l/r; },
- '%': function(l, r) { return l%r; },
- '<': function(l, r) { return l<r; },
- '>': function(l, r) { return l>r; },
- '<=': function(l, r) { return l<=r; },
- '>=': function(l, r) { return l>=r; },
- '==': function(l, r) { return l==r; },
- '!=': function(l, r) { return l!=r; },
- '===': function(l, r) { return l===r; },
- '!==': function(l, r) { return l!==r; },
- '&&': function(l, r) { return l&&r; },
- '||': function(l, r) { return l||r; },
- };
-
- function getFn(arg) {
- return typeof arg == 'function' ? arg : arg.valueFn();
- }
-
- function ASTDelegate() {
- this.expression = null;
- this.filters = [];
- this.deps = {};
- this.currentPath = undefined;
- this.scopeIdent = undefined;
- this.indexIdent = undefined;
- this.dynamicDeps = false;
- }
-
- ASTDelegate.prototype = {
- createUnaryExpression: function(op, argument) {
- if (!unaryOperators[op])
- throw Error('Disallowed operator: ' + op);
-
- argument = getFn(argument);
-
- return function(model, observer, filterRegistry) {
- return unaryOperators[op](argument(model, observer, filterRegistry));
- };
- },
-
- createBinaryExpression: function(op, left, right) {
- if (!binaryOperators[op])
- throw Error('Disallowed operator: ' + op);
-
- left = getFn(left);
- right = getFn(right);
-
- switch (op) {
- case '||':
- this.dynamicDeps = true;
- return function(model, observer, filterRegistry) {
- return left(model, observer, filterRegistry) ||
- right(model, observer, filterRegistry);
- };
- case '&&':
- this.dynamicDeps = true;
- return function(model, observer, filterRegistry) {
- return left(model, observer, filterRegistry) &&
- right(model, observer, filterRegistry);
- };
- }
-
- return function(model, observer, filterRegistry) {
- return binaryOperators[op](left(model, observer, filterRegistry),
- right(model, observer, filterRegistry));
- };
- },
-
- createConditionalExpression: function(test, consequent, alternate) {
- test = getFn(test);
- consequent = getFn(consequent);
- alternate = getFn(alternate);
-
- this.dynamicDeps = true;
-
- return function(model, observer, filterRegistry) {
- return test(model, observer, filterRegistry) ?
- consequent(model, observer, filterRegistry) :
- alternate(model, observer, filterRegistry);
- }
- },
-
- createIdentifier: function(name) {
- var ident = new IdentPath(name);
- ident.type = 'Identifier';
- return ident;
- },
-
- createMemberExpression: function(accessor, object, property) {
- var ex = new MemberExpression(object, property, accessor);
- if (ex.dynamicDeps)
- this.dynamicDeps = true;
- return ex;
- },
-
- createCallExpression: function(expression, args) {
- if (!(expression instanceof IdentPath))
- throw Error('Only identifier function invocations are allowed');
-
- var filter = new Filter(expression.name, args);
-
- return function(model, observer, filterRegistry) {
- return filter.transform(model, observer, filterRegistry, false);
- };
- },
-
- createLiteral: function(token) {
- return new Literal(token.value);
- },
-
- createArrayExpression: function(elements) {
- for (var i = 0; i < elements.length; i++)
- elements[i] = getFn(elements[i]);
-
- return function(model, observer, filterRegistry) {
- var arr = []
- for (var i = 0; i < elements.length; i++)
- arr.push(elements[i](model, observer, filterRegistry));
- return arr;
- }
- },
-
- createProperty: function(kind, key, value) {
- return {
- key: key instanceof IdentPath ? key.name : key.value,
- value: value
- };
- },
-
- createObjectExpression: function(properties) {
- for (var i = 0; i < properties.length; i++)
- properties[i].value = getFn(properties[i].value);
-
- return function(model, observer, filterRegistry) {
- var obj = {};
- for (var i = 0; i < properties.length; i++)
- obj[properties[i].key] =
- properties[i].value(model, observer, filterRegistry);
- return obj;
- }
- },
-
- createFilter: function(name, args) {
- this.filters.push(new Filter(name, args));
- },
-
- createAsExpression: function(expression, scopeIdent) {
- this.expression = expression;
- this.scopeIdent = scopeIdent;
- },
-
- createInExpression: function(scopeIdent, indexIdent, expression) {
- this.expression = expression;
- this.scopeIdent = scopeIdent;
- this.indexIdent = indexIdent;
- },
-
- createTopLevel: function(expression) {
- this.expression = expression;
- },
-
- createThisExpression: notImplemented
- }
-
- function ConstantObservable(value) {
- this.value_ = value;
- }
-
- ConstantObservable.prototype = {
- open: function() { return this.value_; },
- discardChanges: function() { return this.value_; },
- deliver: function() {},
- close: function() {},
- }
-
- function Expression(delegate) {
- this.scopeIdent = delegate.scopeIdent;
- this.indexIdent = delegate.indexIdent;
-
- if (!delegate.expression)
- throw Error('No expression found.');
-
- this.expression = delegate.expression;
- getFn(this.expression); // forces enumeration of path dependencies
-
- this.filters = delegate.filters;
- this.dynamicDeps = delegate.dynamicDeps;
- }
-
- Expression.prototype = {
- getBinding: function(model, filterRegistry, oneTime) {
- if (oneTime)
- return this.getValue(model, undefined, filterRegistry);
-
- var observer = new CompoundObserver();
- // captures deps.
- var firstValue = this.getValue(model, observer, filterRegistry);
- var firstTime = true;
- var self = this;
-
- function valueFn() {
- // deps cannot have changed on first value retrieval.
- if (firstTime) {
- firstTime = false;
- return firstValue;
- }
-
- if (self.dynamicDeps)
- observer.startReset();
-
- var value = self.getValue(model,
- self.dynamicDeps ? observer : undefined,
- filterRegistry);
- if (self.dynamicDeps)
- observer.finishReset();
-
- return value;
- }
-
- function setValueFn(newValue) {
- self.setValue(model, newValue, filterRegistry);
- return newValue;
- }
-
- return new ObserverTransform(observer, valueFn, setValueFn, true);
- },
-
- getValue: function(model, observer, filterRegistry) {
- var value = getFn(this.expression)(model, observer, filterRegistry);
- for (var i = 0; i < this.filters.length; i++) {
- value = this.filters[i].transform(model, observer, filterRegistry,
- false, [value]);
- }
-
- return value;
- },
-
- setValue: function(model, newValue, filterRegistry) {
- var count = this.filters ? this.filters.length : 0;
- while (count-- > 0) {
- newValue = this.filters[count].transform(model, undefined,
- filterRegistry, true, [newValue]);
- }
-
- if (this.expression.setValue)
- return this.expression.setValue(model, newValue);
- }
- }
-
- /**
- * Converts a style property name to a css property name. For example:
- * "WebkitUserSelect" to "-webkit-user-select"
- */
- function convertStylePropertyName(name) {
- return String(name).replace(/[A-Z]/g, function(c) {
- return '-' + c.toLowerCase();
- });
- }
-
- var parentScopeName = '@' + Math.random().toString(36).slice(2);
-
- // Single ident paths must bind directly to the appropriate scope object.
- // I.e. Pushed values in two-bindings need to be assigned to the actual model
- // object.
- function findScope(model, prop) {
- while (model[parentScopeName] &&
- !Object.prototype.hasOwnProperty.call(model, prop)) {
- model = model[parentScopeName];
- }
-
- return model;
- }
-
- function isLiteralExpression(pathString) {
- switch (pathString) {
- case '':
- return false;
-
- case 'false':
- case 'null':
- case 'true':
- return true;
- }
-
- if (!isNaN(Number(pathString)))
- return true;
-
- return false;
- };
-
- function PolymerExpressions() {}
-
- PolymerExpressions.prototype = {
- // "built-in" filters
- styleObject: function(value) {
- var parts = [];
- for (var key in value) {
- parts.push(convertStylePropertyName(key) + ': ' + value[key]);
- }
- return parts.join('; ');
- },
-
- tokenList: function(value) {
- var tokens = [];
- for (var key in value) {
- if (value[key])
- tokens.push(key);
- }
- return tokens.join(' ');
- },
-
- // binding delegate API
- prepareInstancePositionChanged: function(template) {
- var indexIdent = template.polymerExpressionIndexIdent_;
- if (!indexIdent)
- return;
-
- return function(templateInstance, index) {
- templateInstance.model[indexIdent] = index;
- };
- },
-
- prepareBinding: function(pathString, name, node) {
- var path = Path.get(pathString);
-
- if (!isLiteralExpression(pathString) && path.valid) {
- if (path.length == 1) {
- return function(model, node, oneTime) {
- if (oneTime)
- return path.getValueFrom(model);
-
- var scope = findScope(model, path[0]);
- return new PathObserver(scope, path);
- };
- }
- return; // bail out early if pathString is simple path.
- }
-
- return prepareBinding(pathString, name, node, this);
- },
-
- prepareInstanceModel: function(template) {
- var scopeName = template.polymerExpressionScopeIdent_;
- if (!scopeName)
- return;
-
- var parentScope = template.templateInstance ?
- template.templateInstance.model :
- template.model;
-
- var indexName = template.polymerExpressionIndexIdent_;
-
- return function(model) {
- return createScopeObject(parentScope, model, scopeName, indexName);
- };
- }
- };
-
- var createScopeObject = ('__proto__' in {}) ?
- function(parentScope, model, scopeName, indexName) {
- var scope = {};
- scope[scopeName] = model;
- scope[indexName] = undefined;
- scope[parentScopeName] = parentScope;
- scope.__proto__ = parentScope;
- return scope;
- } :
- function(parentScope, model, scopeName, indexName) {
- var scope = Object.create(parentScope);
- Object.defineProperty(scope, scopeName,
- { value: model, configurable: true, writable: true });
- Object.defineProperty(scope, indexName,
- { value: undefined, configurable: true, writable: true });
- Object.defineProperty(scope, parentScopeName,
- { value: parentScope, configurable: true, writable: true });
- return scope;
- };
-
- global.PolymerExpressions = PolymerExpressions;
- PolymerExpressions.getExpression = getExpression;
-})(this);
-
-Polymer = {
- version: '0.5.5'
-};
-
-// TODO(sorvell): this ensures Polymer is an object and not a function
-// Platform is currently defining it as a function to allow for async loading
-// of polymer; once we refine the loading process this likely goes away.
-if (typeof window.Polymer === 'function') {
- Polymer = {};
-}
-
-
-(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);
-
-/*
- Build only script.
-
- Ensures scripts needed for basic x-platform compatibility
- will be run when platform.js is not loaded.
- */
-if (!window.WebComponents) {
-
-/*
- On supported platforms, platform.js is not needed. To retain compatibility
- with the polyfills, we stub out minimal functionality.
- */
-if (!window.WebComponents) {
-
- WebComponents = {
- flush: function() {},
- flags: {log: {}}
- };
-
- Platform = WebComponents;
-
- CustomElements = {
- useNative: true,
- ready: true,
- takeRecords: function() {},
- instanceof: function(obj, base) {
- return obj instanceof base;
- }
- };
-
- HTMLImports = {
- useNative: true
- };
-
-
- addEventListener('HTMLImportsLoaded', function() {
- document.dispatchEvent(
- new CustomEvent('WebComponentsReady', {bubbles: true})
- );
- });
-
-
- // ShadowDOM
- ShadowDOMPolyfill = null;
- wrap = unwrap = function(n){
- return n;
- };
-
-}
-
-/*
- Create polyfill scope and feature detect native support.
-*/
-window.HTMLImports = window.HTMLImports || {flags:{}};
-
-(function(scope) {
-
-/**
- Basic setup and simple module executer. We collect modules and then execute
- the code later, only if it's necessary for polyfilling.
-*/
-var IMPORT_LINK_TYPE = 'import';
-var useNative = Boolean(IMPORT_LINK_TYPE in document.createElement('link'));
-
-/**
- Support `currentScript` on all browsers as `document._currentScript.`
-
- 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.
-*/
-// NOTE: ShadowDOMPolyfill intrusion.
-var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill);
-var wrap = function(node) {
- return hasShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) : node;
-};
-var rootDocument = wrap(document);
-
-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(rootDocument, '_currentScript', currentScriptDescriptor);
-
-/**
- Add support for the `HTMLImportsLoaded` event and the `HTMLImports.whenReady`
- method. This api is necessary because unlike the native implementation,
- script elements do not force imports to resolve. Instead, users should wrap
- code in either an `HTMLImportsLoaded` hander or after load time in an
- `HTMLImports.whenReady(callback)` call.
-
- NOTE: This module also supports these apis under the native implementation.
- Therefore, if this file is loaded, the same code can be used under both
- the polyfill and native implementation.
- */
-
-var isIE = /Trident/.test(navigator.userAgent);
-
-// call a callback when all HTMLImports in the document at call time
-// (or at least document ready) 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 whenReady(callback, doc) {
- doc = doc || rootDocument;
- // 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);
- }
- };
- 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();
- }
-}
-
-// NOTE: test for native imports loading is based on explicitly watching
-// all imports (see below).
-// However, we cannot rely on this entirely without watching the entire document
-// for import links. For perf reasons, currently only head is watched.
-// Instead, we fallback to checking if the import property is available
-// and the document is not itself loading.
-function isImportLoaded(link) {
- return useNative ? link.__loaded ||
- (link.import && link.import.readyState !== 'loading') :
- 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 handleImports(nodes) {
- for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) {
- if (isImport(n)) {
- handleImport(n);
- }
- }
- }
-
- 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);
- }
- }
- })();
-
-}
-
-// 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.
-whenReady(function() {
- HTMLImports.ready = true;
- HTMLImports.readyTime = new Date().getTime();
- rootDocument.dispatchEvent(
- new CustomEvent('HTMLImportsLoaded', {bubbles: true})
- );
-});
-
-// exports
-scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
-scope.useNative = useNative;
-scope.rootDocument = rootDocument;
-scope.whenReady = whenReady;
-scope.isIE = isIE;
-
-})(HTMLImports);
-
-(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);
-
-/*
- Build only script.
-
- Ensures scripts needed for basic x-platform compatibility
- will be run when platform.js is not loaded.
- */
-}
-(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 (typeof navigator != 'undefined' && 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 && s !== '';
- }
-
- 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[i]);
- }
- },
-
- 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(){
- return function(fn) {
- return Promise.resolve().then(fn);
- }
- })() :
- (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 = {
- objects: objects,
- get rootObject() { return rootObj; },
- set rootObject(value) {
- rootObj = value;
- rootObjProps = {};
- },
- open: function(obs, object) {
- 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);
- if (lastObservedSet === this)
- lastObservedSet = null;
- },
- };
-
- return record;
- }
-
- var lastObservedSet;
-
- function getObservedSet(observer, obj) {
- if (!lastObservedSet || lastObservedSet.rootObject !== obj) {
- lastObservedSet = observedSetCache.pop() || newObservedSet();
- lastObservedSet.rootObject = 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;
-
- global.Platform = global.Platform || {};
-
- global.Platform.performMicrotaskCheckpoint = function() {
- if (runningMicrotaskCheckpoint)
- 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;
- }
-
- // Export the observe-js object for **Node.js**, with backwards-compatibility
- // for the old `require()` API. Also ensure `exports` is not a DOM Element.
- // If we're in the browser, export as a global object.
-
- var expose = global;
-
- if (typeof exports !== 'undefined' && !exports.nodeType) {
- if (typeof module !== 'undefined' && module.exports) {
- exports = module.exports;
- }
- expose = exports;
- }
-
- expose.Observer = Observer;
- expose.Observer.runEOM_ = runEOM;
- expose.Observer.observerSentinel_ = observerSentinel; // for testing.
- expose.Observer.hasObjectObserve = hasObserve;
- expose.ArrayObserver = ArrayObserver;
- expose.ArrayObserver.calculateSplices = function(current, previous) {
- return arraySplice.calculateSplices(current, previous);
- };
-
- expose.ArraySplice = ArraySplice;
- expose.ObjectObserver = ObjectObserver;
- expose.PathObserver = PathObserver;
- expose.CompoundObserver = CompoundObserver;
- expose.Path = Path;
- expose.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() {
- var isNum = property == 'value' && input.type == 'number';
- observable.setValue(isNum ? input.valueAsNumber : 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,
- 'if': 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);
-
-(function(scope) {
- 'use strict';
-
- // feature detect for URL constructor
- var hasWorkingUrl = false;
- if (!scope.forceJURL) {
- try {
- var u = new URL('b', 'http://a');
- u.pathname = 'c%20d';
- hasWorkingUrl = u.href === 'http://a/c%20d';
- } catch(e) {}
- }
-
- if (hasWorkingUrl)
- return;
-
- var relative = Object.create(null);
- relative['ftp'] = 21;
- relative['file'] = 0;
- relative['gopher'] = 70;
- relative['http'] = 80;
- relative['https'] = 443;
- relative['ws'] = 80;
- relative['wss'] = 443;
-
- var relativePathDotMapping = Object.create(null);
- relativePathDotMapping['%2e'] = '.';
- relativePathDotMapping['.%2e'] = '..';
- relativePathDotMapping['%2e.'] = '..';
- relativePathDotMapping['%2e%2e'] = '..';
-
- function isRelativeScheme(scheme) {
- return relative[scheme] !== undefined;
- }
-
- function invalid() {
- clear.call(this);
- this._isInvalid = true;
- }
-
- function IDNAToASCII(h) {
- if ('' == h) {
- invalid.call(this)
- }
- // XXX
- return h.toLowerCase()
- }
-
- function percentEscape(c) {
- var unicode = c.charCodeAt(0);
- if (unicode > 0x20 &&
- unicode < 0x7F &&
- // " # < > ? `
- [0x22, 0x23, 0x3C, 0x3E, 0x3F, 0x60].indexOf(unicode) == -1
- ) {
- return c;
- }
- return encodeURIComponent(c);
- }
-
- function percentEscapeQuery(c) {
- // XXX This actually needs to encode c using encoding and then
- // convert the bytes one-by-one.
-
- var unicode = c.charCodeAt(0);
- if (unicode > 0x20 &&
- unicode < 0x7F &&
- // " # < > ` (do not escape '?')
- [0x22, 0x23, 0x3C, 0x3E, 0x60].indexOf(unicode) == -1
- ) {
- return c;
- }
- return encodeURIComponent(c);
- }
-
- var EOF = undefined,
- ALPHA = /[a-zA-Z]/,
- ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
-
- function parse(input, stateOverride, base) {
- function err(message) {
- errors.push(message)
- }
-
- var state = stateOverride || 'scheme start',
- cursor = 0,
- buffer = '',
- seenAt = false,
- seenBracket = false,
- errors = [];
-
- loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) {
- var c = input[cursor];
- switch (state) {
- case 'scheme start':
- if (c && ALPHA.test(c)) {
- buffer += c.toLowerCase(); // ASCII-safe
- state = 'scheme';
- } else if (!stateOverride) {
- buffer = '';
- state = 'no scheme';
- continue;
- } else {
- err('Invalid scheme.');
- break loop;
- }
- break;
-
- case 'scheme':
- if (c && ALPHANUMERIC.test(c)) {
- buffer += c.toLowerCase(); // ASCII-safe
- } else if (':' == c) {
- this._scheme = buffer;
- buffer = '';
- if (stateOverride) {
- break loop;
- }
- if (isRelativeScheme(this._scheme)) {
- this._isRelative = true;
- }
- if ('file' == this._scheme) {
- state = 'relative';
- } else if (this._isRelative && base && base._scheme == this._scheme) {
- state = 'relative or authority';
- } else if (this._isRelative) {
- state = 'authority first slash';
- } else {
- state = 'scheme data';
- }
- } else if (!stateOverride) {
- buffer = '';
- cursor = 0;
- state = 'no scheme';
- continue;
- } else if (EOF == c) {
- break loop;
- } else {
- err('Code point not allowed in scheme: ' + c)
- break loop;
- }
- break;
-
- case 'scheme data':
- if ('?' == c) {
- query = '?';
- state = 'query';
- } else if ('#' == c) {
- this._fragment = '#';
- state = 'fragment';
- } else {
- // XXX error handling
- if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
- this._schemeData += percentEscape(c);
- }
- }
- break;
-
- case 'no scheme':
- if (!base || !(isRelativeScheme(base._scheme))) {
- err('Missing scheme.');
- invalid.call(this);
- } else {
- state = 'relative';
- continue;
- }
- break;
-
- case 'relative or authority':
- if ('/' == c && '/' == input[cursor+1]) {
- state = 'authority ignore slashes';
- } else {
- err('Expected /, got: ' + c);
- state = 'relative';
- continue
- }
- break;
-
- case 'relative':
- this._isRelative = true;
- if ('file' != this._scheme)
- this._scheme = base._scheme;
- if (EOF == c) {
- this._host = base._host;
- this._port = base._port;
- this._path = base._path.slice();
- this._query = base._query;
- break loop;
- } else if ('/' == c || '\\' == c) {
- if ('\\' == c)
- err('\\ is an invalid code point.');
- state = 'relative slash';
- } else if ('?' == c) {
- this._host = base._host;
- this._port = base._port;
- this._path = base._path.slice();
- this._query = '?';
- state = 'query';
- } else if ('#' == c) {
- this._host = base._host;
- this._port = base._port;
- this._path = base._path.slice();
- this._query = base._query;
- this._fragment = '#';
- state = 'fragment';
- } else {
- var nextC = input[cursor+1]
- var nextNextC = input[cursor+2]
- if (
- 'file' != this._scheme || !ALPHA.test(c) ||
- (nextC != ':' && nextC != '|') ||
- (EOF != nextNextC && '/' != nextNextC && '\\' != nextNextC && '?' != nextNextC && '#' != nextNextC)) {
- this._host = base._host;
- this._port = base._port;
- this._path = base._path.slice();
- this._path.pop();
- }
- state = 'relative path';
- continue;
- }
- break;
-
- case 'relative slash':
- if ('/' == c || '\\' == c) {
- if ('\\' == c) {
- err('\\ is an invalid code point.');
- }
- if ('file' == this._scheme) {
- state = 'file host';
- } else {
- state = 'authority ignore slashes';
- }
- } else {
- if ('file' != this._scheme) {
- this._host = base._host;
- this._port = base._port;
- }
- state = 'relative path';
- continue;
- }
- break;
-
- case 'authority first slash':
- if ('/' == c) {
- state = 'authority second slash';
- } else {
- err("Expected '/', got: " + c);
- state = 'authority ignore slashes';
- continue;
- }
- break;
-
- case 'authority second slash':
- state = 'authority ignore slashes';
- if ('/' != c) {
- err("Expected '/', got: " + c);
- continue;
- }
- break;
-
- case 'authority ignore slashes':
- if ('/' != c && '\\' != c) {
- state = 'authority';
- continue;
- } else {
- err('Expected authority, got: ' + c);
- }
- break;
-
- case 'authority':
- if ('@' == c) {
- if (seenAt) {
- err('@ already seen.');
- buffer += '%40';
- }
- seenAt = true;
- for (var i = 0; i < buffer.length; i++) {
- var cp = buffer[i];
- if ('\t' == cp || '\n' == cp || '\r' == cp) {
- err('Invalid whitespace in authority.');
- continue;
- }
- // XXX check URL code points
- if (':' == cp && null === this._password) {
- this._password = '';
- continue;
- }
- var tempC = percentEscape(cp);
- (null !== this._password) ? this._password += tempC : this._username += tempC;
- }
- buffer = '';
- } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
- cursor -= buffer.length;
- buffer = '';
- state = 'host';
- continue;
- } else {
- buffer += c;
- }
- break;
-
- case 'file host':
- if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
- if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ':' || buffer[1] == '|')) {
- state = 'relative path';
- } else if (buffer.length == 0) {
- state = 'relative path start';
- } else {
- this._host = IDNAToASCII.call(this, buffer);
- buffer = '';
- state = 'relative path start';
- }
- continue;
- } else if ('\t' == c || '\n' == c || '\r' == c) {
- err('Invalid whitespace in file host.');
- } else {
- buffer += c;
- }
- break;
-
- case 'host':
- case 'hostname':
- if (':' == c && !seenBracket) {
- // XXX host parsing
- this._host = IDNAToASCII.call(this, buffer);
- buffer = '';
- state = 'port';
- if ('hostname' == stateOverride) {
- break loop;
- }
- } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
- this._host = IDNAToASCII.call(this, buffer);
- buffer = '';
- state = 'relative path start';
- if (stateOverride) {
- break loop;
- }
- continue;
- } else if ('\t' != c && '\n' != c && '\r' != c) {
- if ('[' == c) {
- seenBracket = true;
- } else if (']' == c) {
- seenBracket = false;
- }
- buffer += c;
- } else {
- err('Invalid code point in host/hostname: ' + c);
- }
- break;
-
- case 'port':
- if (/[0-9]/.test(c)) {
- buffer += c;
- } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c || stateOverride) {
- if ('' != buffer) {
- var temp = parseInt(buffer, 10);
- if (temp != relative[this._scheme]) {
- this._port = temp + '';
- }
- buffer = '';
- }
- if (stateOverride) {
- break loop;
- }
- state = 'relative path start';
- continue;
- } else if ('\t' == c || '\n' == c || '\r' == c) {
- err('Invalid code point in port: ' + c);
- } else {
- invalid.call(this);
- }
- break;
-
- case 'relative path start':
- if ('\\' == c)
- err("'\\' not allowed in path.");
- state = 'relative path';
- if ('/' != c && '\\' != c) {
- continue;
- }
- break;
-
- case 'relative path':
- if (EOF == c || '/' == c || '\\' == c || (!stateOverride && ('?' == c || '#' == c))) {
- if ('\\' == c) {
- err('\\ not allowed in relative path.');
- }
- var tmp;
- if (tmp = relativePathDotMapping[buffer.toLowerCase()]) {
- buffer = tmp;
- }
- if ('..' == buffer) {
- this._path.pop();
- if ('/' != c && '\\' != c) {
- this._path.push('');
- }
- } else if ('.' == buffer && '/' != c && '\\' != c) {
- this._path.push('');
- } else if ('.' != buffer) {
- if ('file' == this._scheme && this._path.length == 0 && buffer.length == 2 && ALPHA.test(buffer[0]) && buffer[1] == '|') {
- buffer = buffer[0] + ':';
- }
- this._path.push(buffer);
- }
- buffer = '';
- if ('?' == c) {
- this._query = '?';
- state = 'query';
- } else if ('#' == c) {
- this._fragment = '#';
- state = 'fragment';
- }
- } else if ('\t' != c && '\n' != c && '\r' != c) {
- buffer += percentEscape(c);
- }
- break;
-
- case 'query':
- if (!stateOverride && '#' == c) {
- this._fragment = '#';
- state = 'fragment';
- } else if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
- this._query += percentEscapeQuery(c);
- }
- break;
-
- case 'fragment':
- if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
- this._fragment += c;
- }
- break;
- }
-
- cursor++;
- }
- }
-
- function clear() {
- this._scheme = '';
- this._schemeData = '';
- this._username = '';
- this._password = null;
- this._host = '';
- this._port = '';
- this._path = [];
- this._query = '';
- this._fragment = '';
- this._isInvalid = false;
- this._isRelative = false;
- }
-
- // Does not process domain names or IP addresses.
- // Does not handle encoding for the query parameter.
- function jURL(url, base /* , encoding */) {
- if (base !== undefined && !(base instanceof jURL))
- base = new jURL(String(base));
-
- this._url = url;
- clear.call(this);
-
- var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, '');
- // encoding = encoding || 'utf-8'
-
- parse.call(this, input, null, base);
- }
-
- jURL.prototype = {
- get href() {
- if (this._isInvalid)
- return this._url;
-
- var authority = '';
- if ('' != this._username || null != this._password) {
- authority = this._username +
- (null != this._password ? ':' + this._password : '') + '@';
- }
-
- return this.protocol +
- (this._isRelative ? '//' + authority + this.host : '') +
- this.pathname + this._query + this._fragment;
- },
- set href(href) {
- clear.call(this);
- parse.call(this, href);
- },
-
- get protocol() {
- return this._scheme + ':';
- },
- set protocol(protocol) {
- if (this._isInvalid)
- return;
- parse.call(this, protocol + ':', 'scheme start');
- },
-
- get host() {
- return this._isInvalid ? '' : this._port ?
- this._host + ':' + this._port : this._host;
- },
- set host(host) {
- if (this._isInvalid || !this._isRelative)
- return;
- parse.call(this, host, 'host');
- },
-
- get hostname() {
- return this._host;
- },
- set hostname(hostname) {
- if (this._isInvalid || !this._isRelative)
- return;
- parse.call(this, hostname, 'hostname');
- },
-
- get port() {
- return this._port;
- },
- set port(port) {
- if (this._isInvalid || !this._isRelative)
- return;
- parse.call(this, port, 'port');
- },
-
- get pathname() {
- return this._isInvalid ? '' : this._isRelative ?
- '/' + this._path.join('/') : this._schemeData;
- },
- set pathname(pathname) {
- if (this._isInvalid || !this._isRelative)
- return;
- this._path = [];
- parse.call(this, pathname, 'relative path start');
- },
-
- get search() {
- return this._isInvalid || !this._query || '?' == this._query ?
- '' : this._query;
- },
- set search(search) {
- if (this._isInvalid || !this._isRelative)
- return;
- this._query = '?';
- if ('?' == search[0])
- search = search.slice(1);
- parse.call(this, search, 'query');
- },
-
- get hash() {
- return this._isInvalid || !this._fragment || '#' == this._fragment ?
- '' : this._fragment;
- },
- set hash(hash) {
- if (this._isInvalid)
- return;
- this._fragment = '#';
- if ('#' == hash[0])
- hash = hash.slice(1);
- parse.call(this, hash, 'fragment');
- },
-
- get origin() {
- var host;
- if (this._isInvalid || !this._scheme) {
- return '';
- }
- // javascript: Gecko returns String(""), WebKit/Blink String("null")
- // Gecko throws error for "data://"
- // data: Gecko returns "", Blink returns "data://", WebKit returns "null"
- // Gecko returns String("") for file: mailto:
- // WebKit/Blink returns String("SCHEME://") for file: mailto:
- switch (this._scheme) {
- case 'data':
- case 'file':
- case 'javascript':
- case 'mailto':
- return 'null';
- }
- host = this.host;
- if (!host) {
- return '';
- }
- return this._scheme + '://' + host;
- }
- };
-
- // Copy over the static methods
- var OriginalURL = scope.URL;
- if (OriginalURL) {
- jURL.createObjectURL = function(blob) {
- // IE extension allows a second optional options argument.
- // http://msdn.microsoft.com/en-us/library/ie/hh772302(v=vs.85).aspx
- return OriginalURL.createObjectURL.apply(OriginalURL, arguments);
- };
- jURL.revokeObjectURL = function(url) {
- OriginalURL.revokeObjectURL(url);
- };
- }
-
- scope.URL = jURL;
-
-})(this);
-
-(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;
-// bc
-Platform.endOfMicrotask = endOfMicrotask;
-
-})(Polymer);
-
-
-(function(scope) {
-
-/**
- * @class Polymer
- */
-
-// imports
-var endOfMicrotask = scope.endOfMicrotask;
-
-// logging
-var log = window.WebComponents ? WebComponents.flags.log : {};
-
-// 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);
-
-
-/**
- * Force any pending data changes to be observed before
- * the next task. Data changes are processed asynchronously but are guaranteed
- * to be processed, for example, before painting. This method should rarely be
- * needed. It does nothing when Object.observe is available;
- * when Object.observe is not available, Polymer automatically flushes data
- * changes approximately every 1/10 second.
- * Therefore, `flush` should only be used when a data mutation should be
- * observed sooner than this.
- *
- * @method flush
- */
-// flush (with logging)
-var flushing;
-function flush() {
- if (!flushing) {
- flushing = true;
- endOfMicrotask(function() {
- flushing = false;
- log.data && console.group('flush');
- Platform.performMicrotaskCheckpoint();
- log.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();
- // watch document visiblity to toggle dirty-checking
- var visibilityHandler = function() {
- // only flush if the page is visibile
- if (document.visibilityState === 'hidden') {
- if (scope.flushPoll) {
- clearInterval(scope.flushPoll);
- }
- } else {
- scope.flushPoll = setInterval(flush, FLUSH_POLL_INTERVAL);
- }
- };
- if (typeof document.visibilityState === 'string') {
- document.addEventListener('visibilitychange', visibilityHandler);
- }
- visibilityHandler();
- });
-} 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;
-// bc
-Platform.flush = flush;
-
-})(window.Polymer);
-
-
-(function(scope) {
-
-var urlResolver = {
- resolveDom: function(root, url) {
- url = url || baseUrl(root);
- 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, baseUrl(template));
- },
- 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 || baseUrl(style);
- 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 || baseUrl(node);
- 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 = '{{.*}}';
-var URL_HASH = '#';
-
-function baseUrl(node) {
- var u = new URL(node.ownerDocument.baseURI);
- u.search = '';
- u.hash = '';
- return u;
-}
-
-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;
- }
- // do not resolve '#' links, they are used for routing
- if (url && url[0] === '#') {
- return url;
- }
- var u = new URL(url, baseUrl);
- return keepAbsolute ? u.href : makeDocumentRelPath(u.href);
-}
-
-function makeDocumentRelPath(url) {
- var root = baseUrl(document.documentElement);
- 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('..');
- }
- // empty '#' is discarded but we need to preserve it.
- var hash = (targetUrl.href.slice(-1) === URL_HASH) ? URL_HASH : targetUrl.hash;
- return t.join('/') + targetUrl.search + hash;
-}
-
-// exports
-scope.urlResolver = urlResolver;
-
-})(Polymer);
-
-(function(scope) {
- var endOfMicrotask = Polymer.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);
-
-(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);
-
-(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);
-
-(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);
-
-(function(scope) {
-
- // dom polyfill, additions, and utility methods
-
- 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);
-
-(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__;
- }
-
- // utility function to precompute name tags for functions
- // in a (unchained) prototype
- function hintSuper(prototype) {
- // tag functions with their prototype name to optimize
- // super call invocations
- for (var n in prototype) {
- var pd = Object.getOwnPropertyDescriptor(prototype, n);
- if (pd && typeof pd.value === 'function') {
- pd.value.nom = n;
- }
- }
- }
-
- // exports
-
- scope.super = $super;
-
-})(Polymer);
-
-(function(scope) {
-
- function noopHandler(value) {
- return value;
- }
-
- // helper for deserializing properties of various types to strings
- var typeHandlers = {
- string: noopHandler,
- 'undefined': noopHandler,
- date: function(value) {
- return new Date(Date.parse(value) || Date.now());
- },
- boolean: function(value) {
- if (value === '') {
- return true;
- }
- return value === 'false' ? false : !!value;
- },
- number: function(value) {
- var n = parseFloat(value);
- // hex values like "0xFFFF" parseFloat as 0
- if (n === 0) {
- n = parseInt(value);
- }
- return isNaN(n) ? value : n;
- // this code disabled because encoded values (like "0xFFFF")
- // do not round trip to their original format
- //return (String(floatVal) === value) ? floatVal : value;
- },
- object: function(value, currentValue) {
- if (currentValue === null) {
- return value;
- }
- try {
- // If the string is an object, we can parse is with the JSON library.
- // include convenience replace for single-quotes. If the author omits
- // quotes altogether, parse will fail.
- return JSON.parse(value.replace(/'/g, '"'));
- } catch(e) {
- // The object isn't valid JSON, return the raw value
- return value;
- }
- },
- // avoid deserialization of functions
- 'function': function(value, currentValue) {
- return currentValue;
- }
- };
-
- function deserializeValue(value, currentValue) {
- // attempt to infer type from default value
- var inferredType = typeof currentValue;
- // invent 'date' type value for Date
- if (currentValue instanceof Date) {
- inferredType = 'date';
- }
- // delegate deserialization via type string
- return typeHandlers[inferredType](value, currentValue);
- }
-
- // exports
-
- scope.deserializeValue = deserializeValue;
-
-})(Polymer);
-
-(function(scope) {
-
- // imports
-
- var extend = scope.extend;
-
- // module
-
- var api = {};
-
- api.declaration = {};
- api.instance = {};
-
- api.publish = function(apis, prototype) {
- for (var n in apis) {
- extend(prototype, apis[n]);
- }
- };
-
- // exports
-
- scope.api = api;
-
-})(Polymer);
-
-(function(scope) {
-
- /**
- * @class polymer-base
- */
-
- var utils = {
-
- /**
- * Invokes a function asynchronously. The context of the callback
- * function is bound to 'this' automatically. Returns a handle which may
- * be passed to <a href="#cancelAsync">cancelAsync</a> to cancel the
- * asynchronous call.
- *
- * @method async
- * @param {Function|String} method
- * @param {any|Array} args
- * @param {number} timeout
- */
- async: function(method, args, timeout) {
- // when polyfilling Object.observe, ensure changes
- // propagate before executing the async method
- Polymer.flush();
- // second argument to `apply` must be an array
- args = (args && args.length) ? args : [args];
- // function to invoke
- var fn = function() {
- (this[method] || method).apply(this, args);
- }.bind(this);
- // execute `fn` sooner or later
- var handle = timeout ? setTimeout(fn, timeout) :
- requestAnimationFrame(fn);
- // NOTE: switch on inverting handle to determine which time is used.
- return timeout ? handle : ~handle;
- },
-
- /**
- * Cancels a pending callback that was scheduled via
- * <a href="#async">async</a>.
- *
- * @method cancelAsync
- * @param {handle} handle Handle of the `async` to cancel.
- */
- cancelAsync: function(handle) {
- if (handle < 0) {
- cancelAnimationFrame(~handle);
- } else {
- clearTimeout(handle);
- }
- },
-
- /**
- * Fire an event.
- *
- * @method fire
- * @returns {Object} event
- * @param {string} type An event name.
- * @param {any} detail
- * @param {Node} onNode Target node.
- * @param {Boolean} bubbles Set false to prevent bubbling, defaults to true
- * @param {Boolean} cancelable Set false to prevent cancellation, defaults to true
- */
- fire: function(type, detail, onNode, bubbles, cancelable) {
- var node = onNode || this;
- var detail = detail === null || detail === undefined ? {} : detail;
- var event = new CustomEvent(type, {
- bubbles: bubbles !== undefined ? bubbles : true,
- cancelable: cancelable !== undefined ? cancelable : true,
- detail: detail
- });
- node.dispatchEvent(event);
- return event;
- },
-
- /**
- * Fire an event asynchronously.
- *
- * @method asyncFire
- * @param {string} type An event name.
- * @param detail
- * @param {Node} toNode Target node.
- */
- asyncFire: function(/*inType, inDetail*/) {
- this.async("fire", arguments);
- },
-
- /**
- * Remove class from old, add class to anew, if they exist.
- *
- * @param classFollows
- * @param anew A node.
- * @param old A node
- * @param className
- */
- classFollows: function(anew, old, className) {
- if (old) {
- old.classList.remove(className);
- }
- if (anew) {
- anew.classList.add(className);
- }
- },
-
- /**
- * Inject HTML which contains markup bound to this element into
- * a target element (replacing target element content).
- *
- * @param String html to inject
- * @param Element target element
- */
- injectBoundHTML: function(html, element) {
- var template = document.createElement('template');
- template.innerHTML = html;
- var fragment = this.instanceTemplate(template);
- if (element) {
- element.textContent = '';
- element.appendChild(fragment);
- }
- return fragment;
- }
- };
-
- // no-operation function for handy stubs
- var nop = function() {};
-
- // null-object for handy stubs
- var nob = {};
-
- // deprecated
-
- utils.asyncMethod = utils.async;
-
- // exports
-
- scope.api.instance.utils = utils;
- scope.nop = nop;
- scope.nob = nob;
-
-})(Polymer);
-
-(function(scope) {
-
- // imports
-
- var log = window.WebComponents ? WebComponents.flags.log : {};
- var EVENT_PREFIX = 'on-';
-
- // instance events api
- var events = {
- // read-only
- EVENT_PREFIX: EVENT_PREFIX,
- // event listeners on host
- addHostListeners: function() {
- var events = this.eventDelegates;
- log.events && (Object.keys(events).length > 0) && console.log('[%s] addHostListeners:', this.localName, events);
- // NOTE: host events look like bindings but really are not;
- // (1) we don't want the attribute to be set and (2) we want to support
- // multiple event listeners ('host' and 'instance') and Node.bind
- // by default supports 1 thing being bound.
- for (var type in events) {
- var methodName = events[type];
- PolymerGestures.addEventListener(this, type, this.element.getEventHandler(this, this, methodName));
- }
- },
- // call 'method' or function method on 'obj' with 'args', if the method exists
- dispatchMethod: function(obj, method, args) {
- if (obj) {
- log.events && console.group('[%s] dispatch [%s]', obj.localName, method);
- var fn = typeof method === 'function' ? method : obj[method];
- if (fn) {
- fn[args ? 'apply' : 'call'](obj, args);
- }
- log.events && console.groupEnd();
- // NOTE: dirty check right after calling method to ensure
- // changes apply quickly; in a very complicated app using high
- // frequency events, this can be a perf concern; in this case,
- // imperative handlers can be used to avoid flushing.
- Polymer.flush();
- }
- }
- };
-
- // exports
-
- scope.api.instance.events = events;
-
- /**
- * @class Polymer
- */
-
- /**
- * Add a gesture aware event handler to the given `node`. Can be used
- * in place of `element.addEventListener` and ensures gestures will function
- * as expected on mobile platforms. Please note that Polymer's declarative
- * event handlers include this functionality by default.
- *
- * @method addEventListener
- * @param {Node} node node on which to listen
- * @param {String} eventType name of the event
- * @param {Function} handlerFn event handler function
- * @param {Boolean} capture set to true to invoke event capturing
- * @type Function
- */
- // alias PolymerGestures event listener logic
- scope.addEventListener = function(node, eventType, handlerFn, capture) {
- PolymerGestures.addEventListener(wrap(node), eventType, handlerFn, capture);
- };
-
- /**
- * Remove a gesture aware event handler on the given `node`. To remove an
- * event listener, the exact same arguments are required that were passed
- * to `Polymer.addEventListener`.
- *
- * @method removeEventListener
- * @param {Node} node node on which to listen
- * @param {String} eventType name of the event
- * @param {Function} handlerFn event handler function
- * @param {Boolean} capture set to true to invoke event capturing
- * @type Function
- */
- scope.removeEventListener = function(node, eventType, handlerFn, capture) {
- PolymerGestures.removeEventListener(wrap(node), eventType, handlerFn, capture);
- };
-
-})(Polymer);
-
-(function(scope) {
-
- // instance api for attributes
-
- var attributes = {
- // copy attributes defined in the element declaration to the instance
- // e.g. <polymer-element name="x-foo" tabIndex="0"> tabIndex is copied
- // to the element instance here.
- copyInstanceAttributes: function () {
- var a$ = this._instanceAttributes;
- for (var k in a$) {
- if (!this.hasAttribute(k)) {
- this.setAttribute(k, a$[k]);
- }
- }
- },
- // for each attribute on this, deserialize value to property as needed
- takeAttributes: function() {
- // if we have no publish lookup table, we have no attributes to take
- // TODO(sjmiles): ad hoc
- if (this._publishLC) {
- for (var i=0, a$=this.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++) {
- this.attributeToProperty(a.name, a.value);
- }
- }
- },
- // if attribute 'name' is mapped to a property, deserialize
- // 'value' into that property
- attributeToProperty: function(name, value) {
- // try to match this attribute to a property (attributes are
- // all lower-case, so this is case-insensitive search)
- var name = this.propertyForAttribute(name);
- if (name) {
- // filter out 'mustached' values, these are to be
- // replaced with bound-data and are not yet values
- // themselves
- if (value && value.search(scope.bindPattern) >= 0) {
- return;
- }
- // get original value
- var currentValue = this[name];
- // deserialize Boolean or Number values from attribute
- var value = this.deserializeValue(value, currentValue);
- // only act if the value has changed
- if (value !== currentValue) {
- // install new value (has side-effects)
- this[name] = value;
- }
- }
- },
- // return the published property matching name, or undefined
- propertyForAttribute: function(name) {
- var match = this._publishLC && this._publishLC[name];
- return match;
- },
- // convert representation of `stringValue` based on type of `currentValue`
- deserializeValue: function(stringValue, currentValue) {
- return scope.deserializeValue(stringValue, currentValue);
- },
- // convert to a string value based on the type of `inferredType`
- serializeValue: function(value, inferredType) {
- if (inferredType === 'boolean') {
- return value ? '' : undefined;
- } else if (inferredType !== 'object' && inferredType !== 'function'
- && value !== undefined) {
- return value;
- }
- },
- // serializes `name` property value and updates the corresponding attribute
- // note that reflection is opt-in.
- reflectPropertyToAttribute: function(name) {
- var inferredType = typeof this[name];
- // try to intelligently serialize property value
- var serializedValue = this.serializeValue(this[name], inferredType);
- // boolean properties must reflect as boolean attributes
- if (serializedValue !== undefined) {
- this.setAttribute(name, serializedValue);
- // TODO(sorvell): we should remove attr for all properties
- // that have undefined serialization; however, we will need to
- // refine the attr reflection system to achieve this; pica, for example,
- // relies on having inferredType object properties not removed as
- // attrs.
- } else if (inferredType === 'boolean') {
- this.removeAttribute(name);
- }
- }
- };
-
- // exports
-
- scope.api.instance.attributes = attributes;
-
-})(Polymer);
-
-(function(scope) {
-
- /**
- * @class polymer-base
- */
-
- // imports
-
- var log = window.WebComponents ? WebComponents.flags.log : {};
-
- // magic words
-
- var OBSERVE_SUFFIX = 'Changed';
-
- // element api
-
- var empty = [];
-
- var updateRecord = {
- object: undefined,
- type: 'update',
- name: undefined,
- oldValue: undefined
- };
-
- var numberIsNaN = Number.isNaN || function(value) {
- return typeof value === 'number' && 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;
- }
-
- // capture A's value if B's value is null or undefined,
- // otherwise use B's value
- function resolveBindingValue(oldValue, value) {
- if (value === undefined && oldValue === null) {
- return value;
- }
- return (value === null || value === undefined) ? oldValue : value;
- }
-
- var properties = {
-
- // creates a CompoundObserver to observe property changes
- // NOTE, this is only done there are any properties in the `observe` object
- createPropertyObserver: function() {
- var n$ = this._observeNames;
- if (n$ && n$.length) {
- var o = this._propertyObserver = new CompoundObserver(true);
- this.registerObserver(o);
- // TODO(sorvell): may not be kosher to access the value here (this[n]);
- // previously we looked at the descriptor on the prototype
- // this doesn't work for inheritance and not for accessors without
- // a value property
- for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
- o.addPath(this, n);
- this.observeArrayValue(n, this[n], null);
- }
- }
- },
-
- // start observing property changes
- openPropertyObserver: function() {
- if (this._propertyObserver) {
- this._propertyObserver.open(this.notifyPropertyChanges, this);
- }
- },
-
- // handler for property changes; routes changes to observing methods
- // note: array valued properties are observed for array splices
- notifyPropertyChanges: function(newValues, oldValues, paths) {
- var name, method, called = {};
- for (var i in oldValues) {
- // note: paths is of form [object, path, object, path]
- name = paths[2 * i + 1];
- method = this.observe[name];
- if (method) {
- var ov = oldValues[i], nv = newValues[i];
- // observes the value if it is an array
- this.observeArrayValue(name, nv, ov);
- if (!called[method]) {
- // only invoke change method if one of ov or nv is not (undefined | null)
- if ((ov !== undefined && ov !== null) || (nv !== undefined && nv !== null)) {
- called[method] = true;
- // TODO(sorvell): call method with the set of values it's expecting;
- // e.g. 'foo bar': 'invalidate' expects the new and old values for
- // foo and bar. Currently we give only one of these and then
- // deliver all the arguments.
- this.invokeMethod(method, [ov, nv, arguments]);
- }
- }
- }
- }
- },
-
- // call method iff it exists.
- invokeMethod: function(method, args) {
- var fn = this[method] || method;
- if (typeof fn === 'function') {
- fn.apply(this, args);
- }
- },
-
- /**
- * Force any pending property changes to synchronously deliver to
- * handlers specified in the `observe` object.
- * Note, normally changes are processed at microtask time.
- *
- * @method deliverChanges
- */
- deliverChanges: function() {
- if (this._propertyObserver) {
- this._propertyObserver.deliver();
- }
- },
-
- observeArrayValue: function(name, value, old) {
- // we only care if there are registered side-effects
- var callbackName = this.observe[name];
- if (callbackName) {
- // if we are observing the previous value, stop
- if (Array.isArray(old)) {
- log.observe && console.log('[%s] observeArrayValue: unregister observer [%s]', this.localName, name);
- this.closeNamedObserver(name + '__array');
- }
- // if the new value is an array, being observing it
- 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(splices) {
- this.invokeMethod(callbackName, [splices]);
- }, this);
- this.registerNamedObserver(name + '__array', observer);
- }
- }
- },
-
- emitPropertyChangeRecord: function(name, value, oldValue) {
- var object = this;
- if (areSameValue(value, oldValue)) {
- return;
- }
- // invoke property change side effects
- this._propertyChanged(name, value, oldValue);
- // emit change record
- if (!Observer.hasObjectObserve) {
- return;
- }
- var notifier = this._objectNotifier;
- if (!notifier) {
- notifier = this._objectNotifier = Object.getNotifier(this);
- }
- updateRecord.object = this;
- updateRecord.name = name;
- updateRecord.oldValue = oldValue;
- notifier.notify(updateRecord);
- },
-
- _propertyChanged: function(name, value, oldValue) {
- if (this.reflect[name]) {
- this.reflectPropertyToAttribute(name);
- }
- },
-
- // creates a property binding (called via bind) to a published property.
- bindProperty: function(property, observable, oneTime) {
- if (oneTime) {
- this[property] = observable;
- return;
- }
- var computed = this.element.prototype.computed;
- // Binding an "out-only" value to a computed property. Note that
- // since this observer isn't opened, it doesn't need to be closed on
- // cleanup.
- if (computed && computed[property]) {
- var privateComputedBoundValue = property + 'ComputedBoundObservable_';
- this[privateComputedBoundValue] = observable;
- return;
- }
- return this.bindToAccessor(property, observable, resolveBindingValue);
- },
-
- // NOTE property `name` must be published. This makes it an accessor.
- bindToAccessor: function(name, observable, resolveFn) {
- var privateName = name + '_';
- var privateObservable = name + 'Observable_';
- // Present for properties which are computed and published and have a
- // bound value.
- var privateComputedBoundValue = name + 'ComputedBoundObservable_';
- this[privateObservable] = observable;
- var oldValue = this[privateName];
- // observable callback
- var self = this;
- function updateValue(value, oldValue) {
- self[privateName] = value;
- var setObserveable = self[privateComputedBoundValue];
- if (setObserveable && typeof setObserveable.setValue == 'function') {
- setObserveable.setValue(value);
- }
- self.emitPropertyChangeRecord(name, value, oldValue);
- }
- // resolve initial value
- var value = observable.open(updateValue);
- if (resolveFn && !areSameValue(oldValue, value)) {
- var resolvedValue = resolveFn(oldValue, value);
- if (!areSameValue(value, resolvedValue)) {
- value = resolvedValue;
- if (observable.setValue) {
- observable.setValue(value);
- }
- }
- }
- updateValue(value, oldValue);
- // register and return observable
- var observer = {
- close: function() {
- observable.close();
- self[privateObservable] = undefined;
- self[privateComputedBoundValue] = undefined;
- }
- };
- this.registerObserver(observer);
- return observer;
- },
-
- createComputedProperties: function() {
- if (!this._computedNames) {
- return;
- }
- for (var i = 0; i < this._computedNames.length; i++) {
- var name = this._computedNames[i];
- var expressionText = this.computed[name];
- try {
- var expression = PolymerExpressions.getExpression(expressionText);
- var observable = expression.getBinding(this, this.element.syntax);
- this.bindToAccessor(name, observable);
- } catch (ex) {
- console.error('Failed to create computed property', ex);
- }
- }
- },
-
- // property bookkeeping
- registerObserver: function(observer) {
- if (!this._observers) {
- this._observers = [observer];
- return;
- }
- this._observers.push(observer);
- },
-
- closeObservers: function() {
- if (!this._observers) {
- return;
- }
- // observer array items are arrays of observers.
- var observers = this._observers;
- for (var i = 0; i < observers.length; i++) {
- var observer = observers[i];
- if (observer && typeof observer.close == 'function') {
- observer.close();
- }
- }
- this._observers = [];
- },
-
- // bookkeeping observers for memory management
- registerNamedObserver: function(name, observer) {
- var o$ = this._namedObservers || (this._namedObservers = {});
- o$[name] = observer;
- },
-
- closeNamedObserver: function(name) {
- var o$ = this._namedObservers;
- if (o$ && o$[name]) {
- o$[name].close();
- o$[name] = null;
- return true;
- }
- },
-
- closeNamedObservers: function() {
- if (this._namedObservers) {
- for (var i in this._namedObservers) {
- this.closeNamedObserver(i);
- }
- this._namedObservers = {};
- }
- }
-
- };
-
- // logging
- var LOG_OBSERVE = '[%s] watching [%s]';
- var LOG_OBSERVED = '[%s#%s] watch: [%s] now [%s] was [%s]';
- var LOG_CHANGED = '[%s#%s] propertyChanged: [%s] now [%s] was [%s]';
-
- // exports
-
- scope.api.instance.properties = properties;
-
-})(Polymer);
-
-(function(scope) {
-
- /**
- * @class polymer-base
- */
-
- // imports
-
- var log = window.WebComponents ? WebComponents.flags.log : {};
-
- // element api supporting mdv
- var mdv = {
-
- /**
- * Creates dom cloned from the given template, instantiating bindings
- * with this element as the template model and `PolymerExpressions` as the
- * binding delegate.
- *
- * @method instanceTemplate
- * @param {Template} template source template from which to create dom.
- */
- 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);
- var dom = template.createInstance(this, syntax);
- var observers = dom.bindings_;
- for (var i = 0; i < observers.length; i++) {
- this.registerObserver(observers[i]);
- }
- return dom;
- },
-
- // Called by TemplateBinding/NodeBind to setup a binding to the given
- // property. It's overridden here to support property bindings
- // in addition to attribute bindings that are supported by default.
- bind: function(name, observable, oneTime) {
- var property = this.propertyForAttribute(name);
- if (!property) {
- // TODO(sjmiles): this mixin method must use the special form
- // of `super` installed by `mixinMethod` in declaration/prototype.js
- return this.mixinSuper(arguments);
- } else {
- // use n-way Polymer binding
- var observer = this.bindProperty(property, observable, oneTime);
- // NOTE: reflecting binding information is typically required only for
- // tooling. It has a performance cost so it's opt-in in Node.bind.
- if (Platform.enableBindingsReflection && observer) {
- observer.path = observable.path_;
- this._recordBinding(property, observer);
- }
- if (this.reflect[property]) {
- this.reflectPropertyToAttribute(property);
- }
- return observer;
- }
- },
-
- _recordBinding: function(name, observer) {
- this.bindings_ = this.bindings_ || {};
- this.bindings_[name] = observer;
- },
-
- // Called by TemplateBinding when all bindings on an element have been
- // executed. This signals that all element inputs have been gathered
- // and it's safe to ready the element, create shadow-root and start
- // data-observation.
- bindFinished: function() {
- this.makeElementReady();
- },
-
- // called at detached time to signal that an element's bindings should be
- // cleaned up. This is done asynchronously so that users have the chance
- // to call `cancelUnbindAll` to prevent unbinding.
- asyncUnbindAll: function() {
- if (!this._unbound) {
- log.unbind && console.log('[%s] asyncUnbindAll', this.localName);
- this._unbindAllJob = this.job(this._unbindAllJob, this.unbindAll, 0);
- }
- },
-
- /**
- * This method should rarely be used and only if
- * <a href="#cancelUnbindAll">`cancelUnbindAll`</a> has been called to
- * prevent element unbinding. In this case, the element's bindings will
- * not be automatically cleaned up and it cannot be garbage collected
- * by the system. If memory pressure is a concern or a
- * large amount of elements need to be managed in this way, `unbindAll`
- * can be called to deactivate the element's bindings and allow its
- * memory to be reclaimed.
- *
- * @method unbindAll
- */
- unbindAll: function() {
- if (!this._unbound) {
- this.closeObservers();
- this.closeNamedObservers();
- this._unbound = true;
- }
- },
-
- /**
- * Call in `detached` to prevent the element from unbinding when it is
- * detached from the dom. The element is unbound as a cleanup step that
- * allows its memory to be reclaimed.
- * If `cancelUnbindAll` is used, consider calling
- * <a href="#unbindAll">`unbindAll`</a> when the element is no longer
- * needed. This will allow its memory to be reclaimed.
- *
- * @method cancelUnbindAll
- */
- cancelUnbindAll: function() {
- if (this._unbound) {
- log.unbind && console.warn('[%s] already unbound, cannot cancel unbindAll', this.localName);
- return;
- }
- log.unbind && console.log('[%s] cancelUnbindAll', this.localName);
- if (this._unbindAllJob) {
- this._unbindAllJob = this._unbindAllJob.stop();
- }
- }
-
- };
-
- function unbindNodeTree(node) {
- forNodeTree(node, _nodeUnbindAll);
- }
-
- function _nodeUnbindAll(node) {
- node.unbindAll();
- }
-
- function forNodeTree(node, callback) {
- if (node) {
- callback(node);
- for (var child = node.firstChild; child; child = child.nextSibling) {
- forNodeTree(child, callback);
- }
- }
- }
-
- var mustachePattern = /\{\{([^{}]*)}}/;
-
- // exports
-
- scope.bindPattern = mustachePattern;
- scope.api.instance.mdv = mdv;
-
-})(Polymer);
-
-(function(scope) {
-
- /**
- * Common prototype for all Polymer Elements.
- *
- * @class polymer-base
- * @homepage polymer.github.io
- */
- var base = {
- /**
- * Tags this object as the canonical Base prototype.
- *
- * @property PolymerBase
- * @type boolean
- * @default true
- */
- PolymerBase: true,
-
- /**
- * Debounce signals.
- *
- * Call `job` to defer a named signal, and all subsequent matching signals,
- * until a wait time has elapsed with no new signal.
- *
- * debouncedClickAction: function(e) {
- * // processClick only when it's been 100ms since the last click
- * this.job('click', function() {
- * this.processClick;
- * }, 100);
- * }
- *
- * @method job
- * @param String {String} job A string identifier for the job to debounce.
- * @param Function {Function} callback A function that is called (with `this` context) when the wait time elapses.
- * @param Number {Number} wait Time in milliseconds (ms) after the last signal that must elapse before invoking `callback`
- * @type Handle
- */
- job: function(job, callback, wait) {
- if (typeof job === 'string') {
- var n = '___' + job;
- this[n] = Polymer.job.call(this, this[n], callback, wait);
- } else {
- // TODO(sjmiles): suggest we deprecate this call signature
- return Polymer.job.call(this, job, callback, wait);
- }
- },
-
- /**
- * Invoke a superclass method.
- *
- * Use `super()` to invoke the most recently overridden call to the
- * currently executing function.
- *
- * To pass arguments through, use the literal `arguments` as the parameter
- * to `super()`.
- *
- * nextPageAction: function(e) {
- * // invoke the superclass version of `nextPageAction`
- * this.super(arguments);
- * }
- *
- * To pass custom arguments, arrange them in an array.
- *
- * appendSerialNo: function(value, serial) {
- * // prefix the superclass serial number with our lot # before
- * // invoking the superlcass
- * return this.super([value, this.lotNo + serial])
- * }
- *
- * @method super
- * @type Any
- * @param {args) An array of arguments to use when calling the superclass method, or null.
- */
- super: Polymer.super,
-
- /**
- * Lifecycle method called when the element is instantiated.
- *
- * Override `created` to perform custom create-time tasks. No need to call
- * super-class `created` unless you are extending another Polymer element.
- * Created is called before the element creates `shadowRoot` or prepares
- * data-observation.
- *
- * @method created
- * @type void
- */
- created: function() {
- },
-
- /**
- * Lifecycle method called when the element has populated it's `shadowRoot`,
- * prepared data-observation, and made itself ready for API interaction.
- *
- * @method ready
- * @type void
- */
- ready: function() {
- },
-
- /**
- * Low-level lifecycle method called as part of standard Custom Elements
- * operation. Polymer implements this method to provide basic default
- * functionality. For custom create-time tasks, implement `created`
- * instead, which is called immediately after `createdCallback`.
- *
- * @method createdCallback
- */
- createdCallback: function() {
- if (this.templateInstance && this.templateInstance.model) {
- console.warn('Attributes on ' + this.localName + ' were data bound ' +
- 'prior to Polymer upgrading the element. This may result in ' +
- 'incorrect binding types.');
- }
- this.created();
- this.prepareElement();
- if (!this.ownerDocument.isStagingDocument) {
- this.makeElementReady();
- }
- },
-
- // system entry point, do not override
- prepareElement: function() {
- if (this._elementPrepared) {
- console.warn('Element already prepared', this.localName);
- return;
- }
- this._elementPrepared = true;
- // storage for shadowRoots info
- this.shadowRoots = {};
- // install property observers
- this.createPropertyObserver();
- this.openPropertyObserver();
- // install boilerplate attributes
- this.copyInstanceAttributes();
- // process input attributes
- this.takeAttributes();
- // add event listeners
- this.addHostListeners();
- },
-
- // system entry point, do not override
- makeElementReady: function() {
- if (this._readied) {
- return;
- }
- this._readied = true;
- this.createComputedProperties();
- this.parseDeclarations(this.__proto__);
- // NOTE: Support use of the `unresolved` attribute to help polyfill
- // custom elements' `:unresolved` feature.
- this.removeAttribute('unresolved');
- // user entry point
- this.ready();
- },
-
- /**
- * Low-level lifecycle method called as part of standard Custom Elements
- * operation. Polymer implements this method to provide basic default
- * functionality. For custom tasks in your element, implement `attributeChanged`
- * instead, which is called immediately after `attributeChangedCallback`.
- *
- * @method attributeChangedCallback
- */
- attributeChangedCallback: function(name, oldValue) {
- // TODO(sjmiles): adhoc filter
- if (name !== 'class' && name !== 'style') {
- this.attributeToProperty(name, this.getAttribute(name));
- }
- if (this.attributeChanged) {
- this.attributeChanged.apply(this, arguments);
- }
- },
-
- /**
- * Low-level lifecycle method called as part of standard Custom Elements
- * operation. Polymer implements this method to provide basic default
- * functionality. For custom create-time tasks, implement `attached`
- * instead, which is called immediately after `attachedCallback`.
- *
- * @method attachedCallback
- */
- attachedCallback: function() {
- // when the element is attached, prevent it from unbinding.
- this.cancelUnbindAll();
- // invoke user action
- if (this.attached) {
- this.attached();
- }
- if (!this.hasBeenAttached) {
- this.hasBeenAttached = true;
- if (this.domReady) {
- this.async('domReady');
- }
- }
- },
-
- /**
- * Implement to access custom elements in dom descendants, ancestors,
- * or siblings. Because custom elements upgrade in document order,
- * elements accessed in `ready` or `attached` may not be upgraded. When
- * `domReady` is called, all registered custom elements are guaranteed
- * to have been upgraded.
- *
- * @method domReady
- */
-
- /**
- * Low-level lifecycle method called as part of standard Custom Elements
- * operation. Polymer implements this method to provide basic default
- * functionality. For custom create-time tasks, implement `detached`
- * instead, which is called immediately after `detachedCallback`.
- *
- * @method detachedCallback
- */
- detachedCallback: function() {
- if (!this.preventDispose) {
- this.asyncUnbindAll();
- }
- // invoke user action
- if (this.detached) {
- this.detached();
- }
- // TODO(sorvell): bc
- if (this.leftView) {
- this.leftView();
- }
- },
-
- /**
- * Walks the prototype-chain of this element and allows specific
- * classes a chance to process static declarations.
- *
- * In particular, each polymer-element has it's own `template`.
- * `parseDeclarations` is used to accumulate all element `template`s
- * from an inheritance chain.
- *
- * `parseDeclaration` static methods implemented in the chain are called
- * recursively, oldest first, with the `<polymer-element>` associated
- * with the current prototype passed as an argument.
- *
- * An element may override this method to customize shadow-root generation.
- *
- * @method parseDeclarations
- */
- parseDeclarations: function(p) {
- if (p && p.element) {
- this.parseDeclarations(p.__proto__);
- p.parseDeclaration.call(this, p.element);
- }
- },
-
- /**
- * Perform init-time actions based on static information in the
- * `<polymer-element>` instance argument.
- *
- * For example, the standard implementation locates the template associated
- * with the given `<polymer-element>` and stamps it into a shadow-root to
- * implement shadow inheritance.
- *
- * An element may override this method for custom behavior.
- *
- * @method parseDeclaration
- */
- parseDeclaration: function(elementElement) {
- var template = this.fetchTemplate(elementElement);
- if (template) {
- var root = this.shadowFromTemplate(template);
- this.shadowRoots[elementElement.name] = root;
- }
- },
-
- /**
- * Given a `<polymer-element>`, find an associated template (if any) to be
- * used for shadow-root generation.
- *
- * An element may override this method for custom behavior.
- *
- * @method fetchTemplate
- */
- fetchTemplate: function(elementElement) {
- return elementElement.querySelector('template');
- },
-
- /**
- * Create a shadow-root in this host and stamp `template` as it's
- * content.
- *
- * An element may override this method for custom behavior.
- *
- * @method shadowFromTemplate
- */
- shadowFromTemplate: function(template) {
- if (template) {
- // make a shadow root
- var root = this.createShadowRoot();
- // stamp template
- // which includes parsing and applying MDV bindings before being
- // inserted (to avoid {{}} in attribute values)
- // e.g. to prevent <img src="images/{{icon}}"> from generating a 404.
- var dom = this.instanceTemplate(template);
- // append to shadow dom
- root.appendChild(dom);
- // perform post-construction initialization tasks on shadow root
- this.shadowRootReady(root, template);
- // return the created shadow root
- return root;
- }
- },
-
- // utility function that stamps a <template> into light-dom
- lightFromTemplate: function(template, refNode) {
- if (template) {
- // TODO(sorvell): mark this element as an eventController so that
- // event listeners on bound nodes inside it will be called on it.
- // Note, the expectation here is that events on all descendants
- // should be handled by this element.
- this.eventController = this;
- // stamp template
- // which includes parsing and applying MDV bindings before being
- // inserted (to avoid {{}} in attribute values)
- // e.g. to prevent <img src="images/{{icon}}"> from generating a 404.
- var dom = this.instanceTemplate(template);
- // append to shadow dom
- if (refNode) {
- this.insertBefore(dom, refNode);
- } else {
- this.appendChild(dom);
- }
- // perform post-construction initialization tasks on ahem, light root
- this.shadowRootReady(this);
- // return the created shadow root
- return dom;
- }
- },
-
- shadowRootReady: function(root) {
- // locate nodes with id and store references to them in this.$ hash
- this.marshalNodeReferences(root);
- },
-
- // locate nodes with id and store references to them in this.$ hash
- marshalNodeReferences: function(root) {
- // establish $ instance variable
- var $ = this.$ = this.$ || {};
- // populate $ from nodes with ID from the LOCAL tree
- if (root) {
- var n$ = root.querySelectorAll("[id]");
- for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
- $[n.id] = n;
- };
- }
- },
-
- /**
- * Register a one-time callback when a child-list or sub-tree mutation
- * occurs on node.
- *
- * For persistent callbacks, call onMutation from your listener.
- *
- * @method onMutation
- * @param Node {Node} node Node to watch for mutations.
- * @param Function {Function} listener Function to call on mutation. The function is invoked as `listener.call(this, observer, mutations);` where `observer` is the MutationObserver that triggered the notification, and `mutations` is the native mutation list.
- */
- onMutation: function(node, listener) {
- var observer = new MutationObserver(function(mutations) {
- listener.call(this, observer, mutations);
- observer.disconnect();
- }.bind(this));
- observer.observe(node, {childList: true, subtree: true});
- }
- };
-
- /**
- * @class Polymer
- */
-
- /**
- * Returns true if the object includes <a href="#polymer-base">polymer-base</a> in it's prototype chain.
- *
- * @method isBase
- * @param Object {Object} object Object to test.
- * @type Boolean
- */
- function isBase(object) {
- return object.hasOwnProperty('PolymerBase')
- }
-
- // name a base constructor for dev tools
-
- /**
- * The Polymer base-class constructor.
- *
- * @property Base
- * @type Function
- */
- function PolymerBase() {};
- PolymerBase.prototype = base;
- base.constructor = PolymerBase;
-
- // exports
-
- scope.Base = PolymerBase;
- scope.isBase = isBase;
- scope.api.instance.base = base;
-
-})(Polymer);
-
-(function(scope) {
-
- // imports
-
- var log = window.WebComponents ? WebComponents.flags.log : {};
- var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
-
- // magic words
-
- var STYLE_SCOPE_ATTRIBUTE = 'element';
- var STYLE_CONTROLLER_SCOPE = 'controller';
-
- var styles = {
- STYLE_SCOPE_ATTRIBUTE: STYLE_SCOPE_ATTRIBUTE,
- /**
- * Installs external stylesheets and <style> elements with the attribute
- * polymer-scope='controller' into the scope of element. This is intended
- * to be a called during custom element construction.
- */
- installControllerStyles: function() {
- // apply controller styles, but only if they are not yet applied
- var scope = this.findStyleScope();
- if (scope && !this.scopeHasNamedStyle(scope, this.localName)) {
- // allow inherited controller styles
- var proto = getPrototypeOf(this), cssText = '';
- while (proto && proto.element) {
- cssText += proto.element.cssTextForScope(STYLE_CONTROLLER_SCOPE);
- proto = getPrototypeOf(proto);
- }
- if (cssText) {
- this.installScopeCssText(cssText, scope);
- }
- }
- },
- installScopeStyle: function(style, name, scope) {
- var scope = scope || this.findStyleScope(), name = name || '';
- if (scope && !this.scopeHasNamedStyle(scope, this.localName + name)) {
- var cssText = '';
- if (style instanceof Array) {
- for (var i=0, l=style.length, s; (i<l) && (s=style[i]); i++) {
- cssText += s.textContent + '\n\n';
- }
- } else {
- cssText = style.textContent;
- }
- this.installScopeCssText(cssText, scope, name);
- }
- },
- installScopeCssText: function(cssText, scope, name) {
- scope = scope || this.findStyleScope();
- name = name || '';
- if (!scope) {
- return;
- }
- if (hasShadowDOMPolyfill) {
- cssText = shimCssText(cssText, scope.host);
- }
- var style = this.element.cssTextToScopeStyle(cssText,
- STYLE_CONTROLLER_SCOPE);
- Polymer.applyStyleToScope(style, scope);
- // cache that this style has been applied
- this.styleCacheForScope(scope)[this.localName + name] = true;
- },
- findStyleScope: function(node) {
- // find the shadow root that contains this element
- var n = node || this;
- while (n.parentNode) {
- n = n.parentNode;
- }
- return n;
- },
- scopeHasNamedStyle: function(scope, name) {
- var cache = this.styleCacheForScope(scope);
- return cache[name];
- },
- styleCacheForScope: function(scope) {
- if (hasShadowDOMPolyfill) {
- var scopeName = scope.host ? scope.host.localName : scope.localName;
- return polyfillScopeStyleCache[scopeName] || (polyfillScopeStyleCache[scopeName] = {});
- } else {
- return scope._scopeStyles = (scope._scopeStyles || {});
- }
- }
- };
-
- var polyfillScopeStyleCache = {};
-
- // NOTE: use raw prototype traversal so that we ensure correct traversal
- // on platforms where the protoype chain is simulated via __proto__ (IE10)
- function getPrototypeOf(prototype) {
- return prototype.__proto__;
- }
-
- function shimCssText(cssText, host) {
- var name = '', is = false;
- if (host) {
- name = host.localName;
- is = host.hasAttribute('is');
- }
- var selector = WebComponents.ShadowCSS.makeScopeSelector(name, is);
- return WebComponents.ShadowCSS.shimCssText(cssText, selector);
- }
-
- // exports
-
- scope.api.instance.styles = styles;
-
-})(Polymer);
-
-(function(scope) {
-
- // imports
-
- var extend = scope.extend;
- var api = scope.api;
-
- // imperative implementation: Polymer()
-
- // specify an 'own' prototype for tag `name`
- function element(name, prototype) {
- if (typeof name !== 'string') {
- var script = prototype || document._currentScript;
- prototype = name;
- name = script && script.parentNode && script.parentNode.getAttribute ?
- script.parentNode.getAttribute('name') : '';
- if (!name) {
- throw 'Element name could not be inferred.';
- }
- }
- if (getRegisteredPrototype(name)) {
- throw 'Already registered (Polymer) prototype for element ' + name;
- }
- // cache the prototype
- registerPrototype(name, prototype);
- // notify the registrar waiting for 'name', if any
- notifyPrototype(name);
- }
-
- // async prototype source
-
- function waitingForPrototype(name, client) {
- waitPrototype[name] = client;
- }
-
- var waitPrototype = {};
-
- function notifyPrototype(name) {
- if (waitPrototype[name]) {
- waitPrototype[name].registerWhenReady();
- delete waitPrototype[name];
- }
- }
-
- // utility and bookkeeping
-
- // maps tag names to prototypes, as registered with
- // Polymer. Prototypes associated with a tag name
- // using document.registerElement are available from
- // HTMLElement.getPrototypeForTag().
- // If an element was fully registered by Polymer, then
- // Polymer.getRegisteredPrototype(name) ===
- // HTMLElement.getPrototypeForTag(name)
-
- var prototypesByName = {};
-
- function registerPrototype(name, prototype) {
- return prototypesByName[name] = prototype || {};
- }
-
- function getRegisteredPrototype(name) {
- return prototypesByName[name];
- }
-
- function instanceOfType(element, type) {
- if (typeof type !== 'string') {
- return false;
- }
- var proto = HTMLElement.getPrototypeForTag(type);
- var ctor = proto && proto.constructor;
- if (!ctor) {
- return false;
- }
- if (CustomElements.instanceof) {
- return CustomElements.instanceof(element, ctor);
- }
- return element instanceof ctor;
- }
-
- // exports
-
- scope.getRegisteredPrototype = getRegisteredPrototype;
- scope.waitingForPrototype = waitingForPrototype;
- scope.instanceOfType = instanceOfType;
-
- // namespace shenanigans so we can expose our scope on the registration
- // function
-
- // make window.Polymer reference `element()`
-
- window.Polymer = element;
-
- // TODO(sjmiles): find a way to do this that is less terrible
- // copy window.Polymer properties onto `element()`
-
- extend(Polymer, scope);
-
- // Under the HTMLImports polyfill, scripts in the main document
- // do not block on imports; we want to allow calls to Polymer in the main
- // document. WebComponents collects those calls until we can process them, which
- // we do here.
-
- if (WebComponents.consumeDeclarations) {
- WebComponents.consumeDeclarations(function(declarations) {
- if (declarations) {
- for (var i=0, l=declarations.length, d; (i<l) && (d=declarations[i]); i++) {
- element.apply(null, d);
- }
- }
- });
- }
-
-})(Polymer);
-
-(function(scope) {
-
-/**
- * @class polymer-base
- */
-
- /**
- * Resolve a url path to be relative to a `base` url. If unspecified, `base`
- * defaults to the element's ownerDocument url. Can be used to resolve
- * paths from element's in templates loaded in HTMLImports to be relative
- * to the document containing the element. Polymer automatically does this for
- * url attributes in element templates; however, if a url, for
- * example, contains a binding, then `resolvePath` can be used to ensure it is
- * relative to the element document. For example, in an element's template,
- *
- * <a href="{{resolvePath(path)}}">Resolved</a>
- *
- * @method resolvePath
- * @param {String} url Url path to resolve.
- * @param {String} base Optional base url against which to resolve, defaults
- * to the element's ownerDocument url.
- * returns {String} resolved url.
- */
-
-var path = {
- resolveElementPaths: function(node) {
- Polymer.urlResolver.resolveDom(node);
- },
- addResolvePathApi: function() {
- // let assetpath attribute modify the resolve path
- var assetPath = this.getAttribute('assetpath') || '';
- var root = new URL(assetPath, this.ownerDocument.baseURI);
- this.prototype.resolvePath = function(urlPath, base) {
- var u = new URL(urlPath, base || root);
- return u.href;
- };
- }
-};
-
-// exports
-scope.api.declaration.path = path;
-
-})(Polymer);
-
-(function(scope) {
-
- // imports
-
- var log = window.WebComponents ? WebComponents.flags.log : {};
- var api = scope.api.instance.styles;
- var STYLE_SCOPE_ATTRIBUTE = api.STYLE_SCOPE_ATTRIBUTE;
-
- var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
-
- // magic words
-
- var STYLE_SELECTOR = 'style';
- var STYLE_LOADABLE_MATCH = '@import';
- var SHEET_SELECTOR = 'link[rel=stylesheet]';
- var STYLE_GLOBAL_SCOPE = 'global';
- var SCOPE_ATTR = 'polymer-scope';
-
- var styles = {
- // returns true if resources are loading
- loadStyles: function(callback) {
- var template = this.fetchTemplate();
- var content = template && this.templateContent();
- if (content) {
- this.convertSheetsToStyles(content);
- var styles = this.findLoadableStyles(content);
- if (styles.length) {
- var templateUrl = template.ownerDocument.baseURI;
- return Polymer.styleResolver.loadStyles(styles, templateUrl, callback);
- }
- }
- if (callback) {
- callback();
- }
- },
- convertSheetsToStyles: function(root) {
- var s$ = root.querySelectorAll(SHEET_SELECTOR);
- for (var i=0, l=s$.length, s, c; (i<l) && (s=s$[i]); i++) {
- c = createStyleElement(importRuleForSheet(s, this.ownerDocument.baseURI),
- this.ownerDocument);
- this.copySheetAttributes(c, s);
- s.parentNode.replaceChild(c, s);
- }
- },
- copySheetAttributes: function(style, link) {
- for (var i=0, a$=link.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++) {
- if (a.name !== 'rel' && a.name !== 'href') {
- style.setAttribute(a.name, a.value);
- }
- }
- },
- findLoadableStyles: function(root) {
- var loadables = [];
- if (root) {
- var s$ = root.querySelectorAll(STYLE_SELECTOR);
- for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) {
- if (s.textContent.match(STYLE_LOADABLE_MATCH)) {
- loadables.push(s);
- }
- }
- }
- return loadables;
- },
- /**
- * Install external stylesheets loaded in <polymer-element> elements into the
- * element's template.
- * @param elementElement The <element> element to style.
- */
- installSheets: function() {
- this.cacheSheets();
- this.cacheStyles();
- this.installLocalSheets();
- this.installGlobalStyles();
- },
- /**
- * Remove all sheets from element and store for later use.
- */
- cacheSheets: function() {
- this.sheets = this.findNodes(SHEET_SELECTOR);
- this.sheets.forEach(function(s) {
- if (s.parentNode) {
- s.parentNode.removeChild(s);
- }
- });
- },
- cacheStyles: function() {
- this.styles = this.findNodes(STYLE_SELECTOR + '[' + SCOPE_ATTR + ']');
- this.styles.forEach(function(s) {
- if (s.parentNode) {
- s.parentNode.removeChild(s);
- }
- });
- },
- /**
- * Takes external stylesheets loaded in an <element> element and moves
- * their content into a <style> element inside the <element>'s template.
- * The sheet is then removed from the <element>. This is done only so
- * that if the element is loaded in the main document, the sheet does
- * not become active.
- * Note, ignores sheets with the attribute 'polymer-scope'.
- * @param elementElement The <element> element to style.
- */
- installLocalSheets: function () {
- var sheets = this.sheets.filter(function(s) {
- return !s.hasAttribute(SCOPE_ATTR);
- });
- var content = this.templateContent();
- if (content) {
- var cssText = '';
- sheets.forEach(function(sheet) {
- cssText += cssTextFromSheet(sheet) + '\n';
- });
- if (cssText) {
- var style = createStyleElement(cssText, this.ownerDocument);
- content.insertBefore(style, content.firstChild);
- }
- }
- },
- findNodes: function(selector, matcher) {
- var nodes = this.querySelectorAll(selector).array();
- var content = this.templateContent();
- if (content) {
- var templateNodes = content.querySelectorAll(selector).array();
- nodes = nodes.concat(templateNodes);
- }
- return matcher ? nodes.filter(matcher) : nodes;
- },
- /**
- * Promotes external stylesheets and <style> elements with the attribute
- * polymer-scope='global' into global scope.
- * This is particularly useful for defining @keyframe rules which
- * currently do not function in scoped or shadow style elements.
- * (See wkb.ug/72462)
- * @param elementElement The <element> element to style.
- */
- // TODO(sorvell): remove when wkb.ug/72462 is addressed.
- installGlobalStyles: function() {
- var style = this.styleForScope(STYLE_GLOBAL_SCOPE);
- applyStyleToScope(style, document.head);
- },
- cssTextForScope: function(scopeDescriptor) {
- var cssText = '';
- // handle stylesheets
- var selector = '[' + SCOPE_ATTR + '=' + scopeDescriptor + ']';
- var matcher = function(s) {
- return matchesSelector(s, selector);
- };
- var sheets = this.sheets.filter(matcher);
- sheets.forEach(function(sheet) {
- cssText += cssTextFromSheet(sheet) + '\n\n';
- });
- // handle cached style elements
- var styles = this.styles.filter(matcher);
- styles.forEach(function(style) {
- cssText += style.textContent + '\n\n';
- });
- return cssText;
- },
- styleForScope: function(scopeDescriptor) {
- var cssText = this.cssTextForScope(scopeDescriptor);
- return this.cssTextToScopeStyle(cssText, scopeDescriptor);
- },
- cssTextToScopeStyle: function(cssText, scopeDescriptor) {
- if (cssText) {
- var style = createStyleElement(cssText);
- style.setAttribute(STYLE_SCOPE_ATTRIBUTE, this.getAttribute('name') +
- '-' + scopeDescriptor);
- return style;
- }
- }
- };
-
- function importRuleForSheet(sheet, baseUrl) {
- var href = new URL(sheet.getAttribute('href'), baseUrl).href;
- return '@import \'' + href + '\';';
- }
-
- function applyStyleToScope(style, scope) {
- if (style) {
- if (scope === document) {
- scope = document.head;
- }
- if (hasShadowDOMPolyfill) {
- scope = document.head;
- }
- // TODO(sorvell): necessary for IE
- // see https://connect.microsoft.com/IE/feedback/details/790212/
- // cloning-a-style-element-and-adding-to-document-produces
- // -unexpected-result#details
- // var clone = style.cloneNode(true);
- var clone = createStyleElement(style.textContent);
- var attr = style.getAttribute(STYLE_SCOPE_ATTRIBUTE);
- if (attr) {
- clone.setAttribute(STYLE_SCOPE_ATTRIBUTE, attr);
- }
- // TODO(sorvell): probably too brittle; try to figure out
- // where to put the element.
- var refNode = scope.firstElementChild;
- if (scope === document.head) {
- var selector = 'style[' + STYLE_SCOPE_ATTRIBUTE + ']';
- var s$ = document.head.querySelectorAll(selector);
- if (s$.length) {
- refNode = s$[s$.length-1].nextElementSibling;
- }
- }
- scope.insertBefore(clone, refNode);
- }
- }
-
- function createStyleElement(cssText, scope) {
- scope = scope || document;
- scope = scope.createElement ? scope : scope.ownerDocument;
- var style = scope.createElement('style');
- style.textContent = cssText;
- return style;
- }
-
- function cssTextFromSheet(sheet) {
- return (sheet && sheet.__resource) || '';
- }
-
- function matchesSelector(node, inSelector) {
- if (matches) {
- return matches.call(node, inSelector);
- }
- }
- var p = HTMLElement.prototype;
- var matches = p.matches || p.matchesSelector || p.webkitMatchesSelector
- || p.mozMatchesSelector;
-
- // exports
-
- scope.api.declaration.styles = styles;
- scope.applyStyleToScope = applyStyleToScope;
-
-})(Polymer);
-
-(function(scope) {
-
- // imports
-
- var log = window.WebComponents ? WebComponents.flags.log : {};
- var api = scope.api.instance.events;
- var EVENT_PREFIX = api.EVENT_PREFIX;
-
- var mixedCaseEventTypes = {};
- [
- 'webkitAnimationStart',
- 'webkitAnimationEnd',
- 'webkitTransitionEnd',
- 'DOMFocusOut',
- 'DOMFocusIn',
- 'DOMMouseScroll'
- ].forEach(function(e) {
- mixedCaseEventTypes[e.toLowerCase()] = e;
- });
-
- // polymer-element declarative api: events feature
- var events = {
- parseHostEvents: function() {
- // our delegates map
- var delegates = this.prototype.eventDelegates;
- // extract data from attributes into delegates
- this.addAttributeDelegates(delegates);
- },
- addAttributeDelegates: function(delegates) {
- // for each attribute
- for (var i=0, a; a=this.attributes[i]; i++) {
- // does it have magic marker identifying it as an event delegate?
- if (this.hasEventPrefix(a.name)) {
- // if so, add the info to delegates
- delegates[this.removeEventPrefix(a.name)] = a.value.replace('{{', '')
- .replace('}}', '').trim();
- }
- }
- },
- // starts with 'on-'
- hasEventPrefix: function (n) {
- return n && (n[0] === 'o') && (n[1] === 'n') && (n[2] === '-');
- },
- removeEventPrefix: function(n) {
- return n.slice(prefixLength);
- },
- findController: function(node) {
- while (node.parentNode) {
- if (node.eventController) {
- return node.eventController;
- }
- node = node.parentNode;
- }
- return node.host;
- },
- getEventHandler: function(controller, target, method) {
- var events = this;
- return function(e) {
- if (!controller || !controller.PolymerBase) {
- controller = events.findController(target);
- }
-
- var args = [e, e.detail, e.currentTarget];
- controller.dispatchMethod(controller, method, args);
- };
- },
- prepareEventBinding: function(pathString, name, node) {
- if (!this.hasEventPrefix(name))
- return;
-
- var eventType = this.removeEventPrefix(name);
- eventType = mixedCaseEventTypes[eventType] || eventType;
-
- var events = this;
-
- return function(model, node, oneTime) {
- var handler = events.getEventHandler(undefined, node, pathString);
- PolymerGestures.addEventListener(node, eventType, handler);
-
- if (oneTime)
- return;
-
- // TODO(rafaelw): This is really pointless work. Aside from the cost
- // of these allocations, NodeBind is going to setAttribute back to its
- // current value. Fixing this would mean changing the TemplateBinding
- // binding delegate API.
- function bindingValue() {
- return '{{ ' + pathString + ' }}';
- }
-
- return {
- open: bindingValue,
- discardChanges: bindingValue,
- close: function() {
- PolymerGestures.removeEventListener(node, eventType, handler);
- }
- };
- };
- }
- };
-
- var prefixLength = EVENT_PREFIX.length;
-
- // exports
- scope.api.declaration.events = events;
-
-})(Polymer);
-
-(function(scope) {
-
- // element api
-
- var observationBlacklist = ['attribute'];
-
- var properties = {
- inferObservers: function(prototype) {
- // called before prototype.observe is chained to inherited object
- var observe = prototype.observe, property;
- for (var n in prototype) {
- if (n.slice(-7) === 'Changed') {
- property = n.slice(0, -7);
- if (this.canObserveProperty(property)) {
- if (!observe) {
- observe = (prototype.observe = {});
- }
- observe[property] = observe[property] || n;
- }
- }
- }
- },
- canObserveProperty: function(property) {
- return (observationBlacklist.indexOf(property) < 0);
- },
- explodeObservers: function(prototype) {
- // called before prototype.observe is chained to inherited object
- var o = prototype.observe;
- if (o) {
- var exploded = {};
- for (var n in o) {
- var names = n.split(' ');
- for (var i=0, ni; ni=names[i]; i++) {
- exploded[ni] = o[n];
- }
- }
- prototype.observe = exploded;
- }
- },
- optimizePropertyMaps: function(prototype) {
- if (prototype.observe) {
- // construct name list
- var a = prototype._observeNames = [];
- for (var n in prototype.observe) {
- var names = n.split(' ');
- for (var i=0, ni; ni=names[i]; i++) {
- a.push(ni);
- }
- }
- }
- if (prototype.publish) {
- // construct name list
- var a = prototype._publishNames = [];
- for (var n in prototype.publish) {
- a.push(n);
- }
- }
- if (prototype.computed) {
- // construct name list
- var a = prototype._computedNames = [];
- for (var n in prototype.computed) {
- a.push(n);
- }
- }
- },
- publishProperties: function(prototype, base) {
- // if we have any properties to publish
- var publish = prototype.publish;
- if (publish) {
- // transcribe `publish` entries onto own prototype
- this.requireProperties(publish, prototype, base);
- // warn and remove accessor names that are broken on some browsers
- this.filterInvalidAccessorNames(publish);
- // construct map of lower-cased property names
- prototype._publishLC = this.lowerCaseMap(publish);
- }
- var computed = prototype.computed;
- if (computed) {
- // warn and remove accessor names that are broken on some browsers
- this.filterInvalidAccessorNames(computed);
- }
- },
- // Publishing/computing a property where the name might conflict with a
- // browser property is not currently supported to help users of Polymer
- // avoid browser bugs:
- //
- // https://code.google.com/p/chromium/issues/detail?id=43394
- // https://bugs.webkit.org/show_bug.cgi?id=49739
- //
- // We can lift this restriction when those bugs are fixed.
- filterInvalidAccessorNames: function(propertyNames) {
- for (var name in propertyNames) {
- // Check if the name is in our blacklist.
- if (this.propertyNameBlacklist[name]) {
- console.warn('Cannot define property "' + name + '" for element "' +
- this.name + '" because it has the same name as an HTMLElement ' +
- 'property, and not all browsers support overriding that. ' +
- 'Consider giving it a different name.');
- // Remove the invalid accessor from the list.
- delete propertyNames[name];
- }
- }
- },
- //
- // `name: value` entries in the `publish` object may need to generate
- // matching properties on the prototype.
- //
- // Values that are objects may have a `reflect` property, which
- // signals that the value describes property control metadata.
- // In metadata objects, the prototype default value (if any)
- // is encoded in the `value` property.
- //
- // publish: {
- // foo: 5,
- // bar: {value: true, reflect: true},
- // zot: {}
- // }
- //
- // `reflect` metadata property controls whether changes to the property
- // are reflected back to the attribute (default false).
- //
- // A value is stored on the prototype unless it's === `undefined`,
- // in which case the base chain is checked for a value.
- // If the basal value is also undefined, `null` is stored on the prototype.
- //
- // The reflection data is stored on another prototype object, `reflect`
- // which also can be specified directly.
- //
- // reflect: {
- // foo: true
- // }
- //
- requireProperties: function(propertyInfos, prototype, base) {
- // per-prototype storage for reflected properties
- prototype.reflect = prototype.reflect || {};
- // ensure a prototype value for each property
- // and update the property's reflect to attribute status
- for (var n in propertyInfos) {
- var value = propertyInfos[n];
- // value has metadata if it has a `reflect` property
- if (value && value.reflect !== undefined) {
- prototype.reflect[n] = Boolean(value.reflect);
- value = value.value;
- }
- // only set a value if one is specified
- if (value !== undefined) {
- prototype[n] = value;
- }
- }
- },
- lowerCaseMap: function(properties) {
- var map = {};
- for (var n in properties) {
- map[n.toLowerCase()] = n;
- }
- return map;
- },
- createPropertyAccessor: function(name, ignoreWrites) {
- var proto = this.prototype;
-
- var privateName = name + '_';
- var privateObservable = name + 'Observable_';
- proto[privateName] = proto[name];
-
- Object.defineProperty(proto, name, {
- get: function() {
- var observable = this[privateObservable];
- if (observable)
- observable.deliver();
-
- return this[privateName];
- },
- set: function(value) {
- if (ignoreWrites) {
- return this[privateName];
- }
-
- var observable = this[privateObservable];
- if (observable) {
- observable.setValue(value);
- return;
- }
-
- var oldValue = this[privateName];
- this[privateName] = value;
- this.emitPropertyChangeRecord(name, value, oldValue);
-
- return value;
- },
- configurable: true
- });
- },
- createPropertyAccessors: function(prototype) {
- var n$ = prototype._computedNames;
- if (n$ && n$.length) {
- for (var i=0, l=n$.length, n, fn; (i<l) && (n=n$[i]); i++) {
- this.createPropertyAccessor(n, true);
- }
- }
- var n$ = prototype._publishNames;
- if (n$ && n$.length) {
- for (var i=0, l=n$.length, n, fn; (i<l) && (n=n$[i]); i++) {
- // If the property is computed and published, the accessor is created
- // above.
- if (!prototype.computed || !prototype.computed[n]) {
- this.createPropertyAccessor(n);
- }
- }
- }
- },
- // This list contains some property names that people commonly want to use,
- // but won't work because of Chrome/Safari bugs. It isn't an exhaustive
- // list. In particular it doesn't contain any property names found on
- // subtypes of HTMLElement (e.g. name, value). Rather it attempts to catch
- // some common cases.
- propertyNameBlacklist: {
- children: 1,
- 'class': 1,
- id: 1,
- hidden: 1,
- style: 1,
- title: 1,
- }
- };
-
- // exports
-
- scope.api.declaration.properties = properties;
-
-})(Polymer);
-
-(function(scope) {
-
- // magic words
-
- var ATTRIBUTES_ATTRIBUTE = 'attributes';
- var ATTRIBUTES_REGEX = /\s|,/;
-
- // attributes api
-
- var attributes = {
-
- inheritAttributesObjects: function(prototype) {
- // chain our lower-cased publish map to the inherited version
- this.inheritObject(prototype, 'publishLC');
- // chain our instance attributes map to the inherited version
- this.inheritObject(prototype, '_instanceAttributes');
- },
-
- publishAttributes: function(prototype, base) {
- // merge names from 'attributes' attribute into the 'publish' object
- var attributes = this.getAttribute(ATTRIBUTES_ATTRIBUTE);
- if (attributes) {
- // create a `publish` object if needed.
- // the `publish` object is only relevant to this prototype, the
- // publishing logic in `declaration/properties.js` is responsible for
- // managing property values on the prototype chain.
- // TODO(sjmiles): the `publish` object is later chained to it's
- // ancestor object, presumably this is only for
- // reflection or other non-library uses.
- var publish = prototype.publish || (prototype.publish = {});
- // names='a b c' or names='a,b,c'
- var names = attributes.split(ATTRIBUTES_REGEX);
- // record each name for publishing
- for (var i=0, l=names.length, n; i<l; i++) {
- // remove excess ws
- n = names[i].trim();
- // looks weird, but causes n to exist on `publish` if it does not;
- // a more careful test would need expensive `in` operator
- if (n && publish[n] === undefined) {
- publish[n] = undefined;
- }
- }
- }
- },
-
- // record clonable attributes from <element>
- accumulateInstanceAttributes: function() {
- // inherit instance attributes
- var clonable = this.prototype._instanceAttributes;
- // merge attributes from element
- var a$ = this.attributes;
- for (var i=0, l=a$.length, a; (i<l) && (a=a$[i]); i++) {
- if (this.isInstanceAttribute(a.name)) {
- clonable[a.name] = a.value;
- }
- }
- },
-
- isInstanceAttribute: function(name) {
- return !this.blackList[name] && name.slice(0,3) !== 'on-';
- },
-
- // do not clone these attributes onto instances
- blackList: {
- name: 1,
- 'extends': 1,
- constructor: 1,
- noscript: 1,
- assetpath: 1,
- 'cache-csstext': 1
- }
-
- };
-
- // add ATTRIBUTES_ATTRIBUTE to the blacklist
- attributes.blackList[ATTRIBUTES_ATTRIBUTE] = 1;
-
- // exports
-
- scope.api.declaration.attributes = attributes;
-
-})(Polymer);
-
-(function(scope) {
-
- // imports
- var events = scope.api.declaration.events;
-
- var syntax = new PolymerExpressions();
- var prepareBinding = syntax.prepareBinding;
-
- // Polymer takes a first crack at the binding to see if it's a declarative
- // event handler.
- syntax.prepareBinding = function(pathString, name, node) {
- return events.prepareEventBinding(pathString, name, node) ||
- prepareBinding.call(syntax, pathString, name, node);
- };
-
- // declaration api supporting mdv
- var mdv = {
- syntax: syntax,
- fetchTemplate: function() {
- return this.querySelector('template');
- },
- templateContent: function() {
- var template = this.fetchTemplate();
- return template && template.content;
- },
- installBindingDelegate: function(template) {
- if (template) {
- template.bindingDelegate = this.syntax;
- }
- }
- };
-
- // exports
- scope.api.declaration.mdv = mdv;
-
-})(Polymer);
-
-(function(scope) {
-
- // imports
-
- var api = scope.api;
- var isBase = scope.isBase;
- var extend = scope.extend;
-
- var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
-
- // prototype api
-
- var prototype = {
-
- register: function(name, extendeeName) {
- // build prototype combining extendee, Polymer base, and named api
- this.buildPrototype(name, extendeeName);
- // register our custom element with the platform
- this.registerPrototype(name, extendeeName);
- // reference constructor in a global named by 'constructor' attribute
- this.publishConstructor();
- },
-
- buildPrototype: function(name, extendeeName) {
- // get our custom prototype (before chaining)
- var extension = scope.getRegisteredPrototype(name);
- // get basal prototype
- var base = this.generateBasePrototype(extendeeName);
- // implement declarative features
- this.desugarBeforeChaining(extension, base);
- // join prototypes
- this.prototype = this.chainPrototypes(extension, base);
- // more declarative features
- this.desugarAfterChaining(name, extendeeName);
- },
-
- desugarBeforeChaining: function(prototype, base) {
- // back reference declaration element
- // TODO(sjmiles): replace `element` with `elementElement` or `declaration`
- prototype.element = this;
- // transcribe `attributes` declarations onto own prototype's `publish`
- this.publishAttributes(prototype, base);
- // `publish` properties to the prototype and to attribute watch
- this.publishProperties(prototype, base);
- // infer observers for `observe` list based on method names
- this.inferObservers(prototype);
- // desugar compound observer syntax, e.g. 'a b c'
- this.explodeObservers(prototype);
- },
-
- chainPrototypes: function(prototype, base) {
- // chain various meta-data objects to inherited versions
- this.inheritMetaData(prototype, base);
- // chain custom api to inherited
- var chained = this.chainObject(prototype, base);
- // x-platform fixup
- ensurePrototypeTraversal(chained);
- return chained;
- },
-
- inheritMetaData: function(prototype, base) {
- // chain observe object to inherited
- this.inheritObject('observe', prototype, base);
- // chain publish object to inherited
- this.inheritObject('publish', prototype, base);
- // chain reflect object to inherited
- this.inheritObject('reflect', prototype, base);
- // chain our lower-cased publish map to the inherited version
- this.inheritObject('_publishLC', prototype, base);
- // chain our instance attributes map to the inherited version
- this.inheritObject('_instanceAttributes', prototype, base);
- // chain our event delegates map to the inherited version
- this.inheritObject('eventDelegates', prototype, base);
- },
-
- // implement various declarative features
- desugarAfterChaining: function(name, extendee) {
- // build side-chained lists to optimize iterations
- this.optimizePropertyMaps(this.prototype);
- this.createPropertyAccessors(this.prototype);
- // install mdv delegate on template
- this.installBindingDelegate(this.fetchTemplate());
- // install external stylesheets as if they are inline
- this.installSheets();
- // adjust any paths in dom from imports
- this.resolveElementPaths(this);
- // compile list of attributes to copy to instances
- this.accumulateInstanceAttributes();
- // parse on-* delegates declared on `this` element
- this.parseHostEvents();
- //
- // install a helper method this.resolvePath to aid in
- // setting resource urls. e.g.
- // this.$.image.src = this.resolvePath('images/foo.png')
- this.addResolvePathApi();
- // under ShadowDOMPolyfill, transforms to approximate missing CSS features
- if (hasShadowDOMPolyfill) {
- WebComponents.ShadowCSS.shimStyling(this.templateContent(), name,
- extendee);
- }
- // allow custom element access to the declarative context
- if (this.prototype.registerCallback) {
- this.prototype.registerCallback(this);
- }
- },
-
- // if a named constructor is requested in element, map a reference
- // to the constructor to the given symbol
- publishConstructor: function() {
- var symbol = this.getAttribute('constructor');
- if (symbol) {
- window[symbol] = this.ctor;
- }
- },
-
- // build prototype combining extendee, Polymer base, and named api
- generateBasePrototype: function(extnds) {
- var prototype = this.findBasePrototype(extnds);
- if (!prototype) {
- // create a prototype based on tag-name extension
- var prototype = HTMLElement.getPrototypeForTag(extnds);
- // insert base api in inheritance chain (if needed)
- prototype = this.ensureBaseApi(prototype);
- // memoize this base
- memoizedBases[extnds] = prototype;
- }
- return prototype;
- },
-
- findBasePrototype: function(name) {
- return memoizedBases[name];
- },
-
- // install Polymer instance api into prototype chain, as needed
- ensureBaseApi: function(prototype) {
- if (prototype.PolymerBase) {
- return prototype;
- }
- var extended = Object.create(prototype);
- // we need a unique copy of base api for each base prototype
- // therefore we 'extend' here instead of simply chaining
- api.publish(api.instance, extended);
- // TODO(sjmiles): sharing methods across prototype chains is
- // not supported by 'super' implementation which optimizes
- // by memoizing prototype relationships.
- // Probably we should have a version of 'extend' that is
- // share-aware: it could study the text of each function,
- // look for usage of 'super', and wrap those functions in
- // closures.
- // As of now, there is only one problematic method, so
- // we just patch it manually.
- // To avoid re-entrancy problems, the special super method
- // installed is called `mixinSuper` and the mixin method
- // must use this method instead of the default `super`.
- this.mixinMethod(extended, prototype, api.instance.mdv, 'bind');
- // return buffed-up prototype
- return extended;
- },
-
- mixinMethod: function(extended, prototype, api, name) {
- var $super = function(args) {
- return prototype[name].apply(this, args);
- };
- extended[name] = function() {
- this.mixinSuper = $super;
- return api[name].apply(this, arguments);
- }
- },
-
- // ensure prototype[name] inherits from a prototype.prototype[name]
- inheritObject: function(name, prototype, base) {
- // require an object
- var source = prototype[name] || {};
- // chain inherited properties onto a new object
- prototype[name] = this.chainObject(source, base[name]);
- },
-
- // register 'prototype' to custom element 'name', store constructor
- registerPrototype: function(name, extendee) {
- var info = {
- prototype: this.prototype
- }
- // native element must be specified in extends
- var typeExtension = this.findTypeExtension(extendee);
- if (typeExtension) {
- info.extends = typeExtension;
- }
- // register the prototype with HTMLElement for name lookup
- HTMLElement.register(name, this.prototype);
- // register the custom type
- this.ctor = document.registerElement(name, info);
- },
-
- findTypeExtension: function(name) {
- if (name && name.indexOf('-') < 0) {
- return name;
- } else {
- var p = this.findBasePrototype(name);
- if (p.element) {
- return this.findTypeExtension(p.element.extends);
- }
- }
- }
-
- };
-
- // memoize base prototypes
- var memoizedBases = {};
-
- // implementation of 'chainObject' depends on support for __proto__
- if (Object.__proto__) {
- prototype.chainObject = function(object, inherited) {
- if (object && inherited && object !== inherited) {
- object.__proto__ = inherited;
- }
- return object;
- }
- } else {
- prototype.chainObject = function(object, inherited) {
- if (object && inherited && object !== inherited) {
- var chained = Object.create(inherited);
- object = extend(chained, object);
- }
- return object;
- }
- }
-
- // On platforms that do not support __proto__ (versions of IE), the prototype
- // chain of a custom element is simulated via installation of __proto__.
- // Although custom elements manages this, we install it here so it's
- // available during desugaring.
- function ensurePrototypeTraversal(prototype) {
- if (!Object.__proto__) {
- var ancestor = Object.getPrototypeOf(prototype);
- prototype.__proto__ = ancestor;
- if (isBase(ancestor)) {
- ancestor.__proto__ = Object.getPrototypeOf(ancestor);
- }
- }
- }
-
- // exports
-
- api.declaration.prototype = prototype;
-
-})(Polymer);
-
-(function(scope) {
-
- /*
-
- Elements are added to a registration queue so that they register in
- the proper order at the appropriate time. We do this for a few reasons:
-
- * to enable elements to load resources (like stylesheets)
- asynchronously. We need to do this until the platform provides an efficient
- alternative. One issue is that remote @import stylesheets are
- re-fetched whenever stamped into a shadowRoot.
-
- * to ensure elements loaded 'at the same time' (e.g. via some set of
- imports) are registered as a batch. This allows elements to be enured from
- upgrade ordering as long as they query the dom tree 1 task after
- upgrade (aka domReady). This is a performance tradeoff. On the one hand,
- elements that could register while imports are loading are prevented from
- doing so. On the other, grouping upgrades into a single task means less
- incremental work (for example style recalcs), Also, we can ensure the
- document is in a known state at the single quantum of time when
- elements upgrade.
-
- */
- var queue = {
-
- // tell the queue to wait for an element to be ready
- wait: function(element) {
- if (!element.__queue) {
- element.__queue = {};
- elements.push(element);
- }
- },
-
- // enqueue an element to the next spot in the queue.
- enqueue: function(element, check, go) {
- var shouldAdd = element.__queue && !element.__queue.check;
- if (shouldAdd) {
- queueForElement(element).push(element);
- element.__queue.check = check;
- element.__queue.go = go;
- }
- return (this.indexOf(element) !== 0);
- },
-
- indexOf: function(element) {
- var i = queueForElement(element).indexOf(element);
- if (i >= 0 && document.contains(element)) {
- i += (HTMLImports.useNative || HTMLImports.ready) ?
- importQueue.length : 1e9;
- }
- return i;
- },
-
- // tell the queue an element is ready to be registered
- go: function(element) {
- var readied = this.remove(element);
- if (readied) {
- element.__queue.flushable = true;
- this.addToFlushQueue(readied);
- this.check();
- }
- },
-
- remove: function(element) {
- var i = this.indexOf(element);
- if (i !== 0) {
- //console.warn('queue order wrong', i);
- return;
- }
- return queueForElement(element).shift();
- },
-
- check: function() {
- // next
- var element = this.nextElement();
- if (element) {
- element.__queue.check.call(element);
- }
- if (this.canReady()) {
- this.ready();
- return true;
- }
- },
-
- nextElement: function() {
- return nextQueued();
- },
-
- canReady: function() {
- return !this.waitToReady && this.isEmpty();
- },
-
- isEmpty: function() {
- for (var i=0, l=elements.length, e; (i<l) &&
- (e=elements[i]); i++) {
- if (e.__queue && !e.__queue.flushable) {
- return;
- }
- }
- return true;
- },
-
- addToFlushQueue: function(element) {
- flushQueue.push(element);
- },
-
- flush: function() {
- // prevent re-entrance
- if (this.flushing) {
- return;
- }
- this.flushing = true;
- var element;
- while (flushQueue.length) {
- element = flushQueue.shift();
- element.__queue.go.call(element);
- element.__queue = null;
- }
- this.flushing = false;
- },
-
- ready: function() {
- // 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.
- var polyfillWasReady = CustomElements.ready;
- CustomElements.ready = false;
- this.flush();
- if (!CustomElements.useNative) {
- CustomElements.upgradeDocumentTree(document);
- }
- CustomElements.ready = polyfillWasReady;
- Polymer.flush();
- requestAnimationFrame(this.flushReadyCallbacks);
- },
-
- addReadyCallback: function(callback) {
- if (callback) {
- readyCallbacks.push(callback);
- }
- },
-
- flushReadyCallbacks: function() {
- if (readyCallbacks) {
- var fn;
- while (readyCallbacks.length) {
- fn = readyCallbacks.shift();
- fn();
- }
- }
- },
-
- /**
- Returns a list of elements that have had polymer-elements created but
- are not yet ready to register. The list is an array of element definitions.
- */
- waitingFor: function() {
- var e$ = [];
- for (var i=0, l=elements.length, e; (i<l) &&
- (e=elements[i]); i++) {
- if (e.__queue && !e.__queue.flushable) {
- e$.push(e);
- }
- }
- return e$;
- },
-
- waitToReady: true
-
- };
-
- var elements = [];
- var flushQueue = [];
- var importQueue = [];
- var mainQueue = [];
- var readyCallbacks = [];
-
- function queueForElement(element) {
- return document.contains(element) ? mainQueue : importQueue;
- }
-
- function nextQueued() {
- return importQueue.length ? importQueue[0] : mainQueue[0];
- }
-
- function whenReady(callback) {
- queue.waitToReady = true;
- Polymer.endOfMicrotask(function() {
- HTMLImports.whenReady(function() {
- queue.addReadyCallback(callback);
- queue.waitToReady = false;
- queue.check();
- });
- });
- }
-
- /**
- Forces polymer to register any pending elements. Can be used to abort
- waiting for elements that are partially defined.
- @param timeout {Integer} Optional timeout in milliseconds
- */
- function forceReady(timeout) {
- if (timeout === undefined) {
- queue.ready();
- return;
- }
- var handle = setTimeout(function() {
- queue.ready();
- }, timeout);
- Polymer.whenReady(function() {
- clearTimeout(handle);
- });
- }
-
- // exports
- scope.elements = elements;
- scope.waitingFor = queue.waitingFor.bind(queue);
- scope.forceReady = forceReady;
- scope.queue = queue;
- scope.whenReady = scope.whenPolymerReady = whenReady;
-})(Polymer);
-
-(function(scope) {
-
- // imports
-
- var extend = scope.extend;
- var api = scope.api;
- var queue = scope.queue;
- var whenReady = scope.whenReady;
- var getRegisteredPrototype = scope.getRegisteredPrototype;
- var waitingForPrototype = scope.waitingForPrototype;
-
- // declarative implementation: <polymer-element>
-
- var prototype = extend(Object.create(HTMLElement.prototype), {
-
- createdCallback: function() {
- if (this.getAttribute('name')) {
- this.init();
- }
- },
-
- init: function() {
- // fetch declared values
- this.name = this.getAttribute('name');
- this.extends = this.getAttribute('extends');
- queue.wait(this);
- // initiate any async resource fetches
- this.loadResources();
- // register when all constraints are met
- this.registerWhenReady();
- },
-
- // TODO(sorvell): we currently queue in the order the prototypes are
- // registered, but we should queue in the order that polymer-elements
- // are registered. We are currently blocked from doing this based on
- // crbug.com/395686.
- registerWhenReady: function() {
- if (this.registered
- || this.waitingForPrototype(this.name)
- || this.waitingForQueue()
- || this.waitingForResources()) {
- return;
- }
- queue.go(this);
- },
-
- _register: function() {
- //console.log('registering', this.name);
- // warn if extending from a custom element not registered via Polymer
- if (isCustomTag(this.extends) && !isRegistered(this.extends)) {
- console.warn('%s is attempting to extend %s, an unregistered element ' +
- 'or one that was not registered with Polymer.', this.name,
- this.extends);
- }
- this.register(this.name, this.extends);
- this.registered = true;
- },
-
- waitingForPrototype: function(name) {
- if (!getRegisteredPrototype(name)) {
- // then wait for a prototype
- waitingForPrototype(name, this);
- // emulate script if user is not supplying one
- this.handleNoScript(name);
- // prototype not ready yet
- return true;
- }
- },
-
- handleNoScript: function(name) {
- // if explicitly marked as 'noscript'
- if (this.hasAttribute('noscript') && !this.noscript) {
- this.noscript = true;
- // imperative element registration
- Polymer(name);
- }
- },
-
- waitingForResources: function() {
- return this._needsResources;
- },
-
- // NOTE: Elements must be queued in proper order for inheritance/composition
- // dependency resolution. Previously this was enforced for inheritance,
- // and by rule for composition. It's now entirely by rule.
- waitingForQueue: function() {
- return queue.enqueue(this, this.registerWhenReady, this._register);
- },
-
- loadResources: function() {
- this._needsResources = true;
- this.loadStyles(function() {
- this._needsResources = false;
- this.registerWhenReady();
- }.bind(this));
- }
-
- });
-
- // semi-pluggable APIs
-
- // TODO(sjmiles): should be fully pluggable (aka decoupled, currently
- // the various plugins are allowed to depend on each other directly)
- api.publish(api.declaration, prototype);
-
- // utility and bookkeeping
-
- function isRegistered(name) {
- return Boolean(HTMLElement.getPrototypeForTag(name));
- }
-
- function isCustomTag(name) {
- return (name && name.indexOf('-') >= 0);
- }
-
- // boot tasks
-
- whenReady(function() {
- document.body.removeAttribute('unresolved');
- document.dispatchEvent(
- new CustomEvent('polymer-ready', {bubbles: true})
- );
- });
-
- // register polymer-element with document
-
- document.registerElement('polymer-element', {prototype: prototype});
-
-})(Polymer);
-
-(function(scope) {
-
-/**
- * @class Polymer
- */
-
-var whenReady = scope.whenReady;
-
-/**
- * Loads the set of HTMLImports contained in `node`. Notifies when all
- * the imports have loaded by calling the `callback` function argument.
- * This method can be used to lazily load imports. For example, given a
- * template:
- *
- * <template>
- * <link rel="import" href="my-import1.html">
- * <link rel="import" href="my-import2.html">
- * </template>
- *
- * Polymer.importElements(template.content, function() {
- * console.log('imports lazily loaded');
- * });
- *
- * @method importElements
- * @param {Node} node Node containing the HTMLImports to load.
- * @param {Function} callback Callback called when all imports have loaded.
- */
-function importElements(node, callback) {
- if (node) {
- document.head.appendChild(node);
- whenReady(callback);
- } else if (callback) {
- callback();
- }
-}
-
-/**
- * Loads an HTMLImport for each url specified in the `urls` array.
- * Notifies when all the imports have loaded by calling the `callback`
- * function argument. This method can be used to lazily load imports.
- * For example,
- *
- * Polymer.import(['my-import1.html', 'my-import2.html'], function() {
- * console.log('imports lazily loaded');
- * });
- *
- * @method import
- * @param {Array} urls Array of urls to load as HTMLImports.
- * @param {Function} callback Callback called when all imports have loaded.
- */
-function _import(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 = _import;
-scope.importElements = importElements;
-
-})(Polymer);
-
-/**
- * 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.
- * The `auto-binding` element itself serves as the model and controller for the
- * elements it contains. Both data and event handlers can be bound.
- *
- * The `auto-binding` element acts just like a template that is bound to
- * a model. It stamps its content in the dom adjacent to itself. When the
- * content is stamped, the `template-bound` event is fired.
- *
- * Example:
- *
- * <template is="auto-binding">
- * <div>Say something: <input value="{{value}}"></div>
- * <div>You said: {{value}}</div>
- * <button on-tap="{{buttonTap}}">Tap me!</button>
- * </template>
- * <script>
- * var template = document.querySelector('template');
- * template.value = 'something';
- * template.buttonTap = function() {
- * console.log('tap!');
- * };
- * </script>
- *
- * @module Polymer
- * @status stable
-*/
-
-(function() {
-
- var element = document.createElement('polymer-element');
- element.setAttribute('name', 'auto-binding');
- element.setAttribute('extends', 'template');
- element.init();
-
- Polymer('auto-binding', {
-
- createdCallback: function() {
- this.syntax = this.bindingDelegate = this.makeSyntax();
- // delay stamping until polymer-ready so that auto-binding is not
- // required to load last.
- Polymer.whenPolymerReady(function() {
- this.model = this;
- this.setAttribute('bind', '');
- // we don't bother with an explicit signal here, we could ust a MO
- // if necessary
- this.async(function() {
- // note: this will marshall *all* the elements in the parentNode
- // rather than just stamped ones. We'd need to use createInstance
- // to fix this or something else fancier.
- this.marshalNodeReferences(this.parentNode);
- // template stamping is asynchronous so stamping isn't complete
- // by polymer-ready; fire an event so users can use stamped elements
- this.fire('template-bound');
- });
- }.bind(this));
- },
-
- makeSyntax: function() {
- var events = Object.create(Polymer.api.declaration.events);
- var self = this;
- events.findController = function() { return self.model; };
-
- var syntax = new PolymerExpressions();
- var prepareBinding = syntax.prepareBinding;
- syntax.prepareBinding = function(pathString, name, node) {
- return events.prepareEventBinding(pathString, name, node) ||
- prepareBinding.call(syntax, pathString, name, node);
- };
- return syntax;
- }
-
- });
-
-})();
« no previous file with comments | « packages/polymer_interop/lib/src/js/polymer.html ('k') | packages/polymer_interop/lib/src/js/polymer.min.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698