Home | History | Annotate | Download | only in polymer
      1 /**
      2  * @license
      3  * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
      4  * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
      5  * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
      6  * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
      7  * Code distributed by Google as part of the polymer project is also
      8  * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
      9  */
     10 // @version 0.5.5
     11 window.PolymerGestures = {};
     12 
     13 (function(scope) {
     14   var hasFullPath = false;
     15 
     16   // test for full event path support
     17   var pathTest = document.createElement('meta');
     18   if (pathTest.createShadowRoot) {
     19     var sr = pathTest.createShadowRoot();
     20     var s = document.createElement('span');
     21     sr.appendChild(s);
     22     pathTest.addEventListener('testpath', function(ev) {
     23       if (ev.path) {
     24         // if the span is in the event path, then path[0] is the real source for all events
     25         hasFullPath = ev.path[0] === s;
     26       }
     27       ev.stopPropagation();
     28     });
     29     var ev = new CustomEvent('testpath', {bubbles: true});
     30     // must add node to DOM to trigger event listener
     31     document.head.appendChild(pathTest);
     32     s.dispatchEvent(ev);
     33     pathTest.parentNode.removeChild(pathTest);
     34     sr = s = null;
     35   }
     36   pathTest = null;
     37 
     38   var target = {
     39     shadow: function(inEl) {
     40       if (inEl) {
     41         return inEl.shadowRoot || inEl.webkitShadowRoot;
     42       }
     43     },
     44     canTarget: function(shadow) {
     45       return shadow && Boolean(shadow.elementFromPoint);
     46     },
     47     targetingShadow: function(inEl) {
     48       var s = this.shadow(inEl);
     49       if (this.canTarget(s)) {
     50         return s;
     51       }
     52     },
     53     olderShadow: function(shadow) {
     54       var os = shadow.olderShadowRoot;
     55       if (!os) {
     56         var se = shadow.querySelector('shadow');
     57         if (se) {
     58           os = se.olderShadowRoot;
     59         }
     60       }
     61       return os;
     62     },
     63     allShadows: function(element) {
     64       var shadows = [], s = this.shadow(element);
     65       while(s) {
     66         shadows.push(s);
     67         s = this.olderShadow(s);
     68       }
     69       return shadows;
     70     },
     71     searchRoot: function(inRoot, x, y) {
     72       var t, st, sr, os;
     73       if (inRoot) {
     74         t = inRoot.elementFromPoint(x, y);
     75         if (t) {
     76           // found element, check if it has a ShadowRoot
     77           sr = this.targetingShadow(t);
     78         } else if (inRoot !== document) {
     79           // check for sibling roots
     80           sr = this.olderShadow(inRoot);
     81         }
     82         // search other roots, fall back to light dom element
     83         return this.searchRoot(sr, x, y) || t;
     84       }
     85     },
     86     owner: function(element) {
     87       if (!element) {
     88         return document;
     89       }
     90       var s = element;
     91       // walk up until you hit the shadow root or document
     92       while (s.parentNode) {
     93         s = s.parentNode;
     94       }
     95       // the owner element is expected to be a Document or ShadowRoot
     96       if (s.nodeType != Node.DOCUMENT_NODE && s.nodeType != Node.DOCUMENT_FRAGMENT_NODE) {
     97         s = document;
     98       }
     99       return s;
    100     },
    101     findTarget: function(inEvent) {
    102       if (hasFullPath && inEvent.path && inEvent.path.length) {
    103         return inEvent.path[0];
    104       }
    105       var x = inEvent.clientX, y = inEvent.clientY;
    106       // if the listener is in the shadow root, it is much faster to start there
    107       var s = this.owner(inEvent.target);
    108       // if x, y is not in this root, fall back to document search
    109       if (!s.elementFromPoint(x, y)) {
    110         s = document;
    111       }
    112       return this.searchRoot(s, x, y);
    113     },
    114     findTouchAction: function(inEvent) {
    115       var n;
    116       if (hasFullPath && inEvent.path && inEvent.path.length) {
    117         var path = inEvent.path;
    118         for (var i = 0; i < path.length; i++) {
    119           n = path[i];
    120           if (n.nodeType === Node.ELEMENT_NODE && n.hasAttribute('touch-action')) {
    121             return n.getAttribute('touch-action');
    122           }
    123         }
    124       } else {
    125         n = inEvent.target;
    126         while(n) {
    127           if (n.nodeType === Node.ELEMENT_NODE && n.hasAttribute('touch-action')) {
    128             return n.getAttribute('touch-action');
    129           }
    130           n = n.parentNode || n.host;
    131         }
    132       }
    133       // auto is default
    134       return "auto";
    135     },
    136     LCA: function(a, b) {
    137       if (a === b) {
    138         return a;
    139       }
    140       if (a && !b) {
    141         return a;
    142       }
    143       if (b && !a) {
    144         return b;
    145       }
    146       if (!b && !a) {
    147         return document;
    148       }
    149       // fast case, a is a direct descendant of b or vice versa
    150       if (a.contains && a.contains(b)) {
    151         return a;
    152       }
    153       if (b.contains && b.contains(a)) {
    154         return b;
    155       }
    156       var adepth = this.depth(a);
    157       var bdepth = this.depth(b);
    158       var d = adepth - bdepth;
    159       if (d >= 0) {
    160         a = this.walk(a, d);
    161       } else {
    162         b = this.walk(b, -d);
    163       }
    164       while (a && b && a !== b) {
    165         a = a.parentNode || a.host;
    166         b = b.parentNode || b.host;
    167       }
    168       return a;
    169     },
    170     walk: function(n, u) {
    171       for (var i = 0; n && (i < u); i++) {
    172         n = n.parentNode || n.host;
    173       }
    174       return n;
    175     },
    176     depth: function(n) {
    177       var d = 0;
    178       while(n) {
    179         d++;
    180         n = n.parentNode || n.host;
    181       }
    182       return d;
    183     },
    184     deepContains: function(a, b) {
    185       var common = this.LCA(a, b);
    186       // if a is the common ancestor, it must "deeply" contain b
    187       return common === a;
    188     },
    189     insideNode: function(node, x, y) {
    190       var rect = node.getBoundingClientRect();
    191       return (rect.left <= x) && (x <= rect.right) && (rect.top <= y) && (y <= rect.bottom);
    192     },
    193     path: function(event) {
    194       var p;
    195       if (hasFullPath && event.path && event.path.length) {
    196         p = event.path;
    197       } else {
    198         p = [];
    199         var n = this.findTarget(event);
    200         while (n) {
    201           p.push(n);
    202           n = n.parentNode || n.host;
    203         }
    204       }
    205       return p;
    206     }
    207   };
    208   scope.targetFinding = target;
    209   /**
    210    * Given an event, finds the "deepest" node that could have been the original target before ShadowDOM retargetting
    211    *
    212    * @param {Event} Event An event object with clientX and clientY properties
    213    * @return {Element} The probable event origninator
    214    */
    215   scope.findTarget = target.findTarget.bind(target);
    216   /**
    217    * Determines if the "container" node deeply contains the "containee" node, including situations where the "containee" is contained by one or more ShadowDOM
    218    * roots.
    219    *
    220    * @param {Node} container
    221    * @param {Node} containee
    222    * @return {Boolean}
    223    */
    224   scope.deepContains = target.deepContains.bind(target);
    225 
    226   /**
    227    * Determines if the x/y position is inside the given node.
    228    *
    229    * Example:
    230    *
    231    *     function upHandler(event) {
    232    *       var innode = PolymerGestures.insideNode(event.target, event.clientX, event.clientY);
    233    *       if (innode) {
    234    *         // wait for tap?
    235    *       } else {
    236    *         // tap will never happen
    237    *       }
    238    *     }
    239    *
    240    * @param {Node} node
    241    * @param {Number} x Screen X position
    242    * @param {Number} y screen Y position
    243    * @return {Boolean}
    244    */
    245   scope.insideNode = target.insideNode;
    246 
    247 })(window.PolymerGestures);
    248 
    249 (function() {
    250   function shadowSelector(v) {
    251     return 'html /deep/ ' + selector(v);
    252   }
    253   function selector(v) {
    254     return '[touch-action="' + v + '"]';
    255   }
    256   function rule(v) {
    257     return '{ -ms-touch-action: ' + v + '; touch-action: ' + v + ';}';
    258   }
    259   var attrib2css = [
    260     'none',
    261     'auto',
    262     'pan-x',
    263     'pan-y',
    264     {
    265       rule: 'pan-x pan-y',
    266       selectors: [
    267         'pan-x pan-y',
    268         'pan-y pan-x'
    269       ]
    270     },
    271     'manipulation'
    272   ];
    273   var styles = '';
    274   // only install stylesheet if the browser has touch action support
    275   var hasTouchAction = typeof document.head.style.touchAction === 'string';
    276   // only add shadow selectors if shadowdom is supported
    277   var hasShadowRoot = !window.ShadowDOMPolyfill && document.head.createShadowRoot;
    278 
    279   if (hasTouchAction) {
    280     attrib2css.forEach(function(r) {
    281       if (String(r) === r) {
    282         styles += selector(r) + rule(r) + '\n';
    283         if (hasShadowRoot) {
    284           styles += shadowSelector(r) + rule(r) + '\n';
    285         }
    286       } else {
    287         styles += r.selectors.map(selector) + rule(r.rule) + '\n';
    288         if (hasShadowRoot) {
    289           styles += r.selectors.map(shadowSelector) + rule(r.rule) + '\n';
    290         }
    291       }
    292     });
    293 
    294     var el = document.createElement('style');
    295     el.textContent = styles;
    296     document.head.appendChild(el);
    297   }
    298 })();
    299 
    300 /**
    301  * This is the constructor for new PointerEvents.
    302  *
    303  * New Pointer Events must be given a type, and an optional dictionary of
    304  * initialization properties.
    305  *
    306  * Due to certain platform requirements, events returned from the constructor
    307  * identify as MouseEvents.
    308  *
    309  * @constructor
    310  * @param {String} inType The type of the event to create.
    311  * @param {Object} [inDict] An optional dictionary of initial event properties.
    312  * @return {Event} A new PointerEvent of type `inType` and initialized with properties from `inDict`.
    313  */
    314 (function(scope) {
    315 
    316   var MOUSE_PROPS = [
    317     'bubbles',
    318     'cancelable',
    319     'view',
    320     'detail',
    321     'screenX',
    322     'screenY',
    323     'clientX',
    324     'clientY',
    325     'ctrlKey',
    326     'altKey',
    327     'shiftKey',
    328     'metaKey',
    329     'button',
    330     'relatedTarget',
    331     'pageX',
    332     'pageY'
    333   ];
    334 
    335   var MOUSE_DEFAULTS = [
    336     false,
    337     false,
    338     null,
    339     null,
    340     0,
    341     0,
    342     0,
    343     0,
    344     false,
    345     false,
    346     false,
    347     false,
    348     0,
    349     null,
    350     0,
    351     0
    352   ];
    353 
    354   var NOP_FACTORY = function(){ return function(){}; };
    355 
    356   var eventFactory = {
    357     // TODO(dfreedm): this is overridden by tap recognizer, needs review
    358     preventTap: NOP_FACTORY,
    359     makeBaseEvent: function(inType, inDict) {
    360       var e = document.createEvent('Event');
    361       e.initEvent(inType, inDict.bubbles || false, inDict.cancelable || false);
    362       e.preventTap = eventFactory.preventTap(e);
    363       return e;
    364     },
    365     makeGestureEvent: function(inType, inDict) {
    366       inDict = inDict || Object.create(null);
    367 
    368       var e = this.makeBaseEvent(inType, inDict);
    369       for (var i = 0, keys = Object.keys(inDict), k; i < keys.length; i++) {
    370         k = keys[i];
    371         if( k !== 'bubbles' && k !== 'cancelable' ) {
    372            e[k] = inDict[k];
    373         }
    374       }
    375       return e;
    376     },
    377     makePointerEvent: function(inType, inDict) {
    378       inDict = inDict || Object.create(null);
    379 
    380       var e = this.makeBaseEvent(inType, inDict);
    381       // define inherited MouseEvent properties
    382       for(var i = 2, p; i < MOUSE_PROPS.length; i++) {
    383         p = MOUSE_PROPS[i];
    384         e[p] = inDict[p] || MOUSE_DEFAULTS[i];
    385       }
    386       e.buttons = inDict.buttons || 0;
    387 
    388       // Spec requires that pointers without pressure specified use 0.5 for down
    389       // state and 0 for up state.
    390       var pressure = 0;
    391       if (inDict.pressure) {
    392         pressure = inDict.pressure;
    393       } else {
    394         pressure = e.buttons ? 0.5 : 0;
    395       }
    396 
    397       // add x/y properties aliased to clientX/Y
    398       e.x = e.clientX;
    399       e.y = e.clientY;
    400 
    401       // define the properties of the PointerEvent interface
    402       e.pointerId = inDict.pointerId || 0;
    403       e.width = inDict.width || 0;
    404       e.height = inDict.height || 0;
    405       e.pressure = pressure;
    406       e.tiltX = inDict.tiltX || 0;
    407       e.tiltY = inDict.tiltY || 0;
    408       e.pointerType = inDict.pointerType || '';
    409       e.hwTimestamp = inDict.hwTimestamp || 0;
    410       e.isPrimary = inDict.isPrimary || false;
    411       e._source = inDict._source || '';
    412       return e;
    413     }
    414   };
    415 
    416   scope.eventFactory = eventFactory;
    417 })(window.PolymerGestures);
    418 
    419 /**
    420  * This module implements an map of pointer states
    421  */
    422 (function(scope) {
    423   var USE_MAP = window.Map && window.Map.prototype.forEach;
    424   var POINTERS_FN = function(){ return this.size; };
    425   function PointerMap() {
    426     if (USE_MAP) {
    427       var m = new Map();
    428       m.pointers = POINTERS_FN;
    429       return m;
    430     } else {
    431       this.keys = [];
    432       this.values = [];
    433     }
    434   }
    435 
    436   PointerMap.prototype = {
    437     set: function(inId, inEvent) {
    438       var i = this.keys.indexOf(inId);
    439       if (i > -1) {
    440         this.values[i] = inEvent;
    441       } else {
    442         this.keys.push(inId);
    443         this.values.push(inEvent);
    444       }
    445     },
    446     has: function(inId) {
    447       return this.keys.indexOf(inId) > -1;
    448     },
    449     'delete': function(inId) {
    450       var i = this.keys.indexOf(inId);
    451       if (i > -1) {
    452         this.keys.splice(i, 1);
    453         this.values.splice(i, 1);
    454       }
    455     },
    456     get: function(inId) {
    457       var i = this.keys.indexOf(inId);
    458       return this.values[i];
    459     },
    460     clear: function() {
    461       this.keys.length = 0;
    462       this.values.length = 0;
    463     },
    464     // return value, key, map
    465     forEach: function(callback, thisArg) {
    466       this.values.forEach(function(v, i) {
    467         callback.call(thisArg, v, this.keys[i], this);
    468       }, this);
    469     },
    470     pointers: function() {
    471       return this.keys.length;
    472     }
    473   };
    474 
    475   scope.PointerMap = PointerMap;
    476 })(window.PolymerGestures);
    477 
    478 (function(scope) {
    479   var CLONE_PROPS = [
    480     // MouseEvent
    481     'bubbles',
    482     'cancelable',
    483     'view',
    484     'detail',
    485     'screenX',
    486     'screenY',
    487     'clientX',
    488     'clientY',
    489     'ctrlKey',
    490     'altKey',
    491     'shiftKey',
    492     'metaKey',
    493     'button',
    494     'relatedTarget',
    495     // DOM Level 3
    496     'buttons',
    497     // PointerEvent
    498     'pointerId',
    499     'width',
    500     'height',
    501     'pressure',
    502     'tiltX',
    503     'tiltY',
    504     'pointerType',
    505     'hwTimestamp',
    506     'isPrimary',
    507     // event instance
    508     'type',
    509     'target',
    510     'currentTarget',
    511     'which',
    512     'pageX',
    513     'pageY',
    514     'timeStamp',
    515     // gesture addons
    516     'preventTap',
    517     'tapPrevented',
    518     '_source'
    519   ];
    520 
    521   var CLONE_DEFAULTS = [
    522     // MouseEvent
    523     false,
    524     false,
    525     null,
    526     null,
    527     0,
    528     0,
    529     0,
    530     0,
    531     false,
    532     false,
    533     false,
    534     false,
    535     0,
    536     null,
    537     // DOM Level 3
    538     0,
    539     // PointerEvent
    540     0,
    541     0,
    542     0,
    543     0,
    544     0,
    545     0,
    546     '',
    547     0,
    548     false,
    549     // event instance
    550     '',
    551     null,
    552     null,
    553     0,
    554     0,
    555     0,
    556     0,
    557     function(){},
    558     false
    559   ];
    560 
    561   var HAS_SVG_INSTANCE = (typeof SVGElementInstance !== 'undefined');
    562 
    563   var eventFactory = scope.eventFactory;
    564 
    565   // set of recognizers to run for the currently handled event
    566   var currentGestures;
    567 
    568   /**
    569    * This module is for normalizing events. Mouse and Touch events will be
    570    * collected here, and fire PointerEvents that have the same semantics, no
    571    * matter the source.
    572    * Events fired:
    573    *   - pointerdown: a pointing is added
    574    *   - pointerup: a pointer is removed
    575    *   - pointermove: a pointer is moved
    576    *   - pointerover: a pointer crosses into an element
    577    *   - pointerout: a pointer leaves an element
    578    *   - pointercancel: a pointer will no longer generate events
    579    */
    580   var dispatcher = {
    581     IS_IOS: false,
    582     pointermap: new scope.PointerMap(),
    583     requiredGestures: new scope.PointerMap(),
    584     eventMap: Object.create(null),
    585     // Scope objects for native events.
    586     // This exists for ease of testing.
    587     eventSources: Object.create(null),
    588     eventSourceList: [],
    589     gestures: [],
    590     // map gesture event -> {listeners: int, index: gestures[int]}
    591     dependencyMap: {
    592       // make sure down and up are in the map to trigger "register"
    593       down: {listeners: 0, index: -1},
    594       up: {listeners: 0, index: -1}
    595     },
    596     gestureQueue: [],
    597     /**
    598      * Add a new event source that will generate pointer events.
    599      *
    600      * `inSource` must contain an array of event names named `events`, and
    601      * functions with the names specified in the `events` array.
    602      * @param {string} name A name for the event source
    603      * @param {Object} source A new source of platform events.
    604      */
    605     registerSource: function(name, source) {
    606       var s = source;
    607       var newEvents = s.events;
    608       if (newEvents) {
    609         newEvents.forEach(function(e) {
    610           if (s[e]) {
    611             this.eventMap[e] = s[e].bind(s);
    612           }
    613         }, this);
    614         this.eventSources[name] = s;
    615         this.eventSourceList.push(s);
    616       }
    617     },
    618     registerGesture: function(name, source) {
    619       var obj = Object.create(null);
    620       obj.listeners = 0;
    621       obj.index = this.gestures.length;
    622       for (var i = 0, g; i < source.exposes.length; i++) {
    623         g = source.exposes[i].toLowerCase();
    624         this.dependencyMap[g] = obj;
    625       }
    626       this.gestures.push(source);
    627     },
    628     register: function(element, initial) {
    629       var l = this.eventSourceList.length;
    630       for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
    631         // call eventsource register
    632         es.register.call(es, element, initial);
    633       }
    634     },
    635     unregister: function(element) {
    636       var l = this.eventSourceList.length;
    637       for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
    638         // call eventsource register
    639         es.unregister.call(es, element);
    640       }
    641     },
    642     // EVENTS
    643     down: function(inEvent) {
    644       this.requiredGestures.set(inEvent.pointerId, currentGestures);
    645       this.fireEvent('down', inEvent);
    646     },
    647     move: function(inEvent) {
    648       // pipe move events into gesture queue directly
    649       inEvent.type = 'move';
    650       this.fillGestureQueue(inEvent);
    651     },
    652     up: function(inEvent) {
    653       this.fireEvent('up', inEvent);
    654       this.requiredGestures.delete(inEvent.pointerId);
    655     },
    656     cancel: function(inEvent) {
    657       inEvent.tapPrevented = true;
    658       this.fireEvent('up', inEvent);
    659       this.requiredGestures.delete(inEvent.pointerId);
    660     },
    661     addGestureDependency: function(node, currentGestures) {
    662       var gesturesWanted = node._pgEvents;
    663       if (gesturesWanted && currentGestures) {
    664         var gk = Object.keys(gesturesWanted);
    665         for (var i = 0, r, ri, g; i < gk.length; i++) {
    666           // gesture
    667           g = gk[i];
    668           if (gesturesWanted[g] > 0) {
    669             // lookup gesture recognizer
    670             r = this.dependencyMap[g];
    671             // recognizer index
    672             ri = r ? r.index : -1;
    673             currentGestures[ri] = true;
    674           }
    675         }
    676       }
    677     },
    678     // LISTENER LOGIC
    679     eventHandler: function(inEvent) {
    680       // This is used to prevent multiple dispatch of events from
    681       // platform events. This can happen when two elements in different scopes
    682       // are set up to create pointer events, which is relevant to Shadow DOM.
    683 
    684       var type = inEvent.type;
    685 
    686       // only generate the list of desired events on "down"
    687       if (type === 'touchstart' || type === 'mousedown' || type === 'pointerdown' || type === 'MSPointerDown') {
    688         if (!inEvent._handledByPG) {
    689           currentGestures = {};
    690         }
    691 
    692         // in IOS mode, there is only a listener on the document, so this is not re-entrant
    693         if (this.IS_IOS) {
    694           var ev = inEvent;
    695           if (type === 'touchstart') {
    696             var ct = inEvent.changedTouches[0];
    697             // set up a fake event to give to the path builder
    698             ev = {target: inEvent.target, clientX: ct.clientX, clientY: ct.clientY, path: inEvent.path};
    699           }
    700           // use event path if available, otherwise build a path from target finding
    701           var nodes = inEvent.path || scope.targetFinding.path(ev);
    702           for (var i = 0, n; i < nodes.length; i++) {
    703             n = nodes[i];
    704             this.addGestureDependency(n, currentGestures);
    705           }
    706         } else {
    707           this.addGestureDependency(inEvent.currentTarget, currentGestures);
    708         }
    709       }
    710 
    711       if (inEvent._handledByPG) {
    712         return;
    713       }
    714       var fn = this.eventMap && this.eventMap[type];
    715       if (fn) {
    716         fn(inEvent);
    717       }
    718       inEvent._handledByPG = true;
    719     },
    720     // set up event listeners
    721     listen: function(target, events) {
    722       for (var i = 0, l = events.length, e; (i < l) && (e = events[i]); i++) {
    723         this.addEvent(target, e);
    724       }
    725     },
    726     // remove event listeners
    727     unlisten: function(target, events) {
    728       for (var i = 0, l = events.length, e; (i < l) && (e = events[i]); i++) {
    729         this.removeEvent(target, e);
    730       }
    731     },
    732     addEvent: function(target, eventName) {
    733       target.addEventListener(eventName, this.boundHandler);
    734     },
    735     removeEvent: function(target, eventName) {
    736       target.removeEventListener(eventName, this.boundHandler);
    737     },
    738     // EVENT CREATION AND TRACKING
    739     /**
    740      * Creates a new Event of type `inType`, based on the information in
    741      * `inEvent`.
    742      *
    743      * @param {string} inType A string representing the type of event to create
    744      * @param {Event} inEvent A platform event with a target
    745      * @return {Event} A PointerEvent of type `inType`
    746      */
    747     makeEvent: function(inType, inEvent) {
    748       var e = eventFactory.makePointerEvent(inType, inEvent);
    749       e.preventDefault = inEvent.preventDefault;
    750       e.tapPrevented = inEvent.tapPrevented;
    751       e._target = e._target || inEvent.target;
    752       return e;
    753     },
    754     // make and dispatch an event in one call
    755     fireEvent: function(inType, inEvent) {
    756       var e = this.makeEvent(inType, inEvent);
    757       return this.dispatchEvent(e);
    758     },
    759     /**
    760      * Returns a snapshot of inEvent, with writable properties.
    761      *
    762      * @param {Event} inEvent An event that contains properties to copy.
    763      * @return {Object} An object containing shallow copies of `inEvent`'s
    764      *    properties.
    765      */
    766     cloneEvent: function(inEvent) {
    767       var eventCopy = Object.create(null), p;
    768       for (var i = 0; i < CLONE_PROPS.length; i++) {
    769         p = CLONE_PROPS[i];
    770         eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i];
    771         // Work around SVGInstanceElement shadow tree
    772         // Return the <use> element that is represented by the instance for Safari, Chrome, IE.
    773         // This is the behavior implemented by Firefox.
    774         if (p === 'target' || p === 'relatedTarget') {
    775           if (HAS_SVG_INSTANCE && eventCopy[p] instanceof SVGElementInstance) {
    776             eventCopy[p] = eventCopy[p].correspondingUseElement;
    777           }
    778         }
    779       }
    780       // keep the semantics of preventDefault
    781       eventCopy.preventDefault = function() {
    782         inEvent.preventDefault();
    783       };
    784       return eventCopy;
    785     },
    786     /**
    787      * Dispatches the event to its target.
    788      *
    789      * @param {Event} inEvent The event to be dispatched.
    790      * @return {Boolean} True if an event handler returns true, false otherwise.
    791      */
    792     dispatchEvent: function(inEvent) {
    793       var t = inEvent._target;
    794       if (t) {
    795         t.dispatchEvent(inEvent);
    796         // clone the event for the gesture system to process
    797         // clone after dispatch to pick up gesture prevention code
    798         var clone = this.cloneEvent(inEvent);
    799         clone.target = t;
    800         this.fillGestureQueue(clone);
    801       }
    802     },
    803     gestureTrigger: function() {
    804       // process the gesture queue
    805       for (var i = 0, e, rg; i < this.gestureQueue.length; i++) {
    806         e = this.gestureQueue[i];
    807         rg = e._requiredGestures;
    808         if (rg) {
    809           for (var j = 0, g, fn; j < this.gestures.length; j++) {
    810             // only run recognizer if an element in the source event's path is listening for those gestures
    811             if (rg[j]) {
    812               g = this.gestures[j];
    813               fn = g[e.type];
    814               if (fn) {
    815                 fn.call(g, e);
    816               }
    817             }
    818           }
    819         }
    820       }
    821       this.gestureQueue.length = 0;
    822     },
    823     fillGestureQueue: function(ev) {
    824       // only trigger the gesture queue once
    825       if (!this.gestureQueue.length) {
    826         requestAnimationFrame(this.boundGestureTrigger);
    827       }
    828       ev._requiredGestures = this.requiredGestures.get(ev.pointerId);
    829       this.gestureQueue.push(ev);
    830     }
    831   };
    832   dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher);
    833   dispatcher.boundGestureTrigger = dispatcher.gestureTrigger.bind(dispatcher);
    834   scope.dispatcher = dispatcher;
    835 
    836   /**
    837    * Listen for `gesture` on `node` with the `handler` function
    838    *
    839    * If `handler` is the first listener for `gesture`, the underlying gesture recognizer is then enabled.
    840    *
    841    * @param {Element} node
    842    * @param {string} gesture
    843    * @return Boolean `gesture` is a valid gesture
    844    */
    845   scope.activateGesture = function(node, gesture) {
    846     var g = gesture.toLowerCase();
    847     var dep = dispatcher.dependencyMap[g];
    848     if (dep) {
    849       var recognizer = dispatcher.gestures[dep.index];
    850       if (!node._pgListeners) {
    851         dispatcher.register(node);
    852         node._pgListeners = 0;
    853       }
    854       // TODO(dfreedm): re-evaluate bookkeeping to avoid using attributes
    855       if (recognizer) {
    856         var touchAction = recognizer.defaultActions && recognizer.defaultActions[g];
    857         var actionNode;
    858         switch(node.nodeType) {
    859           case Node.ELEMENT_NODE:
    860             actionNode = node;
    861           break;
    862           case Node.DOCUMENT_FRAGMENT_NODE:
    863             actionNode = node.host;
    864           break;
    865           default:
    866             actionNode = null;
    867           break;
    868         }
    869         if (touchAction && actionNode && !actionNode.hasAttribute('touch-action')) {
    870           actionNode.setAttribute('touch-action', touchAction);
    871         }
    872       }
    873       if (!node._pgEvents) {
    874         node._pgEvents = {};
    875       }
    876       node._pgEvents[g] = (node._pgEvents[g] || 0) + 1;
    877       node._pgListeners++;
    878     }
    879     return Boolean(dep);
    880   };
    881 
    882   /**
    883    *
    884    * Listen for `gesture` from `node` with `handler` function.
    885    *
    886    * @param {Element} node
    887    * @param {string} gesture
    888    * @param {Function} handler
    889    * @param {Boolean} capture
    890    */
    891   scope.addEventListener = function(node, gesture, handler, capture) {
    892     if (handler) {
    893       scope.activateGesture(node, gesture);
    894       node.addEventListener(gesture, handler, capture);
    895     }
    896   };
    897 
    898   /**
    899    * Tears down the gesture configuration for `node`
    900    *
    901    * If `handler` is the last listener for `gesture`, the underlying gesture recognizer is disabled.
    902    *
    903    * @param {Element} node
    904    * @param {string} gesture
    905    * @return Boolean `gesture` is a valid gesture
    906    */
    907   scope.deactivateGesture = function(node, gesture) {
    908     var g = gesture.toLowerCase();
    909     var dep = dispatcher.dependencyMap[g];
    910     if (dep) {
    911       if (node._pgListeners > 0) {
    912         node._pgListeners--;
    913       }
    914       if (node._pgListeners === 0) {
    915         dispatcher.unregister(node);
    916       }
    917       if (node._pgEvents) {
    918         if (node._pgEvents[g] > 0) {
    919           node._pgEvents[g]--;
    920         } else {
    921           node._pgEvents[g] = 0;
    922         }
    923       }
    924     }
    925     return Boolean(dep);
    926   };
    927 
    928   /**
    929    * Stop listening for `gesture` from `node` with `handler` function.
    930    *
    931    * @param {Element} node
    932    * @param {string} gesture
    933    * @param {Function} handler
    934    * @param {Boolean} capture
    935    */
    936   scope.removeEventListener = function(node, gesture, handler, capture) {
    937     if (handler) {
    938       scope.deactivateGesture(node, gesture);
    939       node.removeEventListener(gesture, handler, capture);
    940     }
    941   };
    942 })(window.PolymerGestures);
    943 
    944 (function(scope) {
    945   var dispatcher = scope.dispatcher;
    946   var pointermap = dispatcher.pointermap;
    947   // radius around touchend that swallows mouse events
    948   var DEDUP_DIST = 25;
    949 
    950   var WHICH_TO_BUTTONS = [0, 1, 4, 2];
    951 
    952   var currentButtons = 0;
    953 
    954   var FIREFOX_LINUX = /Linux.*Firefox\//i;
    955 
    956   var HAS_BUTTONS = (function() {
    957     // firefox on linux returns spec-incorrect values for mouseup.buttons
    958     // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.buttons#See_also
    959     // https://codereview.chromium.org/727593003/#msg16
    960     if (FIREFOX_LINUX.test(navigator.userAgent)) {
    961       return false;
    962     }
    963     try {
    964       return new MouseEvent('test', {buttons: 1}).buttons === 1;
    965     } catch (e) {
    966       return false;
    967     }
    968   })();
    969 
    970   // handler block for native mouse events
    971   var mouseEvents = {
    972     POINTER_ID: 1,
    973     POINTER_TYPE: 'mouse',
    974     events: [
    975       'mousedown',
    976       'mousemove',
    977       'mouseup'
    978     ],
    979     exposes: [
    980       'down',
    981       'up',
    982       'move'
    983     ],
    984     register: function(target) {
    985       dispatcher.listen(target, this.events);
    986     },
    987     unregister: function(target) {
    988       if (target.nodeType === Node.DOCUMENT_NODE) {
    989         return;
    990       }
    991       dispatcher.unlisten(target, this.events);
    992     },
    993     lastTouches: [],
    994     // collide with the global mouse listener
    995     isEventSimulatedFromTouch: function(inEvent) {
    996       var lts = this.lastTouches;
    997       var x = inEvent.clientX, y = inEvent.clientY;
    998       for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
    999         // simulated mouse events will be swallowed near a primary touchend
   1000         var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
   1001         if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) {
   1002           return true;
   1003         }
   1004       }
   1005     },
   1006     prepareEvent: function(inEvent) {
   1007       var e = dispatcher.cloneEvent(inEvent);
   1008       e.pointerId = this.POINTER_ID;
   1009       e.isPrimary = true;
   1010       e.pointerType = this.POINTER_TYPE;
   1011       e._source = 'mouse';
   1012       if (!HAS_BUTTONS) {
   1013         var type = inEvent.type;
   1014         var bit = WHICH_TO_BUTTONS[inEvent.which] || 0;
   1015         if (type === 'mousedown') {
   1016           currentButtons |= bit;
   1017         } else if (type === 'mouseup') {
   1018           currentButtons &= ~bit;
   1019         }
   1020         e.buttons = currentButtons;
   1021       }
   1022       return e;
   1023     },
   1024     mousedown: function(inEvent) {
   1025       if (!this.isEventSimulatedFromTouch(inEvent)) {
   1026         var p = pointermap.has(this.POINTER_ID);
   1027         var e = this.prepareEvent(inEvent);
   1028         e.target = scope.findTarget(inEvent);
   1029         pointermap.set(this.POINTER_ID, e.target);
   1030         dispatcher.down(e);
   1031       }
   1032     },
   1033     mousemove: function(inEvent) {
   1034       if (!this.isEventSimulatedFromTouch(inEvent)) {
   1035         var target = pointermap.get(this.POINTER_ID);
   1036         if (target) {
   1037           var e = this.prepareEvent(inEvent);
   1038           e.target = target;
   1039           // handle case where we missed a mouseup
   1040           if ((HAS_BUTTONS ? e.buttons : e.which) === 0) {
   1041             if (!HAS_BUTTONS) {
   1042               currentButtons = e.buttons = 0;
   1043             }
   1044             dispatcher.cancel(e);
   1045             this.cleanupMouse(e.buttons);
   1046           } else {
   1047             dispatcher.move(e);
   1048           }
   1049         }
   1050       }
   1051     },
   1052     mouseup: function(inEvent) {
   1053       if (!this.isEventSimulatedFromTouch(inEvent)) {
   1054         var e = this.prepareEvent(inEvent);
   1055         e.relatedTarget = scope.findTarget(inEvent);
   1056         e.target = pointermap.get(this.POINTER_ID);
   1057         dispatcher.up(e);
   1058         this.cleanupMouse(e.buttons);
   1059       }
   1060     },
   1061     cleanupMouse: function(buttons) {
   1062       if (buttons === 0) {
   1063         pointermap.delete(this.POINTER_ID);
   1064       }
   1065     }
   1066   };
   1067 
   1068   scope.mouseEvents = mouseEvents;
   1069 })(window.PolymerGestures);
   1070 
   1071 (function(scope) {
   1072   var dispatcher = scope.dispatcher;
   1073   var allShadows = scope.targetFinding.allShadows.bind(scope.targetFinding);
   1074   var pointermap = dispatcher.pointermap;
   1075   var touchMap = Array.prototype.map.call.bind(Array.prototype.map);
   1076   // This should be long enough to ignore compat mouse events made by touch
   1077   var DEDUP_TIMEOUT = 2500;
   1078   var DEDUP_DIST = 25;
   1079   var CLICK_COUNT_TIMEOUT = 200;
   1080   var HYSTERESIS = 20;
   1081   var ATTRIB = 'touch-action';
   1082   // TODO(dfreedm): disable until http://crbug.com/399765 is resolved
   1083   // var HAS_TOUCH_ACTION = ATTRIB in document.head.style;
   1084   var HAS_TOUCH_ACTION = false;
   1085 
   1086   // handler block for native touch events
   1087   var touchEvents = {
   1088     IS_IOS: false,
   1089     events: [
   1090       'touchstart',
   1091       'touchmove',
   1092       'touchend',
   1093       'touchcancel'
   1094     ],
   1095     exposes: [
   1096       'down',
   1097       'up',
   1098       'move'
   1099     ],
   1100     register: function(target, initial) {
   1101       if (this.IS_IOS ? initial : !initial) {
   1102         dispatcher.listen(target, this.events);
   1103       }
   1104     },
   1105     unregister: function(target) {
   1106       if (!this.IS_IOS) {
   1107         dispatcher.unlisten(target, this.events);
   1108       }
   1109     },
   1110     scrollTypes: {
   1111       EMITTER: 'none',
   1112       XSCROLLER: 'pan-x',
   1113       YSCROLLER: 'pan-y',
   1114     },
   1115     touchActionToScrollType: function(touchAction) {
   1116       var t = touchAction;
   1117       var st = this.scrollTypes;
   1118       if (t === st.EMITTER) {
   1119         return 'none';
   1120       } else if (t === st.XSCROLLER) {
   1121         return 'X';
   1122       } else if (t === st.YSCROLLER) {
   1123         return 'Y';
   1124       } else {
   1125         return 'XY';
   1126       }
   1127     },
   1128     POINTER_TYPE: 'touch',
   1129     firstTouch: null,
   1130     isPrimaryTouch: function(inTouch) {
   1131       return this.firstTouch === inTouch.identifier;
   1132     },
   1133     setPrimaryTouch: function(inTouch) {
   1134       // set primary touch if there no pointers, or the only pointer is the mouse
   1135       if (pointermap.pointers() === 0 || (pointermap.pointers() === 1 && pointermap.has(1))) {
   1136         this.firstTouch = inTouch.identifier;
   1137         this.firstXY = {X: inTouch.clientX, Y: inTouch.clientY};
   1138         this.firstTarget = inTouch.target;
   1139         this.scrolling = null;
   1140         this.cancelResetClickCount();
   1141       }
   1142     },
   1143     removePrimaryPointer: function(inPointer) {
   1144       if (inPointer.isPrimary) {
   1145         this.firstTouch = null;
   1146         this.firstXY = null;
   1147         this.resetClickCount();
   1148       }
   1149     },
   1150     clickCount: 0,
   1151     resetId: null,
   1152     resetClickCount: function() {
   1153       var fn = function() {
   1154         this.clickCount = 0;
   1155         this.resetId = null;
   1156       }.bind(this);
   1157       this.resetId = setTimeout(fn, CLICK_COUNT_TIMEOUT);
   1158     },
   1159     cancelResetClickCount: function() {
   1160       if (this.resetId) {
   1161         clearTimeout(this.resetId);
   1162       }
   1163     },
   1164     typeToButtons: function(type) {
   1165       var ret = 0;
   1166       if (type === 'touchstart' || type === 'touchmove') {
   1167         ret = 1;
   1168       }
   1169       return ret;
   1170     },
   1171     findTarget: function(touch, id) {
   1172       if (this.currentTouchEvent.type === 'touchstart') {
   1173         if (this.isPrimaryTouch(touch)) {
   1174           var fastPath = {
   1175             clientX: touch.clientX,
   1176             clientY: touch.clientY,
   1177             path: this.currentTouchEvent.path,
   1178             target: this.currentTouchEvent.target
   1179           };
   1180           return scope.findTarget(fastPath);
   1181         } else {
   1182           return scope.findTarget(touch);
   1183         }
   1184       }
   1185       // reuse target we found in touchstart
   1186       return pointermap.get(id);
   1187     },
   1188     touchToPointer: function(inTouch) {
   1189       var cte = this.currentTouchEvent;
   1190       var e = dispatcher.cloneEvent(inTouch);
   1191       // Spec specifies that pointerId 1 is reserved for Mouse.
   1192       // Touch identifiers can start at 0.
   1193       // Add 2 to the touch identifier for compatibility.
   1194       var id = e.pointerId = inTouch.identifier + 2;
   1195       e.target = this.findTarget(inTouch, id);
   1196       e.bubbles = true;
   1197       e.cancelable = true;
   1198       e.detail = this.clickCount;
   1199       e.buttons = this.typeToButtons(cte.type);
   1200       e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0;
   1201       e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0;
   1202       e.pressure = inTouch.webkitForce || inTouch.force || 0.5;
   1203       e.isPrimary = this.isPrimaryTouch(inTouch);
   1204       e.pointerType = this.POINTER_TYPE;
   1205       e._source = 'touch';
   1206       // forward touch preventDefaults
   1207       var self = this;
   1208       e.preventDefault = function() {
   1209         self.scrolling = false;
   1210         self.firstXY = null;
   1211         cte.preventDefault();
   1212       };
   1213       return e;
   1214     },
   1215     processTouches: function(inEvent, inFunction) {
   1216       var tl = inEvent.changedTouches;
   1217       this.currentTouchEvent = inEvent;
   1218       for (var i = 0, t, p; i < tl.length; i++) {
   1219         t = tl[i];
   1220         p = this.touchToPointer(t);
   1221         if (inEvent.type === 'touchstart') {
   1222           pointermap.set(p.pointerId, p.target);
   1223         }
   1224         if (pointermap.has(p.pointerId)) {
   1225           inFunction.call(this, p);
   1226         }
   1227         if (inEvent.type === 'touchend' || inEvent._cancel) {
   1228           this.cleanUpPointer(p);
   1229         }
   1230       }
   1231     },
   1232     // For single axis scrollers, determines whether the element should emit
   1233     // pointer events or behave as a scroller
   1234     shouldScroll: function(inEvent) {
   1235       if (this.firstXY) {
   1236         var ret;
   1237         var touchAction = scope.targetFinding.findTouchAction(inEvent);
   1238         var scrollAxis = this.touchActionToScrollType(touchAction);
   1239         if (scrollAxis === 'none') {
   1240           // this element is a touch-action: none, should never scroll
   1241           ret = false;
   1242         } else if (scrollAxis === 'XY') {
   1243           // this element should always scroll
   1244           ret = true;
   1245         } else {
   1246           var t = inEvent.changedTouches[0];
   1247           // check the intended scroll axis, and other axis
   1248           var a = scrollAxis;
   1249           var oa = scrollAxis === 'Y' ? 'X' : 'Y';
   1250           var da = Math.abs(t['client' + a] - this.firstXY[a]);
   1251           var doa = Math.abs(t['client' + oa] - this.firstXY[oa]);
   1252           // if delta in the scroll axis > delta other axis, scroll instead of
   1253           // making events
   1254           ret = da >= doa;
   1255         }
   1256         return ret;
   1257       }
   1258     },
   1259     findTouch: function(inTL, inId) {
   1260       for (var i = 0, l = inTL.length, t; i < l && (t = inTL[i]); i++) {
   1261         if (t.identifier === inId) {
   1262           return true;
   1263         }
   1264       }
   1265     },
   1266     // In some instances, a touchstart can happen without a touchend. This
   1267     // leaves the pointermap in a broken state.
   1268     // Therefore, on every touchstart, we remove the touches that did not fire a
   1269     // touchend event.
   1270     // To keep state globally consistent, we fire a
   1271     // pointercancel for this "abandoned" touch
   1272     vacuumTouches: function(inEvent) {
   1273       var tl = inEvent.touches;
   1274       // pointermap.pointers() should be < tl.length here, as the touchstart has not
   1275       // been processed yet.
   1276       if (pointermap.pointers() >= tl.length) {
   1277         var d = [];
   1278         pointermap.forEach(function(value, key) {
   1279           // Never remove pointerId == 1, which is mouse.
   1280           // Touch identifiers are 2 smaller than their pointerId, which is the
   1281           // index in pointermap.
   1282           if (key !== 1 && !this.findTouch(tl, key - 2)) {
   1283             var p = value;
   1284             d.push(p);
   1285           }
   1286         }, this);
   1287         d.forEach(function(p) {
   1288           this.cancel(p);
   1289           pointermap.delete(p.pointerId);
   1290         }, this);
   1291       }
   1292     },
   1293     touchstart: function(inEvent) {
   1294       this.vacuumTouches(inEvent);
   1295       this.setPrimaryTouch(inEvent.changedTouches[0]);
   1296       this.dedupSynthMouse(inEvent);
   1297       if (!this.scrolling) {
   1298         this.clickCount++;
   1299         this.processTouches(inEvent, this.down);
   1300       }
   1301     },
   1302     down: function(inPointer) {
   1303       dispatcher.down(inPointer);
   1304     },
   1305     touchmove: function(inEvent) {
   1306       if (HAS_TOUCH_ACTION) {
   1307         // touchevent.cancelable == false is sent when the page is scrolling under native Touch Action in Chrome 36
   1308         // https://groups.google.com/a/chromium.org/d/msg/input-dev/wHnyukcYBcA/b9kmtwM1jJQJ
   1309         if (inEvent.cancelable) {
   1310           this.processTouches(inEvent, this.move);
   1311         }
   1312       } else {
   1313         if (!this.scrolling) {
   1314           if (this.scrolling === null && this.shouldScroll(inEvent)) {
   1315             this.scrolling = true;
   1316           } else {
   1317             this.scrolling = false;
   1318             inEvent.preventDefault();
   1319             this.processTouches(inEvent, this.move);
   1320           }
   1321         } else if (this.firstXY) {
   1322           var t = inEvent.changedTouches[0];
   1323           var dx = t.clientX - this.firstXY.X;
   1324           var dy = t.clientY - this.firstXY.Y;
   1325           var dd = Math.sqrt(dx * dx + dy * dy);
   1326           if (dd >= HYSTERESIS) {
   1327             this.touchcancel(inEvent);
   1328             this.scrolling = true;
   1329             this.firstXY = null;
   1330           }
   1331         }
   1332       }
   1333     },
   1334     move: function(inPointer) {
   1335       dispatcher.move(inPointer);
   1336     },
   1337     touchend: function(inEvent) {
   1338       this.dedupSynthMouse(inEvent);
   1339       this.processTouches(inEvent, this.up);
   1340     },
   1341     up: function(inPointer) {
   1342       inPointer.relatedTarget = scope.findTarget(inPointer);
   1343       dispatcher.up(inPointer);
   1344     },
   1345     cancel: function(inPointer) {
   1346       dispatcher.cancel(inPointer);
   1347     },
   1348     touchcancel: function(inEvent) {
   1349       inEvent._cancel = true;
   1350       this.processTouches(inEvent, this.cancel);
   1351     },
   1352     cleanUpPointer: function(inPointer) {
   1353       pointermap['delete'](inPointer.pointerId);
   1354       this.removePrimaryPointer(inPointer);
   1355     },
   1356     // prevent synth mouse events from creating pointer events
   1357     dedupSynthMouse: function(inEvent) {
   1358       var lts = scope.mouseEvents.lastTouches;
   1359       var t = inEvent.changedTouches[0];
   1360       // only the primary finger will synth mouse events
   1361       if (this.isPrimaryTouch(t)) {
   1362         // remember x/y of last touch
   1363         var lt = {x: t.clientX, y: t.clientY};
   1364         lts.push(lt);
   1365         var fn = (function(lts, lt){
   1366           var i = lts.indexOf(lt);
   1367           if (i > -1) {
   1368             lts.splice(i, 1);
   1369           }
   1370         }).bind(null, lts, lt);
   1371         setTimeout(fn, DEDUP_TIMEOUT);
   1372       }
   1373     }
   1374   };
   1375 
   1376   // prevent "ghost clicks" that come from elements that were removed in a touch handler
   1377   var STOP_PROP_FN = Event.prototype.stopImmediatePropagation || Event.prototype.stopPropagation;
   1378   document.addEventListener('click', function(ev) {
   1379     var x = ev.clientX, y = ev.clientY;
   1380     // check if a click is within DEDUP_DIST px radius of the touchstart
   1381     var closeTo = function(touch) {
   1382       var dx = Math.abs(x - touch.x), dy = Math.abs(y - touch.y);
   1383       return (dx <= DEDUP_DIST && dy <= DEDUP_DIST);
   1384     };
   1385     // if click coordinates are close to touch coordinates, assume the click came from a touch
   1386     var wasTouched = scope.mouseEvents.lastTouches.some(closeTo);
   1387     // if the click came from touch, and the touchstart target is not in the path of the click event,
   1388     // then the touchstart target was probably removed, and the click should be "busted"
   1389     var path = scope.targetFinding.path(ev);
   1390     if (wasTouched) {
   1391       for (var i = 0; i < path.length; i++) {
   1392         if (path[i] === touchEvents.firstTarget) {
   1393           return;
   1394         }
   1395       }
   1396       ev.preventDefault();
   1397       STOP_PROP_FN.call(ev);
   1398     }
   1399   }, true);
   1400 
   1401   scope.touchEvents = touchEvents;
   1402 })(window.PolymerGestures);
   1403 
   1404 (function(scope) {
   1405   var dispatcher = scope.dispatcher;
   1406   var pointermap = dispatcher.pointermap;
   1407   var HAS_BITMAP_TYPE = window.MSPointerEvent && typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE === 'number';
   1408   var msEvents = {
   1409     events: [
   1410       'MSPointerDown',
   1411       'MSPointerMove',
   1412       'MSPointerUp',
   1413       'MSPointerCancel',
   1414     ],
   1415     register: function(target) {
   1416       dispatcher.listen(target, this.events);
   1417     },
   1418     unregister: function(target) {
   1419       if (target.nodeType === Node.DOCUMENT_NODE) {
   1420         return;
   1421       }
   1422       dispatcher.unlisten(target, this.events);
   1423     },
   1424     POINTER_TYPES: [
   1425       '',
   1426       'unavailable',
   1427       'touch',
   1428       'pen',
   1429       'mouse'
   1430     ],
   1431     prepareEvent: function(inEvent) {
   1432       var e = inEvent;
   1433       e = dispatcher.cloneEvent(inEvent);
   1434       if (HAS_BITMAP_TYPE) {
   1435         e.pointerType = this.POINTER_TYPES[inEvent.pointerType];
   1436       }
   1437       e._source = 'ms';
   1438       return e;
   1439     },
   1440     cleanup: function(id) {
   1441       pointermap['delete'](id);
   1442     },
   1443     MSPointerDown: function(inEvent) {
   1444       var e = this.prepareEvent(inEvent);
   1445       e.target = scope.findTarget(inEvent);
   1446       pointermap.set(inEvent.pointerId, e.target);
   1447       dispatcher.down(e);
   1448     },
   1449     MSPointerMove: function(inEvent) {
   1450       var target = pointermap.get(inEvent.pointerId);
   1451       if (target) {
   1452         var e = this.prepareEvent(inEvent);
   1453         e.target = target;
   1454         dispatcher.move(e);
   1455       }
   1456     },
   1457     MSPointerUp: function(inEvent) {
   1458       var e = this.prepareEvent(inEvent);
   1459       e.relatedTarget = scope.findTarget(inEvent);
   1460       e.target = pointermap.get(e.pointerId);
   1461       dispatcher.up(e);
   1462       this.cleanup(inEvent.pointerId);
   1463     },
   1464     MSPointerCancel: function(inEvent) {
   1465       var e = this.prepareEvent(inEvent);
   1466       e.relatedTarget = scope.findTarget(inEvent);
   1467       e.target = pointermap.get(e.pointerId);
   1468       dispatcher.cancel(e);
   1469       this.cleanup(inEvent.pointerId);
   1470     }
   1471   };
   1472 
   1473   scope.msEvents = msEvents;
   1474 })(window.PolymerGestures);
   1475 
   1476 (function(scope) {
   1477   var dispatcher = scope.dispatcher;
   1478   var pointermap = dispatcher.pointermap;
   1479   var pointerEvents = {
   1480     events: [
   1481       'pointerdown',
   1482       'pointermove',
   1483       'pointerup',
   1484       'pointercancel'
   1485     ],
   1486     prepareEvent: function(inEvent) {
   1487       var e = dispatcher.cloneEvent(inEvent);
   1488       e._source = 'pointer';
   1489       return e;
   1490     },
   1491     register: function(target) {
   1492       dispatcher.listen(target, this.events);
   1493     },
   1494     unregister: function(target) {
   1495       if (target.nodeType === Node.DOCUMENT_NODE) {
   1496         return;
   1497       }
   1498       dispatcher.unlisten(target, this.events);
   1499     },
   1500     cleanup: function(id) {
   1501       pointermap['delete'](id);
   1502     },
   1503     pointerdown: function(inEvent) {
   1504       var e = this.prepareEvent(inEvent);
   1505       e.target = scope.findTarget(inEvent);
   1506       pointermap.set(e.pointerId, e.target);
   1507       dispatcher.down(e);
   1508     },
   1509     pointermove: function(inEvent) {
   1510       var target = pointermap.get(inEvent.pointerId);
   1511       if (target) {
   1512         var e = this.prepareEvent(inEvent);
   1513         e.target = target;
   1514         dispatcher.move(e);
   1515       }
   1516     },
   1517     pointerup: function(inEvent) {
   1518       var e = this.prepareEvent(inEvent);
   1519       e.relatedTarget = scope.findTarget(inEvent);
   1520       e.target = pointermap.get(e.pointerId);
   1521       dispatcher.up(e);
   1522       this.cleanup(inEvent.pointerId);
   1523     },
   1524     pointercancel: function(inEvent) {
   1525       var e = this.prepareEvent(inEvent);
   1526       e.relatedTarget = scope.findTarget(inEvent);
   1527       e.target = pointermap.get(e.pointerId);
   1528       dispatcher.cancel(e);
   1529       this.cleanup(inEvent.pointerId);
   1530     }
   1531   };
   1532 
   1533   scope.pointerEvents = pointerEvents;
   1534 })(window.PolymerGestures);
   1535 
   1536 /**
   1537  * This module contains the handlers for native platform events.
   1538  * From here, the dispatcher is called to create unified pointer events.
   1539  * Included are touch events (v1), mouse events, and MSPointerEvents.
   1540  */
   1541 (function(scope) {
   1542 
   1543   var dispatcher = scope.dispatcher;
   1544   var nav = window.navigator;
   1545 
   1546   if (window.PointerEvent) {
   1547     dispatcher.registerSource('pointer', scope.pointerEvents);
   1548   } else if (nav.msPointerEnabled) {
   1549     dispatcher.registerSource('ms', scope.msEvents);
   1550   } else {
   1551     dispatcher.registerSource('mouse', scope.mouseEvents);
   1552     if (window.ontouchstart !== undefined) {
   1553       dispatcher.registerSource('touch', scope.touchEvents);
   1554     }
   1555   }
   1556 
   1557   // Work around iOS bugs https://bugs.webkit.org/show_bug.cgi?id=135628 and https://bugs.webkit.org/show_bug.cgi?id=136506
   1558   var ua = navigator.userAgent;
   1559   var IS_IOS = ua.match(/iPad|iPhone|iPod/) && 'ontouchstart' in window;
   1560 
   1561   dispatcher.IS_IOS = IS_IOS;
   1562   scope.touchEvents.IS_IOS = IS_IOS;
   1563 
   1564   dispatcher.register(document, true);
   1565 })(window.PolymerGestures);
   1566 
   1567 /**
   1568  * This event denotes the beginning of a series of tracking events.
   1569  *
   1570  * @module PointerGestures
   1571  * @submodule Events
   1572  * @class trackstart
   1573  */
   1574 /**
   1575  * Pixels moved in the x direction since trackstart.
   1576  * @type Number
   1577  * @property dx
   1578  */
   1579 /**
   1580  * Pixes moved in the y direction since trackstart.
   1581  * @type Number
   1582  * @property dy
   1583  */
   1584 /**
   1585  * Pixels moved in the x direction since the last track.
   1586  * @type Number
   1587  * @property ddx
   1588  */
   1589 /**
   1590  * Pixles moved in the y direction since the last track.
   1591  * @type Number
   1592  * @property ddy
   1593  */
   1594 /**
   1595  * The clientX position of the track gesture.
   1596  * @type Number
   1597  * @property clientX
   1598  */
   1599 /**
   1600  * The clientY position of the track gesture.
   1601  * @type Number
   1602  * @property clientY
   1603  */
   1604 /**
   1605  * The pageX position of the track gesture.
   1606  * @type Number
   1607  * @property pageX
   1608  */
   1609 /**
   1610  * The pageY position of the track gesture.
   1611  * @type Number
   1612  * @property pageY
   1613  */
   1614 /**
   1615  * The screenX position of the track gesture.
   1616  * @type Number
   1617  * @property screenX
   1618  */
   1619 /**
   1620  * The screenY position of the track gesture.
   1621  * @type Number
   1622  * @property screenY
   1623  */
   1624 /**
   1625  * The last x axis direction of the pointer.
   1626  * @type Number
   1627  * @property xDirection
   1628  */
   1629 /**
   1630  * The last y axis direction of the pointer.
   1631  * @type Number
   1632  * @property yDirection
   1633  */
   1634 /**
   1635  * A shared object between all tracking events.
   1636  * @type Object
   1637  * @property trackInfo
   1638  */
   1639 /**
   1640  * The element currently under the pointer.
   1641  * @type Element
   1642  * @property relatedTarget
   1643  */
   1644 /**
   1645  * The type of pointer that make the track gesture.
   1646  * @type String
   1647  * @property pointerType
   1648  */
   1649 /**
   1650  *
   1651  * This event fires for all pointer movement being tracked.
   1652  *
   1653  * @class track
   1654  * @extends trackstart
   1655  */
   1656 /**
   1657  * This event fires when the pointer is no longer being tracked.
   1658  *
   1659  * @class trackend
   1660  * @extends trackstart
   1661  */
   1662 
   1663  (function(scope) {
   1664    var dispatcher = scope.dispatcher;
   1665    var eventFactory = scope.eventFactory;
   1666    var pointermap = new scope.PointerMap();
   1667    var track = {
   1668      events: [
   1669        'down',
   1670        'move',
   1671        'up',
   1672      ],
   1673      exposes: [
   1674       'trackstart',
   1675       'track',
   1676       'trackx',
   1677       'tracky',
   1678       'trackend'
   1679      ],
   1680      defaultActions: {
   1681        'track': 'none',
   1682        'trackx': 'pan-y',
   1683        'tracky': 'pan-x'
   1684      },
   1685      WIGGLE_THRESHOLD: 4,
   1686      clampDir: function(inDelta) {
   1687        return inDelta > 0 ? 1 : -1;
   1688      },
   1689      calcPositionDelta: function(inA, inB) {
   1690        var x = 0, y = 0;
   1691        if (inA && inB) {
   1692          x = inB.pageX - inA.pageX;
   1693          y = inB.pageY - inA.pageY;
   1694        }
   1695        return {x: x, y: y};
   1696      },
   1697      fireTrack: function(inType, inEvent, inTrackingData) {
   1698        var t = inTrackingData;
   1699        var d = this.calcPositionDelta(t.downEvent, inEvent);
   1700        var dd = this.calcPositionDelta(t.lastMoveEvent, inEvent);
   1701        if (dd.x) {
   1702          t.xDirection = this.clampDir(dd.x);
   1703        } else if (inType === 'trackx') {
   1704          return;
   1705        }
   1706        if (dd.y) {
   1707          t.yDirection = this.clampDir(dd.y);
   1708        } else if (inType === 'tracky') {
   1709          return;
   1710        }
   1711        var gestureProto = {
   1712          bubbles: true,
   1713          cancelable: true,
   1714          trackInfo: t.trackInfo,
   1715          relatedTarget: inEvent.relatedTarget,
   1716          pointerType: inEvent.pointerType,
   1717          pointerId: inEvent.pointerId,
   1718          _source: 'track'
   1719        };
   1720        if (inType !== 'tracky') {
   1721          gestureProto.x = inEvent.x;
   1722          gestureProto.dx = d.x;
   1723          gestureProto.ddx = dd.x;
   1724          gestureProto.clientX = inEvent.clientX;
   1725          gestureProto.pageX = inEvent.pageX;
   1726          gestureProto.screenX = inEvent.screenX;
   1727          gestureProto.xDirection = t.xDirection;
   1728        }
   1729        if (inType !== 'trackx') {
   1730          gestureProto.dy = d.y;
   1731          gestureProto.ddy = dd.y;
   1732          gestureProto.y = inEvent.y;
   1733          gestureProto.clientY = inEvent.clientY;
   1734          gestureProto.pageY = inEvent.pageY;
   1735          gestureProto.screenY = inEvent.screenY;
   1736          gestureProto.yDirection = t.yDirection;
   1737        }
   1738        var e = eventFactory.makeGestureEvent(inType, gestureProto);
   1739        t.downTarget.dispatchEvent(e);
   1740      },
   1741      down: function(inEvent) {
   1742        if (inEvent.isPrimary && (inEvent.pointerType === 'mouse' ? inEvent.buttons === 1 : true)) {
   1743          var p = {
   1744            downEvent: inEvent,
   1745            downTarget: inEvent.target,
   1746            trackInfo: {},
   1747            lastMoveEvent: null,
   1748            xDirection: 0,
   1749            yDirection: 0,
   1750            tracking: false
   1751          };
   1752          pointermap.set(inEvent.pointerId, p);
   1753        }
   1754      },
   1755      move: function(inEvent) {
   1756        var p = pointermap.get(inEvent.pointerId);
   1757        if (p) {
   1758          if (!p.tracking) {
   1759            var d = this.calcPositionDelta(p.downEvent, inEvent);
   1760            var move = d.x * d.x + d.y * d.y;
   1761            // start tracking only if finger moves more than WIGGLE_THRESHOLD
   1762            if (move > this.WIGGLE_THRESHOLD) {
   1763              p.tracking = true;
   1764              p.lastMoveEvent = p.downEvent;
   1765              this.fireTrack('trackstart', inEvent, p);
   1766            }
   1767          }
   1768          if (p.tracking) {
   1769            this.fireTrack('track', inEvent, p);
   1770            this.fireTrack('trackx', inEvent, p);
   1771            this.fireTrack('tracky', inEvent, p);
   1772          }
   1773          p.lastMoveEvent = inEvent;
   1774        }
   1775      },
   1776      up: function(inEvent) {
   1777        var p = pointermap.get(inEvent.pointerId);
   1778        if (p) {
   1779          if (p.tracking) {
   1780            this.fireTrack('trackend', inEvent, p);
   1781          }
   1782          pointermap.delete(inEvent.pointerId);
   1783        }
   1784      }
   1785    };
   1786    dispatcher.registerGesture('track', track);
   1787  })(window.PolymerGestures);
   1788 
   1789 /**
   1790  * This event is fired when a pointer is held down for 200ms.
   1791  *
   1792  * @module PointerGestures
   1793  * @submodule Events
   1794  * @class hold
   1795  */
   1796 /**
   1797  * Type of pointer that made the holding event.
   1798  * @type String
   1799  * @property pointerType
   1800  */
   1801 /**
   1802  * Screen X axis position of the held pointer
   1803  * @type Number
   1804  * @property clientX
   1805  */
   1806 /**
   1807  * Screen Y axis position of the held pointer
   1808  * @type Number
   1809  * @property clientY
   1810  */
   1811 /**
   1812  * Type of pointer that made the holding event.
   1813  * @type String
   1814  * @property pointerType
   1815  */
   1816 /**
   1817  * This event is fired every 200ms while a pointer is held down.
   1818  *
   1819  * @class holdpulse
   1820  * @extends hold
   1821  */
   1822 /**
   1823  * Milliseconds pointer has been held down.
   1824  * @type Number
   1825  * @property holdTime
   1826  */
   1827 /**
   1828  * This event is fired when a held pointer is released or moved.
   1829  *
   1830  * @class release
   1831  */
   1832 
   1833 (function(scope) {
   1834   var dispatcher = scope.dispatcher;
   1835   var eventFactory = scope.eventFactory;
   1836   var hold = {
   1837     // wait at least HOLD_DELAY ms between hold and pulse events
   1838     HOLD_DELAY: 200,
   1839     // pointer can move WIGGLE_THRESHOLD pixels before not counting as a hold
   1840     WIGGLE_THRESHOLD: 16,
   1841     events: [
   1842       'down',
   1843       'move',
   1844       'up',
   1845     ],
   1846     exposes: [
   1847       'hold',
   1848       'holdpulse',
   1849       'release'
   1850     ],
   1851     heldPointer: null,
   1852     holdJob: null,
   1853     pulse: function() {
   1854       var hold = Date.now() - this.heldPointer.timeStamp;
   1855       var type = this.held ? 'holdpulse' : 'hold';
   1856       this.fireHold(type, hold);
   1857       this.held = true;
   1858     },
   1859     cancel: function() {
   1860       clearInterval(this.holdJob);
   1861       if (this.held) {
   1862         this.fireHold('release');
   1863       }
   1864       this.held = false;
   1865       this.heldPointer = null;
   1866       this.target = null;
   1867       this.holdJob = null;
   1868     },
   1869     down: function(inEvent) {
   1870       if (inEvent.isPrimary && !this.heldPointer) {
   1871         this.heldPointer = inEvent;
   1872         this.target = inEvent.target;
   1873         this.holdJob = setInterval(this.pulse.bind(this), this.HOLD_DELAY);
   1874       }
   1875     },
   1876     up: function(inEvent) {
   1877       if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) {
   1878         this.cancel();
   1879       }
   1880     },
   1881     move: function(inEvent) {
   1882       if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) {
   1883         var x = inEvent.clientX - this.heldPointer.clientX;
   1884         var y = inEvent.clientY - this.heldPointer.clientY;
   1885         if ((x * x + y * y) > this.WIGGLE_THRESHOLD) {
   1886           this.cancel();
   1887         }
   1888       }
   1889     },
   1890     fireHold: function(inType, inHoldTime) {
   1891       var p = {
   1892         bubbles: true,
   1893         cancelable: true,
   1894         pointerType: this.heldPointer.pointerType,
   1895         pointerId: this.heldPointer.pointerId,
   1896         x: this.heldPointer.clientX,
   1897         y: this.heldPointer.clientY,
   1898         _source: 'hold'
   1899       };
   1900       if (inHoldTime) {
   1901         p.holdTime = inHoldTime;
   1902       }
   1903       var e = eventFactory.makeGestureEvent(inType, p);
   1904       this.target.dispatchEvent(e);
   1905     }
   1906   };
   1907   dispatcher.registerGesture('hold', hold);
   1908 })(window.PolymerGestures);
   1909 
   1910 /**
   1911  * This event is fired when a pointer quickly goes down and up, and is used to
   1912  * denote activation.
   1913  *
   1914  * Any gesture event can prevent the tap event from being created by calling
   1915  * `event.preventTap`.
   1916  *
   1917  * Any pointer event can prevent the tap by setting the `tapPrevented` property
   1918  * on itself.
   1919  *
   1920  * @module PointerGestures
   1921  * @submodule Events
   1922  * @class tap
   1923  */
   1924 /**
   1925  * X axis position of the tap.
   1926  * @property x
   1927  * @type Number
   1928  */
   1929 /**
   1930  * Y axis position of the tap.
   1931  * @property y
   1932  * @type Number
   1933  */
   1934 /**
   1935  * Type of the pointer that made the tap.
   1936  * @property pointerType
   1937  * @type String
   1938  */
   1939 (function(scope) {
   1940   var dispatcher = scope.dispatcher;
   1941   var eventFactory = scope.eventFactory;
   1942   var pointermap = new scope.PointerMap();
   1943   var tap = {
   1944     events: [
   1945       'down',
   1946       'up'
   1947     ],
   1948     exposes: [
   1949       'tap'
   1950     ],
   1951     down: function(inEvent) {
   1952       if (inEvent.isPrimary && !inEvent.tapPrevented) {
   1953         pointermap.set(inEvent.pointerId, {
   1954           target: inEvent.target,
   1955           buttons: inEvent.buttons,
   1956           x: inEvent.clientX,
   1957           y: inEvent.clientY
   1958         });
   1959       }
   1960     },
   1961     shouldTap: function(e, downState) {
   1962       var tap = true;
   1963       if (e.pointerType === 'mouse') {
   1964         // only allow left click to tap for mouse
   1965         tap = (e.buttons ^ 1) && (downState.buttons & 1);
   1966       }
   1967       return tap && !e.tapPrevented;
   1968     },
   1969     up: function(inEvent) {
   1970       var start = pointermap.get(inEvent.pointerId);
   1971       if (start && this.shouldTap(inEvent, start)) {
   1972         // up.relatedTarget is target currently under finger
   1973         var t = scope.targetFinding.LCA(start.target, inEvent.relatedTarget);
   1974         if (t) {
   1975           var e = eventFactory.makeGestureEvent('tap', {
   1976             bubbles: true,
   1977             cancelable: true,
   1978             x: inEvent.clientX,
   1979             y: inEvent.clientY,
   1980             detail: inEvent.detail,
   1981             pointerType: inEvent.pointerType,
   1982             pointerId: inEvent.pointerId,
   1983             altKey: inEvent.altKey,
   1984             ctrlKey: inEvent.ctrlKey,
   1985             metaKey: inEvent.metaKey,
   1986             shiftKey: inEvent.shiftKey,
   1987             _source: 'tap'
   1988           });
   1989           t.dispatchEvent(e);
   1990         }
   1991       }
   1992       pointermap.delete(inEvent.pointerId);
   1993     }
   1994   };
   1995   // patch eventFactory to remove id from tap's pointermap for preventTap calls
   1996   eventFactory.preventTap = function(e) {
   1997     return function() {
   1998       e.tapPrevented = true;
   1999       pointermap.delete(e.pointerId);
   2000     };
   2001   };
   2002   dispatcher.registerGesture('tap', tap);
   2003 })(window.PolymerGestures);
   2004 
   2005 /*
   2006  * Basic strategy: find the farthest apart points, use as diameter of circle
   2007  * react to size change and rotation of the chord
   2008  */
   2009 
   2010 /**
   2011  * @module pointer-gestures
   2012  * @submodule Events
   2013  * @class pinch
   2014  */
   2015 /**
   2016  * Scale of the pinch zoom gesture
   2017  * @property scale
   2018  * @type Number
   2019  */
   2020 /**
   2021  * Center X position of pointers causing pinch
   2022  * @property centerX
   2023  * @type Number
   2024  */
   2025 /**
   2026  * Center Y position of pointers causing pinch
   2027  * @property centerY
   2028  * @type Number
   2029  */
   2030 
   2031 /**
   2032  * @module pointer-gestures
   2033  * @submodule Events
   2034  * @class rotate
   2035  */
   2036 /**
   2037  * Angle (in degrees) of rotation. Measured from starting positions of pointers.
   2038  * @property angle
   2039  * @type Number
   2040  */
   2041 /**
   2042  * Center X position of pointers causing rotation
   2043  * @property centerX
   2044  * @type Number
   2045  */
   2046 /**
   2047  * Center Y position of pointers causing rotation
   2048  * @property centerY
   2049  * @type Number
   2050  */
   2051 (function(scope) {
   2052   var dispatcher = scope.dispatcher;
   2053   var eventFactory = scope.eventFactory;
   2054   var pointermap = new scope.PointerMap();
   2055   var RAD_TO_DEG = 180 / Math.PI;
   2056   var pinch = {
   2057     events: [
   2058       'down',
   2059       'up',
   2060       'move',
   2061       'cancel'
   2062     ],
   2063     exposes: [
   2064       'pinchstart',
   2065       'pinch',
   2066       'pinchend',
   2067       'rotate'
   2068     ],
   2069     defaultActions: {
   2070       'pinch': 'none',
   2071       'rotate': 'none'
   2072     },
   2073     reference: {},
   2074     down: function(inEvent) {
   2075       pointermap.set(inEvent.pointerId, inEvent);
   2076       if (pointermap.pointers() == 2) {
   2077         var points = this.calcChord();
   2078         var angle = this.calcAngle(points);
   2079         this.reference = {
   2080           angle: angle,
   2081           diameter: points.diameter,
   2082           target: scope.targetFinding.LCA(points.a.target, points.b.target)
   2083         };
   2084 
   2085         this.firePinch('pinchstart', points.diameter, points);
   2086       }
   2087     },
   2088     up: function(inEvent) {
   2089       var p = pointermap.get(inEvent.pointerId);
   2090       var num = pointermap.pointers();
   2091       if (p) {
   2092         if (num === 2) {
   2093           // fire 'pinchend' before deleting pointer
   2094           var points = this.calcChord();
   2095           this.firePinch('pinchend', points.diameter, points);
   2096         }
   2097         pointermap.delete(inEvent.pointerId);
   2098       }
   2099     },
   2100     move: function(inEvent) {
   2101       if (pointermap.has(inEvent.pointerId)) {
   2102         pointermap.set(inEvent.pointerId, inEvent);
   2103         if (pointermap.pointers() > 1) {
   2104           this.calcPinchRotate();
   2105         }
   2106       }
   2107     },
   2108     cancel: function(inEvent) {
   2109         this.up(inEvent);
   2110     },
   2111     firePinch: function(type, diameter, points) {
   2112       var zoom = diameter / this.reference.diameter;
   2113       var e = eventFactory.makeGestureEvent(type, {
   2114         bubbles: true,
   2115         cancelable: true,
   2116         scale: zoom,
   2117         centerX: points.center.x,
   2118         centerY: points.center.y,
   2119         _source: 'pinch'
   2120       });
   2121       this.reference.target.dispatchEvent(e);
   2122     },
   2123     fireRotate: function(angle, points) {
   2124       var diff = Math.round((angle - this.reference.angle) % 360);
   2125       var e = eventFactory.makeGestureEvent('rotate', {
   2126         bubbles: true,
   2127         cancelable: true,
   2128         angle: diff,
   2129         centerX: points.center.x,
   2130         centerY: points.center.y,
   2131         _source: 'pinch'
   2132       });
   2133       this.reference.target.dispatchEvent(e);
   2134     },
   2135     calcPinchRotate: function() {
   2136       var points = this.calcChord();
   2137       var diameter = points.diameter;
   2138       var angle = this.calcAngle(points);
   2139       if (diameter != this.reference.diameter) {
   2140         this.firePinch('pinch', diameter, points);
   2141       }
   2142       if (angle != this.reference.angle) {
   2143         this.fireRotate(angle, points);
   2144       }
   2145     },
   2146     calcChord: function() {
   2147       var pointers = [];
   2148       pointermap.forEach(function(p) {
   2149         pointers.push(p);
   2150       });
   2151       var dist = 0;
   2152       // start with at least two pointers
   2153       var points = {a: pointers[0], b: pointers[1]};
   2154       var x, y, d;
   2155       for (var i = 0; i < pointers.length; i++) {
   2156         var a = pointers[i];
   2157         for (var j = i + 1; j < pointers.length; j++) {
   2158           var b = pointers[j];
   2159           x = Math.abs(a.clientX - b.clientX);
   2160           y = Math.abs(a.clientY - b.clientY);
   2161           d = x + y;
   2162           if (d > dist) {
   2163             dist = d;
   2164             points = {a: a, b: b};
   2165           }
   2166         }
   2167       }
   2168       x = Math.abs(points.a.clientX + points.b.clientX) / 2;
   2169       y = Math.abs(points.a.clientY + points.b.clientY) / 2;
   2170       points.center = { x: x, y: y };
   2171       points.diameter = dist;
   2172       return points;
   2173     },
   2174     calcAngle: function(points) {
   2175       var x = points.a.clientX - points.b.clientX;
   2176       var y = points.a.clientY - points.b.clientY;
   2177       return (360 + Math.atan2(y, x) * RAD_TO_DEG) % 360;
   2178     }
   2179   };
   2180   dispatcher.registerGesture('pinch', pinch);
   2181 })(window.PolymerGestures);
   2182 
   2183 (function (global) {
   2184     'use strict';
   2185 
   2186     var Token,
   2187         TokenName,
   2188         Syntax,
   2189         Messages,
   2190         source,
   2191         index,
   2192         length,
   2193         delegate,
   2194         lookahead,
   2195         state;
   2196 
   2197     Token = {
   2198         BooleanLiteral: 1,
   2199         EOF: 2,
   2200         Identifier: 3,
   2201         Keyword: 4,
   2202         NullLiteral: 5,
   2203         NumericLiteral: 6,
   2204         Punctuator: 7,
   2205         StringLiteral: 8
   2206     };
   2207 
   2208     TokenName = {};
   2209     TokenName[Token.BooleanLiteral] = 'Boolean';
   2210     TokenName[Token.EOF] = '<end>';
   2211     TokenName[Token.Identifier] = 'Identifier';
   2212     TokenName[Token.Keyword] = 'Keyword';
   2213     TokenName[Token.NullLiteral] = 'Null';
   2214     TokenName[Token.NumericLiteral] = 'Numeric';
   2215     TokenName[Token.Punctuator] = 'Punctuator';
   2216     TokenName[Token.StringLiteral] = 'String';
   2217 
   2218     Syntax = {
   2219         ArrayExpression: 'ArrayExpression',
   2220         BinaryExpression: 'BinaryExpression',
   2221         CallExpression: 'CallExpression',
   2222         ConditionalExpression: 'ConditionalExpression',
   2223         EmptyStatement: 'EmptyStatement',
   2224         ExpressionStatement: 'ExpressionStatement',
   2225         Identifier: 'Identifier',
   2226         Literal: 'Literal',
   2227         LabeledStatement: 'LabeledStatement',
   2228         LogicalExpression: 'LogicalExpression',
   2229         MemberExpression: 'MemberExpression',
   2230         ObjectExpression: 'ObjectExpression',
   2231         Program: 'Program',
   2232         Property: 'Property',
   2233         ThisExpression: 'ThisExpression',
   2234         UnaryExpression: 'UnaryExpression'
   2235     };
   2236 
   2237     // Error messages should be identical to V8.
   2238     Messages = {
   2239         UnexpectedToken:  'Unexpected token %0',
   2240         UnknownLabel: 'Undefined label \'%0\'',
   2241         Redeclaration: '%0 \'%1\' has already been declared'
   2242     };
   2243 
   2244     // Ensure the condition is true, otherwise throw an error.
   2245     // This is only to have a better contract semantic, i.e. another safety net
   2246     // to catch a logic error. The condition shall be fulfilled in normal case.
   2247     // Do NOT use this to enforce a certain condition on any user input.
   2248 
   2249     function assert(condition, message) {
   2250         if (!condition) {
   2251             throw new Error('ASSERT: ' + message);
   2252         }
   2253     }
   2254 
   2255     function isDecimalDigit(ch) {
   2256         return (ch >= 48 && ch <= 57);   // 0..9
   2257     }
   2258 
   2259 
   2260     // 7.2 White Space
   2261 
   2262     function isWhiteSpace(ch) {
   2263         return (ch === 32) ||  // space
   2264             (ch === 9) ||      // tab
   2265             (ch === 0xB) ||
   2266             (ch === 0xC) ||
   2267             (ch === 0xA0) ||
   2268             (ch >= 0x1680 && '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(String.fromCharCode(ch)) > 0);
   2269     }
   2270 
   2271     // 7.3 Line Terminators
   2272 
   2273     function isLineTerminator(ch) {
   2274         return (ch === 10) || (ch === 13) || (ch === 0x2028) || (ch === 0x2029);
   2275     }
   2276 
   2277     // 7.6 Identifier Names and Identifiers
   2278 
   2279     function isIdentifierStart(ch) {
   2280         return (ch === 36) || (ch === 95) ||  // $ (dollar) and _ (underscore)
   2281             (ch >= 65 && ch <= 90) ||         // A..Z
   2282             (ch >= 97 && ch <= 122);          // a..z
   2283     }
   2284 
   2285     function isIdentifierPart(ch) {
   2286         return (ch === 36) || (ch === 95) ||  // $ (dollar) and _ (underscore)
   2287             (ch >= 65 && ch <= 90) ||         // A..Z
   2288             (ch >= 97 && ch <= 122) ||        // a..z
   2289             (ch >= 48 && ch <= 57);           // 0..9
   2290     }
   2291 
   2292     // 7.6.1.1 Keywords
   2293 
   2294     function isKeyword(id) {
   2295         return (id === 'this')
   2296     }
   2297 
   2298     // 7.4 Comments
   2299 
   2300     function skipWhitespace() {
   2301         while (index < length && isWhiteSpace(source.charCodeAt(index))) {
   2302            ++index;
   2303         }
   2304     }
   2305 
   2306     function getIdentifier() {
   2307         var start, ch;
   2308 
   2309         start = index++;
   2310         while (index < length) {
   2311             ch = source.charCodeAt(index);
   2312             if (isIdentifierPart(ch)) {
   2313                 ++index;
   2314             } else {
   2315                 break;
   2316             }
   2317         }
   2318 
   2319         return source.slice(start, index);
   2320     }
   2321 
   2322     function scanIdentifier() {
   2323         var start, id, type;
   2324 
   2325         start = index;
   2326 
   2327         id = getIdentifier();
   2328 
   2329         // There is no keyword or literal with only one character.
   2330         // Thus, it must be an identifier.
   2331         if (id.length === 1) {
   2332             type = Token.Identifier;
   2333         } else if (isKeyword(id)) {
   2334             type = Token.Keyword;
   2335         } else if (id === 'null') {
   2336             type = Token.NullLiteral;
   2337         } else if (id === 'true' || id === 'false') {
   2338             type = Token.BooleanLiteral;
   2339         } else {
   2340             type = Token.Identifier;
   2341         }
   2342 
   2343         return {
   2344             type: type,
   2345             value: id,
   2346             range: [start, index]
   2347         };
   2348     }
   2349 
   2350 
   2351     // 7.7 Punctuators
   2352 
   2353     function scanPunctuator() {
   2354         var start = index,
   2355             code = source.charCodeAt(index),
   2356             code2,
   2357             ch1 = source[index],
   2358             ch2;
   2359 
   2360         switch (code) {
   2361 
   2362         // Check for most common single-character punctuators.
   2363         case 46:   // . dot
   2364         case 40:   // ( open bracket
   2365         case 41:   // ) close bracket
   2366         case 59:   // ; semicolon
   2367         case 44:   // , comma
   2368         case 123:  // { open curly brace
   2369         case 125:  // } close curly brace
   2370         case 91:   // [
   2371         case 93:   // ]
   2372         case 58:   // :
   2373         case 63:   // ?
   2374             ++index;
   2375             return {
   2376                 type: Token.Punctuator,
   2377                 value: String.fromCharCode(code),
   2378                 range: [start, index]
   2379             };
   2380 
   2381         default:
   2382             code2 = source.charCodeAt(index + 1);
   2383 
   2384             // '=' (char #61) marks an assignment or comparison operator.
   2385             if (code2 === 61) {
   2386                 switch (code) {
   2387                 case 37:  // %
   2388                 case 38:  // &
   2389                 case 42:  // *:
   2390                 case 43:  // +
   2391                 case 45:  // -
   2392                 case 47:  // /
   2393                 case 60:  // <
   2394                 case 62:  // >
   2395                 case 124: // |
   2396                     index += 2;
   2397                     return {
   2398                         type: Token.Punctuator,
   2399                         value: String.fromCharCode(code) + String.fromCharCode(code2),
   2400                         range: [start, index]
   2401                     };
   2402 
   2403                 case 33: // !
   2404                 case 61: // =
   2405                     index += 2;
   2406 
   2407                     // !== and ===
   2408                     if (source.charCodeAt(index) === 61) {
   2409                         ++index;
   2410                     }
   2411                     return {
   2412                         type: Token.Punctuator,
   2413                         value: source.slice(start, index),
   2414                         range: [start, index]
   2415                     };
   2416                 default:
   2417                     break;
   2418                 }
   2419             }
   2420             break;
   2421         }
   2422 
   2423         // Peek more characters.
   2424 
   2425         ch2 = source[index + 1];
   2426 
   2427         // Other 2-character punctuators: && ||
   2428 
   2429         if (ch1 === ch2 && ('&|'.indexOf(ch1) >= 0)) {
   2430             index += 2;
   2431             return {
   2432                 type: Token.Punctuator,
   2433                 value: ch1 + ch2,
   2434                 range: [start, index]
   2435             };
   2436         }
   2437 
   2438         if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
   2439             ++index;
   2440             return {
   2441                 type: Token.Punctuator,
   2442                 value: ch1,
   2443                 range: [start, index]
   2444             };
   2445         }
   2446 
   2447         throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
   2448     }
   2449 
   2450     // 7.8.3 Numeric Literals
   2451     function scanNumericLiteral() {
   2452         var number, start, ch;
   2453 
   2454         ch = source[index];
   2455         assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'),
   2456             'Numeric literal must start with a decimal digit or a decimal point');
   2457 
   2458         start = index;
   2459         number = '';
   2460         if (ch !== '.') {
   2461             number = source[index++];
   2462             ch = source[index];
   2463 
   2464             // Hex number starts with '0x'.
   2465             // Octal number starts with '0'.
   2466             if (number === '0') {
   2467                 // decimal number starts with '0' such as '09' is illegal.
   2468                 if (ch && isDecimalDigit(ch.charCodeAt(0))) {
   2469                     throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
   2470                 }
   2471             }
   2472 
   2473             while (isDecimalDigit(source.charCodeAt(index))) {
   2474                 number += source[index++];
   2475             }
   2476             ch = source[index];
   2477         }
   2478 
   2479         if (ch === '.') {
   2480             number += source[index++];
   2481             while (isDecimalDigit(source.charCodeAt(index))) {
   2482                 number += source[index++];
   2483             }
   2484             ch = source[index];
   2485         }
   2486 
   2487         if (ch === 'e' || ch === 'E') {
   2488             number += source[index++];
   2489 
   2490             ch = source[index];
   2491             if (ch === '+' || ch === '-') {
   2492                 number += source[index++];
   2493             }
   2494             if (isDecimalDigit(source.charCodeAt(index))) {
   2495                 while (isDecimalDigit(source.charCodeAt(index))) {
   2496                     number += source[index++];
   2497                 }
   2498             } else {
   2499                 throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
   2500             }
   2501         }
   2502 
   2503         if (isIdentifierStart(source.charCodeAt(index))) {
   2504             throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
   2505         }
   2506 
   2507         return {
   2508             type: Token.NumericLiteral,
   2509             value: parseFloat(number),
   2510             range: [start, index]
   2511         };
   2512     }
   2513 
   2514     // 7.8.4 String Literals
   2515 
   2516     function scanStringLiteral() {
   2517         var str = '', quote, start, ch, octal = false;
   2518 
   2519         quote = source[index];
   2520         assert((quote === '\'' || quote === '"'),
   2521             'String literal must starts with a quote');
   2522 
   2523         start = index;
   2524         ++index;
   2525 
   2526         while (index < length) {
   2527             ch = source[index++];
   2528 
   2529             if (ch === quote) {
   2530                 quote = '';
   2531                 break;
   2532             } else if (ch === '\\') {
   2533                 ch = source[index++];
   2534                 if (!ch || !isLineTerminator(ch.charCodeAt(0))) {
   2535                     switch (ch) {
   2536                     case 'n':
   2537                         str += '\n';
   2538                         break;
   2539                     case 'r':
   2540                         str += '\r';
   2541                         break;
   2542                     case 't':
   2543                         str += '\t';
   2544                         break;
   2545                     case 'b':
   2546                         str += '\b';
   2547                         break;
   2548                     case 'f':
   2549                         str += '\f';
   2550                         break;
   2551                     case 'v':
   2552                         str += '\x0B';
   2553                         break;
   2554 
   2555                     default:
   2556                         str += ch;
   2557                         break;
   2558                     }
   2559                 } else {
   2560                     if (ch ===  '\r' && source[index] === '\n') {
   2561                         ++index;
   2562                     }
   2563                 }
   2564             } else if (isLineTerminator(ch.charCodeAt(0))) {
   2565                 break;
   2566             } else {
   2567                 str += ch;
   2568             }
   2569         }
   2570 
   2571         if (quote !== '') {
   2572             throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
   2573         }
   2574 
   2575         return {
   2576             type: Token.StringLiteral,
   2577             value: str,
   2578             octal: octal,
   2579             range: [start, index]
   2580         };
   2581     }
   2582 
   2583     function isIdentifierName(token) {
   2584         return token.type === Token.Identifier ||
   2585             token.type === Token.Keyword ||
   2586             token.type === Token.BooleanLiteral ||
   2587             token.type === Token.NullLiteral;
   2588     }
   2589 
   2590     function advance() {
   2591         var ch;
   2592 
   2593         skipWhitespace();
   2594 
   2595         if (index >= length) {
   2596             return {
   2597                 type: Token.EOF,
   2598                 range: [index, index]
   2599             };
   2600         }
   2601 
   2602         ch = source.charCodeAt(index);
   2603 
   2604         // Very common: ( and ) and ;
   2605         if (ch === 40 || ch === 41 || ch === 58) {
   2606             return scanPunctuator();
   2607         }
   2608 
   2609         // String literal starts with single quote (#39) or double quote (#34).
   2610         if (ch === 39 || ch === 34) {
   2611             return scanStringLiteral();
   2612         }
   2613 
   2614         if (isIdentifierStart(ch)) {
   2615             return scanIdentifier();
   2616         }
   2617 
   2618         // Dot (.) char #46 can also start a floating-point number, hence the need
   2619         // to check the next character.
   2620         if (ch === 46) {
   2621             if (isDecimalDigit(source.charCodeAt(index + 1))) {
   2622                 return scanNumericLiteral();
   2623             }
   2624             return scanPunctuator();
   2625         }
   2626 
   2627         if (isDecimalDigit(ch)) {
   2628             return scanNumericLiteral();
   2629         }
   2630 
   2631         return scanPunctuator();
   2632     }
   2633 
   2634     function lex() {
   2635         var token;
   2636 
   2637         token = lookahead;
   2638         index = token.range[1];
   2639 
   2640         lookahead = advance();
   2641 
   2642         index = token.range[1];
   2643 
   2644         return token;
   2645     }
   2646 
   2647     function peek() {
   2648         var pos;
   2649 
   2650         pos = index;
   2651         lookahead = advance();
   2652         index = pos;
   2653     }
   2654 
   2655     // Throw an exception
   2656 
   2657     function throwError(token, messageFormat) {
   2658         var error,
   2659             args = Array.prototype.slice.call(arguments, 2),
   2660             msg = messageFormat.replace(
   2661                 /%(\d)/g,
   2662                 function (whole, index) {
   2663                     assert(index < args.length, 'Message reference must be in range');
   2664                     return args[index];
   2665                 }
   2666             );
   2667 
   2668         error = new Error(msg);
   2669         error.index = index;
   2670         error.description = msg;
   2671         throw error;
   2672     }
   2673 
   2674     // Throw an exception because of the token.
   2675 
   2676     function throwUnexpected(token) {
   2677         throwError(token, Messages.UnexpectedToken, token.value);
   2678     }
   2679 
   2680     // Expect the next token to match the specified punctuator.
   2681     // If not, an exception will be thrown.
   2682 
   2683     function expect(value) {
   2684         var token = lex();
   2685         if (token.type !== Token.Punctuator || token.value !== value) {
   2686             throwUnexpected(token);
   2687         }
   2688     }
   2689 
   2690     // Return true if the next token matches the specified punctuator.
   2691 
   2692     function match(value) {
   2693         return lookahead.type === Token.Punctuator && lookahead.value === value;
   2694     }
   2695 
   2696     // Return true if the next token matches the specified keyword
   2697 
   2698     function matchKeyword(keyword) {
   2699         return lookahead.type === Token.Keyword && lookahead.value === keyword;
   2700     }
   2701 
   2702     function consumeSemicolon() {
   2703         // Catch the very common case first: immediately a semicolon (char #59).
   2704         if (source.charCodeAt(index) === 59) {
   2705             lex();
   2706             return;
   2707         }
   2708 
   2709         skipWhitespace();
   2710 
   2711         if (match(';')) {
   2712             lex();
   2713             return;
   2714         }
   2715 
   2716         if (lookahead.type !== Token.EOF && !match('}')) {
   2717             throwUnexpected(lookahead);
   2718         }
   2719     }
   2720 
   2721     // 11.1.4 Array Initialiser
   2722 
   2723     function parseArrayInitialiser() {
   2724         var elements = [];
   2725 
   2726         expect('[');
   2727 
   2728         while (!match(']')) {
   2729             if (match(',')) {
   2730                 lex();
   2731                 elements.push(null);
   2732             } else {
   2733                 elements.push(parseExpression());
   2734 
   2735                 if (!match(']')) {
   2736                     expect(',');
   2737                 }
   2738             }
   2739         }
   2740 
   2741         expect(']');
   2742 
   2743         return delegate.createArrayExpression(elements);
   2744     }
   2745 
   2746     // 11.1.5 Object Initialiser
   2747 
   2748     function parseObjectPropertyKey() {
   2749         var token;
   2750 
   2751         skipWhitespace();
   2752         token = lex();
   2753 
   2754         // Note: This function is called only from parseObjectProperty(), where
   2755         // EOF and Punctuator tokens are already filtered out.
   2756         if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) {
   2757             return delegate.createLiteral(token);
   2758         }
   2759 
   2760         return delegate.createIdentifier(token.value);
   2761     }
   2762 
   2763     function parseObjectProperty() {
   2764         var token, key;
   2765 
   2766         token = lookahead;
   2767         skipWhitespace();
   2768 
   2769         if (token.type === Token.EOF || token.type === Token.Punctuator) {
   2770             throwUnexpected(token);
   2771         }
   2772 
   2773         key = parseObjectPropertyKey();
   2774         expect(':');
   2775         return delegate.createProperty('init', key, parseExpression());
   2776     }
   2777 
   2778     function parseObjectInitialiser() {
   2779         var properties = [];
   2780 
   2781         expect('{');
   2782 
   2783         while (!match('}')) {
   2784             properties.push(parseObjectProperty());
   2785 
   2786             if (!match('}')) {
   2787                 expect(',');
   2788             }
   2789         }
   2790 
   2791         expect('}');
   2792 
   2793         return delegate.createObjectExpression(properties);
   2794     }
   2795 
   2796     // 11.1.6 The Grouping Operator
   2797 
   2798     function parseGroupExpression() {
   2799         var expr;
   2800 
   2801         expect('(');
   2802 
   2803         expr = parseExpression();
   2804 
   2805         expect(')');
   2806 
   2807         return expr;
   2808     }
   2809 
   2810 
   2811     // 11.1 Primary Expressions
   2812 
   2813     function parsePrimaryExpression() {
   2814         var type, token, expr;
   2815 
   2816         if (match('(')) {
   2817             return parseGroupExpression();
   2818         }
   2819 
   2820         type = lookahead.type;
   2821 
   2822         if (type === Token.Identifier) {
   2823             expr = delegate.createIdentifier(lex().value);
   2824         } else if (type === Token.StringLiteral || type === Token.NumericLiteral) {
   2825             expr = delegate.createLiteral(lex());
   2826         } else if (type === Token.Keyword) {
   2827             if (matchKeyword('this')) {
   2828                 lex();
   2829                 expr = delegate.createThisExpression();
   2830             }
   2831         } else if (type === Token.BooleanLiteral) {
   2832             token = lex();
   2833             token.value = (token.value === 'true');
   2834             expr = delegate.createLiteral(token);
   2835         } else if (type === Token.NullLiteral) {
   2836             token = lex();
   2837             token.value = null;
   2838             expr = delegate.createLiteral(token);
   2839         } else if (match('[')) {
   2840             expr = parseArrayInitialiser();
   2841         } else if (match('{')) {
   2842             expr = parseObjectInitialiser();
   2843         }
   2844 
   2845         if (expr) {
   2846             return expr;
   2847         }
   2848 
   2849         throwUnexpected(lex());
   2850     }
   2851 
   2852     // 11.2 Left-Hand-Side Expressions
   2853 
   2854     function parseArguments() {
   2855         var args = [];
   2856 
   2857         expect('(');
   2858 
   2859         if (!match(')')) {
   2860             while (index < length) {
   2861                 args.push(parseExpression());
   2862                 if (match(')')) {
   2863                     break;
   2864                 }
   2865                 expect(',');
   2866             }
   2867         }
   2868 
   2869         expect(')');
   2870 
   2871         return args;
   2872     }
   2873 
   2874     function parseNonComputedProperty() {
   2875         var token;
   2876 
   2877         token = lex();
   2878 
   2879         if (!isIdentifierName(token)) {
   2880             throwUnexpected(token);
   2881         }
   2882 
   2883         return delegate.createIdentifier(token.value);
   2884     }
   2885 
   2886     function parseNonComputedMember() {
   2887         expect('.');
   2888 
   2889         return parseNonComputedProperty();
   2890     }
   2891 
   2892     function parseComputedMember() {
   2893         var expr;
   2894 
   2895         expect('[');
   2896 
   2897         expr = parseExpression();
   2898 
   2899         expect(']');
   2900 
   2901         return expr;
   2902     }
   2903 
   2904     function parseLeftHandSideExpression() {
   2905         var expr, args, property;
   2906 
   2907         expr = parsePrimaryExpression();
   2908 
   2909         while (true) {
   2910             if (match('[')) {
   2911                 property = parseComputedMember();
   2912                 expr = delegate.createMemberExpression('[', expr, property);
   2913             } else if (match('.')) {
   2914                 property = parseNonComputedMember();
   2915                 expr = delegate.createMemberExpression('.', expr, property);
   2916             } else if (match('(')) {
   2917                 args = parseArguments();
   2918                 expr = delegate.createCallExpression(expr, args);
   2919             } else {
   2920                 break;
   2921             }
   2922         }
   2923 
   2924         return expr;
   2925     }
   2926 
   2927     // 11.3 Postfix Expressions
   2928 
   2929     var parsePostfixExpression = parseLeftHandSideExpression;
   2930 
   2931     // 11.4 Unary Operators
   2932 
   2933     function parseUnaryExpression() {
   2934         var token, expr;
   2935 
   2936         if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) {
   2937             expr = parsePostfixExpression();
   2938         } else if (match('+') || match('-') || match('!')) {
   2939             token = lex();
   2940             expr = parseUnaryExpression();
   2941             expr = delegate.createUnaryExpression(token.value, expr);
   2942         } else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
   2943             throwError({}, Messages.UnexpectedToken);
   2944         } else {
   2945             expr = parsePostfixExpression();
   2946         }
   2947 
   2948         return expr;
   2949     }
   2950 
   2951     function binaryPrecedence(token) {
   2952         var prec = 0;
   2953 
   2954         if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
   2955             return 0;
   2956         }
   2957 
   2958         switch (token.value) {
   2959         case '||':
   2960             prec = 1;
   2961             break;
   2962 
   2963         case '&&':
   2964             prec = 2;
   2965             break;
   2966 
   2967         case '==':
   2968         case '!=':
   2969         case '===':
   2970         case '!==':
   2971             prec = 6;
   2972             break;
   2973 
   2974         case '<':
   2975         case '>':
   2976         case '<=':
   2977         case '>=':
   2978         case 'instanceof':
   2979             prec = 7;
   2980             break;
   2981 
   2982         case 'in':
   2983             prec = 7;
   2984             break;
   2985 
   2986         case '+':
   2987         case '-':
   2988             prec = 9;
   2989             break;
   2990 
   2991         case '*':
   2992         case '/':
   2993         case '%':
   2994             prec = 11;
   2995             break;
   2996 
   2997         default:
   2998             break;
   2999         }
   3000 
   3001         return prec;
   3002     }
   3003 
   3004     // 11.5 Multiplicative Operators
   3005     // 11.6 Additive Operators
   3006     // 11.7 Bitwise Shift Operators
   3007     // 11.8 Relational Operators
   3008     // 11.9 Equality Operators
   3009     // 11.10 Binary Bitwise Operators
   3010     // 11.11 Binary Logical Operators
   3011 
   3012     function parseBinaryExpression() {
   3013         var expr, token, prec, stack, right, operator, left, i;
   3014 
   3015         left = parseUnaryExpression();
   3016 
   3017         token = lookahead;
   3018         prec = binaryPrecedence(token);
   3019         if (prec === 0) {
   3020             return left;
   3021         }
   3022         token.prec = prec;
   3023         lex();
   3024 
   3025         right = parseUnaryExpression();
   3026 
   3027         stack = [left, token, right];
   3028 
   3029         while ((prec = binaryPrecedence(lookahead)) > 0) {
   3030 
   3031             // Reduce: make a binary expression from the three topmost entries.
   3032             while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) {
   3033                 right = stack.pop();
   3034                 operator = stack.pop().value;
   3035                 left = stack.pop();
   3036                 expr = delegate.createBinaryExpression(operator, left, right);
   3037                 stack.push(expr);
   3038             }
   3039 
   3040             // Shift.
   3041             token = lex();
   3042             token.prec = prec;
   3043             stack.push(token);
   3044             expr = parseUnaryExpression();
   3045             stack.push(expr);
   3046         }
   3047 
   3048         // Final reduce to clean-up the stack.
   3049         i = stack.length - 1;
   3050         expr = stack[i];
   3051         while (i > 1) {
   3052             expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr);
   3053             i -= 2;
   3054         }
   3055 
   3056         return expr;
   3057     }
   3058 
   3059 
   3060     // 11.12 Conditional Operator
   3061 
   3062     function parseConditionalExpression() {
   3063         var expr, consequent, alternate;
   3064 
   3065         expr = parseBinaryExpression();
   3066 
   3067         if (match('?')) {
   3068             lex();
   3069             consequent = parseConditionalExpression();
   3070             expect(':');
   3071             alternate = parseConditionalExpression();
   3072 
   3073             expr = delegate.createConditionalExpression(expr, consequent, alternate);
   3074         }
   3075 
   3076         return expr;
   3077     }
   3078 
   3079     // Simplification since we do not support AssignmentExpression.
   3080     var parseExpression = parseConditionalExpression;
   3081 
   3082     // Polymer Syntax extensions
   3083 
   3084     // Filter ::
   3085     //   Identifier
   3086     //   Identifier "(" ")"
   3087     //   Identifier "(" FilterArguments ")"
   3088 
   3089     function parseFilter() {
   3090         var identifier, args;
   3091 
   3092         identifier = lex();
   3093 
   3094         if (identifier.type !== Token.Identifier) {
   3095             throwUnexpected(identifier);
   3096         }
   3097 
   3098         args = match('(') ? parseArguments() : [];
   3099 
   3100         return delegate.createFilter(identifier.value, args);
   3101     }
   3102 
   3103     // Filters ::
   3104     //   "|" Filter
   3105     //   Filters "|" Filter
   3106 
   3107     function parseFilters() {
   3108         while (match('|')) {
   3109             lex();
   3110             parseFilter();
   3111         }
   3112     }
   3113 
   3114     // TopLevel ::
   3115     //   LabelledExpressions
   3116     //   AsExpression
   3117     //   InExpression
   3118     //   FilterExpression
   3119 
   3120     // AsExpression ::
   3121     //   FilterExpression as Identifier
   3122 
   3123     // InExpression ::
   3124     //   Identifier, Identifier in FilterExpression
   3125     //   Identifier in FilterExpression
   3126 
   3127     // FilterExpression ::
   3128     //   Expression
   3129     //   Expression Filters
   3130 
   3131     function parseTopLevel() {
   3132         skipWhitespace();
   3133         peek();
   3134 
   3135         var expr = parseExpression();
   3136         if (expr) {
   3137             if (lookahead.value === ',' || lookahead.value == 'in' &&
   3138                        expr.type === Syntax.Identifier) {
   3139                 parseInExpression(expr);
   3140             } else {
   3141                 parseFilters();
   3142                 if (lookahead.value === 'as') {
   3143                     parseAsExpression(expr);
   3144                 } else {
   3145                     delegate.createTopLevel(expr);
   3146                 }
   3147             }
   3148         }
   3149 
   3150         if (lookahead.type !== Token.EOF) {
   3151             throwUnexpected(lookahead);
   3152         }
   3153     }
   3154 
   3155     function parseAsExpression(expr) {
   3156         lex();  // as
   3157         var identifier = lex().value;
   3158         delegate.createAsExpression(expr, identifier);
   3159     }
   3160 
   3161     function parseInExpression(identifier) {
   3162         var indexName;
   3163         if (lookahead.value === ',') {
   3164             lex();
   3165             if (lookahead.type !== Token.Identifier)
   3166                 throwUnexpected(lookahead);
   3167             indexName = lex().value;
   3168         }
   3169 
   3170         lex();  // in
   3171         var expr = parseExpression();
   3172         parseFilters();
   3173         delegate.createInExpression(identifier.name, indexName, expr);
   3174     }
   3175 
   3176     function parse(code, inDelegate) {
   3177         delegate = inDelegate;
   3178         source = code;
   3179         index = 0;
   3180         length = source.length;
   3181         lookahead = null;
   3182         state = {
   3183             labelSet: {}
   3184         };
   3185 
   3186         return parseTopLevel();
   3187     }
   3188 
   3189     global.esprima = {
   3190         parse: parse
   3191     };
   3192 })(this);
   3193 
   3194 // Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
   3195 // This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
   3196 // The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
   3197 // The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
   3198 // Code distributed by Google as part of the polymer project is also
   3199 // subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
   3200 
   3201 (function (global) {
   3202   'use strict';
   3203 
   3204   function prepareBinding(expressionText, name, node, filterRegistry) {
   3205     var expression;
   3206     try {
   3207       expression = getExpression(expressionText);
   3208       if (expression.scopeIdent &&
   3209           (node.nodeType !== Node.ELEMENT_NODE ||
   3210            node.tagName !== 'TEMPLATE' ||
   3211            (name !== 'bind' && name !== 'repeat'))) {
   3212         throw Error('as and in can only be used within <template bind/repeat>');
   3213       }
   3214     } catch (ex) {
   3215       console.error('Invalid expression syntax: ' + expressionText, ex);
   3216       return;
   3217     }
   3218 
   3219     return function(model, node, oneTime) {
   3220       var binding = expression.getBinding(model, filterRegistry, oneTime);
   3221       if (expression.scopeIdent && binding) {
   3222         node.polymerExpressionScopeIdent_ = expression.scopeIdent;
   3223         if (expression.indexIdent)
   3224           node.polymerExpressionIndexIdent_ = expression.indexIdent;
   3225       }
   3226 
   3227       return binding;
   3228     }
   3229   }
   3230 
   3231   // TODO(rafaelw): Implement simple LRU.
   3232   var expressionParseCache = Object.create(null);
   3233 
   3234   function getExpression(expressionText) {
   3235     var expression = expressionParseCache[expressionText];
   3236     if (!expression) {
   3237       var delegate = new ASTDelegate();
   3238       esprima.parse(expressionText, delegate);
   3239       expression = new Expression(delegate);
   3240       expressionParseCache[expressionText] = expression;
   3241     }
   3242     return expression;
   3243   }
   3244 
   3245   function Literal(value) {
   3246     this.value = value;
   3247     this.valueFn_ = undefined;
   3248   }
   3249 
   3250   Literal.prototype = {
   3251     valueFn: function() {
   3252       if (!this.valueFn_) {
   3253         var value = this.value;
   3254         this.valueFn_ = function() {
   3255           return value;
   3256         }
   3257       }
   3258 
   3259       return this.valueFn_;
   3260     }
   3261   }
   3262 
   3263   function IdentPath(name) {
   3264     this.name = name;
   3265     this.path = Path.get(name);
   3266   }
   3267 
   3268   IdentPath.prototype = {
   3269     valueFn: function() {
   3270       if (!this.valueFn_) {
   3271         var name = this.name;
   3272         var path = this.path;
   3273         this.valueFn_ = function(model, observer) {
   3274           if (observer)
   3275             observer.addPath(model, path);
   3276 
   3277           return path.getValueFrom(model);
   3278         }
   3279       }
   3280 
   3281       return this.valueFn_;
   3282     },
   3283 
   3284     setValue: function(model, newValue) {
   3285       if (this.path.length == 1)
   3286         model = findScope(model, this.path[0]);
   3287 
   3288       return this.path.setValueFrom(model, newValue);
   3289     }
   3290   };
   3291 
   3292   function MemberExpression(object, property, accessor) {
   3293     this.computed = accessor == '[';
   3294 
   3295     this.dynamicDeps = typeof object == 'function' ||
   3296                        object.dynamicDeps ||
   3297                        (this.computed && !(property instanceof Literal));
   3298 
   3299     this.simplePath =
   3300         !this.dynamicDeps &&
   3301         (property instanceof IdentPath || property instanceof Literal) &&
   3302         (object instanceof MemberExpression || object instanceof IdentPath);
   3303 
   3304     this.object = this.simplePath ? object : getFn(object);
   3305     this.property = !this.computed || this.simplePath ?
   3306         property : getFn(property);
   3307   }
   3308 
   3309   MemberExpression.prototype = {
   3310     get fullPath() {
   3311       if (!this.fullPath_) {
   3312 
   3313         var parts = this.object instanceof MemberExpression ?
   3314             this.object.fullPath.slice() : [this.object.name];
   3315         parts.push(this.property instanceof IdentPath ?
   3316             this.property.name : this.property.value);
   3317         this.fullPath_ = Path.get(parts);
   3318       }
   3319 
   3320       return this.fullPath_;
   3321     },
   3322 
   3323     valueFn: function() {
   3324       if (!this.valueFn_) {
   3325         var object = this.object;
   3326 
   3327         if (this.simplePath) {
   3328           var path = this.fullPath;
   3329 
   3330           this.valueFn_ = function(model, observer) {
   3331             if (observer)
   3332               observer.addPath(model, path);
   3333 
   3334             return path.getValueFrom(model);
   3335           };
   3336         } else if (!this.computed) {
   3337           var path = Path.get(this.property.name);
   3338 
   3339           this.valueFn_ = function(model, observer, filterRegistry) {
   3340             var context = object(model, observer, filterRegistry);
   3341 
   3342             if (observer)
   3343               observer.addPath(context, path);
   3344 
   3345             return path.getValueFrom(context);
   3346           }
   3347         } else {
   3348           // Computed property.
   3349           var property = this.property;
   3350 
   3351           this.valueFn_ = function(model, observer, filterRegistry) {
   3352             var context = object(model, observer, filterRegistry);
   3353             var propName = property(model, observer, filterRegistry);
   3354             if (observer)
   3355               observer.addPath(context, [propName]);
   3356 
   3357             return context ? context[propName] : undefined;
   3358           };
   3359         }
   3360       }
   3361       return this.valueFn_;
   3362     },
   3363 
   3364     setValue: function(model, newValue) {
   3365       if (this.simplePath) {
   3366         this.fullPath.setValueFrom(model, newValue);
   3367         return newValue;
   3368       }
   3369 
   3370       var object = this.object(model);
   3371       var propName = this.property instanceof IdentPath ? this.property.name :
   3372           this.property(model);
   3373       return object[propName] = newValue;
   3374     }
   3375   };
   3376 
   3377   function Filter(name, args) {
   3378     this.name = name;
   3379     this.args = [];
   3380     for (var i = 0; i < args.length; i++) {
   3381       this.args[i] = getFn(args[i]);
   3382     }
   3383   }
   3384 
   3385   Filter.prototype = {
   3386     transform: function(model, observer, filterRegistry, toModelDirection,
   3387                         initialArgs) {
   3388       var context = model;
   3389       var fn = context[this.name];
   3390 
   3391       if (!fn) {
   3392         fn = filterRegistry[this.name];
   3393         if (!fn) {
   3394           console.error('Cannot find function or filter: ' + this.name);
   3395           return;
   3396         }
   3397       }
   3398 
   3399       // If toModelDirection is falsey, then the "normal" (dom-bound) direction
   3400       // is used. Otherwise, it looks for a 'toModel' property function on the
   3401       // object.
   3402       if (toModelDirection) {
   3403         fn = fn.toModel;
   3404       } else if (typeof fn.toDOM == 'function') {
   3405         fn = fn.toDOM;
   3406       }
   3407 
   3408       if (typeof fn != 'function') {
   3409         console.error('Cannot find function or filter: ' + this.name);
   3410         return;
   3411       }
   3412 
   3413       var args = initialArgs || [];
   3414       for (var i = 0; i < this.args.length; i++) {
   3415         args.push(getFn(this.args[i])(model, observer, filterRegistry));
   3416       }
   3417 
   3418       return fn.apply(context, args);
   3419     }
   3420   };
   3421 
   3422   function notImplemented() { throw Error('Not Implemented'); }
   3423 
   3424   var unaryOperators = {
   3425     '+': function(v) { return +v; },
   3426     '-': function(v) { return -v; },
   3427     '!': function(v) { return !v; }
   3428   };
   3429 
   3430   var binaryOperators = {
   3431     '+': function(l, r) { return l+r; },
   3432     '-': function(l, r) { return l-r; },
   3433     '*': function(l, r) { return l*r; },
   3434     '/': function(l, r) { return l/r; },
   3435     '%': function(l, r) { return l%r; },
   3436     '<': function(l, r) { return l<r; },
   3437     '>': function(l, r) { return l>r; },
   3438     '<=': function(l, r) { return l<=r; },
   3439     '>=': function(l, r) { return l>=r; },
   3440     '==': function(l, r) { return l==r; },
   3441     '!=': function(l, r) { return l!=r; },
   3442     '===': function(l, r) { return l===r; },
   3443     '!==': function(l, r) { return l!==r; },
   3444     '&&': function(l, r) { return l&&r; },
   3445     '||': function(l, r) { return l||r; },
   3446   };
   3447 
   3448   function getFn(arg) {
   3449     return typeof arg == 'function' ? arg : arg.valueFn();
   3450   }
   3451 
   3452   function ASTDelegate() {
   3453     this.expression = null;
   3454     this.filters = [];
   3455     this.deps = {};
   3456     this.currentPath = undefined;
   3457     this.scopeIdent = undefined;
   3458     this.indexIdent = undefined;
   3459     this.dynamicDeps = false;
   3460   }
   3461 
   3462   ASTDelegate.prototype = {
   3463     createUnaryExpression: function(op, argument) {
   3464       if (!unaryOperators[op])
   3465         throw Error('Disallowed operator: ' + op);
   3466 
   3467       argument = getFn(argument);
   3468 
   3469       return function(model, observer, filterRegistry) {
   3470         return unaryOperators[op](argument(model, observer, filterRegistry));
   3471       };
   3472     },
   3473 
   3474     createBinaryExpression: function(op, left, right) {
   3475       if (!binaryOperators[op])
   3476         throw Error('Disallowed operator: ' + op);
   3477 
   3478       left = getFn(left);
   3479       right = getFn(right);
   3480 
   3481       switch (op) {
   3482         case '||':
   3483           this.dynamicDeps = true;
   3484           return function(model, observer, filterRegistry) {
   3485             return left(model, observer, filterRegistry) ||
   3486                 right(model, observer, filterRegistry);
   3487           };
   3488         case '&&':
   3489           this.dynamicDeps = true;
   3490           return function(model, observer, filterRegistry) {
   3491             return left(model, observer, filterRegistry) &&
   3492                 right(model, observer, filterRegistry);
   3493           };
   3494       }
   3495 
   3496       return function(model, observer, filterRegistry) {
   3497         return binaryOperators[op](left(model, observer, filterRegistry),
   3498                                    right(model, observer, filterRegistry));
   3499       };
   3500     },
   3501 
   3502     createConditionalExpression: function(test, consequent, alternate) {
   3503       test = getFn(test);
   3504       consequent = getFn(consequent);
   3505       alternate = getFn(alternate);
   3506 
   3507       this.dynamicDeps = true;
   3508 
   3509       return function(model, observer, filterRegistry) {
   3510         return test(model, observer, filterRegistry) ?
   3511             consequent(model, observer, filterRegistry) :
   3512             alternate(model, observer, filterRegistry);
   3513       }
   3514     },
   3515 
   3516     createIdentifier: function(name) {
   3517       var ident = new IdentPath(name);
   3518       ident.type = 'Identifier';
   3519       return ident;
   3520     },
   3521 
   3522     createMemberExpression: function(accessor, object, property) {
   3523       var ex = new MemberExpression(object, property, accessor);
   3524       if (ex.dynamicDeps)
   3525         this.dynamicDeps = true;
   3526       return ex;
   3527     },
   3528 
   3529     createCallExpression: function(expression, args) {
   3530       if (!(expression instanceof IdentPath))
   3531         throw Error('Only identifier function invocations are allowed');
   3532 
   3533       var filter = new Filter(expression.name, args);
   3534 
   3535       return function(model, observer, filterRegistry) {
   3536         return filter.transform(model, observer, filterRegistry, false);
   3537       };
   3538     },
   3539 
   3540     createLiteral: function(token) {
   3541       return new Literal(token.value);
   3542     },
   3543 
   3544     createArrayExpression: function(elements) {
   3545       for (var i = 0; i < elements.length; i++)
   3546         elements[i] = getFn(elements[i]);
   3547 
   3548       return function(model, observer, filterRegistry) {
   3549         var arr = []
   3550         for (var i = 0; i < elements.length; i++)
   3551           arr.push(elements[i](model, observer, filterRegistry));
   3552         return arr;
   3553       }
   3554     },
   3555 
   3556     createProperty: function(kind, key, value) {
   3557       return {
   3558         key: key instanceof IdentPath ? key.name : key.value,
   3559         value: value
   3560       };
   3561     },
   3562 
   3563     createObjectExpression: function(properties) {
   3564       for (var i = 0; i < properties.length; i++)
   3565         properties[i].value = getFn(properties[i].value);
   3566 
   3567       return function(model, observer, filterRegistry) {
   3568         var obj = {};
   3569         for (var i = 0; i < properties.length; i++)
   3570           obj[properties[i].key] =
   3571               properties[i].value(model, observer, filterRegistry);
   3572         return obj;
   3573       }
   3574     },
   3575 
   3576     createFilter: function(name, args) {
   3577       this.filters.push(new Filter(name, args));
   3578     },
   3579 
   3580     createAsExpression: function(expression, scopeIdent) {
   3581       this.expression = expression;
   3582       this.scopeIdent = scopeIdent;
   3583     },
   3584 
   3585     createInExpression: function(scopeIdent, indexIdent, expression) {
   3586       this.expression = expression;
   3587       this.scopeIdent = scopeIdent;
   3588       this.indexIdent = indexIdent;
   3589     },
   3590 
   3591     createTopLevel: function(expression) {
   3592       this.expression = expression;
   3593     },
   3594 
   3595     createThisExpression: notImplemented
   3596   }
   3597 
   3598   function ConstantObservable(value) {
   3599     this.value_ = value;
   3600   }
   3601 
   3602   ConstantObservable.prototype = {
   3603     open: function() { return this.value_; },
   3604     discardChanges: function() { return this.value_; },
   3605     deliver: function() {},
   3606     close: function() {},
   3607   }
   3608 
   3609   function Expression(delegate) {
   3610     this.scopeIdent = delegate.scopeIdent;
   3611     this.indexIdent = delegate.indexIdent;
   3612 
   3613     if (!delegate.expression)
   3614       throw Error('No expression found.');
   3615 
   3616     this.expression = delegate.expression;
   3617     getFn(this.expression); // forces enumeration of path dependencies
   3618 
   3619     this.filters = delegate.filters;
   3620     this.dynamicDeps = delegate.dynamicDeps;
   3621   }
   3622 
   3623   Expression.prototype = {
   3624     getBinding: function(model, filterRegistry, oneTime) {
   3625       if (oneTime)
   3626         return this.getValue(model, undefined, filterRegistry);
   3627 
   3628       var observer = new CompoundObserver();
   3629       // captures deps.
   3630       var firstValue = this.getValue(model, observer, filterRegistry);
   3631       var firstTime = true;
   3632       var self = this;
   3633 
   3634       function valueFn() {
   3635         // deps cannot have changed on first value retrieval.
   3636         if (firstTime) {
   3637           firstTime = false;
   3638           return firstValue;
   3639         }
   3640 
   3641         if (self.dynamicDeps)
   3642           observer.startReset();
   3643 
   3644         var value = self.getValue(model,
   3645                                   self.dynamicDeps ? observer : undefined,
   3646                                   filterRegistry);
   3647         if (self.dynamicDeps)
   3648           observer.finishReset();
   3649 
   3650         return value;
   3651       }
   3652 
   3653       function setValueFn(newValue) {
   3654         self.setValue(model, newValue, filterRegistry);
   3655         return newValue;
   3656       }
   3657 
   3658       return new ObserverTransform(observer, valueFn, setValueFn, true);
   3659     },
   3660 
   3661     getValue: function(model, observer, filterRegistry) {
   3662       var value = getFn(this.expression)(model, observer, filterRegistry);
   3663       for (var i = 0; i < this.filters.length; i++) {
   3664         value = this.filters[i].transform(model, observer, filterRegistry,
   3665             false, [value]);
   3666       }
   3667 
   3668       return value;
   3669     },
   3670 
   3671     setValue: function(model, newValue, filterRegistry) {
   3672       var count = this.filters ? this.filters.length : 0;
   3673       while (count-- > 0) {
   3674         newValue = this.filters[count].transform(model, undefined,
   3675             filterRegistry, true, [newValue]);
   3676       }
   3677 
   3678       if (this.expression.setValue)
   3679         return this.expression.setValue(model, newValue);
   3680     }
   3681   }
   3682 
   3683   /**
   3684    * Converts a style property name to a css property name. For example:
   3685    * "WebkitUserSelect" to "-webkit-user-select"
   3686    */
   3687   function convertStylePropertyName(name) {
   3688     return String(name).replace(/[A-Z]/g, function(c) {
   3689       return '-' + c.toLowerCase();
   3690     });
   3691   }
   3692 
   3693   var parentScopeName = '@' + Math.random().toString(36).slice(2);
   3694 
   3695   // Single ident paths must bind directly to the appropriate scope object.
   3696   // I.e. Pushed values in two-bindings need to be assigned to the actual model
   3697   // object.
   3698   function findScope(model, prop) {
   3699     while (model[parentScopeName] &&
   3700            !Object.prototype.hasOwnProperty.call(model, prop)) {
   3701       model = model[parentScopeName];
   3702     }
   3703 
   3704     return model;
   3705   }
   3706 
   3707   function isLiteralExpression(pathString) {
   3708     switch (pathString) {
   3709       case '':
   3710         return false;
   3711 
   3712       case 'false':
   3713       case 'null':
   3714       case 'true':
   3715         return true;
   3716     }
   3717 
   3718     if (!isNaN(Number(pathString)))
   3719       return true;
   3720 
   3721     return false;
   3722   };
   3723 
   3724   function PolymerExpressions() {}
   3725 
   3726   PolymerExpressions.prototype = {
   3727     // "built-in" filters
   3728     styleObject: function(value) {
   3729       var parts = [];
   3730       for (var key in value) {
   3731         parts.push(convertStylePropertyName(key) + ': ' + value[key]);
   3732       }
   3733       return parts.join('; ');
   3734     },
   3735 
   3736     tokenList: function(value) {
   3737       var tokens = [];
   3738       for (var key in value) {
   3739         if (value[key])
   3740           tokens.push(key);
   3741       }
   3742       return tokens.join(' ');
   3743     },
   3744 
   3745     // binding delegate API
   3746     prepareInstancePositionChanged: function(template) {
   3747       var indexIdent = template.polymerExpressionIndexIdent_;
   3748       if (!indexIdent)
   3749         return;
   3750 
   3751       return function(templateInstance, index) {
   3752         templateInstance.model[indexIdent] = index;
   3753       };
   3754     },
   3755 
   3756     prepareBinding: function(pathString, name, node) {
   3757       var path = Path.get(pathString);
   3758 
   3759       if (!isLiteralExpression(pathString) && path.valid) {
   3760         if (path.length == 1) {
   3761           return function(model, node, oneTime) {
   3762             if (oneTime)
   3763               return path.getValueFrom(model);
   3764 
   3765             var scope = findScope(model, path[0]);
   3766             return new PathObserver(scope, path);
   3767           };
   3768         }
   3769         return; // bail out early if pathString is simple path.
   3770       }
   3771 
   3772       return prepareBinding(pathString, name, node, this);
   3773     },
   3774 
   3775     prepareInstanceModel: function(template) {
   3776       var scopeName = template.polymerExpressionScopeIdent_;
   3777       if (!scopeName)
   3778         return;
   3779 
   3780       var parentScope = template.templateInstance ?
   3781           template.templateInstance.model :
   3782           template.model;
   3783 
   3784       var indexName = template.polymerExpressionIndexIdent_;
   3785 
   3786       return function(model) {
   3787         return createScopeObject(parentScope, model, scopeName, indexName);
   3788       };
   3789     }
   3790   };
   3791 
   3792   var createScopeObject = ('__proto__' in {}) ?
   3793     function(parentScope, model, scopeName, indexName) {
   3794       var scope = {};
   3795       scope[scopeName] = model;
   3796       scope[indexName] = undefined;
   3797       scope[parentScopeName] = parentScope;
   3798       scope.__proto__ = parentScope;
   3799       return scope;
   3800     } :
   3801     function(parentScope, model, scopeName, indexName) {
   3802       var scope = Object.create(parentScope);
   3803       Object.defineProperty(scope, scopeName,
   3804           { value: model, configurable: true, writable: true });
   3805       Object.defineProperty(scope, indexName,
   3806           { value: undefined, configurable: true, writable: true });
   3807       Object.defineProperty(scope, parentScopeName,
   3808           { value: parentScope, configurable: true, writable: true });
   3809       return scope;
   3810     };
   3811 
   3812   global.PolymerExpressions = PolymerExpressions;
   3813   PolymerExpressions.getExpression = getExpression;
   3814 })(this);
   3815 
   3816 Polymer = {
   3817   version: '0.5.5'
   3818 };
   3819 
   3820 // TODO(sorvell): this ensures Polymer is an object and not a function
   3821 // Platform is currently defining it as a function to allow for async loading
   3822 // of polymer; once we refine the loading process this likely goes away.
   3823 if (typeof window.Polymer === 'function') {
   3824   Polymer = {};
   3825 }
   3826 
   3827 
   3828 (function(scope) {
   3829 
   3830   function withDependencies(task, depends) {
   3831     depends = depends || [];
   3832     if (!depends.map) {
   3833       depends = [depends];
   3834     }
   3835     return task.apply(this, depends.map(marshal));
   3836   }
   3837 
   3838   function module(name, dependsOrFactory, moduleFactory) {
   3839     var module;
   3840     switch (arguments.length) {
   3841       case 0:
   3842         return;
   3843       case 1:
   3844         module = null;
   3845         break;
   3846       case 2:
   3847         // dependsOrFactory is `factory` in this case
   3848         module = dependsOrFactory.apply(this);
   3849         break;
   3850       default:
   3851         // dependsOrFactory is `depends` in this case
   3852         module = withDependencies(moduleFactory, dependsOrFactory);
   3853         break;
   3854     }
   3855     modules[name] = module;
   3856   };
   3857 
   3858   function marshal(name) {
   3859     return modules[name];
   3860   }
   3861 
   3862   var modules = {};
   3863 
   3864   function using(depends, task) {
   3865     HTMLImports.whenImportsReady(function() {
   3866       withDependencies(task, depends);
   3867     });
   3868   };
   3869 
   3870   // exports
   3871 
   3872   scope.marshal = marshal;
   3873   // `module` confuses commonjs detectors
   3874   scope.modularize = module;
   3875   scope.using = using;
   3876 
   3877 })(window);
   3878 
   3879 /*
   3880 	Build only script.
   3881 
   3882   Ensures scripts needed for basic x-platform compatibility
   3883   will be run when platform.js is not loaded.
   3884  */
   3885 if (!window.WebComponents) {
   3886 
   3887 /*
   3888 	On supported platforms, platform.js is not needed. To retain compatibility
   3889 	with the polyfills, we stub out minimal functionality.
   3890  */
   3891 if (!window.WebComponents) {
   3892 
   3893   WebComponents = {
   3894   	flush: function() {},
   3895     flags: {log: {}}
   3896   };
   3897 
   3898   Platform = WebComponents;
   3899 
   3900   CustomElements = {
   3901   	useNative: true,
   3902     ready: true,
   3903     takeRecords: function() {},
   3904     instanceof: function(obj, base) {
   3905       return obj instanceof base;
   3906     }
   3907   };
   3908 
   3909   HTMLImports = {
   3910   	useNative: true
   3911   };
   3912 
   3913 
   3914   addEventListener('HTMLImportsLoaded', function() {
   3915     document.dispatchEvent(
   3916       new CustomEvent('WebComponentsReady', {bubbles: true})
   3917     );
   3918   });
   3919 
   3920 
   3921   // ShadowDOM
   3922   ShadowDOMPolyfill = null;
   3923   wrap = unwrap = function(n){
   3924     return n;
   3925   };
   3926 
   3927 }
   3928 
   3929 /*
   3930   Create polyfill scope and feature detect native support.
   3931 */
   3932 window.HTMLImports = window.HTMLImports || {flags:{}};
   3933 
   3934 (function(scope) {
   3935 
   3936 /**
   3937   Basic setup and simple module executer. We collect modules and then execute
   3938   the code later, only if it's necessary for polyfilling.
   3939 */
   3940 var IMPORT_LINK_TYPE = 'import';
   3941 var useNative = Boolean(IMPORT_LINK_TYPE in document.createElement('link'));
   3942 
   3943 /**
   3944   Support `currentScript` on all browsers as `document._currentScript.`
   3945 
   3946   NOTE: We cannot polyfill `document.currentScript` because it's not possible
   3947   both to override and maintain the ability to capture the native value.
   3948   Therefore we choose to expose `_currentScript` both when native imports
   3949   and the polyfill are in use.
   3950 */
   3951 // NOTE: ShadowDOMPolyfill intrusion.
   3952 var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill);
   3953 var wrap = function(node) {
   3954   return hasShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) : node;
   3955 };
   3956 var rootDocument = wrap(document);
   3957 
   3958 var currentScriptDescriptor = {
   3959   get: function() {
   3960     var script = HTMLImports.currentScript || document.currentScript ||
   3961         // NOTE: only works when called in synchronously executing code.
   3962         // readyState should check if `loading` but IE10 is
   3963         // interactive when scripts run so we cheat.
   3964         (document.readyState !== 'complete' ?
   3965         document.scripts[document.scripts.length - 1] : null);
   3966     return wrap(script);
   3967   },
   3968   configurable: true
   3969 };
   3970 
   3971 Object.defineProperty(document, '_currentScript', currentScriptDescriptor);
   3972 Object.defineProperty(rootDocument, '_currentScript', currentScriptDescriptor);
   3973 
   3974 /**
   3975   Add support for the `HTMLImportsLoaded` event and the `HTMLImports.whenReady`
   3976   method. This api is necessary because unlike the native implementation,
   3977   script elements do not force imports to resolve. Instead, users should wrap
   3978   code in either an `HTMLImportsLoaded` hander or after load time in an
   3979   `HTMLImports.whenReady(callback)` call.
   3980 
   3981   NOTE: This module also supports these apis under the native implementation.
   3982   Therefore, if this file is loaded, the same code can be used under both
   3983   the polyfill and native implementation.
   3984  */
   3985 
   3986 var isIE = /Trident/.test(navigator.userAgent);
   3987 
   3988 // call a callback when all HTMLImports in the document at call time
   3989 // (or at least document ready) have loaded.
   3990 // 1. ensure the document is in a ready state (has dom), then
   3991 // 2. watch for loading of imports and call callback when done
   3992 function whenReady(callback, doc) {
   3993   doc = doc || rootDocument;
   3994   // if document is loading, wait and try again
   3995   whenDocumentReady(function() {
   3996     watchImportsLoad(callback, doc);
   3997   }, doc);
   3998 }
   3999 
   4000 // call the callback when the document is in a ready state (has dom)
   4001 var requiredReadyState = isIE ? 'complete' : 'interactive';
   4002 var READY_EVENT = 'readystatechange';
   4003 function isDocumentReady(doc) {
   4004   return (doc.readyState === 'complete' ||
   4005       doc.readyState === requiredReadyState);
   4006 }
   4007 
   4008 // call <callback> when we ensure the document is in a ready state
   4009 function whenDocumentReady(callback, doc) {
   4010   if (!isDocumentReady(doc)) {
   4011     var checkReady = function() {
   4012       if (doc.readyState === 'complete' ||
   4013           doc.readyState === requiredReadyState) {
   4014         doc.removeEventListener(READY_EVENT, checkReady);
   4015         whenDocumentReady(callback, doc);
   4016       }
   4017     };
   4018     doc.addEventListener(READY_EVENT, checkReady);
   4019   } else if (callback) {
   4020     callback();
   4021   }
   4022 }
   4023 
   4024 function markTargetLoaded(event) {
   4025   event.target.__loaded = true;
   4026 }
   4027 
   4028 // call <callback> when we ensure all imports have loaded
   4029 function watchImportsLoad(callback, doc) {
   4030   var imports = doc.querySelectorAll('link[rel=import]');
   4031   var loaded = 0, l = imports.length;
   4032   function checkDone(d) {
   4033     if ((loaded == l) && callback) {
   4034        callback();
   4035     }
   4036   }
   4037   function loadedImport(e) {
   4038     markTargetLoaded(e);
   4039     loaded++;
   4040     checkDone();
   4041   }
   4042   if (l) {
   4043     for (var i=0, imp; (i<l) && (imp=imports[i]); i++) {
   4044       if (isImportLoaded(imp)) {
   4045         loadedImport.call(imp, {target: imp});
   4046       } else {
   4047         imp.addEventListener('load', loadedImport);
   4048         imp.addEventListener('error', loadedImport);
   4049       }
   4050     }
   4051   } else {
   4052     checkDone();
   4053   }
   4054 }
   4055 
   4056 // NOTE: test for native imports loading is based on explicitly watching
   4057 // all imports (see below).
   4058 // However, we cannot rely on this entirely without watching the entire document
   4059 // for import links. For perf reasons, currently only head is watched.
   4060 // Instead, we fallback to checking if the import property is available
   4061 // and the document is not itself loading.
   4062 function isImportLoaded(link) {
   4063   return useNative ? link.__loaded ||
   4064       (link.import && link.import.readyState !== 'loading') :
   4065       link.__importParsed;
   4066 }
   4067 
   4068 // TODO(sorvell): Workaround for
   4069 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=25007, should be removed when
   4070 // this bug is addressed.
   4071 // (1) Install a mutation observer to see when HTMLImports have loaded
   4072 // (2) if this script is run during document load it will watch any existing
   4073 // imports for loading.
   4074 //
   4075 // NOTE: The workaround has restricted functionality: (1) it's only compatible
   4076 // with imports that are added to document.head since the mutation observer
   4077 // watches only head for perf reasons, (2) it requires this script
   4078 // to run before any imports have completed loading.
   4079 if (useNative) {
   4080   new MutationObserver(function(mxns) {
   4081     for (var i=0, l=mxns.length, m; (i < l) && (m=mxns[i]); i++) {
   4082       if (m.addedNodes) {
   4083         handleImports(m.addedNodes);
   4084       }
   4085     }
   4086   }).observe(document.head, {childList: true});
   4087 
   4088   function handleImports(nodes) {
   4089     for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) {
   4090       if (isImport(n)) {
   4091         handleImport(n);
   4092       }
   4093     }
   4094   }
   4095 
   4096   function isImport(element) {
   4097     return element.localName === 'link' && element.rel === 'import';
   4098   }
   4099 
   4100   function handleImport(element) {
   4101     var loaded = element.import;
   4102     if (loaded) {
   4103       markTargetLoaded({target: element});
   4104     } else {
   4105       element.addEventListener('load', markTargetLoaded);
   4106       element.addEventListener('error', markTargetLoaded);
   4107     }
   4108   }
   4109 
   4110   // make sure to catch any imports that are in the process of loading
   4111   // when this script is run.
   4112   (function() {
   4113     if (document.readyState === 'loading') {
   4114       var imports = document.querySelectorAll('link[rel=import]');
   4115       for (var i=0, l=imports.length, imp; (i<l) && (imp=imports[i]); i++) {
   4116         handleImport(imp);
   4117       }
   4118     }
   4119   })();
   4120 
   4121 }
   4122 
   4123 // Fire the 'HTMLImportsLoaded' event when imports in document at load time
   4124 // have loaded. This event is required to simulate the script blocking
   4125 // behavior of native imports. A main document script that needs to be sure
   4126 // imports have loaded should wait for this event.
   4127 whenReady(function() {
   4128   HTMLImports.ready = true;
   4129   HTMLImports.readyTime = new Date().getTime();
   4130   rootDocument.dispatchEvent(
   4131     new CustomEvent('HTMLImportsLoaded', {bubbles: true})
   4132   );
   4133 });
   4134 
   4135 // exports
   4136 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
   4137 scope.useNative = useNative;
   4138 scope.rootDocument = rootDocument;
   4139 scope.whenReady = whenReady;
   4140 scope.isIE = isIE;
   4141 
   4142 })(HTMLImports);
   4143 
   4144 (function(scope) {
   4145 
   4146   // TODO(sorvell): It's desireable to provide a default stylesheet
   4147   // that's convenient for styling unresolved elements, but
   4148   // it's cumbersome to have to include this manually in every page.
   4149   // It would make sense to put inside some HTMLImport but
   4150   // the HTMLImports polyfill does not allow loading of stylesheets
   4151   // that block rendering. Therefore this injection is tolerated here.
   4152   var style = document.createElement('style');
   4153   style.textContent = ''
   4154       + 'body {'
   4155       + 'transition: opacity ease-in 0.2s;'
   4156       + ' } \n'
   4157       + 'body[unresolved] {'
   4158       + 'opacity: 0; display: block; overflow: hidden;'
   4159       + ' } \n'
   4160       ;
   4161   var head = document.querySelector('head');
   4162   head.insertBefore(style, head.firstChild);
   4163 
   4164 })(Platform);
   4165 
   4166 /*
   4167 	Build only script.
   4168 
   4169   Ensures scripts needed for basic x-platform compatibility
   4170   will be run when platform.js is not loaded.
   4171  */
   4172 }
   4173 (function(global) {
   4174   'use strict';
   4175 
   4176   var testingExposeCycleCount = global.testingExposeCycleCount;
   4177 
   4178   // Detect and do basic sanity checking on Object/Array.observe.
   4179   function detectObjectObserve() {
   4180     if (typeof Object.observe !== 'function' ||
   4181         typeof Array.observe !== 'function') {
   4182       return false;
   4183     }
   4184 
   4185     var records = [];
   4186 
   4187     function callback(recs) {
   4188       records = recs;
   4189     }
   4190 
   4191     var test = {};
   4192     var arr = [];
   4193     Object.observe(test, callback);
   4194     Array.observe(arr, callback);
   4195     test.id = 1;
   4196     test.id = 2;
   4197     delete test.id;
   4198     arr.push(1, 2);
   4199     arr.length = 0;
   4200 
   4201     Object.deliverChangeRecords(callback);
   4202     if (records.length !== 5)
   4203       return false;
   4204 
   4205     if (records[0].type != 'add' ||
   4206         records[1].type != 'update' ||
   4207         records[2].type != 'delete' ||
   4208         records[3].type != 'splice' ||
   4209         records[4].type != 'splice') {
   4210       return false;
   4211     }
   4212 
   4213     Object.unobserve(test, callback);
   4214     Array.unobserve(arr, callback);
   4215 
   4216     return true;
   4217   }
   4218 
   4219   var hasObserve = detectObjectObserve();
   4220 
   4221   function detectEval() {
   4222     // Don't test for eval if we're running in a Chrome App environment.
   4223     // We check for APIs set that only exist in a Chrome App context.
   4224     if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) {
   4225       return false;
   4226     }
   4227 
   4228     // Firefox OS Apps do not allow eval. This feature detection is very hacky
   4229     // but even if some other platform adds support for this function this code
   4230     // will continue to work.
   4231     if (typeof navigator != 'undefined' && navigator.getDeviceStorage) {
   4232       return false;
   4233     }
   4234 
   4235     try {
   4236       var f = new Function('', 'return true;');
   4237       return f();
   4238     } catch (ex) {
   4239       return false;
   4240     }
   4241   }
   4242 
   4243   var hasEval = detectEval();
   4244 
   4245   function isIndex(s) {
   4246     return +s === s >>> 0 && s !== '';
   4247   }
   4248 
   4249   function toNumber(s) {
   4250     return +s;
   4251   }
   4252 
   4253   function isObject(obj) {
   4254     return obj === Object(obj);
   4255   }
   4256 
   4257   var numberIsNaN = global.Number.isNaN || function(value) {
   4258     return typeof value === 'number' && global.isNaN(value);
   4259   }
   4260 
   4261   function areSameValue(left, right) {
   4262     if (left === right)
   4263       return left !== 0 || 1 / left === 1 / right;
   4264     if (numberIsNaN(left) && numberIsNaN(right))
   4265       return true;
   4266 
   4267     return left !== left && right !== right;
   4268   }
   4269 
   4270   var createObject = ('__proto__' in {}) ?
   4271     function(obj) { return obj; } :
   4272     function(obj) {
   4273       var proto = obj.__proto__;
   4274       if (!proto)
   4275         return obj;
   4276       var newObject = Object.create(proto);
   4277       Object.getOwnPropertyNames(obj).forEach(function(name) {
   4278         Object.defineProperty(newObject, name,
   4279                              Object.getOwnPropertyDescriptor(obj, name));
   4280       });
   4281       return newObject;
   4282     };
   4283 
   4284   var identStart = '[\$_a-zA-Z]';
   4285   var identPart = '[\$_a-zA-Z0-9]';
   4286   var identRegExp = new RegExp('^' + identStart + '+' + identPart + '*' + '$');
   4287 
   4288   function getPathCharType(char) {
   4289     if (char === undefined)
   4290       return 'eof';
   4291 
   4292     var code = char.charCodeAt(0);
   4293 
   4294     switch(code) {
   4295       case 0x5B: // [
   4296       case 0x5D: // ]
   4297       case 0x2E: // .
   4298       case 0x22: // "
   4299       case 0x27: // '
   4300       case 0x30: // 0
   4301         return char;
   4302 
   4303       case 0x5F: // _
   4304       case 0x24: // $
   4305         return 'ident';
   4306 
   4307       case 0x20: // Space
   4308       case 0x09: // Tab
   4309       case 0x0A: // Newline
   4310       case 0x0D: // Return
   4311       case 0xA0:  // No-break space
   4312       case 0xFEFF:  // Byte Order Mark
   4313       case 0x2028:  // Line Separator
   4314       case 0x2029:  // Paragraph Separator
   4315         return 'ws';
   4316     }
   4317 
   4318     // a-z, A-Z
   4319     if ((0x61 <= code && code <= 0x7A) || (0x41 <= code && code <= 0x5A))
   4320       return 'ident';
   4321 
   4322     // 1-9
   4323     if (0x31 <= code && code <= 0x39)
   4324       return 'number';
   4325 
   4326     return 'else';
   4327   }
   4328 
   4329   var pathStateMachine = {
   4330     'beforePath': {
   4331       'ws': ['beforePath'],
   4332       'ident': ['inIdent', 'append'],
   4333       '[': ['beforeElement'],
   4334       'eof': ['afterPath']
   4335     },
   4336 
   4337     'inPath': {
   4338       'ws': ['inPath'],
   4339       '.': ['beforeIdent'],
   4340       '[': ['beforeElement'],
   4341       'eof': ['afterPath']
   4342     },
   4343 
   4344     'beforeIdent': {
   4345       'ws': ['beforeIdent'],
   4346       'ident': ['inIdent', 'append']
   4347     },
   4348 
   4349     'inIdent': {
   4350       'ident': ['inIdent', 'append'],
   4351       '0': ['inIdent', 'append'],
   4352       'number': ['inIdent', 'append'],
   4353       'ws': ['inPath', 'push'],
   4354       '.': ['beforeIdent', 'push'],
   4355       '[': ['beforeElement', 'push'],
   4356       'eof': ['afterPath', 'push']
   4357     },
   4358 
   4359     'beforeElement': {
   4360       'ws': ['beforeElement'],
   4361       '0': ['afterZero', 'append'],
   4362       'number': ['inIndex', 'append'],
   4363       "'": ['inSingleQuote', 'append', ''],
   4364       '"': ['inDoubleQuote', 'append', '']
   4365     },
   4366 
   4367     'afterZero': {
   4368       'ws': ['afterElement', 'push'],
   4369       ']': ['inPath', 'push']
   4370     },
   4371 
   4372     'inIndex': {
   4373       '0': ['inIndex', 'append'],
   4374       'number': ['inIndex', 'append'],
   4375       'ws': ['afterElement'],
   4376       ']': ['inPath', 'push']
   4377     },
   4378 
   4379     'inSingleQuote': {
   4380       "'": ['afterElement'],
   4381       'eof': ['error'],
   4382       'else': ['inSingleQuote', 'append']
   4383     },
   4384 
   4385     'inDoubleQuote': {
   4386       '"': ['afterElement'],
   4387       'eof': ['error'],
   4388       'else': ['inDoubleQuote', 'append']
   4389     },
   4390 
   4391     'afterElement': {
   4392       'ws': ['afterElement'],
   4393       ']': ['inPath', 'push']
   4394     }
   4395   }
   4396 
   4397   function noop() {}
   4398 
   4399   function parsePath(path) {
   4400     var keys = [];
   4401     var index = -1;
   4402     var c, newChar, key, type, transition, action, typeMap, mode = 'beforePath';
   4403 
   4404     var actions = {
   4405       push: function() {
   4406         if (key === undefined)
   4407           return;
   4408 
   4409         keys.push(key);
   4410         key = undefined;
   4411       },
   4412 
   4413       append: function() {
   4414         if (key === undefined)
   4415           key = newChar
   4416         else
   4417           key += newChar;
   4418       }
   4419     };
   4420 
   4421     function maybeUnescapeQuote() {
   4422       if (index >= path.length)
   4423         return;
   4424 
   4425       var nextChar = path[index + 1];
   4426       if ((mode == 'inSingleQuote' && nextChar == "'") ||
   4427           (mode == 'inDoubleQuote' && nextChar == '"')) {
   4428         index++;
   4429         newChar = nextChar;
   4430         actions.append();
   4431         return true;
   4432       }
   4433     }
   4434 
   4435     while (mode) {
   4436       index++;
   4437       c = path[index];
   4438 
   4439       if (c == '\\' && maybeUnescapeQuote(mode))
   4440         continue;
   4441 
   4442       type = getPathCharType(c);
   4443       typeMap = pathStateMachine[mode];
   4444       transition = typeMap[type] || typeMap['else'] || 'error';
   4445 
   4446       if (transition == 'error')
   4447         return; // parse error;
   4448 
   4449       mode = transition[0];
   4450       action = actions[transition[1]] || noop;
   4451       newChar = transition[2] === undefined ? c : transition[2];
   4452       action();
   4453 
   4454       if (mode === 'afterPath') {
   4455         return keys;
   4456       }
   4457     }
   4458 
   4459     return; // parse error
   4460   }
   4461 
   4462   function isIdent(s) {
   4463     return identRegExp.test(s);
   4464   }
   4465 
   4466   var constructorIsPrivate = {};
   4467 
   4468   function Path(parts, privateToken) {
   4469     if (privateToken !== constructorIsPrivate)
   4470       throw Error('Use Path.get to retrieve path objects');
   4471 
   4472     for (var i = 0; i < parts.length; i++) {
   4473       this.push(String(parts[i]));
   4474     }
   4475 
   4476     if (hasEval && this.length) {
   4477       this.getValueFrom = this.compiledGetValueFromFn();
   4478     }
   4479   }
   4480 
   4481   // TODO(rafaelw): Make simple LRU cache
   4482   var pathCache = {};
   4483 
   4484   function getPath(pathString) {
   4485     if (pathString instanceof Path)
   4486       return pathString;
   4487 
   4488     if (pathString == null || pathString.length == 0)
   4489       pathString = '';
   4490 
   4491     if (typeof pathString != 'string') {
   4492       if (isIndex(pathString.length)) {
   4493         // Constructed with array-like (pre-parsed) keys
   4494         return new Path(pathString, constructorIsPrivate);
   4495       }
   4496 
   4497       pathString = String(pathString);
   4498     }
   4499 
   4500     var path = pathCache[pathString];
   4501     if (path)
   4502       return path;
   4503 
   4504     var parts = parsePath(pathString);
   4505     if (!parts)
   4506       return invalidPath;
   4507 
   4508     var path = new Path(parts, constructorIsPrivate);
   4509     pathCache[pathString] = path;
   4510     return path;
   4511   }
   4512 
   4513   Path.get = getPath;
   4514 
   4515   function formatAccessor(key) {
   4516     if (isIndex(key)) {
   4517       return '[' + key + ']';
   4518     } else {
   4519       return '["' + key.replace(/"/g, '\\"') + '"]';
   4520     }
   4521   }
   4522 
   4523   Path.prototype = createObject({
   4524     __proto__: [],
   4525     valid: true,
   4526 
   4527     toString: function() {
   4528       var pathString = '';
   4529       for (var i = 0; i < this.length; i++) {
   4530         var key = this[i];
   4531         if (isIdent(key)) {
   4532           pathString += i ? '.' + key : key;
   4533         } else {
   4534           pathString += formatAccessor(key);
   4535         }
   4536       }
   4537 
   4538       return pathString;
   4539     },
   4540 
   4541     getValueFrom: function(obj, directObserver) {
   4542       for (var i = 0; i < this.length; i++) {
   4543         if (obj == null)
   4544           return;
   4545         obj = obj[this[i]];
   4546       }
   4547       return obj;
   4548     },
   4549 
   4550     iterateObjects: function(obj, observe) {
   4551       for (var i = 0; i < this.length; i++) {
   4552         if (i)
   4553           obj = obj[this[i - 1]];
   4554         if (!isObject(obj))
   4555           return;
   4556         observe(obj, this[i]);
   4557       }
   4558     },
   4559 
   4560     compiledGetValueFromFn: function() {
   4561       var str = '';
   4562       var pathString = 'obj';
   4563       str += 'if (obj != null';
   4564       var i = 0;
   4565       var key;
   4566       for (; i < (this.length - 1); i++) {
   4567         key = this[i];
   4568         pathString += isIdent(key) ? '.' + key : formatAccessor(key);
   4569         str += ' &&\n     ' + pathString + ' != null';
   4570       }
   4571       str += ')\n';
   4572 
   4573       var key = this[i];
   4574       pathString += isIdent(key) ? '.' + key : formatAccessor(key);
   4575 
   4576       str += '  return ' + pathString + ';\nelse\n  return undefined;';
   4577       return new Function('obj', str);
   4578     },
   4579 
   4580     setValueFrom: function(obj, value) {
   4581       if (!this.length)
   4582         return false;
   4583 
   4584       for (var i = 0; i < this.length - 1; i++) {
   4585         if (!isObject(obj))
   4586           return false;
   4587         obj = obj[this[i]];
   4588       }
   4589 
   4590       if (!isObject(obj))
   4591         return false;
   4592 
   4593       obj[this[i]] = value;
   4594       return true;
   4595     }
   4596   });
   4597 
   4598   var invalidPath = new Path('', constructorIsPrivate);
   4599   invalidPath.valid = false;
   4600   invalidPath.getValueFrom = invalidPath.setValueFrom = function() {};
   4601 
   4602   var MAX_DIRTY_CHECK_CYCLES = 1000;
   4603 
   4604   function dirtyCheck(observer) {
   4605     var cycles = 0;
   4606     while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check_()) {
   4607       cycles++;
   4608     }
   4609     if (testingExposeCycleCount)
   4610       global.dirtyCheckCycleCount = cycles;
   4611 
   4612     return cycles > 0;
   4613   }
   4614 
   4615   function objectIsEmpty(object) {
   4616     for (var prop in object)
   4617       return false;
   4618     return true;
   4619   }
   4620 
   4621   function diffIsEmpty(diff) {
   4622     return objectIsEmpty(diff.added) &&
   4623            objectIsEmpty(diff.removed) &&
   4624            objectIsEmpty(diff.changed);
   4625   }
   4626 
   4627   function diffObjectFromOldObject(object, oldObject) {
   4628     var added = {};
   4629     var removed = {};
   4630     var changed = {};
   4631 
   4632     for (var prop in oldObject) {
   4633       var newValue = object[prop];
   4634 
   4635       if (newValue !== undefined && newValue === oldObject[prop])
   4636         continue;
   4637 
   4638       if (!(prop in object)) {
   4639         removed[prop] = undefined;
   4640         continue;
   4641       }
   4642 
   4643       if (newValue !== oldObject[prop])
   4644         changed[prop] = newValue;
   4645     }
   4646 
   4647     for (var prop in object) {
   4648       if (prop in oldObject)
   4649         continue;
   4650 
   4651       added[prop] = object[prop];
   4652     }
   4653 
   4654     if (Array.isArray(object) && object.length !== oldObject.length)
   4655       changed.length = object.length;
   4656 
   4657     return {
   4658       added: added,
   4659       removed: removed,
   4660       changed: changed
   4661     };
   4662   }
   4663 
   4664   var eomTasks = [];
   4665   function runEOMTasks() {
   4666     if (!eomTasks.length)
   4667       return false;
   4668 
   4669     for (var i = 0; i < eomTasks.length; i++) {
   4670       eomTasks[i]();
   4671     }
   4672     eomTasks.length = 0;
   4673     return true;
   4674   }
   4675 
   4676   var runEOM = hasObserve ? (function(){
   4677     return function(fn) {
   4678       return Promise.resolve().then(fn);
   4679     }
   4680   })() :
   4681   (function() {
   4682     return function(fn) {
   4683       eomTasks.push(fn);
   4684     };
   4685   })();
   4686 
   4687   var observedObjectCache = [];
   4688 
   4689   function newObservedObject() {
   4690     var observer;
   4691     var object;
   4692     var discardRecords = false;
   4693     var first = true;
   4694 
   4695     function callback(records) {
   4696       if (observer && observer.state_ === OPENED && !discardRecords)
   4697         observer.check_(records);
   4698     }
   4699 
   4700     return {
   4701       open: function(obs) {
   4702         if (observer)
   4703           throw Error('ObservedObject in use');
   4704 
   4705         if (!first)
   4706           Object.deliverChangeRecords(callback);
   4707 
   4708         observer = obs;
   4709         first = false;
   4710       },
   4711       observe: function(obj, arrayObserve) {
   4712         object = obj;
   4713         if (arrayObserve)
   4714           Array.observe(object, callback);
   4715         else
   4716           Object.observe(object, callback);
   4717       },
   4718       deliver: function(discard) {
   4719         discardRecords = discard;
   4720         Object.deliverChangeRecords(callback);
   4721         discardRecords = false;
   4722       },
   4723       close: function() {
   4724         observer = undefined;
   4725         Object.unobserve(object, callback);
   4726         observedObjectCache.push(this);
   4727       }
   4728     };
   4729   }
   4730 
   4731   /*
   4732    * The observedSet abstraction is a perf optimization which reduces the total
   4733    * number of Object.observe observations of a set of objects. The idea is that
   4734    * groups of Observers will have some object dependencies in common and this
   4735    * observed set ensures that each object in the transitive closure of
   4736    * dependencies is only observed once. The observedSet acts as a write barrier
   4737    * such that whenever any change comes through, all Observers are checked for
   4738    * changed values.
   4739    *
   4740    * Note that this optimization is explicitly moving work from setup-time to
   4741    * change-time.
   4742    *
   4743    * TODO(rafaelw): Implement "garbage collection". In order to move work off
   4744    * the critical path, when Observers are closed, their observed objects are
   4745    * not Object.unobserve(d). As a result, it's possible that if the observedSet
   4746    * is kept open, but some Observers have been closed, it could cause "leaks"
   4747    * (prevent otherwise collectable objects from being collected). At some
   4748    * point, we should implement incremental "gc" which keeps a list of
   4749    * observedSets which may need clean-up and does small amounts of cleanup on a
   4750    * timeout until all is clean.
   4751    */
   4752 
   4753   function getObservedObject(observer, object, arrayObserve) {
   4754     var dir = observedObjectCache.pop() || newObservedObject();
   4755     dir.open(observer);
   4756     dir.observe(object, arrayObserve);
   4757     return dir;
   4758   }
   4759 
   4760   var observedSetCache = [];
   4761 
   4762   function newObservedSet() {
   4763     var observerCount = 0;
   4764     var observers = [];
   4765     var objects = [];
   4766     var rootObj;
   4767     var rootObjProps;
   4768 
   4769     function observe(obj, prop) {
   4770       if (!obj)
   4771         return;
   4772 
   4773       if (obj === rootObj)
   4774         rootObjProps[prop] = true;
   4775 
   4776       if (objects.indexOf(obj) < 0) {
   4777         objects.push(obj);
   4778         Object.observe(obj, callback);
   4779       }
   4780 
   4781       observe(Object.getPrototypeOf(obj), prop);
   4782     }
   4783 
   4784     function allRootObjNonObservedProps(recs) {
   4785       for (var i = 0; i < recs.length; i++) {
   4786         var rec = recs[i];
   4787         if (rec.object !== rootObj ||
   4788             rootObjProps[rec.name] ||
   4789             rec.type === 'setPrototype') {
   4790           return false;
   4791         }
   4792       }
   4793       return true;
   4794     }
   4795 
   4796     function callback(recs) {
   4797       if (allRootObjNonObservedProps(recs))
   4798         return;
   4799 
   4800       var observer;
   4801       for (var i = 0; i < observers.length; i++) {
   4802         observer = observers[i];
   4803         if (observer.state_ == OPENED) {
   4804           observer.iterateObjects_(observe);
   4805         }
   4806       }
   4807 
   4808       for (var i = 0; i < observers.length; i++) {
   4809         observer = observers[i];
   4810         if (observer.state_ == OPENED) {
   4811           observer.check_();
   4812         }
   4813       }
   4814     }
   4815 
   4816     var record = {
   4817       objects: objects,
   4818       get rootObject() { return rootObj; },
   4819       set rootObject(value) {
   4820         rootObj = value;
   4821         rootObjProps = {};
   4822       },
   4823       open: function(obs, object) {
   4824         observers.push(obs);
   4825         observerCount++;
   4826         obs.iterateObjects_(observe);
   4827       },
   4828       close: function(obs) {
   4829         observerCount--;
   4830         if (observerCount > 0) {
   4831           return;
   4832         }
   4833 
   4834         for (var i = 0; i < objects.length; i++) {
   4835           Object.unobserve(objects[i], callback);
   4836           Observer.unobservedCount++;
   4837         }
   4838 
   4839         observers.length = 0;
   4840         objects.length = 0;
   4841         rootObj = undefined;
   4842         rootObjProps = undefined;
   4843         observedSetCache.push(this);
   4844         if (lastObservedSet === this)
   4845           lastObservedSet = null;
   4846       },
   4847     };
   4848 
   4849     return record;
   4850   }
   4851 
   4852   var lastObservedSet;
   4853 
   4854   function getObservedSet(observer, obj) {
   4855     if (!lastObservedSet || lastObservedSet.rootObject !== obj) {
   4856       lastObservedSet = observedSetCache.pop() || newObservedSet();
   4857       lastObservedSet.rootObject = obj;
   4858     }
   4859     lastObservedSet.open(observer, obj);
   4860     return lastObservedSet;
   4861   }
   4862 
   4863   var UNOPENED = 0;
   4864   var OPENED = 1;
   4865   var CLOSED = 2;
   4866   var RESETTING = 3;
   4867 
   4868   var nextObserverId = 1;
   4869 
   4870   function Observer() {
   4871     this.state_ = UNOPENED;
   4872     this.callback_ = undefined;
   4873     this.target_ = undefined; // TODO(rafaelw): Should be WeakRef
   4874     this.directObserver_ = undefined;
   4875     this.value_ = undefined;
   4876     this.id_ = nextObserverId++;
   4877   }
   4878 
   4879   Observer.prototype = {
   4880     open: function(callback, target) {
   4881       if (this.state_ != UNOPENED)
   4882         throw Error('Observer has already been opened.');
   4883 
   4884       addToAll(this);
   4885       this.callback_ = callback;
   4886       this.target_ = target;
   4887       this.connect_();
   4888       this.state_ = OPENED;
   4889       return this.value_;
   4890     },
   4891 
   4892     close: function() {
   4893       if (this.state_ != OPENED)
   4894         return;
   4895 
   4896       removeFromAll(this);
   4897       this.disconnect_();
   4898       this.value_ = undefined;
   4899       this.callback_ = undefined;
   4900       this.target_ = undefined;
   4901       this.state_ = CLOSED;
   4902     },
   4903 
   4904     deliver: function() {
   4905       if (this.state_ != OPENED)
   4906         return;
   4907 
   4908       dirtyCheck(this);
   4909     },
   4910 
   4911     report_: function(changes) {
   4912       try {
   4913         this.callback_.apply(this.target_, changes);
   4914       } catch (ex) {
   4915         Observer._errorThrownDuringCallback = true;
   4916         console.error('Exception caught during observer callback: ' +
   4917                        (ex.stack || ex));
   4918       }
   4919     },
   4920 
   4921     discardChanges: function() {
   4922       this.check_(undefined, true);
   4923       return this.value_;
   4924     }
   4925   }
   4926 
   4927   var collectObservers = !hasObserve;
   4928   var allObservers;
   4929   Observer._allObserversCount = 0;
   4930 
   4931   if (collectObservers) {
   4932     allObservers = [];
   4933   }
   4934 
   4935   function addToAll(observer) {
   4936     Observer._allObserversCount++;
   4937     if (!collectObservers)
   4938       return;
   4939 
   4940     allObservers.push(observer);
   4941   }
   4942 
   4943   function removeFromAll(observer) {
   4944     Observer._allObserversCount--;
   4945   }
   4946 
   4947   var runningMicrotaskCheckpoint = false;
   4948 
   4949   global.Platform = global.Platform || {};
   4950 
   4951   global.Platform.performMicrotaskCheckpoint = function() {
   4952     if (runningMicrotaskCheckpoint)
   4953       return;
   4954 
   4955     if (!collectObservers)
   4956       return;
   4957 
   4958     runningMicrotaskCheckpoint = true;
   4959 
   4960     var cycles = 0;
   4961     var anyChanged, toCheck;
   4962 
   4963     do {
   4964       cycles++;
   4965       toCheck = allObservers;
   4966       allObservers = [];
   4967       anyChanged = false;
   4968 
   4969       for (var i = 0; i < toCheck.length; i++) {
   4970         var observer = toCheck[i];
   4971         if (observer.state_ != OPENED)
   4972           continue;
   4973 
   4974         if (observer.check_())
   4975           anyChanged = true;
   4976 
   4977         allObservers.push(observer);
   4978       }
   4979       if (runEOMTasks())
   4980         anyChanged = true;
   4981     } while (cycles < MAX_DIRTY_CHECK_CYCLES && anyChanged);
   4982 
   4983     if (testingExposeCycleCount)
   4984       global.dirtyCheckCycleCount = cycles;
   4985 
   4986     runningMicrotaskCheckpoint = false;
   4987   };
   4988 
   4989   if (collectObservers) {
   4990     global.Platform.clearObservers = function() {
   4991       allObservers = [];
   4992     };
   4993   }
   4994 
   4995   function ObjectObserver(object) {
   4996     Observer.call(this);
   4997     this.value_ = object;
   4998     this.oldObject_ = undefined;
   4999   }
   5000 
   5001   ObjectObserver.prototype = createObject({
   5002     __proto__: Observer.prototype,
   5003 
   5004     arrayObserve: false,
   5005 
   5006     connect_: function(callback, target) {
   5007       if (hasObserve) {
   5008         this.directObserver_ = getObservedObject(this, this.value_,
   5009                                                  this.arrayObserve);
   5010       } else {
   5011         this.oldObject_ = this.copyObject(this.value_);
   5012       }
   5013 
   5014     },
   5015 
   5016     copyObject: function(object) {
   5017       var copy = Array.isArray(object) ? [] : {};
   5018       for (var prop in object) {
   5019         copy[prop] = object[prop];
   5020       };
   5021       if (Array.isArray(object))
   5022         copy.length = object.length;
   5023       return copy;
   5024     },
   5025 
   5026     check_: function(changeRecords, skipChanges) {
   5027       var diff;
   5028       var oldValues;
   5029       if (hasObserve) {
   5030         if (!changeRecords)
   5031           return false;
   5032 
   5033         oldValues = {};
   5034         diff = diffObjectFromChangeRecords(this.value_, changeRecords,
   5035                                            oldValues);
   5036       } else {
   5037         oldValues = this.oldObject_;
   5038         diff = diffObjectFromOldObject(this.value_, this.oldObject_);
   5039       }
   5040 
   5041       if (diffIsEmpty(diff))
   5042         return false;
   5043 
   5044       if (!hasObserve)
   5045         this.oldObject_ = this.copyObject(this.value_);
   5046 
   5047       this.report_([
   5048         diff.added || {},
   5049         diff.removed || {},
   5050         diff.changed || {},
   5051         function(property) {
   5052           return oldValues[property];
   5053         }
   5054       ]);
   5055 
   5056       return true;
   5057     },
   5058 
   5059     disconnect_: function() {
   5060       if (hasObserve) {
   5061         this.directObserver_.close();
   5062         this.directObserver_ = undefined;
   5063       } else {
   5064         this.oldObject_ = undefined;
   5065       }
   5066     },
   5067 
   5068     deliver: function() {
   5069       if (this.state_ != OPENED)
   5070         return;
   5071 
   5072       if (hasObserve)
   5073         this.directObserver_.deliver(false);
   5074       else
   5075         dirtyCheck(this);
   5076     },
   5077 
   5078     discardChanges: function() {
   5079       if (this.directObserver_)
   5080         this.directObserver_.deliver(true);
   5081       else
   5082         this.oldObject_ = this.copyObject(this.value_);
   5083 
   5084       return this.value_;
   5085     }
   5086   });
   5087 
   5088   function ArrayObserver(array) {
   5089     if (!Array.isArray(array))
   5090       throw Error('Provided object is not an Array');
   5091     ObjectObserver.call(this, array);
   5092   }
   5093 
   5094   ArrayObserver.prototype = createObject({
   5095 
   5096     __proto__: ObjectObserver.prototype,
   5097 
   5098     arrayObserve: true,
   5099 
   5100     copyObject: function(arr) {
   5101       return arr.slice();
   5102     },
   5103 
   5104     check_: function(changeRecords) {
   5105       var splices;
   5106       if (hasObserve) {
   5107         if (!changeRecords)
   5108           return false;
   5109         splices = projectArraySplices(this.value_, changeRecords);
   5110       } else {
   5111         splices = calcSplices(this.value_, 0, this.value_.length,
   5112                               this.oldObject_, 0, this.oldObject_.length);
   5113       }
   5114 
   5115       if (!splices || !splices.length)
   5116         return false;
   5117 
   5118       if (!hasObserve)
   5119         this.oldObject_ = this.copyObject(this.value_);
   5120 
   5121       this.report_([splices]);
   5122       return true;
   5123     }
   5124   });
   5125 
   5126   ArrayObserver.applySplices = function(previous, current, splices) {
   5127     splices.forEach(function(splice) {
   5128       var spliceArgs = [splice.index, splice.removed.length];
   5129       var addIndex = splice.index;
   5130       while (addIndex < splice.index + splice.addedCount) {
   5131         spliceArgs.push(current[addIndex]);
   5132         addIndex++;
   5133       }
   5134 
   5135       Array.prototype.splice.apply(previous, spliceArgs);
   5136     });
   5137   };
   5138 
   5139   function PathObserver(object, path) {
   5140     Observer.call(this);
   5141 
   5142     this.object_ = object;
   5143     this.path_ = getPath(path);
   5144     this.directObserver_ = undefined;
   5145   }
   5146 
   5147   PathObserver.prototype = createObject({
   5148     __proto__: Observer.prototype,
   5149 
   5150     get path() {
   5151       return this.path_;
   5152     },
   5153 
   5154     connect_: function() {
   5155       if (hasObserve)
   5156         this.directObserver_ = getObservedSet(this, this.object_);
   5157 
   5158       this.check_(undefined, true);
   5159     },
   5160 
   5161     disconnect_: function() {
   5162       this.value_ = undefined;
   5163 
   5164       if (this.directObserver_) {
   5165         this.directObserver_.close(this);
   5166         this.directObserver_ = undefined;
   5167       }
   5168     },
   5169 
   5170     iterateObjects_: function(observe) {
   5171       this.path_.iterateObjects(this.object_, observe);
   5172     },
   5173 
   5174     check_: function(changeRecords, skipChanges) {
   5175       var oldValue = this.value_;
   5176       this.value_ = this.path_.getValueFrom(this.object_);
   5177       if (skipChanges || areSameValue(this.value_, oldValue))
   5178         return false;
   5179 
   5180       this.report_([this.value_, oldValue, this]);
   5181       return true;
   5182     },
   5183 
   5184     setValue: function(newValue) {
   5185       if (this.path_)
   5186         this.path_.setValueFrom(this.object_, newValue);
   5187     }
   5188   });
   5189 
   5190   function CompoundObserver(reportChangesOnOpen) {
   5191     Observer.call(this);
   5192 
   5193     this.reportChangesOnOpen_ = reportChangesOnOpen;
   5194     this.value_ = [];
   5195     this.directObserver_ = undefined;
   5196     this.observed_ = [];
   5197   }
   5198 
   5199   var observerSentinel = {};
   5200 
   5201   CompoundObserver.prototype = createObject({
   5202     __proto__: Observer.prototype,
   5203 
   5204     connect_: function() {
   5205       if (hasObserve) {
   5206         var object;
   5207         var needsDirectObserver = false;
   5208         for (var i = 0; i < this.observed_.length; i += 2) {
   5209           object = this.observed_[i]
   5210           if (object !== observerSentinel) {
   5211             needsDirectObserver = true;
   5212             break;
   5213           }
   5214         }
   5215 
   5216         if (needsDirectObserver)
   5217           this.directObserver_ = getObservedSet(this, object);
   5218       }
   5219 
   5220       this.check_(undefined, !this.reportChangesOnOpen_);
   5221     },
   5222 
   5223     disconnect_: function() {
   5224       for (var i = 0; i < this.observed_.length; i += 2) {
   5225         if (this.observed_[i] === observerSentinel)
   5226           this.observed_[i + 1].close();
   5227       }
   5228       this.observed_.length = 0;
   5229       this.value_.length = 0;
   5230 
   5231       if (this.directObserver_) {
   5232         this.directObserver_.close(this);
   5233         this.directObserver_ = undefined;
   5234       }
   5235     },
   5236 
   5237     addPath: function(object, path) {
   5238       if (this.state_ != UNOPENED && this.state_ != RESETTING)
   5239         throw Error('Cannot add paths once started.');
   5240 
   5241       var path = getPath(path);
   5242       this.observed_.push(object, path);
   5243       if (!this.reportChangesOnOpen_)
   5244         return;
   5245       var index = this.observed_.length / 2 - 1;
   5246       this.value_[index] = path.getValueFrom(object);
   5247     },
   5248 
   5249     addObserver: function(observer) {
   5250       if (this.state_ != UNOPENED && this.state_ != RESETTING)
   5251         throw Error('Cannot add observers once started.');
   5252 
   5253       this.observed_.push(observerSentinel, observer);
   5254       if (!this.reportChangesOnOpen_)
   5255         return;
   5256       var index = this.observed_.length / 2 - 1;
   5257       this.value_[index] = observer.open(this.deliver, this);
   5258     },
   5259 
   5260     startReset: function() {
   5261       if (this.state_ != OPENED)
   5262         throw Error('Can only reset while open');
   5263 
   5264       this.state_ = RESETTING;
   5265       this.disconnect_();
   5266     },
   5267 
   5268     finishReset: function() {
   5269       if (this.state_ != RESETTING)
   5270         throw Error('Can only finishReset after startReset');
   5271       this.state_ = OPENED;
   5272       this.connect_();
   5273 
   5274       return this.value_;
   5275     },
   5276 
   5277     iterateObjects_: function(observe) {
   5278       var object;
   5279       for (var i = 0; i < this.observed_.length; i += 2) {
   5280         object = this.observed_[i]
   5281         if (object !== observerSentinel)
   5282           this.observed_[i + 1].iterateObjects(object, observe)
   5283       }
   5284     },
   5285 
   5286     check_: function(changeRecords, skipChanges) {
   5287       var oldValues;
   5288       for (var i = 0; i < this.observed_.length; i += 2) {
   5289         var object = this.observed_[i];
   5290         var path = this.observed_[i+1];
   5291         var value;
   5292         if (object === observerSentinel) {
   5293           var observable = path;
   5294           value = this.state_ === UNOPENED ?
   5295               observable.open(this.deliver, this) :
   5296               observable.discardChanges();
   5297         } else {
   5298           value = path.getValueFrom(object);
   5299         }
   5300 
   5301         if (skipChanges) {
   5302           this.value_[i / 2] = value;
   5303           continue;
   5304         }
   5305 
   5306         if (areSameValue(value, this.value_[i / 2]))
   5307           continue;
   5308 
   5309         oldValues = oldValues || [];
   5310         oldValues[i / 2] = this.value_[i / 2];
   5311         this.value_[i / 2] = value;
   5312       }
   5313 
   5314       if (!oldValues)
   5315         return false;
   5316 
   5317       // TODO(rafaelw): Having observed_ as the third callback arg here is
   5318       // pretty lame API. Fix.
   5319       this.report_([this.value_, oldValues, this.observed_]);
   5320       return true;
   5321     }
   5322   });
   5323 
   5324   function identFn(value) { return value; }
   5325 
   5326   function ObserverTransform(observable, getValueFn, setValueFn,
   5327                              dontPassThroughSet) {
   5328     this.callback_ = undefined;
   5329     this.target_ = undefined;
   5330     this.value_ = undefined;
   5331     this.observable_ = observable;
   5332     this.getValueFn_ = getValueFn || identFn;
   5333     this.setValueFn_ = setValueFn || identFn;
   5334     // TODO(rafaelw): This is a temporary hack. PolymerExpressions needs this
   5335     // at the moment because of a bug in it's dependency tracking.
   5336     this.dontPassThroughSet_ = dontPassThroughSet;
   5337   }
   5338 
   5339   ObserverTransform.prototype = {
   5340     open: function(callback, target) {
   5341       this.callback_ = callback;
   5342       this.target_ = target;
   5343       this.value_ =
   5344           this.getValueFn_(this.observable_.open(this.observedCallback_, this));
   5345       return this.value_;
   5346     },
   5347 
   5348     observedCallback_: function(value) {
   5349       value = this.getValueFn_(value);
   5350       if (areSameValue(value, this.value_))
   5351         return;
   5352       var oldValue = this.value_;
   5353       this.value_ = value;
   5354       this.callback_.call(this.target_, this.value_, oldValue);
   5355     },
   5356 
   5357     discardChanges: function() {
   5358       this.value_ = this.getValueFn_(this.observable_.discardChanges());
   5359       return this.value_;
   5360     },
   5361 
   5362     deliver: function() {
   5363       return this.observable_.deliver();
   5364     },
   5365 
   5366     setValue: function(value) {
   5367       value = this.setValueFn_(value);
   5368       if (!this.dontPassThroughSet_ && this.observable_.setValue)
   5369         return this.observable_.setValue(value);
   5370     },
   5371 
   5372     close: function() {
   5373       if (this.observable_)
   5374         this.observable_.close();
   5375       this.callback_ = undefined;
   5376       this.target_ = undefined;
   5377       this.observable_ = undefined;
   5378       this.value_ = undefined;
   5379       this.getValueFn_ = undefined;
   5380       this.setValueFn_ = undefined;
   5381     }
   5382   }
   5383 
   5384   var expectedRecordTypes = {
   5385     add: true,
   5386     update: true,
   5387     delete: true
   5388   };
   5389 
   5390   function diffObjectFromChangeRecords(object, changeRecords, oldValues) {
   5391     var added = {};
   5392     var removed = {};
   5393 
   5394     for (var i = 0; i < changeRecords.length; i++) {
   5395       var record = changeRecords[i];
   5396       if (!expectedRecordTypes[record.type]) {
   5397         console.error('Unknown changeRecord type: ' + record.type);
   5398         console.error(record);
   5399         continue;
   5400       }
   5401 
   5402       if (!(record.name in oldValues))
   5403         oldValues[record.name] = record.oldValue;
   5404 
   5405       if (record.type == 'update')
   5406         continue;
   5407 
   5408       if (record.type == 'add') {
   5409         if (record.name in removed)
   5410           delete removed[record.name];
   5411         else
   5412           added[record.name] = true;
   5413 
   5414         continue;
   5415       }
   5416 
   5417       // type = 'delete'
   5418       if (record.name in added) {
   5419         delete added[record.name];
   5420         delete oldValues[record.name];
   5421       } else {
   5422         removed[record.name] = true;
   5423       }
   5424     }
   5425 
   5426     for (var prop in added)
   5427       added[prop] = object[prop];
   5428 
   5429     for (var prop in removed)
   5430       removed[prop] = undefined;
   5431 
   5432     var changed = {};
   5433     for (var prop in oldValues) {
   5434       if (prop in added || prop in removed)
   5435         continue;
   5436 
   5437       var newValue = object[prop];
   5438       if (oldValues[prop] !== newValue)
   5439         changed[prop] = newValue;
   5440     }
   5441 
   5442     return {
   5443       added: added,
   5444       removed: removed,
   5445       changed: changed
   5446     };
   5447   }
   5448 
   5449   function newSplice(index, removed, addedCount) {
   5450     return {
   5451       index: index,
   5452       removed: removed,
   5453       addedCount: addedCount
   5454     };
   5455   }
   5456 
   5457   var EDIT_LEAVE = 0;
   5458   var EDIT_UPDATE = 1;
   5459   var EDIT_ADD = 2;
   5460   var EDIT_DELETE = 3;
   5461 
   5462   function ArraySplice() {}
   5463 
   5464   ArraySplice.prototype = {
   5465 
   5466     // Note: This function is *based* on the computation of the Levenshtein
   5467     // "edit" distance. The one change is that "updates" are treated as two
   5468     // edits - not one. With Array splices, an update is really a delete
   5469     // followed by an add. By retaining this, we optimize for "keeping" the
   5470     // maximum array items in the original array. For example:
   5471     //
   5472     //   'xxxx123' -> '123yyyy'
   5473     //
   5474     // With 1-edit updates, the shortest path would be just to update all seven
   5475     // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
   5476     // leaves the substring '123' intact.
   5477     calcEditDistances: function(current, currentStart, currentEnd,
   5478                                 old, oldStart, oldEnd) {
   5479       // "Deletion" columns
   5480       var rowCount = oldEnd - oldStart + 1;
   5481       var columnCount = currentEnd - currentStart + 1;
   5482       var distances = new Array(rowCount);
   5483 
   5484       // "Addition" rows. Initialize null column.
   5485       for (var i = 0; i < rowCount; i++) {
   5486         distances[i] = new Array(columnCount);
   5487         distances[i][0] = i;
   5488       }
   5489 
   5490       // Initialize null row
   5491       for (var j = 0; j < columnCount; j++)
   5492         distances[0][j] = j;
   5493 
   5494       for (var i = 1; i < rowCount; i++) {
   5495         for (var j = 1; j < columnCount; j++) {
   5496           if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
   5497             distances[i][j] = distances[i - 1][j - 1];
   5498           else {
   5499             var north = distances[i - 1][j] + 1;
   5500             var west = distances[i][j - 1] + 1;
   5501             distances[i][j] = north < west ? north : west;
   5502           }
   5503         }
   5504       }
   5505 
   5506       return distances;
   5507     },
   5508 
   5509     // This starts at the final weight, and walks "backward" by finding
   5510     // the minimum previous weight recursively until the origin of the weight
   5511     // matrix.
   5512     spliceOperationsFromEditDistances: function(distances) {
   5513       var i = distances.length - 1;
   5514       var j = distances[0].length - 1;
   5515       var current = distances[i][j];
   5516       var edits = [];
   5517       while (i > 0 || j > 0) {
   5518         if (i == 0) {
   5519           edits.push(EDIT_ADD);
   5520           j--;
   5521           continue;
   5522         }
   5523         if (j == 0) {
   5524           edits.push(EDIT_DELETE);
   5525           i--;
   5526           continue;
   5527         }
   5528         var northWest = distances[i - 1][j - 1];
   5529         var west = distances[i - 1][j];
   5530         var north = distances[i][j - 1];
   5531 
   5532         var min;
   5533         if (west < north)
   5534           min = west < northWest ? west : northWest;
   5535         else
   5536           min = north < northWest ? north : northWest;
   5537 
   5538         if (min == northWest) {
   5539           if (northWest == current) {
   5540             edits.push(EDIT_LEAVE);
   5541           } else {
   5542             edits.push(EDIT_UPDATE);
   5543             current = northWest;
   5544           }
   5545           i--;
   5546           j--;
   5547         } else if (min == west) {
   5548           edits.push(EDIT_DELETE);
   5549           i--;
   5550           current = west;
   5551         } else {
   5552           edits.push(EDIT_ADD);
   5553           j--;
   5554           current = north;
   5555         }
   5556       }
   5557 
   5558       edits.reverse();
   5559       return edits;
   5560     },
   5561 
   5562     /**
   5563      * Splice Projection functions:
   5564      *
   5565      * A splice map is a representation of how a previous array of items
   5566      * was transformed into a new array of items. Conceptually it is a list of
   5567      * tuples of
   5568      *
   5569      *   <index, removed, addedCount>
   5570      *
   5571      * which are kept in ascending index order of. The tuple represents that at
   5572      * the |index|, |removed| sequence of items were removed, and counting forward
   5573      * from |index|, |addedCount| items were added.
   5574      */
   5575 
   5576     /**
   5577      * Lacking individual splice mutation information, the minimal set of
   5578      * splices can be synthesized given the previous state and final state of an
   5579      * array. The basic approach is to calculate the edit distance matrix and
   5580      * choose the shortest path through it.
   5581      *
   5582      * Complexity: O(l * p)
   5583      *   l: The length of the current array
   5584      *   p: The length of the old array
   5585      */
   5586     calcSplices: function(current, currentStart, currentEnd,
   5587                           old, oldStart, oldEnd) {
   5588       var prefixCount = 0;
   5589       var suffixCount = 0;
   5590 
   5591       var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
   5592       if (currentStart == 0 && oldStart == 0)
   5593         prefixCount = this.sharedPrefix(current, old, minLength);
   5594 
   5595       if (currentEnd == current.length && oldEnd == old.length)
   5596         suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
   5597 
   5598       currentStart += prefixCount;
   5599       oldStart += prefixCount;
   5600       currentEnd -= suffixCount;
   5601       oldEnd -= suffixCount;
   5602 
   5603       if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
   5604         return [];
   5605 
   5606       if (currentStart == currentEnd) {
   5607         var splice = newSplice(currentStart, [], 0);
   5608         while (oldStart < oldEnd)
   5609           splice.removed.push(old[oldStart++]);
   5610 
   5611         return [ splice ];
   5612       } else if (oldStart == oldEnd)
   5613         return [ newSplice(currentStart, [], currentEnd - currentStart) ];
   5614 
   5615       var ops = this.spliceOperationsFromEditDistances(
   5616           this.calcEditDistances(current, currentStart, currentEnd,
   5617                                  old, oldStart, oldEnd));
   5618 
   5619       var splice = undefined;
   5620       var splices = [];
   5621       var index = currentStart;
   5622       var oldIndex = oldStart;
   5623       for (var i = 0; i < ops.length; i++) {
   5624         switch(ops[i]) {
   5625           case EDIT_LEAVE:
   5626             if (splice) {
   5627               splices.push(splice);
   5628               splice = undefined;
   5629             }
   5630 
   5631             index++;
   5632             oldIndex++;
   5633             break;
   5634           case EDIT_UPDATE:
   5635             if (!splice)
   5636               splice = newSplice(index, [], 0);
   5637 
   5638             splice.addedCount++;
   5639             index++;
   5640 
   5641             splice.removed.push(old[oldIndex]);
   5642             oldIndex++;
   5643             break;
   5644           case EDIT_ADD:
   5645             if (!splice)
   5646               splice = newSplice(index, [], 0);
   5647 
   5648             splice.addedCount++;
   5649             index++;
   5650             break;
   5651           case EDIT_DELETE:
   5652             if (!splice)
   5653               splice = newSplice(index, [], 0);
   5654 
   5655             splice.removed.push(old[oldIndex]);
   5656             oldIndex++;
   5657             break;
   5658         }
   5659       }
   5660 
   5661       if (splice) {
   5662         splices.push(splice);
   5663       }
   5664       return splices;
   5665     },
   5666 
   5667     sharedPrefix: function(current, old, searchLength) {
   5668       for (var i = 0; i < searchLength; i++)
   5669         if (!this.equals(current[i], old[i]))
   5670           return i;
   5671       return searchLength;
   5672     },
   5673 
   5674     sharedSuffix: function(current, old, searchLength) {
   5675       var index1 = current.length;
   5676       var index2 = old.length;
   5677       var count = 0;
   5678       while (count < searchLength && this.equals(current[--index1], old[--index2]))
   5679         count++;
   5680 
   5681       return count;
   5682     },
   5683 
   5684     calculateSplices: function(current, previous) {
   5685       return this.calcSplices(current, 0, current.length, previous, 0,
   5686                               previous.length);
   5687     },
   5688 
   5689     equals: function(currentValue, previousValue) {
   5690       return currentValue === previousValue;
   5691     }
   5692   };
   5693 
   5694   var arraySplice = new ArraySplice();
   5695 
   5696   function calcSplices(current, currentStart, currentEnd,
   5697                        old, oldStart, oldEnd) {
   5698     return arraySplice.calcSplices(current, currentStart, currentEnd,
   5699                                    old, oldStart, oldEnd);
   5700   }
   5701 
   5702   function intersect(start1, end1, start2, end2) {
   5703     // Disjoint
   5704     if (end1 < start2 || end2 < start1)
   5705       return -1;
   5706 
   5707     // Adjacent
   5708     if (end1 == start2 || end2 == start1)
   5709       return 0;
   5710 
   5711     // Non-zero intersect, span1 first
   5712     if (start1 < start2) {
   5713       if (end1 < end2)
   5714         return end1 - start2; // Overlap
   5715       else
   5716         return end2 - start2; // Contained
   5717     } else {
   5718       // Non-zero intersect, span2 first
   5719       if (end2 < end1)
   5720         return end2 - start1; // Overlap
   5721       else
   5722         return end1 - start1; // Contained
   5723     }
   5724   }
   5725 
   5726   function mergeSplice(splices, index, removed, addedCount) {
   5727 
   5728     var splice = newSplice(index, removed, addedCount);
   5729 
   5730     var inserted = false;
   5731     var insertionOffset = 0;
   5732 
   5733     for (var i = 0; i < splices.length; i++) {
   5734       var current = splices[i];
   5735       current.index += insertionOffset;
   5736 
   5737       if (inserted)
   5738         continue;
   5739 
   5740       var intersectCount = intersect(splice.index,
   5741                                      splice.index + splice.removed.length,
   5742                                      current.index,
   5743                                      current.index + current.addedCount);
   5744 
   5745       if (intersectCount >= 0) {
   5746         // Merge the two splices
   5747 
   5748         splices.splice(i, 1);
   5749         i--;
   5750 
   5751         insertionOffset -= current.addedCount - current.removed.length;
   5752 
   5753         splice.addedCount += current.addedCount - intersectCount;
   5754         var deleteCount = splice.removed.length +
   5755                           current.removed.length - intersectCount;
   5756 
   5757         if (!splice.addedCount && !deleteCount) {
   5758           // merged splice is a noop. discard.
   5759           inserted = true;
   5760         } else {
   5761           var removed = current.removed;
   5762 
   5763           if (splice.index < current.index) {
   5764             // some prefix of splice.removed is prepended to current.removed.
   5765             var prepend = splice.removed.slice(0, current.index - splice.index);
   5766             Array.prototype.push.apply(prepend, removed);
   5767             removed = prepend;
   5768           }
   5769 
   5770           if (splice.index + splice.removed.length > current.index + current.addedCount) {
   5771             // some suffix of splice.removed is appended to current.removed.
   5772             var append = splice.removed.slice(current.index + current.addedCount - splice.index);
   5773             Array.prototype.push.apply(removed, append);
   5774           }
   5775 
   5776           splice.removed = removed;
   5777           if (current.index < splice.index) {
   5778             splice.index = current.index;
   5779           }
   5780         }
   5781       } else if (splice.index < current.index) {
   5782         // Insert splice here.
   5783 
   5784         inserted = true;
   5785 
   5786         splices.splice(i, 0, splice);
   5787         i++;
   5788 
   5789         var offset = splice.addedCount - splice.removed.length
   5790         current.index += offset;
   5791         insertionOffset += offset;
   5792       }
   5793     }
   5794 
   5795     if (!inserted)
   5796       splices.push(splice);
   5797   }
   5798 
   5799   function createInitialSplices(array, changeRecords) {
   5800     var splices = [];
   5801 
   5802     for (var i = 0; i < changeRecords.length; i++) {
   5803       var record = changeRecords[i];
   5804       switch(record.type) {
   5805         case 'splice':
   5806           mergeSplice(splices, record.index, record.removed.slice(), record.addedCount);
   5807           break;
   5808         case 'add':
   5809         case 'update':
   5810         case 'delete':
   5811           if (!isIndex(record.name))
   5812             continue;
   5813           var index = toNumber(record.name);
   5814           if (index < 0)
   5815             continue;
   5816           mergeSplice(splices, index, [record.oldValue], 1);
   5817           break;
   5818         default:
   5819           console.error('Unexpected record type: ' + JSON.stringify(record));
   5820           break;
   5821       }
   5822     }
   5823 
   5824     return splices;
   5825   }
   5826 
   5827   function projectArraySplices(array, changeRecords) {
   5828     var splices = [];
   5829 
   5830     createInitialSplices(array, changeRecords).forEach(function(splice) {
   5831       if (splice.addedCount == 1 && splice.removed.length == 1) {
   5832         if (splice.removed[0] !== array[splice.index])
   5833           splices.push(splice);
   5834 
   5835         return
   5836       };
   5837 
   5838       splices = splices.concat(calcSplices(array, splice.index, splice.index + splice.addedCount,
   5839                                            splice.removed, 0, splice.removed.length));
   5840     });
   5841 
   5842     return splices;
   5843   }
   5844 
   5845   // Export the observe-js object for **Node.js**, with backwards-compatibility
   5846   // for the old `require()` API. Also ensure `exports` is not a DOM Element.
   5847   // If we're in the browser, export as a global object.
   5848 
   5849   var expose = global;
   5850 
   5851   if (typeof exports !== 'undefined' && !exports.nodeType) {
   5852     if (typeof module !== 'undefined' && module.exports) {
   5853       exports = module.exports;
   5854     }
   5855     expose = exports;
   5856   }
   5857 
   5858   expose.Observer = Observer;
   5859   expose.Observer.runEOM_ = runEOM;
   5860   expose.Observer.observerSentinel_ = observerSentinel; // for testing.
   5861   expose.Observer.hasObjectObserve = hasObserve;
   5862   expose.ArrayObserver = ArrayObserver;
   5863   expose.ArrayObserver.calculateSplices = function(current, previous) {
   5864     return arraySplice.calculateSplices(current, previous);
   5865   };
   5866 
   5867   expose.ArraySplice = ArraySplice;
   5868   expose.ObjectObserver = ObjectObserver;
   5869   expose.PathObserver = PathObserver;
   5870   expose.CompoundObserver = CompoundObserver;
   5871   expose.Path = Path;
   5872   expose.ObserverTransform = ObserverTransform;
   5873 
   5874 })(typeof global !== 'undefined' && global && typeof module !== 'undefined' && module ? global : this || window);
   5875 
   5876 // Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
   5877 // This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
   5878 // The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
   5879 // The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
   5880 // Code distributed by Google as part of the polymer project is also
   5881 // subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
   5882 
   5883 (function(global) {
   5884   'use strict';
   5885 
   5886   var filter = Array.prototype.filter.call.bind(Array.prototype.filter);
   5887 
   5888   function getTreeScope(node) {
   5889     while (node.parentNode) {
   5890       node = node.parentNode;
   5891     }
   5892 
   5893     return typeof node.getElementById === 'function' ? node : null;
   5894   }
   5895 
   5896   Node.prototype.bind = function(name, observable) {
   5897     console.error('Unhandled binding to Node: ', this, name, observable);
   5898   };
   5899 
   5900   Node.prototype.bindFinished = function() {};
   5901 
   5902   function updateBindings(node, name, binding) {
   5903     var bindings = node.bindings_;
   5904     if (!bindings)
   5905       bindings = node.bindings_ = {};
   5906 
   5907     if (bindings[name])
   5908       binding[name].close();
   5909 
   5910     return bindings[name] = binding;
   5911   }
   5912 
   5913   function returnBinding(node, name, binding) {
   5914     return binding;
   5915   }
   5916 
   5917   function sanitizeValue(value) {
   5918     return value == null ? '' : value;
   5919   }
   5920 
   5921   function updateText(node, value) {
   5922     node.data = sanitizeValue(value);
   5923   }
   5924 
   5925   function textBinding(node) {
   5926     return function(value) {
   5927       return updateText(node, value);
   5928     };
   5929   }
   5930 
   5931   var maybeUpdateBindings = returnBinding;
   5932 
   5933   Object.defineProperty(Platform, 'enableBindingsReflection', {
   5934     get: function() {
   5935       return maybeUpdateBindings === updateBindings;
   5936     },
   5937     set: function(enable) {
   5938       maybeUpdateBindings = enable ? updateBindings : returnBinding;
   5939       return enable;
   5940     },
   5941     configurable: true
   5942   });
   5943 
   5944   Text.prototype.bind = function(name, value, oneTime) {
   5945     if (name !== 'textContent')
   5946       return Node.prototype.bind.call(this, name, value, oneTime);
   5947 
   5948     if (oneTime)
   5949       return updateText(this, value);
   5950 
   5951     var observable = value;
   5952     updateText(this, observable.open(textBinding(this)));
   5953     return maybeUpdateBindings(this, name, observable);
   5954   }
   5955 
   5956   function updateAttribute(el, name, conditional, value) {
   5957     if (conditional) {
   5958       if (value)
   5959         el.setAttribute(name, '');
   5960       else
   5961         el.removeAttribute(name);
   5962       return;
   5963     }
   5964 
   5965     el.setAttribute(name, sanitizeValue(value));
   5966   }
   5967 
   5968   function attributeBinding(el, name, conditional) {
   5969     return function(value) {
   5970       updateAttribute(el, name, conditional, value);
   5971     };
   5972   }
   5973 
   5974   Element.prototype.bind = function(name, value, oneTime) {
   5975     var conditional = name[name.length - 1] == '?';
   5976     if (conditional) {
   5977       this.removeAttribute(name);
   5978       name = name.slice(0, -1);
   5979     }
   5980 
   5981     if (oneTime)
   5982       return updateAttribute(this, name, conditional, value);
   5983 
   5984 
   5985     var observable = value;
   5986     updateAttribute(this, name, conditional,
   5987         observable.open(attributeBinding(this, name, conditional)));
   5988 
   5989     return maybeUpdateBindings(this, name, observable);
   5990   };
   5991 
   5992   var checkboxEventType;
   5993   (function() {
   5994     // Attempt to feature-detect which event (change or click) is fired first
   5995     // for checkboxes.
   5996     var div = document.createElement('div');
   5997     var checkbox = div.appendChild(document.createElement('input'));
   5998     checkbox.setAttribute('type', 'checkbox');
   5999     var first;
   6000     var count = 0;
   6001     checkbox.addEventListener('click', function(e) {
   6002       count++;
   6003       first = first || 'click';
   6004     });
   6005     checkbox.addEventListener('change', function() {
   6006       count++;
   6007       first = first || 'change';
   6008     });
   6009 
   6010     var event = document.createEvent('MouseEvent');
   6011     event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false,
   6012         false, false, false, 0, null);
   6013     checkbox.dispatchEvent(event);
   6014     // WebKit/Blink don't fire the change event if the element is outside the
   6015     // document, so assume 'change' for that case.
   6016     checkboxEventType = count == 1 ? 'change' : first;
   6017   })();
   6018 
   6019   function getEventForInputType(element) {
   6020     switch (element.type) {
   6021       case 'checkbox':
   6022         return checkboxEventType;
   6023       case 'radio':
   6024       case 'select-multiple':
   6025       case 'select-one':
   6026         return 'change';
   6027       case 'range':
   6028         if (/Trident|MSIE/.test(navigator.userAgent))
   6029           return 'change';
   6030       default:
   6031         return 'input';
   6032     }
   6033   }
   6034 
   6035   function updateInput(input, property, value, santizeFn) {
   6036     input[property] = (santizeFn || sanitizeValue)(value);
   6037   }
   6038 
   6039   function inputBinding(input, property, santizeFn) {
   6040     return function(value) {
   6041       return updateInput(input, property, value, santizeFn);
   6042     }
   6043   }
   6044 
   6045   function noop() {}
   6046 
   6047   function bindInputEvent(input, property, observable, postEventFn) {
   6048     var eventType = getEventForInputType(input);
   6049 
   6050     function eventHandler() {
   6051       var isNum = property == 'value' && input.type == 'number';
   6052       observable.setValue(isNum ? input.valueAsNumber : input[property]);
   6053       observable.discardChanges();
   6054       (postEventFn || noop)(input);
   6055       Platform.performMicrotaskCheckpoint();
   6056     }
   6057     input.addEventListener(eventType, eventHandler);
   6058 
   6059     return {
   6060       close: function() {
   6061         input.removeEventListener(eventType, eventHandler);
   6062         observable.close();
   6063       },
   6064 
   6065       observable_: observable
   6066     }
   6067   }
   6068 
   6069   function booleanSanitize(value) {
   6070     return Boolean(value);
   6071   }
   6072 
   6073   // |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
   6074   // Returns an array containing all radio buttons other than |element| that
   6075   // have the same |name|, either in the form that |element| belongs to or,
   6076   // if no form, in the document tree to which |element| belongs.
   6077   //
   6078   // This implementation is based upon the HTML spec definition of a
   6079   // "radio button group":
   6080   //   http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state.html#radio-button-group
   6081   //
   6082   function getAssociatedRadioButtons(element) {
   6083     if (element.form) {
   6084       return filter(element.form.elements, function(el) {
   6085         return el != element &&
   6086             el.tagName == 'INPUT' &&
   6087             el.type == 'radio' &&
   6088             el.name == element.name;
   6089       });
   6090     } else {
   6091       var treeScope = getTreeScope(element);
   6092       if (!treeScope)
   6093         return [];
   6094       var radios = treeScope.querySelectorAll(
   6095           'input[type="radio"][name="' + element.name + '"]');
   6096       return filter(radios, function(el) {
   6097         return el != element && !el.form;
   6098       });
   6099     }
   6100   }
   6101 
   6102   function checkedPostEvent(input) {
   6103     // Only the radio button that is getting checked gets an event. We
   6104     // therefore find all the associated radio buttons and update their
   6105     // check binding manually.
   6106     if (input.tagName === 'INPUT' &&
   6107         input.type === 'radio') {
   6108       getAssociatedRadioButtons(input).forEach(function(radio) {
   6109         var checkedBinding = radio.bindings_.checked;
   6110         if (checkedBinding) {
   6111           // Set the value directly to avoid an infinite call stack.
   6112           checkedBinding.observable_.setValue(false);
   6113         }
   6114       });
   6115     }
   6116   }
   6117 
   6118   HTMLInputElement.prototype.bind = function(name, value, oneTime) {
   6119     if (name !== 'value' && name !== 'checked')
   6120       return HTMLElement.prototype.bind.call(this, name, value, oneTime);
   6121 
   6122     this.removeAttribute(name);
   6123     var sanitizeFn = name == 'checked' ? booleanSanitize : sanitizeValue;
   6124     var postEventFn = name == 'checked' ? checkedPostEvent : noop;
   6125 
   6126     if (oneTime)
   6127       return updateInput(this, name, value, sanitizeFn);
   6128 
   6129 
   6130     var observable = value;
   6131     var binding = bindInputEvent(this, name, observable, postEventFn);
   6132     updateInput(this, name,
   6133                 observable.open(inputBinding(this, name, sanitizeFn)),
   6134                 sanitizeFn);
   6135 
   6136     // Checkboxes may need to update bindings of other checkboxes.
   6137     return updateBindings(this, name, binding);
   6138   }
   6139 
   6140   HTMLTextAreaElement.prototype.bind = function(name, value, oneTime) {
   6141     if (name !== 'value')
   6142       return HTMLElement.prototype.bind.call(this, name, value, oneTime);
   6143 
   6144     this.removeAttribute('value');
   6145 
   6146     if (oneTime)
   6147       return updateInput(this, 'value', value);
   6148 
   6149     var observable = value;
   6150     var binding = bindInputEvent(this, 'value', observable);
   6151     updateInput(this, 'value',
   6152                 observable.open(inputBinding(this, 'value', sanitizeValue)));
   6153     return maybeUpdateBindings(this, name, binding);
   6154   }
   6155 
   6156   function updateOption(option, value) {
   6157     var parentNode = option.parentNode;;
   6158     var select;
   6159     var selectBinding;
   6160     var oldValue;
   6161     if (parentNode instanceof HTMLSelectElement &&
   6162         parentNode.bindings_ &&
   6163         parentNode.bindings_.value) {
   6164       select = parentNode;
   6165       selectBinding = select.bindings_.value;
   6166       oldValue = select.value;
   6167     }
   6168 
   6169     option.value = sanitizeValue(value);
   6170 
   6171     if (select && select.value != oldValue) {
   6172       selectBinding.observable_.setValue(select.value);
   6173       selectBinding.observable_.discardChanges();
   6174       Platform.performMicrotaskCheckpoint();
   6175     }
   6176   }
   6177 
   6178   function optionBinding(option) {
   6179     return function(value) {
   6180       updateOption(option, value);
   6181     }
   6182   }
   6183 
   6184   HTMLOptionElement.prototype.bind = function(name, value, oneTime) {
   6185     if (name !== 'value')
   6186       return HTMLElement.prototype.bind.call(this, name, value, oneTime);
   6187 
   6188     this.removeAttribute('value');
   6189 
   6190     if (oneTime)
   6191       return updateOption(this, value);
   6192 
   6193     var observable = value;
   6194     var binding = bindInputEvent(this, 'value', observable);
   6195     updateOption(this, observable.open(optionBinding(this)));
   6196     return maybeUpdateBindings(this, name, binding);
   6197   }
   6198 
   6199   HTMLSelectElement.prototype.bind = function(name, value, oneTime) {
   6200     if (name === 'selectedindex')
   6201       name = 'selectedIndex';
   6202 
   6203     if (name !== 'selectedIndex' && name !== 'value')
   6204       return HTMLElement.prototype.bind.call(this, name, value, oneTime);
   6205 
   6206     this.removeAttribute(name);
   6207 
   6208     if (oneTime)
   6209       return updateInput(this, name, value);
   6210 
   6211     var observable = value;
   6212     var binding = bindInputEvent(this, name, observable);
   6213     updateInput(this, name,
   6214                 observable.open(inputBinding(this, name)));
   6215 
   6216     // Option update events may need to access select bindings.
   6217     return updateBindings(this, name, binding);
   6218   }
   6219 })(this);
   6220 
   6221 // Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
   6222 // This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
   6223 // The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
   6224 // The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
   6225 // Code distributed by Google as part of the polymer project is also
   6226 // subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
   6227 
   6228 (function(global) {
   6229   'use strict';
   6230 
   6231   function assert(v) {
   6232     if (!v)
   6233       throw new Error('Assertion failed');
   6234   }
   6235 
   6236   var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
   6237 
   6238   function getFragmentRoot(node) {
   6239     var p;
   6240     while (p = node.parentNode) {
   6241       node = p;
   6242     }
   6243 
   6244     return node;
   6245   }
   6246 
   6247   function searchRefId(node, id) {
   6248     if (!id)
   6249       return;
   6250 
   6251     var ref;
   6252     var selector = '#' + id;
   6253     while (!ref) {
   6254       node = getFragmentRoot(node);
   6255 
   6256       if (node.protoContent_)
   6257         ref = node.protoContent_.querySelector(selector);
   6258       else if (node.getElementById)
   6259         ref = node.getElementById(id);
   6260 
   6261       if (ref || !node.templateCreator_)
   6262         break
   6263 
   6264       node = node.templateCreator_;
   6265     }
   6266 
   6267     return ref;
   6268   }
   6269 
   6270   function getInstanceRoot(node) {
   6271     while (node.parentNode) {
   6272       node = node.parentNode;
   6273     }
   6274     return node.templateCreator_ ? node : null;
   6275   }
   6276 
   6277   var Map;
   6278   if (global.Map && typeof global.Map.prototype.forEach === 'function') {
   6279     Map = global.Map;
   6280   } else {
   6281     Map = function() {
   6282       this.keys = [];
   6283       this.values = [];
   6284     };
   6285 
   6286     Map.prototype = {
   6287       set: function(key, value) {
   6288         var index = this.keys.indexOf(key);
   6289         if (index < 0) {
   6290           this.keys.push(key);
   6291           this.values.push(value);
   6292         } else {
   6293           this.values[index] = value;
   6294         }
   6295       },
   6296 
   6297       get: function(key) {
   6298         var index = this.keys.indexOf(key);
   6299         if (index < 0)
   6300           return;
   6301 
   6302         return this.values[index];
   6303       },
   6304 
   6305       delete: function(key, value) {
   6306         var index = this.keys.indexOf(key);
   6307         if (index < 0)
   6308           return false;
   6309 
   6310         this.keys.splice(index, 1);
   6311         this.values.splice(index, 1);
   6312         return true;
   6313       },
   6314 
   6315       forEach: function(f, opt_this) {
   6316         for (var i = 0; i < this.keys.length; i++)
   6317           f.call(opt_this || this, this.values[i], this.keys[i], this);
   6318       }
   6319     };
   6320   }
   6321 
   6322   // JScript does not have __proto__. We wrap all object literals with
   6323   // createObject which uses Object.create, Object.defineProperty and
   6324   // Object.getOwnPropertyDescriptor to create a new object that does the exact
   6325   // same thing. The main downside to this solution is that we have to extract
   6326   // all those property descriptors for IE.
   6327   var createObject = ('__proto__' in {}) ?
   6328       function(obj) { return obj; } :
   6329       function(obj) {
   6330         var proto = obj.__proto__;
   6331         if (!proto)
   6332           return obj;
   6333         var newObject = Object.create(proto);
   6334         Object.getOwnPropertyNames(obj).forEach(function(name) {
   6335           Object.defineProperty(newObject, name,
   6336                                Object.getOwnPropertyDescriptor(obj, name));
   6337         });
   6338         return newObject;
   6339       };
   6340 
   6341   // IE does not support have Document.prototype.contains.
   6342   if (typeof document.contains != 'function') {
   6343     Document.prototype.contains = function(node) {
   6344       if (node === this || node.parentNode === this)
   6345         return true;
   6346       return this.documentElement.contains(node);
   6347     }
   6348   }
   6349 
   6350   var BIND = 'bind';
   6351   var REPEAT = 'repeat';
   6352   var IF = 'if';
   6353 
   6354   var templateAttributeDirectives = {
   6355     'template': true,
   6356     'repeat': true,
   6357     'bind': true,
   6358     'ref': true,
   6359     'if': true
   6360   };
   6361 
   6362   var semanticTemplateElements = {
   6363     'THEAD': true,
   6364     'TBODY': true,
   6365     'TFOOT': true,
   6366     'TH': true,
   6367     'TR': true,
   6368     'TD': true,
   6369     'COLGROUP': true,
   6370     'COL': true,
   6371     'CAPTION': true,
   6372     'OPTION': true,
   6373     'OPTGROUP': true
   6374   };
   6375 
   6376   var hasTemplateElement = typeof HTMLTemplateElement !== 'undefined';
   6377   if (hasTemplateElement) {
   6378     // TODO(rafaelw): Remove when fix for
   6379     // https://codereview.chromium.org/164803002/
   6380     // makes it to Chrome release.
   6381     (function() {
   6382       var t = document.createElement('template');
   6383       var d = t.content.ownerDocument;
   6384       var html = d.appendChild(d.createElement('html'));
   6385       var head = html.appendChild(d.createElement('head'));
   6386       var base = d.createElement('base');
   6387       base.href = document.baseURI;
   6388       head.appendChild(base);
   6389     })();
   6390   }
   6391 
   6392   var allTemplatesSelectors = 'template, ' +
   6393       Object.keys(semanticTemplateElements).map(function(tagName) {
   6394         return tagName.toLowerCase() + '[template]';
   6395       }).join(', ');
   6396 
   6397   function isSVGTemplate(el) {
   6398     return el.tagName == 'template' &&
   6399            el.namespaceURI == 'http://www.w3.org/2000/svg';
   6400   }
   6401 
   6402   function isHTMLTemplate(el) {
   6403     return el.tagName == 'TEMPLATE' &&
   6404            el.namespaceURI == 'http://www.w3.org/1999/xhtml';
   6405   }
   6406 
   6407   function isAttributeTemplate(el) {
   6408     return Boolean(semanticTemplateElements[el.tagName] &&
   6409                    el.hasAttribute('template'));
   6410   }
   6411 
   6412   function isTemplate(el) {
   6413     if (el.isTemplate_ === undefined)
   6414       el.isTemplate_ = el.tagName == 'TEMPLATE' || isAttributeTemplate(el);
   6415 
   6416     return el.isTemplate_;
   6417   }
   6418 
   6419   // FIXME: Observe templates being added/removed from documents
   6420   // FIXME: Expose imperative API to decorate and observe templates in
   6421   // "disconnected tress" (e.g. ShadowRoot)
   6422   document.addEventListener('DOMContentLoaded', function(e) {
   6423     bootstrapTemplatesRecursivelyFrom(document);
   6424     // FIXME: Is this needed? Seems like it shouldn't be.
   6425     Platform.performMicrotaskCheckpoint();
   6426   }, false);
   6427 
   6428   function forAllTemplatesFrom(node, fn) {
   6429     var subTemplates = node.querySelectorAll(allTemplatesSelectors);
   6430 
   6431     if (isTemplate(node))
   6432       fn(node)
   6433     forEach(subTemplates, fn);
   6434   }
   6435 
   6436   function bootstrapTemplatesRecursivelyFrom(node) {
   6437     function bootstrap(template) {
   6438       if (!HTMLTemplateElement.decorate(template))
   6439         bootstrapTemplatesRecursivelyFrom(template.content);
   6440     }
   6441 
   6442     forAllTemplatesFrom(node, bootstrap);
   6443   }
   6444 
   6445   if (!hasTemplateElement) {
   6446     /**
   6447      * This represents a <template> element.
   6448      * @constructor
   6449      * @extends {HTMLElement}
   6450      */
   6451     global.HTMLTemplateElement = function() {
   6452       throw TypeError('Illegal constructor');
   6453     };
   6454   }
   6455 
   6456   var hasProto = '__proto__' in {};
   6457 
   6458   function mixin(to, from) {
   6459     Object.getOwnPropertyNames(from).forEach(function(name) {
   6460       Object.defineProperty(to, name,
   6461                             Object.getOwnPropertyDescriptor(from, name));
   6462     });
   6463   }
   6464 
   6465   // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#dfn-template-contents-owner
   6466   function getOrCreateTemplateContentsOwner(template) {
   6467     var doc = template.ownerDocument
   6468     if (!doc.defaultView)
   6469       return doc;
   6470     var d = doc.templateContentsOwner_;
   6471     if (!d) {
   6472       // TODO(arv): This should either be a Document or HTMLDocument depending
   6473       // on doc.
   6474       d = doc.implementation.createHTMLDocument('');
   6475       while (d.lastChild) {
   6476         d.removeChild(d.lastChild);
   6477       }
   6478       doc.templateContentsOwner_ = d;
   6479     }
   6480     return d;
   6481   }
   6482 
   6483   function getTemplateStagingDocument(template) {
   6484     if (!template.stagingDocument_) {
   6485       var owner = template.ownerDocument;
   6486       if (!owner.stagingDocument_) {
   6487         owner.stagingDocument_ = owner.implementation.createHTMLDocument('');
   6488         owner.stagingDocument_.isStagingDocument = true;
   6489         // TODO(rafaelw): Remove when fix for
   6490         // https://codereview.chromium.org/164803002/
   6491         // makes it to Chrome release.
   6492         var base = owner.stagingDocument_.createElement('base');
   6493         base.href = document.baseURI;
   6494         owner.stagingDocument_.head.appendChild(base);
   6495 
   6496         owner.stagingDocument_.stagingDocument_ = owner.stagingDocument_;
   6497       }
   6498 
   6499       template.stagingDocument_ = owner.stagingDocument_;
   6500     }
   6501 
   6502     return template.stagingDocument_;
   6503   }
   6504 
   6505   // For non-template browsers, the parser will disallow <template> in certain
   6506   // locations, so we allow "attribute templates" which combine the template
   6507   // element with the top-level container node of the content, e.g.
   6508   //
   6509   //   <tr template repeat="{{ foo }}"" class="bar"><td>Bar</td></tr>
   6510   //
   6511   // becomes
   6512   //
   6513   //   <template repeat="{{ foo }}">
   6514   //   + #document-fragment
   6515   //     + <tr class="bar">
   6516   //       + <td>Bar</td>
   6517   //
   6518   function extractTemplateFromAttributeTemplate(el) {
   6519     var template = el.ownerDocument.createElement('template');
   6520     el.parentNode.insertBefore(template, el);
   6521 
   6522     var attribs = el.attributes;
   6523     var count = attribs.length;
   6524     while (count-- > 0) {
   6525       var attrib = attribs[count];
   6526       if (templateAttributeDirectives[attrib.name]) {
   6527         if (attrib.name !== 'template')
   6528           template.setAttribute(attrib.name, attrib.value);
   6529         el.removeAttribute(attrib.name);
   6530       }
   6531     }
   6532 
   6533     return template;
   6534   }
   6535 
   6536   function extractTemplateFromSVGTemplate(el) {
   6537     var template = el.ownerDocument.createElement('template');
   6538     el.parentNode.insertBefore(template, el);
   6539 
   6540     var attribs = el.attributes;
   6541     var count = attribs.length;
   6542     while (count-- > 0) {
   6543       var attrib = attribs[count];
   6544       template.setAttribute(attrib.name, attrib.value);
   6545       el.removeAttribute(attrib.name);
   6546     }
   6547 
   6548     el.parentNode.removeChild(el);
   6549     return template;
   6550   }
   6551 
   6552   function liftNonNativeTemplateChildrenIntoContent(template, el, useRoot) {
   6553     var content = template.content;
   6554     if (useRoot) {
   6555       content.appendChild(el);
   6556       return;
   6557     }
   6558 
   6559     var child;
   6560     while (child = el.firstChild) {
   6561       content.appendChild(child);
   6562     }
   6563   }
   6564 
   6565   var templateObserver;
   6566   if (typeof MutationObserver == 'function') {
   6567     templateObserver = new MutationObserver(function(records) {
   6568       for (var i = 0; i < records.length; i++) {
   6569         records[i].target.refChanged_();
   6570       }
   6571     });
   6572   }
   6573 
   6574   /**
   6575    * Ensures proper API and content model for template elements.
   6576    * @param {HTMLTemplateElement} opt_instanceRef The template element which
   6577    *     |el| template element will return as the value of its ref(), and whose
   6578    *     content will be used as source when createInstance() is invoked.
   6579    */
   6580   HTMLTemplateElement.decorate = function(el, opt_instanceRef) {
   6581     if (el.templateIsDecorated_)
   6582       return false;
   6583 
   6584     var templateElement = el;
   6585     templateElement.templateIsDecorated_ = true;
   6586 
   6587     var isNativeHTMLTemplate = isHTMLTemplate(templateElement) &&
   6588                                hasTemplateElement;
   6589     var bootstrapContents = isNativeHTMLTemplate;
   6590     var liftContents = !isNativeHTMLTemplate;
   6591     var liftRoot = false;
   6592 
   6593     if (!isNativeHTMLTemplate) {
   6594       if (isAttributeTemplate(templateElement)) {
   6595         assert(!opt_instanceRef);
   6596         templateElement = extractTemplateFromAttributeTemplate(el);
   6597         templateElement.templateIsDecorated_ = true;
   6598         isNativeHTMLTemplate = hasTemplateElement;
   6599         liftRoot = true;
   6600       } else if (isSVGTemplate(templateElement)) {
   6601         templateElement = extractTemplateFromSVGTemplate(el);
   6602         templateElement.templateIsDecorated_ = true;
   6603         isNativeHTMLTemplate = hasTemplateElement;
   6604       }
   6605     }
   6606 
   6607     if (!isNativeHTMLTemplate) {
   6608       fixTemplateElementPrototype(templateElement);
   6609       var doc = getOrCreateTemplateContentsOwner(templateElement);
   6610       templateElement.content_ = doc.createDocumentFragment();
   6611     }
   6612 
   6613     if (opt_instanceRef) {
   6614       // template is contained within an instance, its direct content must be
   6615       // empty
   6616       templateElement.instanceRef_ = opt_instanceRef;
   6617     } else if (liftContents) {
   6618       liftNonNativeTemplateChildrenIntoContent(templateElement,
   6619                                                el,
   6620                                                liftRoot);
   6621     } else if (bootstrapContents) {
   6622       bootstrapTemplatesRecursivelyFrom(templateElement.content);
   6623     }
   6624 
   6625     return true;
   6626   };
   6627 
   6628   // TODO(rafaelw): This used to decorate recursively all templates from a given
   6629   // node. This happens by default on 'DOMContentLoaded', but may be needed
   6630   // in subtrees not descendent from document (e.g. ShadowRoot).
   6631   // Review whether this is the right public API.
   6632   HTMLTemplateElement.bootstrap = bootstrapTemplatesRecursivelyFrom;
   6633 
   6634   var htmlElement = global.HTMLUnknownElement || HTMLElement;
   6635 
   6636   var contentDescriptor = {
   6637     get: function() {
   6638       return this.content_;
   6639     },
   6640     enumerable: true,
   6641     configurable: true
   6642   };
   6643 
   6644   if (!hasTemplateElement) {
   6645     // Gecko is more picky with the prototype than WebKit. Make sure to use the
   6646     // same prototype as created in the constructor.
   6647     HTMLTemplateElement.prototype = Object.create(htmlElement.prototype);
   6648 
   6649     Object.defineProperty(HTMLTemplateElement.prototype, 'content',
   6650                           contentDescriptor);
   6651   }
   6652 
   6653   function fixTemplateElementPrototype(el) {
   6654     if (hasProto)
   6655       el.__proto__ = HTMLTemplateElement.prototype;
   6656     else
   6657       mixin(el, HTMLTemplateElement.prototype);
   6658   }
   6659 
   6660   function ensureSetModelScheduled(template) {
   6661     if (!template.setModelFn_) {
   6662       template.setModelFn_ = function() {
   6663         template.setModelFnScheduled_ = false;
   6664         var map = getBindings(template,
   6665             template.delegate_ && template.delegate_.prepareBinding);
   6666         processBindings(template, map, template.model_);
   6667       };
   6668     }
   6669 
   6670     if (!template.setModelFnScheduled_) {
   6671       template.setModelFnScheduled_ = true;
   6672       Observer.runEOM_(template.setModelFn_);
   6673     }
   6674   }
   6675 
   6676   mixin(HTMLTemplateElement.prototype, {
   6677     bind: function(name, value, oneTime) {
   6678       if (name != 'ref')
   6679         return Element.prototype.bind.call(this, name, value, oneTime);
   6680 
   6681       var self = this;
   6682       var ref = oneTime ? value : value.open(function(ref) {
   6683         self.setAttribute('ref', ref);
   6684         self.refChanged_();
   6685       });
   6686 
   6687       this.setAttribute('ref', ref);
   6688       this.refChanged_();
   6689       if (oneTime)
   6690         return;
   6691 
   6692       if (!this.bindings_) {
   6693         this.bindings_ = { ref: value };
   6694       } else {
   6695         this.bindings_.ref = value;
   6696       }
   6697 
   6698       return value;
   6699     },
   6700 
   6701     processBindingDirectives_: function(directives) {
   6702       if (this.iterator_)
   6703         this.iterator_.closeDeps();
   6704 
   6705       if (!directives.if && !directives.bind && !directives.repeat) {
   6706         if (this.iterator_) {
   6707           this.iterator_.close();
   6708           this.iterator_ = undefined;
   6709         }
   6710 
   6711         return;
   6712       }
   6713 
   6714       if (!this.iterator_) {
   6715         this.iterator_ = new TemplateIterator(this);
   6716       }
   6717 
   6718       this.iterator_.updateDependencies(directives, this.model_);
   6719 
   6720       if (templateObserver) {
   6721         templateObserver.observe(this, { attributes: true,
   6722                                          attributeFilter: ['ref'] });
   6723       }
   6724 
   6725       return this.iterator_;
   6726     },
   6727 
   6728     createInstance: function(model, bindingDelegate, delegate_) {
   6729       if (bindingDelegate)
   6730         delegate_ = this.newDelegate_(bindingDelegate);
   6731       else if (!delegate_)
   6732         delegate_ = this.delegate_;
   6733 
   6734       if (!this.refContent_)
   6735         this.refContent_ = this.ref_.content;
   6736       var content = this.refContent_;
   6737       if (content.firstChild === null)
   6738         return emptyInstance;
   6739 
   6740       var map = getInstanceBindingMap(content, delegate_);
   6741       var stagingDocument = getTemplateStagingDocument(this);
   6742       var instance = stagingDocument.createDocumentFragment();
   6743       instance.templateCreator_ = this;
   6744       instance.protoContent_ = content;
   6745       instance.bindings_ = [];
   6746       instance.terminator_ = null;
   6747       var instanceRecord = instance.templateInstance_ = {
   6748         firstNode: null,
   6749         lastNode: null,
   6750         model: model
   6751       };
   6752 
   6753       var i = 0;
   6754       var collectTerminator = false;
   6755       for (var child = content.firstChild; child; child = child.nextSibling) {
   6756         // The terminator of the instance is the clone of the last child of the
   6757         // content. If the last child is an active template, it may produce
   6758         // instances as a result of production, so simply collecting the last
   6759         // child of the instance after it has finished producing may be wrong.
   6760         if (child.nextSibling === null)
   6761           collectTerminator = true;
   6762 
   6763         var clone = cloneAndBindInstance(child, instance, stagingDocument,
   6764                                          map.children[i++],
   6765                                          model,
   6766                                          delegate_,
   6767                                          instance.bindings_);
   6768         clone.templateInstance_ = instanceRecord;
   6769         if (collectTerminator)
   6770           instance.terminator_ = clone;
   6771       }
   6772 
   6773       instanceRecord.firstNode = instance.firstChild;
   6774       instanceRecord.lastNode = instance.lastChild;
   6775       instance.templateCreator_ = undefined;
   6776       instance.protoContent_ = undefined;
   6777       return instance;
   6778     },
   6779 
   6780     get model() {
   6781       return this.model_;
   6782     },
   6783 
   6784     set model(model) {
   6785       this.model_ = model;
   6786       ensureSetModelScheduled(this);
   6787     },
   6788 
   6789     get bindingDelegate() {
   6790       return this.delegate_ && this.delegate_.raw;
   6791     },
   6792 
   6793     refChanged_: function() {
   6794       if (!this.iterator_ || this.refContent_ === this.ref_.content)
   6795         return;
   6796 
   6797       this.refContent_ = undefined;
   6798       this.iterator_.valueChanged();
   6799       this.iterator_.updateIteratedValue(this.iterator_.getUpdatedValue());
   6800     },
   6801 
   6802     clear: function() {
   6803       this.model_ = undefined;
   6804       this.delegate_ = undefined;
   6805       if (this.bindings_ && this.bindings_.ref)
   6806         this.bindings_.ref.close()
   6807       this.refContent_ = undefined;
   6808       if (!this.iterator_)
   6809         return;
   6810       this.iterator_.valueChanged();
   6811       this.iterator_.close()
   6812       this.iterator_ = undefined;
   6813     },
   6814 
   6815     setDelegate_: function(delegate) {
   6816       this.delegate_ = delegate;
   6817       this.bindingMap_ = undefined;
   6818       if (this.iterator_) {
   6819         this.iterator_.instancePositionChangedFn_ = undefined;
   6820         this.iterator_.instanceModelFn_ = undefined;
   6821       }
   6822     },
   6823 
   6824     newDelegate_: function(bindingDelegate) {
   6825       if (!bindingDelegate)
   6826         return;
   6827 
   6828       function delegateFn(name) {
   6829         var fn = bindingDelegate && bindingDelegate[name];
   6830         if (typeof fn != 'function')
   6831           return;
   6832 
   6833         return function() {
   6834           return fn.apply(bindingDelegate, arguments);
   6835         };
   6836       }
   6837 
   6838       return {
   6839         bindingMaps: {},
   6840         raw: bindingDelegate,
   6841         prepareBinding: delegateFn('prepareBinding'),
   6842         prepareInstanceModel: delegateFn('prepareInstanceModel'),
   6843         prepareInstancePositionChanged:
   6844             delegateFn('prepareInstancePositionChanged')
   6845       };
   6846     },
   6847 
   6848     set bindingDelegate(bindingDelegate) {
   6849       if (this.delegate_) {
   6850         throw Error('Template must be cleared before a new bindingDelegate ' +
   6851                     'can be assigned');
   6852       }
   6853 
   6854       this.setDelegate_(this.newDelegate_(bindingDelegate));
   6855     },
   6856 
   6857     get ref_() {
   6858       var ref = searchRefId(this, this.getAttribute('ref'));
   6859       if (!ref)
   6860         ref = this.instanceRef_;
   6861 
   6862       if (!ref)
   6863         return this;
   6864 
   6865       var nextRef = ref.ref_;
   6866       return nextRef ? nextRef : ref;
   6867     }
   6868   });
   6869 
   6870   // Returns
   6871   //   a) undefined if there are no mustaches.
   6872   //   b) [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+] if there is at least one mustache.
   6873   function parseMustaches(s, name, node, prepareBindingFn) {
   6874     if (!s || !s.length)
   6875       return;
   6876 
   6877     var tokens;
   6878     var length = s.length;
   6879     var startIndex = 0, lastIndex = 0, endIndex = 0;
   6880     var onlyOneTime = true;
   6881     while (lastIndex < length) {
   6882       var startIndex = s.indexOf('{{', lastIndex);
   6883       var oneTimeStart = s.indexOf('[[', lastIndex);
   6884       var oneTime = false;
   6885       var terminator = '}}';
   6886 
   6887       if (oneTimeStart >= 0 &&
   6888           (startIndex < 0 || oneTimeStart < startIndex)) {
   6889         startIndex = oneTimeStart;
   6890         oneTime = true;
   6891         terminator = ']]';
   6892       }
   6893 
   6894       endIndex = startIndex < 0 ? -1 : s.indexOf(terminator, startIndex + 2);
   6895 
   6896       if (endIndex < 0) {
   6897         if (!tokens)
   6898           return;
   6899 
   6900         tokens.push(s.slice(lastIndex)); // TEXT
   6901         break;
   6902       }
   6903 
   6904       tokens = tokens || [];
   6905       tokens.push(s.slice(lastIndex, startIndex)); // TEXT
   6906       var pathString = s.slice(startIndex + 2, endIndex).trim();
   6907       tokens.push(oneTime); // ONE_TIME?
   6908       onlyOneTime = onlyOneTime && oneTime;
   6909       var delegateFn = prepareBindingFn &&
   6910                        prepareBindingFn(pathString, name, node);
   6911       // Don't try to parse the expression if there's a prepareBinding function
   6912       if (delegateFn == null) {
   6913         tokens.push(Path.get(pathString)); // PATH
   6914       } else {
   6915         tokens.push(null);
   6916       }
   6917       tokens.push(delegateFn); // DELEGATE_FN
   6918       lastIndex = endIndex + 2;
   6919     }
   6920 
   6921     if (lastIndex === length)
   6922       tokens.push(''); // TEXT
   6923 
   6924     tokens.hasOnePath = tokens.length === 5;
   6925     tokens.isSimplePath = tokens.hasOnePath &&
   6926                           tokens[0] == '' &&
   6927                           tokens[4] == '';
   6928     tokens.onlyOneTime = onlyOneTime;
   6929 
   6930     tokens.combinator = function(values) {
   6931       var newValue = tokens[0];
   6932 
   6933       for (var i = 1; i < tokens.length; i += 4) {
   6934         var value = tokens.hasOnePath ? values : values[(i - 1) / 4];
   6935         if (value !== undefined)
   6936           newValue += value;
   6937         newValue += tokens[i + 3];
   6938       }
   6939 
   6940       return newValue;
   6941     }
   6942 
   6943     return tokens;
   6944   };
   6945 
   6946   function processOneTimeBinding(name, tokens, node, model) {
   6947     if (tokens.hasOnePath) {
   6948       var delegateFn = tokens[3];
   6949       var value = delegateFn ? delegateFn(model, node, true) :
   6950                                tokens[2].getValueFrom(model);
   6951       return tokens.isSimplePath ? value : tokens.combinator(value);
   6952     }
   6953 
   6954     var values = [];
   6955     for (var i = 1; i < tokens.length; i += 4) {
   6956       var delegateFn = tokens[i + 2];
   6957       values[(i - 1) / 4] = delegateFn ? delegateFn(model, node) :
   6958           tokens[i + 1].getValueFrom(model);
   6959     }
   6960 
   6961     return tokens.combinator(values);
   6962   }
   6963 
   6964   function processSinglePathBinding(name, tokens, node, model) {
   6965     var delegateFn = tokens[3];
   6966     var observer = delegateFn ? delegateFn(model, node, false) :
   6967         new PathObserver(model, tokens[2]);
   6968 
   6969     return tokens.isSimplePath ? observer :
   6970         new ObserverTransform(observer, tokens.combinator);
   6971   }
   6972 
   6973   function processBinding(name, tokens, node, model) {
   6974     if (tokens.onlyOneTime)
   6975       return processOneTimeBinding(name, tokens, node, model);
   6976 
   6977     if (tokens.hasOnePath)
   6978       return processSinglePathBinding(name, tokens, node, model);
   6979 
   6980     var observer = new CompoundObserver();
   6981 
   6982     for (var i = 1; i < tokens.length; i += 4) {
   6983       var oneTime = tokens[i];
   6984       var delegateFn = tokens[i + 2];
   6985 
   6986       if (delegateFn) {
   6987         var value = delegateFn(model, node, oneTime);
   6988         if (oneTime)
   6989           observer.addPath(value)
   6990         else
   6991           observer.addObserver(value);
   6992         continue;
   6993       }
   6994 
   6995       var path = tokens[i + 1];
   6996       if (oneTime)
   6997         observer.addPath(path.getValueFrom(model))
   6998       else
   6999         observer.addPath(model, path);
   7000     }
   7001 
   7002     return new ObserverTransform(observer, tokens.combinator);
   7003   }
   7004 
   7005   function processBindings(node, bindings, model, instanceBindings) {
   7006     for (var i = 0; i < bindings.length; i += 2) {
   7007       var name = bindings[i]
   7008       var tokens = bindings[i + 1];
   7009       var value = processBinding(name, tokens, node, model);
   7010       var binding = node.bind(name, value, tokens.onlyOneTime);
   7011       if (binding && instanceBindings)
   7012         instanceBindings.push(binding);
   7013     }
   7014 
   7015     node.bindFinished();
   7016     if (!bindings.isTemplate)
   7017       return;
   7018 
   7019     node.model_ = model;
   7020     var iter = node.processBindingDirectives_(bindings);
   7021     if (instanceBindings && iter)
   7022       instanceBindings.push(iter);
   7023   }
   7024 
   7025   function parseWithDefault(el, name, prepareBindingFn) {
   7026     var v = el.getAttribute(name);
   7027     return parseMustaches(v == '' ? '{{}}' : v, name, el, prepareBindingFn);
   7028   }
   7029 
   7030   function parseAttributeBindings(element, prepareBindingFn) {
   7031     assert(element);
   7032 
   7033     var bindings = [];
   7034     var ifFound = false;
   7035     var bindFound = false;
   7036 
   7037     for (var i = 0; i < element.attributes.length; i++) {
   7038       var attr = element.attributes[i];
   7039       var name = attr.name;
   7040       var value = attr.value;
   7041 
   7042       // Allow bindings expressed in attributes to be prefixed with underbars.
   7043       // We do this to allow correct semantics for browsers that don't implement
   7044       // <template> where certain attributes might trigger side-effects -- and
   7045       // for IE which sanitizes certain attributes, disallowing mustache
   7046       // replacements in their text.
   7047       while (name[0] === '_') {
   7048         name = name.substring(1);
   7049       }
   7050 
   7051       if (isTemplate(element) &&
   7052           (name === IF || name === BIND || name === REPEAT)) {
   7053         continue;
   7054       }
   7055 
   7056       var tokens = parseMustaches(value, name, element,
   7057                                   prepareBindingFn);
   7058       if (!tokens)
   7059         continue;
   7060 
   7061       bindings.push(name, tokens);
   7062     }
   7063 
   7064     if (isTemplate(element)) {
   7065       bindings.isTemplate = true;
   7066       bindings.if = parseWithDefault(element, IF, prepareBindingFn);
   7067       bindings.bind = parseWithDefault(element, BIND, prepareBindingFn);
   7068       bindings.repeat = parseWithDefault(element, REPEAT, prepareBindingFn);
   7069 
   7070       if (bindings.if && !bindings.bind && !bindings.repeat)
   7071         bindings.bind = parseMustaches('{{}}', BIND, element, prepareBindingFn);
   7072     }
   7073 
   7074     return bindings;
   7075   }
   7076 
   7077   function getBindings(node, prepareBindingFn) {
   7078     if (node.nodeType === Node.ELEMENT_NODE)
   7079       return parseAttributeBindings(node, prepareBindingFn);
   7080 
   7081     if (node.nodeType === Node.TEXT_NODE) {
   7082       var tokens = parseMustaches(node.data, 'textContent', node,
   7083                                   prepareBindingFn);
   7084       if (tokens)
   7085         return ['textContent', tokens];
   7086     }
   7087 
   7088     return [];
   7089   }
   7090 
   7091   function cloneAndBindInstance(node, parent, stagingDocument, bindings, model,
   7092                                 delegate,
   7093                                 instanceBindings,
   7094                                 instanceRecord) {
   7095     var clone = parent.appendChild(stagingDocument.importNode(node, false));
   7096 
   7097     var i = 0;
   7098     for (var child = node.firstChild; child; child = child.nextSibling) {
   7099       cloneAndBindInstance(child, clone, stagingDocument,
   7100                             bindings.children[i++],
   7101                             model,
   7102                             delegate,
   7103                             instanceBindings);
   7104     }
   7105 
   7106     if (bindings.isTemplate) {
   7107       HTMLTemplateElement.decorate(clone, node);
   7108       if (delegate)
   7109         clone.setDelegate_(delegate);
   7110     }
   7111 
   7112     processBindings(clone, bindings, model, instanceBindings);
   7113     return clone;
   7114   }
   7115 
   7116   function createInstanceBindingMap(node, prepareBindingFn) {
   7117     var map = getBindings(node, prepareBindingFn);
   7118     map.children = {};
   7119     var index = 0;
   7120     for (var child = node.firstChild; child; child = child.nextSibling) {
   7121       map.children[index++] = createInstanceBindingMap(child, prepareBindingFn);
   7122     }
   7123 
   7124     return map;
   7125   }
   7126 
   7127   var contentUidCounter = 1;
   7128 
   7129   // TODO(rafaelw): Setup a MutationObserver on content which clears the id
   7130   // so that bindingMaps regenerate when the template.content changes.
   7131   function getContentUid(content) {
   7132     var id = content.id_;
   7133     if (!id)
   7134       id = content.id_ = contentUidCounter++;
   7135     return id;
   7136   }
   7137 
   7138   // Each delegate is associated with a set of bindingMaps, one for each
   7139   // content which may be used by a template. The intent is that each binding
   7140   // delegate gets the opportunity to prepare the instance (via the prepare*
   7141   // delegate calls) once across all uses.
   7142   // TODO(rafaelw): Separate out the parse map from the binding map. In the
   7143   // current implementation, if two delegates need a binding map for the same
   7144   // content, the second will have to reparse.
   7145   function getInstanceBindingMap(content, delegate_) {
   7146     var contentId = getContentUid(content);
   7147     if (delegate_) {
   7148       var map = delegate_.bindingMaps[contentId];
   7149       if (!map) {
   7150         map = delegate_.bindingMaps[contentId] =
   7151             createInstanceBindingMap(content, delegate_.prepareBinding) || [];
   7152       }
   7153       return map;
   7154     }
   7155 
   7156     var map = content.bindingMap_;
   7157     if (!map) {
   7158       map = content.bindingMap_ =
   7159           createInstanceBindingMap(content, undefined) || [];
   7160     }
   7161     return map;
   7162   }
   7163 
   7164   Object.defineProperty(Node.prototype, 'templateInstance', {
   7165     get: function() {
   7166       var instance = this.templateInstance_;
   7167       return instance ? instance :
   7168           (this.parentNode ? this.parentNode.templateInstance : undefined);
   7169     }
   7170   });
   7171 
   7172   var emptyInstance = document.createDocumentFragment();
   7173   emptyInstance.bindings_ = [];
   7174   emptyInstance.terminator_ = null;
   7175 
   7176   function TemplateIterator(templateElement) {
   7177     this.closed = false;
   7178     this.templateElement_ = templateElement;
   7179     this.instances = [];
   7180     this.deps = undefined;
   7181     this.iteratedValue = [];
   7182     this.presentValue = undefined;
   7183     this.arrayObserver = undefined;
   7184   }
   7185 
   7186   TemplateIterator.prototype = {
   7187     closeDeps: function() {
   7188       var deps = this.deps;
   7189       if (deps) {
   7190         if (deps.ifOneTime === false)
   7191           deps.ifValue.close();
   7192         if (deps.oneTime === false)
   7193           deps.value.close();
   7194       }
   7195     },
   7196 
   7197     updateDependencies: function(directives, model) {
   7198       this.closeDeps();
   7199 
   7200       var deps = this.deps = {};
   7201       var template = this.templateElement_;
   7202 
   7203       var ifValue = true;
   7204       if (directives.if) {
   7205         deps.hasIf = true;
   7206         deps.ifOneTime = directives.if.onlyOneTime;
   7207         deps.ifValue = processBinding(IF, directives.if, template, model);
   7208 
   7209         ifValue = deps.ifValue;
   7210 
   7211         // oneTime if & predicate is false. nothing else to do.
   7212         if (deps.ifOneTime && !ifValue) {
   7213           this.valueChanged();
   7214           return;
   7215         }
   7216 
   7217         if (!deps.ifOneTime)
   7218           ifValue = ifValue.open(this.updateIfValue, this);
   7219       }
   7220 
   7221       if (directives.repeat) {
   7222         deps.repeat = true;
   7223         deps.oneTime = directives.repeat.onlyOneTime;
   7224         deps.value = processBinding(REPEAT, directives.repeat, template, model);
   7225       } else {
   7226         deps.repeat = false;
   7227         deps.oneTime = directives.bind.onlyOneTime;
   7228         deps.value = processBinding(BIND, directives.bind, template, model);
   7229       }
   7230 
   7231       var value = deps.value;
   7232       if (!deps.oneTime)
   7233         value = value.open(this.updateIteratedValue, this);
   7234 
   7235       if (!ifValue) {
   7236         this.valueChanged();
   7237         return;
   7238       }
   7239 
   7240       this.updateValue(value);
   7241     },
   7242 
   7243     /**
   7244      * Gets the updated value of the bind/repeat. This can potentially call
   7245      * user code (if a bindingDelegate is set up) so we try to avoid it if we
   7246      * already have the value in hand (from Observer.open).
   7247      */
   7248     getUpdatedValue: function() {
   7249       var value = this.deps.value;
   7250       if (!this.deps.oneTime)
   7251         value = value.discardChanges();
   7252       return value;
   7253     },
   7254 
   7255     updateIfValue: function(ifValue) {
   7256       if (!ifValue) {
   7257         this.valueChanged();
   7258         return;
   7259       }
   7260 
   7261       this.updateValue(this.getUpdatedValue());
   7262     },
   7263 
   7264     updateIteratedValue: function(value) {
   7265       if (this.deps.hasIf) {
   7266         var ifValue = this.deps.ifValue;
   7267         if (!this.deps.ifOneTime)
   7268           ifValue = ifValue.discardChanges();
   7269         if (!ifValue) {
   7270           this.valueChanged();
   7271           return;
   7272         }
   7273       }
   7274 
   7275       this.updateValue(value);
   7276     },
   7277 
   7278     updateValue: function(value) {
   7279       if (!this.deps.repeat)
   7280         value = [value];
   7281       var observe = this.deps.repeat &&
   7282                     !this.deps.oneTime &&
   7283                     Array.isArray(value);
   7284       this.valueChanged(value, observe);
   7285     },
   7286 
   7287     valueChanged: function(value, observeValue) {
   7288       if (!Array.isArray(value))
   7289         value = [];
   7290 
   7291       if (value === this.iteratedValue)
   7292         return;
   7293 
   7294       this.unobserve();
   7295       this.presentValue = value;
   7296       if (observeValue) {
   7297         this.arrayObserver = new ArrayObserver(this.presentValue);
   7298         this.arrayObserver.open(this.handleSplices, this);
   7299       }
   7300 
   7301       this.handleSplices(ArrayObserver.calculateSplices(this.presentValue,
   7302                                                         this.iteratedValue));
   7303     },
   7304 
   7305     getLastInstanceNode: function(index) {
   7306       if (index == -1)
   7307         return this.templateElement_;
   7308       var instance = this.instances[index];
   7309       var terminator = instance.terminator_;
   7310       if (!terminator)
   7311         return this.getLastInstanceNode(index - 1);
   7312 
   7313       if (terminator.nodeType !== Node.ELEMENT_NODE ||
   7314           this.templateElement_ === terminator) {
   7315         return terminator;
   7316       }
   7317 
   7318       var subtemplateIterator = terminator.iterator_;
   7319       if (!subtemplateIterator)
   7320         return terminator;
   7321 
   7322       return subtemplateIterator.getLastTemplateNode();
   7323     },
   7324 
   7325     getLastTemplateNode: function() {
   7326       return this.getLastInstanceNode(this.instances.length - 1);
   7327     },
   7328 
   7329     insertInstanceAt: function(index, fragment) {
   7330       var previousInstanceLast = this.getLastInstanceNode(index - 1);
   7331       var parent = this.templateElement_.parentNode;
   7332       this.instances.splice(index, 0, fragment);
   7333 
   7334       parent.insertBefore(fragment, previousInstanceLast.nextSibling);
   7335     },
   7336 
   7337     extractInstanceAt: function(index) {
   7338       var previousInstanceLast = this.getLastInstanceNode(index - 1);
   7339       var lastNode = this.getLastInstanceNode(index);
   7340       var parent = this.templateElement_.parentNode;
   7341       var instance = this.instances.splice(index, 1)[0];
   7342 
   7343       while (lastNode !== previousInstanceLast) {
   7344         var node = previousInstanceLast.nextSibling;
   7345         if (node == lastNode)
   7346           lastNode = previousInstanceLast;
   7347 
   7348         instance.appendChild(parent.removeChild(node));
   7349       }
   7350 
   7351       return instance;
   7352     },
   7353 
   7354     getDelegateFn: function(fn) {
   7355       fn = fn && fn(this.templateElement_);
   7356       return typeof fn === 'function' ? fn : null;
   7357     },
   7358 
   7359     handleSplices: function(splices) {
   7360       if (this.closed || !splices.length)
   7361         return;
   7362 
   7363       var template = this.templateElement_;
   7364 
   7365       if (!template.parentNode) {
   7366         this.close();
   7367         return;
   7368       }
   7369 
   7370       ArrayObserver.applySplices(this.iteratedValue, this.presentValue,
   7371                                  splices);
   7372 
   7373       var delegate = template.delegate_;
   7374       if (this.instanceModelFn_ === undefined) {
   7375         this.instanceModelFn_ =
   7376             this.getDelegateFn(delegate && delegate.prepareInstanceModel);
   7377       }
   7378 
   7379       if (this.instancePositionChangedFn_ === undefined) {
   7380         this.instancePositionChangedFn_ =
   7381             this.getDelegateFn(delegate &&
   7382                                delegate.prepareInstancePositionChanged);
   7383       }
   7384 
   7385       // Instance Removals
   7386       var instanceCache = new Map;
   7387       var removeDelta = 0;
   7388       for (var i = 0; i < splices.length; i++) {
   7389         var splice = splices[i];
   7390         var removed = splice.removed;
   7391         for (var j = 0; j < removed.length; j++) {
   7392           var model = removed[j];
   7393           var instance = this.extractInstanceAt(splice.index + removeDelta);
   7394           if (instance !== emptyInstance) {
   7395             instanceCache.set(model, instance);
   7396           }
   7397         }
   7398 
   7399         removeDelta -= splice.addedCount;
   7400       }
   7401 
   7402       // Instance Insertions
   7403       for (var i = 0; i < splices.length; i++) {
   7404         var splice = splices[i];
   7405         var addIndex = splice.index;
   7406         for (; addIndex < splice.index + splice.addedCount; addIndex++) {
   7407           var model = this.iteratedValue[addIndex];
   7408           var instance = instanceCache.get(model);
   7409           if (instance) {
   7410             instanceCache.delete(model);
   7411           } else {
   7412             if (this.instanceModelFn_) {
   7413               model = this.instanceModelFn_(model);
   7414             }
   7415 
   7416             if (model === undefined) {
   7417               instance = emptyInstance;
   7418             } else {
   7419               instance = template.createInstance(model, undefined, delegate);
   7420             }
   7421           }
   7422 
   7423           this.insertInstanceAt(addIndex, instance);
   7424         }
   7425       }
   7426 
   7427       instanceCache.forEach(function(instance) {
   7428         this.closeInstanceBindings(instance);
   7429       }, this);
   7430 
   7431       if (this.instancePositionChangedFn_)
   7432         this.reportInstancesMoved(splices);
   7433     },
   7434 
   7435     reportInstanceMoved: function(index) {
   7436       var instance = this.instances[index];
   7437       if (instance === emptyInstance)
   7438         return;
   7439 
   7440       this.instancePositionChangedFn_(instance.templateInstance_, index);
   7441     },
   7442 
   7443     reportInstancesMoved: function(splices) {
   7444       var index = 0;
   7445       var offset = 0;
   7446       for (var i = 0; i < splices.length; i++) {
   7447         var splice = splices[i];
   7448         if (offset != 0) {
   7449           while (index < splice.index) {
   7450             this.reportInstanceMoved(index);
   7451             index++;
   7452           }
   7453         } else {
   7454           index = splice.index;
   7455         }
   7456 
   7457         while (index < splice.index + splice.addedCount) {
   7458           this.reportInstanceMoved(index);
   7459           index++;
   7460         }
   7461 
   7462         offset += splice.addedCount - splice.removed.length;
   7463       }
   7464 
   7465       if (offset == 0)
   7466         return;
   7467 
   7468       var length = this.instances.length;
   7469       while (index < length) {
   7470         this.reportInstanceMoved(index);
   7471         index++;
   7472       }
   7473     },
   7474 
   7475     closeInstanceBindings: function(instance) {
   7476       var bindings = instance.bindings_;
   7477       for (var i = 0; i < bindings.length; i++) {
   7478         bindings[i].close();
   7479       }
   7480     },
   7481 
   7482     unobserve: function() {
   7483       if (!this.arrayObserver)
   7484         return;
   7485 
   7486       this.arrayObserver.close();
   7487       this.arrayObserver = undefined;
   7488     },
   7489 
   7490     close: function() {
   7491       if (this.closed)
   7492         return;
   7493       this.unobserve();
   7494       for (var i = 0; i < this.instances.length; i++) {
   7495         this.closeInstanceBindings(this.instances[i]);
   7496       }
   7497 
   7498       this.instances.length = 0;
   7499       this.closeDeps();
   7500       this.templateElement_.iterator_ = undefined;
   7501       this.closed = true;
   7502     }
   7503   };
   7504 
   7505   // Polyfill-specific API.
   7506   HTMLTemplateElement.forAllTemplatesFrom_ = forAllTemplatesFrom;
   7507 })(this);
   7508 
   7509 (function(scope) {
   7510   'use strict';
   7511 
   7512   // feature detect for URL constructor
   7513   var hasWorkingUrl = false;
   7514   if (!scope.forceJURL) {
   7515     try {
   7516       var u = new URL('b', 'http://a');
   7517       u.pathname = 'c%20d';
   7518       hasWorkingUrl = u.href === 'http://a/c%20d';
   7519     } catch(e) {}
   7520   }
   7521 
   7522   if (hasWorkingUrl)
   7523     return;
   7524 
   7525   var relative = Object.create(null);
   7526   relative['ftp'] = 21;
   7527   relative['file'] = 0;
   7528   relative['gopher'] = 70;
   7529   relative['http'] = 80;
   7530   relative['https'] = 443;
   7531   relative['ws'] = 80;
   7532   relative['wss'] = 443;
   7533 
   7534   var relativePathDotMapping = Object.create(null);
   7535   relativePathDotMapping['%2e'] = '.';
   7536   relativePathDotMapping['.%2e'] = '..';
   7537   relativePathDotMapping['%2e.'] = '..';
   7538   relativePathDotMapping['%2e%2e'] = '..';
   7539 
   7540   function isRelativeScheme(scheme) {
   7541     return relative[scheme] !== undefined;
   7542   }
   7543 
   7544   function invalid() {
   7545     clear.call(this);
   7546     this._isInvalid = true;
   7547   }
   7548 
   7549   function IDNAToASCII(h) {
   7550     if ('' == h) {
   7551       invalid.call(this)
   7552     }
   7553     // XXX
   7554     return h.toLowerCase()
   7555   }
   7556 
   7557   function percentEscape(c) {
   7558     var unicode = c.charCodeAt(0);
   7559     if (unicode > 0x20 &&
   7560        unicode < 0x7F &&
   7561        // " # < > ? `
   7562        [0x22, 0x23, 0x3C, 0x3E, 0x3F, 0x60].indexOf(unicode) == -1
   7563       ) {
   7564       return c;
   7565     }
   7566     return encodeURIComponent(c);
   7567   }
   7568 
   7569   function percentEscapeQuery(c) {
   7570     // XXX This actually needs to encode c using encoding and then
   7571     // convert the bytes one-by-one.
   7572 
   7573     var unicode = c.charCodeAt(0);
   7574     if (unicode > 0x20 &&
   7575        unicode < 0x7F &&
   7576        // " # < > ` (do not escape '?')
   7577        [0x22, 0x23, 0x3C, 0x3E, 0x60].indexOf(unicode) == -1
   7578       ) {
   7579       return c;
   7580     }
   7581     return encodeURIComponent(c);
   7582   }
   7583 
   7584   var EOF = undefined,
   7585       ALPHA = /[a-zA-Z]/,
   7586       ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
   7587 
   7588   function parse(input, stateOverride, base) {
   7589     function err(message) {
   7590       errors.push(message)
   7591     }
   7592 
   7593     var state = stateOverride || 'scheme start',
   7594         cursor = 0,
   7595         buffer = '',
   7596         seenAt = false,
   7597         seenBracket = false,
   7598         errors = [];
   7599 
   7600     loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) {
   7601       var c = input[cursor];
   7602       switch (state) {
   7603         case 'scheme start':
   7604           if (c && ALPHA.test(c)) {
   7605             buffer += c.toLowerCase(); // ASCII-safe
   7606             state = 'scheme';
   7607           } else if (!stateOverride) {
   7608             buffer = '';
   7609             state = 'no scheme';
   7610             continue;
   7611           } else {
   7612             err('Invalid scheme.');
   7613             break loop;
   7614           }
   7615           break;
   7616 
   7617         case 'scheme':
   7618           if (c && ALPHANUMERIC.test(c)) {
   7619             buffer += c.toLowerCase(); // ASCII-safe
   7620           } else if (':' == c) {
   7621             this._scheme = buffer;
   7622             buffer = '';
   7623             if (stateOverride) {
   7624               break loop;
   7625             }
   7626             if (isRelativeScheme(this._scheme)) {
   7627               this._isRelative = true;
   7628             }
   7629             if ('file' == this._scheme) {
   7630               state = 'relative';
   7631             } else if (this._isRelative && base && base._scheme == this._scheme) {
   7632               state = 'relative or authority';
   7633             } else if (this._isRelative) {
   7634               state = 'authority first slash';
   7635             } else {
   7636               state = 'scheme data';
   7637             }
   7638           } else if (!stateOverride) {
   7639             buffer = '';
   7640             cursor = 0;
   7641             state = 'no scheme';
   7642             continue;
   7643           } else if (EOF == c) {
   7644             break loop;
   7645           } else {
   7646             err('Code point not allowed in scheme: ' + c)
   7647             break loop;
   7648           }
   7649           break;
   7650 
   7651         case 'scheme data':
   7652           if ('?' == c) {
   7653             query = '?';
   7654             state = 'query';
   7655           } else if ('#' == c) {
   7656             this._fragment = '#';
   7657             state = 'fragment';
   7658           } else {
   7659             // XXX error handling
   7660             if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
   7661               this._schemeData += percentEscape(c);
   7662             }
   7663           }
   7664           break;
   7665 
   7666         case 'no scheme':
   7667           if (!base || !(isRelativeScheme(base._scheme))) {
   7668             err('Missing scheme.');
   7669             invalid.call(this);
   7670           } else {
   7671             state = 'relative';
   7672             continue;
   7673           }
   7674           break;
   7675 
   7676         case 'relative or authority':
   7677           if ('/' == c && '/' == input[cursor+1]) {
   7678             state = 'authority ignore slashes';
   7679           } else {
   7680             err('Expected /, got: ' + c);
   7681             state = 'relative';
   7682             continue
   7683           }
   7684           break;
   7685 
   7686         case 'relative':
   7687           this._isRelative = true;
   7688           if ('file' != this._scheme)
   7689             this._scheme = base._scheme;
   7690           if (EOF == c) {
   7691             this._host = base._host;
   7692             this._port = base._port;
   7693             this._path = base._path.slice();
   7694             this._query = base._query;
   7695             break loop;
   7696           } else if ('/' == c || '\\' == c) {
   7697             if ('\\' == c)
   7698               err('\\ is an invalid code point.');
   7699             state = 'relative slash';
   7700           } else if ('?' == c) {
   7701             this._host = base._host;
   7702             this._port = base._port;
   7703             this._path = base._path.slice();
   7704             this._query = '?';
   7705             state = 'query';
   7706           } else if ('#' == c) {
   7707             this._host = base._host;
   7708             this._port = base._port;
   7709             this._path = base._path.slice();
   7710             this._query = base._query;
   7711             this._fragment = '#';
   7712             state = 'fragment';
   7713           } else {
   7714             var nextC = input[cursor+1]
   7715             var nextNextC = input[cursor+2]
   7716             if (
   7717               'file' != this._scheme || !ALPHA.test(c) ||
   7718               (nextC != ':' && nextC != '|') ||
   7719               (EOF != nextNextC && '/' != nextNextC && '\\' != nextNextC && '?' != nextNextC && '#' != nextNextC)) {
   7720               this._host = base._host;
   7721               this._port = base._port;
   7722               this._path = base._path.slice();
   7723               this._path.pop();
   7724             }
   7725             state = 'relative path';
   7726             continue;
   7727           }
   7728           break;
   7729 
   7730         case 'relative slash':
   7731           if ('/' == c || '\\' == c) {
   7732             if ('\\' == c) {
   7733               err('\\ is an invalid code point.');
   7734             }
   7735             if ('file' == this._scheme) {
   7736               state = 'file host';
   7737             } else {
   7738               state = 'authority ignore slashes';
   7739             }
   7740           } else {
   7741             if ('file' != this._scheme) {
   7742               this._host = base._host;
   7743               this._port = base._port;
   7744             }
   7745             state = 'relative path';
   7746             continue;
   7747           }
   7748           break;
   7749 
   7750         case 'authority first slash':
   7751           if ('/' == c) {
   7752             state = 'authority second slash';
   7753           } else {
   7754             err("Expected '/', got: " + c);
   7755             state = 'authority ignore slashes';
   7756             continue;
   7757           }
   7758           break;
   7759 
   7760         case 'authority second slash':
   7761           state = 'authority ignore slashes';
   7762           if ('/' != c) {
   7763             err("Expected '/', got: " + c);
   7764             continue;
   7765           }
   7766           break;
   7767 
   7768         case 'authority ignore slashes':
   7769           if ('/' != c && '\\' != c) {
   7770             state = 'authority';
   7771             continue;
   7772           } else {
   7773             err('Expected authority, got: ' + c);
   7774           }
   7775           break;
   7776 
   7777         case 'authority':
   7778           if ('@' == c) {
   7779             if (seenAt) {
   7780               err('@ already seen.');
   7781               buffer += '%40';
   7782             }
   7783             seenAt = true;
   7784             for (var i = 0; i < buffer.length; i++) {
   7785               var cp = buffer[i];
   7786               if ('\t' == cp || '\n' == cp || '\r' == cp) {
   7787                 err('Invalid whitespace in authority.');
   7788                 continue;
   7789               }
   7790               // XXX check URL code points
   7791               if (':' == cp && null === this._password) {
   7792                 this._password = '';
   7793                 continue;
   7794               }
   7795               var tempC = percentEscape(cp);
   7796               (null !== this._password) ? this._password += tempC : this._username += tempC;
   7797             }
   7798             buffer = '';
   7799           } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
   7800             cursor -= buffer.length;
   7801             buffer = '';
   7802             state = 'host';
   7803             continue;
   7804           } else {
   7805             buffer += c;
   7806           }
   7807           break;
   7808 
   7809         case 'file host':
   7810           if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
   7811             if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ':' || buffer[1] == '|')) {
   7812               state = 'relative path';
   7813             } else if (buffer.length == 0) {
   7814               state = 'relative path start';
   7815             } else {
   7816               this._host = IDNAToASCII.call(this, buffer);
   7817               buffer = '';
   7818               state = 'relative path start';
   7819             }
   7820             continue;
   7821           } else if ('\t' == c || '\n' == c || '\r' == c) {
   7822             err('Invalid whitespace in file host.');
   7823           } else {
   7824             buffer += c;
   7825           }
   7826           break;
   7827 
   7828         case 'host':
   7829         case 'hostname':
   7830           if (':' == c && !seenBracket) {
   7831             // XXX host parsing
   7832             this._host = IDNAToASCII.call(this, buffer);
   7833             buffer = '';
   7834             state = 'port';
   7835             if ('hostname' == stateOverride) {
   7836               break loop;
   7837             }
   7838           } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
   7839             this._host = IDNAToASCII.call(this, buffer);
   7840             buffer = '';
   7841             state = 'relative path start';
   7842             if (stateOverride) {
   7843               break loop;
   7844             }
   7845             continue;
   7846           } else if ('\t' != c && '\n' != c && '\r' != c) {
   7847             if ('[' == c) {
   7848               seenBracket = true;
   7849             } else if (']' == c) {
   7850               seenBracket = false;
   7851             }
   7852             buffer += c;
   7853           } else {
   7854             err('Invalid code point in host/hostname: ' + c);
   7855           }
   7856           break;
   7857 
   7858         case 'port':
   7859           if (/[0-9]/.test(c)) {
   7860             buffer += c;
   7861           } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c || stateOverride) {
   7862             if ('' != buffer) {
   7863               var temp = parseInt(buffer, 10);
   7864               if (temp != relative[this._scheme]) {
   7865                 this._port = temp + '';
   7866               }
   7867               buffer = '';
   7868             }
   7869             if (stateOverride) {
   7870               break loop;
   7871             }
   7872             state = 'relative path start';
   7873             continue;
   7874           } else if ('\t' == c || '\n' == c || '\r' == c) {
   7875             err('Invalid code point in port: ' + c);
   7876           } else {
   7877             invalid.call(this);
   7878           }
   7879           break;
   7880 
   7881         case 'relative path start':
   7882           if ('\\' == c)
   7883             err("'\\' not allowed in path.");
   7884           state = 'relative path';
   7885           if ('/' != c && '\\' != c) {
   7886             continue;
   7887           }
   7888           break;
   7889 
   7890         case 'relative path':
   7891           if (EOF == c || '/' == c || '\\' == c || (!stateOverride && ('?' == c || '#' == c))) {
   7892             if ('\\' == c) {
   7893               err('\\ not allowed in relative path.');
   7894             }
   7895             var tmp;
   7896             if (tmp = relativePathDotMapping[buffer.toLowerCase()]) {
   7897               buffer = tmp;
   7898             }
   7899             if ('..' == buffer) {
   7900               this._path.pop();
   7901               if ('/' != c && '\\' != c) {
   7902                 this._path.push('');
   7903               }
   7904             } else if ('.' == buffer && '/' != c && '\\' != c) {
   7905               this._path.push('');
   7906             } else if ('.' != buffer) {
   7907               if ('file' == this._scheme && this._path.length == 0 && buffer.length == 2 && ALPHA.test(buffer[0]) && buffer[1] == '|') {
   7908                 buffer = buffer[0] + ':';
   7909               }
   7910               this._path.push(buffer);
   7911             }
   7912             buffer = '';
   7913             if ('?' == c) {
   7914               this._query = '?';
   7915               state = 'query';
   7916             } else if ('#' == c) {
   7917               this._fragment = '#';
   7918               state = 'fragment';
   7919             }
   7920           } else if ('\t' != c && '\n' != c && '\r' != c) {
   7921             buffer += percentEscape(c);
   7922           }
   7923           break;
   7924 
   7925         case 'query':
   7926           if (!stateOverride && '#' == c) {
   7927             this._fragment = '#';
   7928             state = 'fragment';
   7929           } else if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
   7930             this._query += percentEscapeQuery(c);
   7931           }
   7932           break;
   7933 
   7934         case 'fragment':
   7935           if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
   7936             this._fragment += c;
   7937           }
   7938           break;
   7939       }
   7940 
   7941       cursor++;
   7942     }
   7943   }
   7944 
   7945   function clear() {
   7946     this._scheme = '';
   7947     this._schemeData = '';
   7948     this._username = '';
   7949     this._password = null;
   7950     this._host = '';
   7951     this._port = '';
   7952     this._path = [];
   7953     this._query = '';
   7954     this._fragment = '';
   7955     this._isInvalid = false;
   7956     this._isRelative = false;
   7957   }
   7958 
   7959   // Does not process domain names or IP addresses.
   7960   // Does not handle encoding for the query parameter.
   7961   function jURL(url, base /* , encoding */) {
   7962     if (base !== undefined && !(base instanceof jURL))
   7963       base = new jURL(String(base));
   7964 
   7965     this._url = url;
   7966     clear.call(this);
   7967 
   7968     var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, '');
   7969     // encoding = encoding || 'utf-8'
   7970 
   7971     parse.call(this, input, null, base);
   7972   }
   7973 
   7974   jURL.prototype = {
   7975     get href() {
   7976       if (this._isInvalid)
   7977         return this._url;
   7978 
   7979       var authority = '';
   7980       if ('' != this._username || null != this._password) {
   7981         authority = this._username +
   7982             (null != this._password ? ':' + this._password : '') + '@';
   7983       }
   7984 
   7985       return this.protocol +
   7986           (this._isRelative ? '//' + authority + this.host : '') +
   7987           this.pathname + this._query + this._fragment;
   7988     },
   7989     set href(href) {
   7990       clear.call(this);
   7991       parse.call(this, href);
   7992     },
   7993 
   7994     get protocol() {
   7995       return this._scheme + ':';
   7996     },
   7997     set protocol(protocol) {
   7998       if (this._isInvalid)
   7999         return;
   8000       parse.call(this, protocol + ':', 'scheme start');
   8001     },
   8002 
   8003     get host() {
   8004       return this._isInvalid ? '' : this._port ?
   8005           this._host + ':' + this._port : this._host;
   8006     },
   8007     set host(host) {
   8008       if (this._isInvalid || !this._isRelative)
   8009         return;
   8010       parse.call(this, host, 'host');
   8011     },
   8012 
   8013     get hostname() {
   8014       return this._host;
   8015     },
   8016     set hostname(hostname) {
   8017       if (this._isInvalid || !this._isRelative)
   8018         return;
   8019       parse.call(this, hostname, 'hostname');
   8020     },
   8021 
   8022     get port() {
   8023       return this._port;
   8024     },
   8025     set port(port) {
   8026       if (this._isInvalid || !this._isRelative)
   8027         return;
   8028       parse.call(this, port, 'port');
   8029     },
   8030 
   8031     get pathname() {
   8032       return this._isInvalid ? '' : this._isRelative ?
   8033           '/' + this._path.join('/') : this._schemeData;
   8034     },
   8035     set pathname(pathname) {
   8036       if (this._isInvalid || !this._isRelative)
   8037         return;
   8038       this._path = [];
   8039       parse.call(this, pathname, 'relative path start');
   8040     },
   8041 
   8042     get search() {
   8043       return this._isInvalid || !this._query || '?' == this._query ?
   8044           '' : this._query;
   8045     },
   8046     set search(search) {
   8047       if (this._isInvalid || !this._isRelative)
   8048         return;
   8049       this._query = '?';
   8050       if ('?' == search[0])
   8051         search = search.slice(1);
   8052       parse.call(this, search, 'query');
   8053     },
   8054 
   8055     get hash() {
   8056       return this._isInvalid || !this._fragment || '#' == this._fragment ?
   8057           '' : this._fragment;
   8058     },
   8059     set hash(hash) {
   8060       if (this._isInvalid)
   8061         return;
   8062       this._fragment = '#';
   8063       if ('#' == hash[0])
   8064         hash = hash.slice(1);
   8065       parse.call(this, hash, 'fragment');
   8066     },
   8067 
   8068     get origin() {
   8069       var host;
   8070       if (this._isInvalid || !this._scheme) {
   8071         return '';
   8072       }
   8073       // javascript: Gecko returns String(""), WebKit/Blink String("null")
   8074       // Gecko throws error for "data://"
   8075       // data: Gecko returns "", Blink returns "data://", WebKit returns "null"
   8076       // Gecko returns String("") for file: mailto:
   8077       // WebKit/Blink returns String("SCHEME://") for file: mailto:
   8078       switch (this._scheme) {
   8079         case 'data':
   8080         case 'file':
   8081         case 'javascript':
   8082         case 'mailto':
   8083           return 'null';
   8084       }
   8085       host = this.host;
   8086       if (!host) {
   8087         return '';
   8088       }
   8089       return this._scheme + '://' + host;
   8090     }
   8091   };
   8092 
   8093   // Copy over the static methods
   8094   var OriginalURL = scope.URL;
   8095   if (OriginalURL) {
   8096     jURL.createObjectURL = function(blob) {
   8097       // IE extension allows a second optional options argument.
   8098       // http://msdn.microsoft.com/en-us/library/ie/hh772302(v=vs.85).aspx
   8099       return OriginalURL.createObjectURL.apply(OriginalURL, arguments);
   8100     };
   8101     jURL.revokeObjectURL = function(url) {
   8102       OriginalURL.revokeObjectURL(url);
   8103     };
   8104   }
   8105 
   8106   scope.URL = jURL;
   8107 
   8108 })(this);
   8109 
   8110 (function(scope) {
   8111 
   8112 var iterations = 0;
   8113 var callbacks = [];
   8114 var twiddle = document.createTextNode('');
   8115 
   8116 function endOfMicrotask(callback) {
   8117   twiddle.textContent = iterations++;
   8118   callbacks.push(callback);
   8119 }
   8120 
   8121 function atEndOfMicrotask() {
   8122   while (callbacks.length) {
   8123     callbacks.shift()();
   8124   }
   8125 }
   8126 
   8127 new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask)
   8128   .observe(twiddle, {characterData: true})
   8129   ;
   8130 
   8131 // exports
   8132 scope.endOfMicrotask = endOfMicrotask;
   8133 // bc
   8134 Platform.endOfMicrotask = endOfMicrotask;
   8135 
   8136 })(Polymer);
   8137 
   8138 
   8139 (function(scope) {
   8140 
   8141 /**
   8142  * @class Polymer
   8143  */
   8144 
   8145 // imports
   8146 var endOfMicrotask = scope.endOfMicrotask;
   8147 
   8148 // logging
   8149 var log = window.WebComponents ? WebComponents.flags.log : {};
   8150 
   8151 // inject style sheet
   8152 var style = document.createElement('style');
   8153 style.textContent = 'template {display: none !important;} /* injected by platform.js */';
   8154 var head = document.querySelector('head');
   8155 head.insertBefore(style, head.firstChild);
   8156 
   8157 
   8158 /**
   8159  * Force any pending data changes to be observed before
   8160  * the next task. Data changes are processed asynchronously but are guaranteed
   8161  * to be processed, for example, before painting. This method should rarely be
   8162  * needed. It does nothing when Object.observe is available;
   8163  * when Object.observe is not available, Polymer automatically flushes data
   8164  * changes approximately every 1/10 second.
   8165  * Therefore, `flush` should only be used when a data mutation should be
   8166  * observed sooner than this.
   8167  *
   8168  * @method flush
   8169  */
   8170 // flush (with logging)
   8171 var flushing;
   8172 function flush() {
   8173   if (!flushing) {
   8174     flushing = true;
   8175     endOfMicrotask(function() {
   8176       flushing = false;
   8177       log.data && console.group('flush');
   8178       Platform.performMicrotaskCheckpoint();
   8179       log.data && console.groupEnd();
   8180     });
   8181   }
   8182 };
   8183 
   8184 // polling dirty checker
   8185 // flush periodically if platform does not have object observe.
   8186 if (!Observer.hasObjectObserve) {
   8187   var FLUSH_POLL_INTERVAL = 125;
   8188   window.addEventListener('WebComponentsReady', function() {
   8189     flush();
   8190     // watch document visiblity to toggle dirty-checking
   8191     var visibilityHandler = function() {
   8192       // only flush if the page is visibile
   8193       if (document.visibilityState === 'hidden') {
   8194         if (scope.flushPoll) {
   8195           clearInterval(scope.flushPoll);
   8196         }
   8197       } else {
   8198         scope.flushPoll = setInterval(flush, FLUSH_POLL_INTERVAL);
   8199       }
   8200     };
   8201     if (typeof document.visibilityState === 'string') {
   8202       document.addEventListener('visibilitychange', visibilityHandler);
   8203     }
   8204     visibilityHandler();
   8205   });
   8206 } else {
   8207   // make flush a no-op when we have Object.observe
   8208   flush = function() {};
   8209 }
   8210 
   8211 if (window.CustomElements && !CustomElements.useNative) {
   8212   var originalImportNode = Document.prototype.importNode;
   8213   Document.prototype.importNode = function(node, deep) {
   8214     var imported = originalImportNode.call(this, node, deep);
   8215     CustomElements.upgradeAll(imported);
   8216     return imported;
   8217   };
   8218 }
   8219 
   8220 // exports
   8221 scope.flush = flush;
   8222 // bc
   8223 Platform.flush = flush;
   8224 
   8225 })(window.Polymer);
   8226 
   8227 
   8228 (function(scope) {
   8229 
   8230 var urlResolver = {
   8231   resolveDom: function(root, url) {
   8232     url = url || baseUrl(root);
   8233     this.resolveAttributes(root, url);
   8234     this.resolveStyles(root, url);
   8235     // handle template.content
   8236     var templates = root.querySelectorAll('template');
   8237     if (templates) {
   8238       for (var i = 0, l = templates.length, t; (i < l) && (t = templates[i]); i++) {
   8239         if (t.content) {
   8240           this.resolveDom(t.content, url);
   8241         }
   8242       }
   8243     }
   8244   },
   8245   resolveTemplate: function(template) {
   8246     this.resolveDom(template.content, baseUrl(template));
   8247   },
   8248   resolveStyles: function(root, url) {
   8249     var styles = root.querySelectorAll('style');
   8250     if (styles) {
   8251       for (var i = 0, l = styles.length, s; (i < l) && (s = styles[i]); i++) {
   8252         this.resolveStyle(s, url);
   8253       }
   8254     }
   8255   },
   8256   resolveStyle: function(style, url) {
   8257     url = url || baseUrl(style);
   8258     style.textContent = this.resolveCssText(style.textContent, url);
   8259   },
   8260   resolveCssText: function(cssText, baseUrl, keepAbsolute) {
   8261     cssText = replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_URL_REGEXP);
   8262     return replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_IMPORT_REGEXP);
   8263   },
   8264   resolveAttributes: function(root, url) {
   8265     if (root.hasAttributes && root.hasAttributes()) {
   8266       this.resolveElementAttributes(root, url);
   8267     }
   8268     // search for attributes that host urls
   8269     var nodes = root && root.querySelectorAll(URL_ATTRS_SELECTOR);
   8270     if (nodes) {
   8271       for (var i = 0, l = nodes.length, n; (i < l) && (n = nodes[i]); i++) {
   8272         this.resolveElementAttributes(n, url);
   8273       }
   8274     }
   8275   },
   8276   resolveElementAttributes: function(node, url) {
   8277     url = url || baseUrl(node);
   8278     URL_ATTRS.forEach(function(v) {
   8279       var attr = node.attributes[v];
   8280       var value = attr && attr.value;
   8281       var replacement;
   8282       if (value && value.search(URL_TEMPLATE_SEARCH) < 0) {
   8283         if (v === 'style') {
   8284           replacement = replaceUrlsInCssText(value, url, false, CSS_URL_REGEXP);
   8285         } else {
   8286           replacement = resolveRelativeUrl(url, value);
   8287         }
   8288         attr.value = replacement;
   8289       }
   8290     });
   8291   }
   8292 };
   8293 
   8294 var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g;
   8295 var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
   8296 var URL_ATTRS = ['href', 'src', 'action', 'style', 'url'];
   8297 var URL_ATTRS_SELECTOR = '[' + URL_ATTRS.join('],[') + ']';
   8298 var URL_TEMPLATE_SEARCH = '{{.*}}';
   8299 var URL_HASH = '#';
   8300 
   8301 function baseUrl(node) {
   8302   var u = new URL(node.ownerDocument.baseURI);
   8303   u.search = '';
   8304   u.hash = '';
   8305   return u;
   8306 }
   8307 
   8308 function replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, regexp) {
   8309   return cssText.replace(regexp, function(m, pre, url, post) {
   8310     var urlPath = url.replace(/["']/g, '');
   8311     urlPath = resolveRelativeUrl(baseUrl, urlPath, keepAbsolute);
   8312     return pre + '\'' + urlPath + '\'' + post;
   8313   });
   8314 }
   8315 
   8316 function resolveRelativeUrl(baseUrl, url, keepAbsolute) {
   8317   // do not resolve '/' absolute urls
   8318   if (url && url[0] === '/') {
   8319     return url;
   8320   }
   8321   // do not resolve '#' links, they are used for routing
   8322   if (url && url[0] === '#') {
   8323     return url;
   8324   }
   8325   var u = new URL(url, baseUrl);
   8326   return keepAbsolute ? u.href : makeDocumentRelPath(u.href);
   8327 }
   8328 
   8329 function makeDocumentRelPath(url) {
   8330   var root = baseUrl(document.documentElement);
   8331   var u = new URL(url, root);
   8332   if (u.host === root.host && u.port === root.port &&
   8333       u.protocol === root.protocol) {
   8334     return makeRelPath(root, u);
   8335   } else {
   8336     return url;
   8337   }
   8338 }
   8339 
   8340 // make a relative path from source to target
   8341 function makeRelPath(sourceUrl, targetUrl) {
   8342   var source = sourceUrl.pathname;
   8343   var target = targetUrl.pathname;
   8344   var s = source.split('/');
   8345   var t = target.split('/');
   8346   while (s.length && s[0] === t[0]){
   8347     s.shift();
   8348     t.shift();
   8349   }
   8350   for (var i = 0, l = s.length - 1; i < l; i++) {
   8351     t.unshift('..');
   8352   }
   8353   // empty '#' is discarded but we need to preserve it.
   8354   var hash = (targetUrl.href.slice(-1) === URL_HASH) ? URL_HASH : targetUrl.hash;
   8355   return t.join('/') + targetUrl.search + hash;
   8356 }
   8357 
   8358 // exports
   8359 scope.urlResolver = urlResolver;
   8360 
   8361 })(Polymer);
   8362 
   8363 (function(scope) {
   8364   var endOfMicrotask = Polymer.endOfMicrotask;
   8365 
   8366   // Generic url loader
   8367   function Loader(regex) {
   8368     this.cache = Object.create(null);
   8369     this.map = Object.create(null);
   8370     this.requests = 0;
   8371     this.regex = regex;
   8372   }
   8373   Loader.prototype = {
   8374 
   8375     // TODO(dfreedm): there may be a better factoring here
   8376     // extract absolute urls from the text (full of relative urls)
   8377     extractUrls: function(text, base) {
   8378       var matches = [];
   8379       var matched, u;
   8380       while ((matched = this.regex.exec(text))) {
   8381         u = new URL(matched[1], base);
   8382         matches.push({matched: matched[0], url: u.href});
   8383       }
   8384       return matches;
   8385     },
   8386     // take a text blob, a root url, and a callback and load all the urls found within the text
   8387     // returns a map of absolute url to text
   8388     process: function(text, root, callback) {
   8389       var matches = this.extractUrls(text, root);
   8390 
   8391       // every call to process returns all the text this loader has ever received
   8392       var done = callback.bind(null, this.map);
   8393       this.fetch(matches, done);
   8394     },
   8395     // build a mapping of url -> text from matches
   8396     fetch: function(matches, callback) {
   8397       var inflight = matches.length;
   8398 
   8399       // return early if there is no fetching to be done
   8400       if (!inflight) {
   8401         return callback();
   8402       }
   8403 
   8404       // wait for all subrequests to return
   8405       var done = function() {
   8406         if (--inflight === 0) {
   8407           callback();
   8408         }
   8409       };
   8410 
   8411       // start fetching all subrequests
   8412       var m, req, url;
   8413       for (var i = 0; i < inflight; i++) {
   8414         m = matches[i];
   8415         url = m.url;
   8416         req = this.cache[url];
   8417         // if this url has already been requested, skip requesting it again
   8418         if (!req) {
   8419           req = this.xhr(url);
   8420           req.match = m;
   8421           this.cache[url] = req;
   8422         }
   8423         // wait for the request to process its subrequests
   8424         req.wait(done);
   8425       }
   8426     },
   8427     handleXhr: function(request) {
   8428       var match = request.match;
   8429       var url = match.url;
   8430 
   8431       // handle errors with an empty string
   8432       var response = request.response || request.responseText || '';
   8433       this.map[url] = response;
   8434       this.fetch(this.extractUrls(response, url), request.resolve);
   8435     },
   8436     xhr: function(url) {
   8437       this.requests++;
   8438       var request = new XMLHttpRequest();
   8439       request.open('GET', url, true);
   8440       request.send();
   8441       request.onerror = request.onload = this.handleXhr.bind(this, request);
   8442 
   8443       // queue of tasks to run after XHR returns
   8444       request.pending = [];
   8445       request.resolve = function() {
   8446         var pending = request.pending;
   8447         for(var i = 0; i < pending.length; i++) {
   8448           pending[i]();
   8449         }
   8450         request.pending = null;
   8451       };
   8452 
   8453       // if we have already resolved, pending is null, async call the callback
   8454       request.wait = function(fn) {
   8455         if (request.pending) {
   8456           request.pending.push(fn);
   8457         } else {
   8458           endOfMicrotask(fn);
   8459         }
   8460       };
   8461 
   8462       return request;
   8463     }
   8464   };
   8465 
   8466   scope.Loader = Loader;
   8467 })(Polymer);
   8468 
   8469 (function(scope) {
   8470 
   8471 var urlResolver = scope.urlResolver;
   8472 var Loader = scope.Loader;
   8473 
   8474 function StyleResolver() {
   8475   this.loader = new Loader(this.regex);
   8476 }
   8477 StyleResolver.prototype = {
   8478   regex: /@import\s+(?:url)?["'\(]*([^'"\)]*)['"\)]*;/g,
   8479   // Recursively replace @imports with the text at that url
   8480   resolve: function(text, url, callback) {
   8481     var done = function(map) {
   8482       callback(this.flatten(text, url, map));
   8483     }.bind(this);
   8484     this.loader.process(text, url, done);
   8485   },
   8486   // resolve the textContent of a style node
   8487   resolveNode: function(style, url, callback) {
   8488     var text = style.textContent;
   8489     var done = function(text) {
   8490       style.textContent = text;
   8491       callback(style);
   8492     };
   8493     this.resolve(text, url, done);
   8494   },
   8495   // flatten all the @imports to text
   8496   flatten: function(text, base, map) {
   8497     var matches = this.loader.extractUrls(text, base);
   8498     var match, url, intermediate;
   8499     for (var i = 0; i < matches.length; i++) {
   8500       match = matches[i];
   8501       url = match.url;
   8502       // resolve any css text to be relative to the importer, keep absolute url
   8503       intermediate = urlResolver.resolveCssText(map[url], url, true);
   8504       // flatten intermediate @imports
   8505       intermediate = this.flatten(intermediate, base, map);
   8506       text = text.replace(match.matched, intermediate);
   8507     }
   8508     return text;
   8509   },
   8510   loadStyles: function(styles, base, callback) {
   8511     var loaded=0, l = styles.length;
   8512     // called in the context of the style
   8513     function loadedStyle(style) {
   8514       loaded++;
   8515       if (loaded === l && callback) {
   8516         callback();
   8517       }
   8518     }
   8519     for (var i=0, s; (i<l) && (s=styles[i]); i++) {
   8520       this.resolveNode(s, base, loadedStyle);
   8521     }
   8522   }
   8523 };
   8524 
   8525 var styleResolver = new StyleResolver();
   8526 
   8527 // exports
   8528 scope.styleResolver = styleResolver;
   8529 
   8530 })(Polymer);
   8531 
   8532 (function(scope) {
   8533 
   8534   // copy own properties from 'api' to 'prototype, with name hinting for 'super'
   8535   function extend(prototype, api) {
   8536     if (prototype && api) {
   8537       // use only own properties of 'api'
   8538       Object.getOwnPropertyNames(api).forEach(function(n) {
   8539         // acquire property descriptor
   8540         var pd = Object.getOwnPropertyDescriptor(api, n);
   8541         if (pd) {
   8542           // clone property via descriptor
   8543           Object.defineProperty(prototype, n, pd);
   8544           // cache name-of-method for 'super' engine
   8545           if (typeof pd.value == 'function') {
   8546             // hint the 'super' engine
   8547             pd.value.nom = n;
   8548           }
   8549         }
   8550       });
   8551     }
   8552     return prototype;
   8553   }
   8554 
   8555 
   8556   // mixin
   8557 
   8558   // copy all properties from inProps (et al) to inObj
   8559   function mixin(inObj/*, inProps, inMoreProps, ...*/) {
   8560     var obj = inObj || {};
   8561     for (var i = 1; i < arguments.length; i++) {
   8562       var p = arguments[i];
   8563       try {
   8564         for (var n in p) {
   8565           copyProperty(n, p, obj);
   8566         }
   8567       } catch(x) {
   8568       }
   8569     }
   8570     return obj;
   8571   }
   8572 
   8573   // copy property inName from inSource object to inTarget object
   8574   function copyProperty(inName, inSource, inTarget) {
   8575     var pd = getPropertyDescriptor(inSource, inName);
   8576     Object.defineProperty(inTarget, inName, pd);
   8577   }
   8578 
   8579   // get property descriptor for inName on inObject, even if
   8580   // inName exists on some link in inObject's prototype chain
   8581   function getPropertyDescriptor(inObject, inName) {
   8582     if (inObject) {
   8583       var pd = Object.getOwnPropertyDescriptor(inObject, inName);
   8584       return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName);
   8585     }
   8586   }
   8587 
   8588   // exports
   8589 
   8590   scope.extend = extend;
   8591   scope.mixin = mixin;
   8592 
   8593   // for bc
   8594   Platform.mixin = mixin;
   8595 
   8596 })(Polymer);
   8597 
   8598 (function(scope) {
   8599 
   8600   // usage
   8601 
   8602   // invoke cb.call(this) in 100ms, unless the job is re-registered,
   8603   // which resets the timer
   8604   //
   8605   // this.myJob = this.job(this.myJob, cb, 100)
   8606   //
   8607   // returns a job handle which can be used to re-register a job
   8608 
   8609   var Job = function(inContext) {
   8610     this.context = inContext;
   8611     this.boundComplete = this.complete.bind(this)
   8612   };
   8613   Job.prototype = {
   8614     go: function(callback, wait) {
   8615       this.callback = callback;
   8616       var h;
   8617       if (!wait) {
   8618         h = requestAnimationFrame(this.boundComplete);
   8619         this.handle = function() {
   8620           cancelAnimationFrame(h);
   8621         }
   8622       } else {
   8623         h = setTimeout(this.boundComplete, wait);
   8624         this.handle = function() {
   8625           clearTimeout(h);
   8626         }
   8627       }
   8628     },
   8629     stop: function() {
   8630       if (this.handle) {
   8631         this.handle();
   8632         this.handle = null;
   8633       }
   8634     },
   8635     complete: function() {
   8636       if (this.handle) {
   8637         this.stop();
   8638         this.callback.call(this.context);
   8639       }
   8640     }
   8641   };
   8642 
   8643   function job(job, callback, wait) {
   8644     if (job) {
   8645       job.stop();
   8646     } else {
   8647       job = new Job(this);
   8648     }
   8649     job.go(callback, wait);
   8650     return job;
   8651   }
   8652 
   8653   // exports
   8654 
   8655   scope.job = job;
   8656 
   8657 })(Polymer);
   8658 
   8659 (function(scope) {
   8660 
   8661   // dom polyfill, additions, and utility methods
   8662 
   8663   var registry = {};
   8664 
   8665   HTMLElement.register = function(tag, prototype) {
   8666     registry[tag] = prototype;
   8667   };
   8668 
   8669   // get prototype mapped to node <tag>
   8670   HTMLElement.getPrototypeForTag = function(tag) {
   8671     var prototype = !tag ? HTMLElement.prototype : registry[tag];
   8672     // TODO(sjmiles): creating <tag> is likely to have wasteful side-effects
   8673     return prototype || Object.getPrototypeOf(document.createElement(tag));
   8674   };
   8675 
   8676   // we have to flag propagation stoppage for the event dispatcher
   8677   var originalStopPropagation = Event.prototype.stopPropagation;
   8678   Event.prototype.stopPropagation = function() {
   8679     this.cancelBubble = true;
   8680     originalStopPropagation.apply(this, arguments);
   8681   };
   8682 
   8683 
   8684   // polyfill DOMTokenList
   8685   // * add/remove: allow these methods to take multiple classNames
   8686   // * toggle: add a 2nd argument which forces the given state rather
   8687   //  than toggling.
   8688 
   8689   var add = DOMTokenList.prototype.add;
   8690   var remove = DOMTokenList.prototype.remove;
   8691   DOMTokenList.prototype.add = function() {
   8692     for (var i = 0; i < arguments.length; i++) {
   8693       add.call(this, arguments[i]);
   8694     }
   8695   };
   8696   DOMTokenList.prototype.remove = function() {
   8697     for (var i = 0; i < arguments.length; i++) {
   8698       remove.call(this, arguments[i]);
   8699     }
   8700   };
   8701   DOMTokenList.prototype.toggle = function(name, bool) {
   8702     if (arguments.length == 1) {
   8703       bool = !this.contains(name);
   8704     }
   8705     bool ? this.add(name) : this.remove(name);
   8706   };
   8707   DOMTokenList.prototype.switch = function(oldName, newName) {
   8708     oldName && this.remove(oldName);
   8709     newName && this.add(newName);
   8710   };
   8711 
   8712   // add array() to NodeList, NamedNodeMap, HTMLCollection
   8713 
   8714   var ArraySlice = function() {
   8715     return Array.prototype.slice.call(this);
   8716   };
   8717 
   8718   var namedNodeMap = (window.NamedNodeMap || window.MozNamedAttrMap || {});
   8719 
   8720   NodeList.prototype.array = ArraySlice;
   8721   namedNodeMap.prototype.array = ArraySlice;
   8722   HTMLCollection.prototype.array = ArraySlice;
   8723 
   8724   // utility
   8725 
   8726   function createDOM(inTagOrNode, inHTML, inAttrs) {
   8727     var dom = typeof inTagOrNode == 'string' ?
   8728         document.createElement(inTagOrNode) : inTagOrNode.cloneNode(true);
   8729     dom.innerHTML = inHTML;
   8730     if (inAttrs) {
   8731       for (var n in inAttrs) {
   8732         dom.setAttribute(n, inAttrs[n]);
   8733       }
   8734     }
   8735     return dom;
   8736   }
   8737 
   8738   // exports
   8739 
   8740   scope.createDOM = createDOM;
   8741 
   8742 })(Polymer);
   8743 
   8744 (function(scope) {
   8745     // super
   8746 
   8747     // `arrayOfArgs` is an optional array of args like one might pass
   8748     // to `Function.apply`
   8749 
   8750     // TODO(sjmiles):
   8751     //    $super must be installed on an instance or prototype chain
   8752     //    as `super`, and invoked via `this`, e.g.
   8753     //      `this.super();`
   8754 
   8755     //    will not work if function objects are not unique, for example,
   8756     //    when using mixins.
   8757     //    The memoization strategy assumes each function exists on only one
   8758     //    prototype chain i.e. we use the function object for memoizing)
   8759     //    perhaps we can bookkeep on the prototype itself instead
   8760     function $super(arrayOfArgs) {
   8761       // since we are thunking a method call, performance is important here:
   8762       // memoize all lookups, once memoized the fast path calls no other
   8763       // functions
   8764       //
   8765       // find the caller (cannot be `strict` because of 'caller')
   8766       var caller = $super.caller;
   8767       // memoized 'name of method'
   8768       var nom = caller.nom;
   8769       // memoized next implementation prototype
   8770       var _super = caller._super;
   8771       if (!_super) {
   8772         if (!nom) {
   8773           nom = caller.nom = nameInThis.call(this, caller);
   8774         }
   8775         if (!nom) {
   8776           console.warn('called super() on a method not installed declaratively (has no .nom property)');
   8777         }
   8778         // super prototype is either cached or we have to find it
   8779         // by searching __proto__ (at the 'top')
   8780         // invariant: because we cache _super on fn below, we never reach
   8781         // here from inside a series of calls to super(), so it's ok to
   8782         // start searching from the prototype of 'this' (at the 'top')
   8783         // we must never memoize a null super for this reason
   8784         _super = memoizeSuper(caller, nom, getPrototypeOf(this));
   8785       }
   8786       // our super function
   8787       var fn = _super[nom];
   8788       if (fn) {
   8789         // memoize information so 'fn' can call 'super'
   8790         if (!fn._super) {
   8791           // must not memoize null, or we lose our invariant above
   8792           memoizeSuper(fn, nom, _super);
   8793         }
   8794         // invoke the inherited method
   8795         // if 'fn' is not function valued, this will throw
   8796         return fn.apply(this, arrayOfArgs || []);
   8797       }
   8798     }
   8799 
   8800     function nameInThis(value) {
   8801       var p = this.__proto__;
   8802       while (p && p !== HTMLElement.prototype) {
   8803         // TODO(sjmiles): getOwnPropertyNames is absurdly expensive
   8804         var n$ = Object.getOwnPropertyNames(p);
   8805         for (var i=0, l=n$.length, n; i<l && (n=n$[i]); i++) {
   8806           var d = Object.getOwnPropertyDescriptor(p, n);
   8807           if (typeof d.value === 'function' && d.value === value) {
   8808             return n;
   8809           }
   8810         }
   8811         p = p.__proto__;
   8812       }
   8813     }
   8814 
   8815     function memoizeSuper(method, name, proto) {
   8816       // find and cache next prototype containing `name`
   8817       // we need the prototype so we can do another lookup
   8818       // from here
   8819       var s = nextSuper(proto, name, method);
   8820       if (s[name]) {
   8821         // `s` is a prototype, the actual method is `s[name]`
   8822         // tag super method with it's name for quicker lookups
   8823         s[name].nom = name;
   8824       }
   8825       return method._super = s;
   8826     }
   8827 
   8828     function nextSuper(proto, name, caller) {
   8829       // look for an inherited prototype that implements name
   8830       while (proto) {
   8831         if ((proto[name] !== caller) && proto[name]) {
   8832           return proto;
   8833         }
   8834         proto = getPrototypeOf(proto);
   8835       }
   8836       // must not return null, or we lose our invariant above
   8837       // in this case, a super() call was invoked where no superclass
   8838       // method exists
   8839       // TODO(sjmiles): thow an exception?
   8840       return Object;
   8841     }
   8842 
   8843     // NOTE: In some platforms (IE10) the prototype chain is faked via
   8844     // __proto__. Therefore, always get prototype via __proto__ instead of
   8845     // the more standard Object.getPrototypeOf.
   8846     function getPrototypeOf(prototype) {
   8847       return prototype.__proto__;
   8848     }
   8849 
   8850     // utility function to precompute name tags for functions
   8851     // in a (unchained) prototype
   8852     function hintSuper(prototype) {
   8853       // tag functions with their prototype name to optimize
   8854       // super call invocations
   8855       for (var n in prototype) {
   8856         var pd = Object.getOwnPropertyDescriptor(prototype, n);
   8857         if (pd && typeof pd.value === 'function') {
   8858           pd.value.nom = n;
   8859         }
   8860       }
   8861     }
   8862 
   8863     // exports
   8864 
   8865     scope.super = $super;
   8866 
   8867 })(Polymer);
   8868 
   8869 (function(scope) {
   8870 
   8871   function noopHandler(value) {
   8872     return value;
   8873   }
   8874 
   8875   // helper for deserializing properties of various types to strings
   8876   var typeHandlers = {
   8877     string: noopHandler,
   8878     'undefined': noopHandler,
   8879     date: function(value) {
   8880       return new Date(Date.parse(value) || Date.now());
   8881     },
   8882     boolean: function(value) {
   8883       if (value === '') {
   8884         return true;
   8885       }
   8886       return value === 'false' ? false : !!value;
   8887     },
   8888     number: function(value) {
   8889       var n = parseFloat(value);
   8890       // hex values like "0xFFFF" parseFloat as 0
   8891       if (n === 0) {
   8892         n = parseInt(value);
   8893       }
   8894       return isNaN(n) ? value : n;
   8895       // this code disabled because encoded values (like "0xFFFF")
   8896       // do not round trip to their original format
   8897       //return (String(floatVal) === value) ? floatVal : value;
   8898     },
   8899     object: function(value, currentValue) {
   8900       if (currentValue === null) {
   8901         return value;
   8902       }
   8903       try {
   8904         // If the string is an object, we can parse is with the JSON library.
   8905         // include convenience replace for single-quotes. If the author omits
   8906         // quotes altogether, parse will fail.
   8907         return JSON.parse(value.replace(/'/g, '"'));
   8908       } catch(e) {
   8909         // The object isn't valid JSON, return the raw value
   8910         return value;
   8911       }
   8912     },
   8913     // avoid deserialization of functions
   8914     'function': function(value, currentValue) {
   8915       return currentValue;
   8916     }
   8917   };
   8918 
   8919   function deserializeValue(value, currentValue) {
   8920     // attempt to infer type from default value
   8921     var inferredType = typeof currentValue;
   8922     // invent 'date' type value for Date
   8923     if (currentValue instanceof Date) {
   8924       inferredType = 'date';
   8925     }
   8926     // delegate deserialization via type string
   8927     return typeHandlers[inferredType](value, currentValue);
   8928   }
   8929 
   8930   // exports
   8931 
   8932   scope.deserializeValue = deserializeValue;
   8933 
   8934 })(Polymer);
   8935 
   8936 (function(scope) {
   8937 
   8938   // imports
   8939 
   8940   var extend = scope.extend;
   8941 
   8942   // module
   8943 
   8944   var api = {};
   8945 
   8946   api.declaration = {};
   8947   api.instance = {};
   8948 
   8949   api.publish = function(apis, prototype) {
   8950     for (var n in apis) {
   8951       extend(prototype, apis[n]);
   8952     }
   8953   };
   8954 
   8955   // exports
   8956 
   8957   scope.api = api;
   8958 
   8959 })(Polymer);
   8960 
   8961 (function(scope) {
   8962 
   8963   /**
   8964    * @class polymer-base
   8965    */
   8966 
   8967   var utils = {
   8968 
   8969     /**
   8970       * Invokes a function asynchronously. The context of the callback
   8971       * function is bound to 'this' automatically. Returns a handle which may
   8972       * be passed to <a href="#cancelAsync">cancelAsync</a> to cancel the
   8973       * asynchronous call.
   8974       *
   8975       * @method async
   8976       * @param {Function|String} method
   8977       * @param {any|Array} args
   8978       * @param {number} timeout
   8979       */
   8980     async: function(method, args, timeout) {
   8981       // when polyfilling Object.observe, ensure changes
   8982       // propagate before executing the async method
   8983       Polymer.flush();
   8984       // second argument to `apply` must be an array
   8985       args = (args && args.length) ? args : [args];
   8986       // function to invoke
   8987       var fn = function() {
   8988         (this[method] || method).apply(this, args);
   8989       }.bind(this);
   8990       // execute `fn` sooner or later
   8991       var handle = timeout ? setTimeout(fn, timeout) :
   8992           requestAnimationFrame(fn);
   8993       // NOTE: switch on inverting handle to determine which time is used.
   8994       return timeout ? handle : ~handle;
   8995     },
   8996 
   8997     /**
   8998       * Cancels a pending callback that was scheduled via
   8999       * <a href="#async">async</a>.
   9000       *
   9001       * @method cancelAsync
   9002       * @param {handle} handle Handle of the `async` to cancel.
   9003       */
   9004     cancelAsync: function(handle) {
   9005       if (handle < 0) {
   9006         cancelAnimationFrame(~handle);
   9007       } else {
   9008         clearTimeout(handle);
   9009       }
   9010     },
   9011 
   9012     /**
   9013       * Fire an event.
   9014       *
   9015       * @method fire
   9016       * @returns {Object} event
   9017       * @param {string} type An event name.
   9018       * @param {any} detail
   9019       * @param {Node} onNode Target node.
   9020       * @param {Boolean} bubbles Set false to prevent bubbling, defaults to true
   9021       * @param {Boolean} cancelable Set false to prevent cancellation, defaults to true
   9022       */
   9023     fire: function(type, detail, onNode, bubbles, cancelable) {
   9024       var node = onNode || this;
   9025       var detail = detail === null || detail === undefined ? {} : detail;
   9026       var event = new CustomEvent(type, {
   9027         bubbles: bubbles !== undefined ? bubbles : true,
   9028         cancelable: cancelable !== undefined ? cancelable : true,
   9029         detail: detail
   9030       });
   9031       node.dispatchEvent(event);
   9032       return event;
   9033     },
   9034 
   9035     /**
   9036       * Fire an event asynchronously.
   9037       *
   9038       * @method asyncFire
   9039       * @param {string} type An event name.
   9040       * @param detail
   9041       * @param {Node} toNode Target node.
   9042       */
   9043     asyncFire: function(/*inType, inDetail*/) {
   9044       this.async("fire", arguments);
   9045     },
   9046 
   9047     /**
   9048       * Remove class from old, add class to anew, if they exist.
   9049       *
   9050       * @param classFollows
   9051       * @param anew A node.
   9052       * @param old A node
   9053       * @param className
   9054       */
   9055     classFollows: function(anew, old, className) {
   9056       if (old) {
   9057         old.classList.remove(className);
   9058       }
   9059       if (anew) {
   9060         anew.classList.add(className);
   9061       }
   9062     },
   9063 
   9064     /**
   9065       * Inject HTML which contains markup bound to this element into
   9066       * a target element (replacing target element content).
   9067       *
   9068       * @param String html to inject
   9069       * @param Element target element
   9070       */
   9071     injectBoundHTML: function(html, element) {
   9072       var template = document.createElement('template');
   9073       template.innerHTML = html;
   9074       var fragment = this.instanceTemplate(template);
   9075       if (element) {
   9076         element.textContent = '';
   9077         element.appendChild(fragment);
   9078       }
   9079       return fragment;
   9080     }
   9081   };
   9082 
   9083   // no-operation function for handy stubs
   9084   var nop = function() {};
   9085 
   9086   // null-object for handy stubs
   9087   var nob = {};
   9088 
   9089   // deprecated
   9090 
   9091   utils.asyncMethod = utils.async;
   9092 
   9093   // exports
   9094 
   9095   scope.api.instance.utils = utils;
   9096   scope.nop = nop;
   9097   scope.nob = nob;
   9098 
   9099 })(Polymer);
   9100 
   9101 (function(scope) {
   9102 
   9103   // imports
   9104 
   9105   var log = window.WebComponents ? WebComponents.flags.log : {};
   9106   var EVENT_PREFIX = 'on-';
   9107 
   9108   // instance events api
   9109   var events = {
   9110     // read-only
   9111     EVENT_PREFIX: EVENT_PREFIX,
   9112     // event listeners on host
   9113     addHostListeners: function() {
   9114       var events = this.eventDelegates;
   9115       log.events && (Object.keys(events).length > 0) && console.log('[%s] addHostListeners:', this.localName, events);
   9116       // NOTE: host events look like bindings but really are not;
   9117       // (1) we don't want the attribute to be set and (2) we want to support
   9118       // multiple event listeners ('host' and 'instance') and Node.bind
   9119       // by default supports 1 thing being bound.
   9120       for (var type in events) {
   9121         var methodName = events[type];
   9122         PolymerGestures.addEventListener(this, type, this.element.getEventHandler(this, this, methodName));
   9123       }
   9124     },
   9125     // call 'method' or function method on 'obj' with 'args', if the method exists
   9126     dispatchMethod: function(obj, method, args) {
   9127       if (obj) {
   9128         log.events && console.group('[%s] dispatch [%s]', obj.localName, method);
   9129         var fn = typeof method === 'function' ? method : obj[method];
   9130         if (fn) {
   9131           fn[args ? 'apply' : 'call'](obj, args);
   9132         }
   9133         log.events && console.groupEnd();
   9134         // NOTE: dirty check right after calling method to ensure
   9135         // changes apply quickly; in a very complicated app using high
   9136         // frequency events, this can be a perf concern; in this case,
   9137         // imperative handlers can be used to avoid flushing.
   9138         Polymer.flush();
   9139       }
   9140     }
   9141   };
   9142 
   9143   // exports
   9144 
   9145   scope.api.instance.events = events;
   9146 
   9147   /**
   9148    * @class Polymer
   9149    */
   9150 
   9151   /**
   9152    * Add a gesture aware event handler to the given `node`. Can be used
   9153    * in place of `element.addEventListener` and ensures gestures will function
   9154    * as expected on mobile platforms. Please note that Polymer's declarative
   9155    * event handlers include this functionality by default.
   9156    *
   9157    * @method addEventListener
   9158    * @param {Node} node node on which to listen
   9159    * @param {String} eventType name of the event
   9160    * @param {Function} handlerFn event handler function
   9161    * @param {Boolean} capture set to true to invoke event capturing
   9162    * @type Function
   9163    */
   9164   // alias PolymerGestures event listener logic
   9165   scope.addEventListener = function(node, eventType, handlerFn, capture) {
   9166     PolymerGestures.addEventListener(wrap(node), eventType, handlerFn, capture);
   9167   };
   9168 
   9169   /**
   9170    * Remove a gesture aware event handler on the given `node`. To remove an
   9171    * event listener, the exact same arguments are required that were passed
   9172    * to `Polymer.addEventListener`.
   9173    *
   9174    * @method removeEventListener
   9175    * @param {Node} node node on which to listen
   9176    * @param {String} eventType name of the event
   9177    * @param {Function} handlerFn event handler function
   9178    * @param {Boolean} capture set to true to invoke event capturing
   9179    * @type Function
   9180    */
   9181   scope.removeEventListener = function(node, eventType, handlerFn, capture) {
   9182     PolymerGestures.removeEventListener(wrap(node), eventType, handlerFn, capture);
   9183   };
   9184 
   9185 })(Polymer);
   9186 
   9187 (function(scope) {
   9188 
   9189   // instance api for attributes
   9190 
   9191   var attributes = {
   9192     // copy attributes defined in the element declaration to the instance
   9193     // e.g. <polymer-element name="x-foo" tabIndex="0"> tabIndex is copied
   9194     // to the element instance here.
   9195     copyInstanceAttributes: function () {
   9196       var a$ = this._instanceAttributes;
   9197       for (var k in a$) {
   9198         if (!this.hasAttribute(k)) {
   9199           this.setAttribute(k, a$[k]);
   9200         }
   9201       }
   9202     },
   9203     // for each attribute on this, deserialize value to property as needed
   9204     takeAttributes: function() {
   9205       // if we have no publish lookup table, we have no attributes to take
   9206       // TODO(sjmiles): ad hoc
   9207       if (this._publishLC) {
   9208         for (var i=0, a$=this.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++) {
   9209           this.attributeToProperty(a.name, a.value);
   9210         }
   9211       }
   9212     },
   9213     // if attribute 'name' is mapped to a property, deserialize
   9214     // 'value' into that property
   9215     attributeToProperty: function(name, value) {
   9216       // try to match this attribute to a property (attributes are
   9217       // all lower-case, so this is case-insensitive search)
   9218       var name = this.propertyForAttribute(name);
   9219       if (name) {
   9220         // filter out 'mustached' values, these are to be
   9221         // replaced with bound-data and are not yet values
   9222         // themselves
   9223         if (value && value.search(scope.bindPattern) >= 0) {
   9224           return;
   9225         }
   9226         // get original value
   9227         var currentValue = this[name];
   9228         // deserialize Boolean or Number values from attribute
   9229         var value = this.deserializeValue(value, currentValue);
   9230         // only act if the value has changed
   9231         if (value !== currentValue) {
   9232           // install new value (has side-effects)
   9233           this[name] = value;
   9234         }
   9235       }
   9236     },
   9237     // return the published property matching name, or undefined
   9238     propertyForAttribute: function(name) {
   9239       var match = this._publishLC && this._publishLC[name];
   9240       return match;
   9241     },
   9242     // convert representation of `stringValue` based on type of `currentValue`
   9243     deserializeValue: function(stringValue, currentValue) {
   9244       return scope.deserializeValue(stringValue, currentValue);
   9245     },
   9246     // convert to a string value based on the type of `inferredType`
   9247     serializeValue: function(value, inferredType) {
   9248       if (inferredType === 'boolean') {
   9249         return value ? '' : undefined;
   9250       } else if (inferredType !== 'object' && inferredType !== 'function'
   9251           && value !== undefined) {
   9252         return value;
   9253       }
   9254     },
   9255     // serializes `name` property value and updates the corresponding attribute
   9256     // note that reflection is opt-in.
   9257     reflectPropertyToAttribute: function(name) {
   9258       var inferredType = typeof this[name];
   9259       // try to intelligently serialize property value
   9260       var serializedValue = this.serializeValue(this[name], inferredType);
   9261       // boolean properties must reflect as boolean attributes
   9262       if (serializedValue !== undefined) {
   9263         this.setAttribute(name, serializedValue);
   9264         // TODO(sorvell): we should remove attr for all properties
   9265         // that have undefined serialization; however, we will need to
   9266         // refine the attr reflection system to achieve this; pica, for example,
   9267         // relies on having inferredType object properties not removed as
   9268         // attrs.
   9269       } else if (inferredType === 'boolean') {
   9270         this.removeAttribute(name);
   9271       }
   9272     }
   9273   };
   9274 
   9275   // exports
   9276 
   9277   scope.api.instance.attributes = attributes;
   9278 
   9279 })(Polymer);
   9280 
   9281 (function(scope) {
   9282 
   9283   /**
   9284    * @class polymer-base
   9285    */
   9286 
   9287   // imports
   9288 
   9289   var log = window.WebComponents ? WebComponents.flags.log : {};
   9290 
   9291   // magic words
   9292 
   9293   var OBSERVE_SUFFIX = 'Changed';
   9294 
   9295   // element api
   9296 
   9297   var empty = [];
   9298 
   9299   var updateRecord = {
   9300     object: undefined,
   9301     type: 'update',
   9302     name: undefined,
   9303     oldValue: undefined
   9304   };
   9305 
   9306   var numberIsNaN = Number.isNaN || function(value) {
   9307     return typeof value === 'number' && isNaN(value);
   9308   };
   9309 
   9310   function areSameValue(left, right) {
   9311     if (left === right)
   9312       return left !== 0 || 1 / left === 1 / right;
   9313     if (numberIsNaN(left) && numberIsNaN(right))
   9314       return true;
   9315     return left !== left && right !== right;
   9316   }
   9317 
   9318   // capture A's value if B's value is null or undefined,
   9319   // otherwise use B's value
   9320   function resolveBindingValue(oldValue, value) {
   9321     if (value === undefined && oldValue === null) {
   9322       return value;
   9323     }
   9324     return (value === null || value === undefined) ? oldValue : value;
   9325   }
   9326 
   9327   var properties = {
   9328 
   9329     // creates a CompoundObserver to observe property changes
   9330     // NOTE, this is only done there are any properties in the `observe` object
   9331     createPropertyObserver: function() {
   9332       var n$ = this._observeNames;
   9333       if (n$ && n$.length) {
   9334         var o = this._propertyObserver = new CompoundObserver(true);
   9335         this.registerObserver(o);
   9336         // TODO(sorvell): may not be kosher to access the value here (this[n]);
   9337         // previously we looked at the descriptor on the prototype
   9338         // this doesn't work for inheritance and not for accessors without
   9339         // a value property
   9340         for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
   9341           o.addPath(this, n);
   9342           this.observeArrayValue(n, this[n], null);
   9343         }
   9344       }
   9345     },
   9346 
   9347     // start observing property changes
   9348     openPropertyObserver: function() {
   9349       if (this._propertyObserver) {
   9350         this._propertyObserver.open(this.notifyPropertyChanges, this);
   9351       }
   9352     },
   9353 
   9354     // handler for property changes; routes changes to observing methods
   9355     // note: array valued properties are observed for array splices
   9356     notifyPropertyChanges: function(newValues, oldValues, paths) {
   9357       var name, method, called = {};
   9358       for (var i in oldValues) {
   9359         // note: paths is of form [object, path, object, path]
   9360         name = paths[2 * i + 1];
   9361         method = this.observe[name];
   9362         if (method) {
   9363           var ov = oldValues[i], nv = newValues[i];
   9364           // observes the value if it is an array
   9365           this.observeArrayValue(name, nv, ov);
   9366           if (!called[method]) {
   9367             // only invoke change method if one of ov or nv is not (undefined | null)
   9368             if ((ov !== undefined && ov !== null) || (nv !== undefined && nv !== null)) {
   9369               called[method] = true;
   9370               // TODO(sorvell): call method with the set of values it's expecting;
   9371               // e.g. 'foo bar': 'invalidate' expects the new and old values for
   9372               // foo and bar. Currently we give only one of these and then
   9373               // deliver all the arguments.
   9374               this.invokeMethod(method, [ov, nv, arguments]);
   9375             }
   9376           }
   9377         }
   9378       }
   9379     },
   9380 
   9381     // call method iff it exists.
   9382     invokeMethod: function(method, args) {
   9383       var fn = this[method] || method;
   9384       if (typeof fn === 'function') {
   9385         fn.apply(this, args);
   9386       }
   9387     },
   9388 
   9389     /**
   9390      * Force any pending property changes to synchronously deliver to
   9391      * handlers specified in the `observe` object.
   9392      * Note, normally changes are processed at microtask time.
   9393      *
   9394      * @method deliverChanges
   9395      */
   9396     deliverChanges: function() {
   9397       if (this._propertyObserver) {
   9398         this._propertyObserver.deliver();
   9399       }
   9400     },
   9401 
   9402     observeArrayValue: function(name, value, old) {
   9403       // we only care if there are registered side-effects
   9404       var callbackName = this.observe[name];
   9405       if (callbackName) {
   9406         // if we are observing the previous value, stop
   9407         if (Array.isArray(old)) {
   9408           log.observe && console.log('[%s] observeArrayValue: unregister observer [%s]', this.localName, name);
   9409           this.closeNamedObserver(name + '__array');
   9410         }
   9411         // if the new value is an array, being observing it
   9412         if (Array.isArray(value)) {
   9413           log.observe && console.log('[%s] observeArrayValue: register observer [%s]', this.localName, name, value);
   9414           var observer = new ArrayObserver(value);
   9415           observer.open(function(splices) {
   9416             this.invokeMethod(callbackName, [splices]);
   9417           }, this);
   9418           this.registerNamedObserver(name + '__array', observer);
   9419         }
   9420       }
   9421     },
   9422 
   9423     emitPropertyChangeRecord: function(name, value, oldValue) {
   9424       var object = this;
   9425       if (areSameValue(value, oldValue)) {
   9426         return;
   9427       }
   9428       // invoke property change side effects
   9429       this._propertyChanged(name, value, oldValue);
   9430       // emit change record
   9431       if (!Observer.hasObjectObserve) {
   9432         return;
   9433       }
   9434       var notifier = this._objectNotifier;
   9435       if (!notifier) {
   9436         notifier = this._objectNotifier = Object.getNotifier(this);
   9437       }
   9438       updateRecord.object = this;
   9439       updateRecord.name = name;
   9440       updateRecord.oldValue = oldValue;
   9441       notifier.notify(updateRecord);
   9442     },
   9443 
   9444     _propertyChanged: function(name, value, oldValue) {
   9445       if (this.reflect[name]) {
   9446         this.reflectPropertyToAttribute(name);
   9447       }
   9448     },
   9449 
   9450     // creates a property binding (called via bind) to a published property.
   9451     bindProperty: function(property, observable, oneTime) {
   9452       if (oneTime) {
   9453         this[property] = observable;
   9454         return;
   9455       }
   9456       var computed = this.element.prototype.computed;
   9457       // Binding an "out-only" value to a computed property. Note that
   9458       // since this observer isn't opened, it doesn't need to be closed on
   9459       // cleanup.
   9460       if (computed && computed[property]) {
   9461         var privateComputedBoundValue = property + 'ComputedBoundObservable_';
   9462         this[privateComputedBoundValue] = observable;
   9463         return;
   9464       }
   9465       return this.bindToAccessor(property, observable, resolveBindingValue);
   9466     },
   9467 
   9468     // NOTE property `name` must be published. This makes it an accessor.
   9469     bindToAccessor: function(name, observable, resolveFn) {
   9470       var privateName = name + '_';
   9471       var privateObservable  = name + 'Observable_';
   9472       // Present for properties which are computed and published and have a
   9473       // bound value.
   9474       var privateComputedBoundValue = name + 'ComputedBoundObservable_';
   9475       this[privateObservable] = observable;
   9476       var oldValue = this[privateName];
   9477       // observable callback
   9478       var self = this;
   9479       function updateValue(value, oldValue) {
   9480         self[privateName] = value;
   9481         var setObserveable = self[privateComputedBoundValue];
   9482         if (setObserveable && typeof setObserveable.setValue == 'function') {
   9483           setObserveable.setValue(value);
   9484         }
   9485         self.emitPropertyChangeRecord(name, value, oldValue);
   9486       }
   9487       // resolve initial value
   9488       var value = observable.open(updateValue);
   9489       if (resolveFn && !areSameValue(oldValue, value)) {
   9490         var resolvedValue = resolveFn(oldValue, value);
   9491         if (!areSameValue(value, resolvedValue)) {
   9492           value = resolvedValue;
   9493           if (observable.setValue) {
   9494             observable.setValue(value);
   9495           }
   9496         }
   9497       }
   9498       updateValue(value, oldValue);
   9499       // register and return observable
   9500       var observer = {
   9501         close: function() {
   9502           observable.close();
   9503           self[privateObservable] = undefined;
   9504           self[privateComputedBoundValue] = undefined;
   9505         }
   9506       };
   9507       this.registerObserver(observer);
   9508       return observer;
   9509     },
   9510 
   9511     createComputedProperties: function() {
   9512       if (!this._computedNames) {
   9513         return;
   9514       }
   9515       for (var i = 0; i < this._computedNames.length; i++) {
   9516         var name = this._computedNames[i];
   9517         var expressionText = this.computed[name];
   9518         try {
   9519           var expression = PolymerExpressions.getExpression(expressionText);
   9520           var observable = expression.getBinding(this, this.element.syntax);
   9521           this.bindToAccessor(name, observable);
   9522         } catch (ex) {
   9523           console.error('Failed to create computed property', ex);
   9524         }
   9525       }
   9526     },
   9527 
   9528     // property bookkeeping
   9529     registerObserver: function(observer) {
   9530       if (!this._observers) {
   9531         this._observers = [observer];
   9532         return;
   9533       }
   9534       this._observers.push(observer);
   9535     },
   9536 
   9537     closeObservers: function() {
   9538       if (!this._observers) {
   9539         return;
   9540       }
   9541       // observer array items are arrays of observers.
   9542       var observers = this._observers;
   9543       for (var i = 0; i < observers.length; i++) {
   9544         var observer = observers[i];
   9545         if (observer && typeof observer.close == 'function') {
   9546           observer.close();
   9547         }
   9548       }
   9549       this._observers = [];
   9550     },
   9551 
   9552     // bookkeeping observers for memory management
   9553     registerNamedObserver: function(name, observer) {
   9554       var o$ = this._namedObservers || (this._namedObservers = {});
   9555       o$[name] = observer;
   9556     },
   9557 
   9558     closeNamedObserver: function(name) {
   9559       var o$ = this._namedObservers;
   9560       if (o$ && o$[name]) {
   9561         o$[name].close();
   9562         o$[name] = null;
   9563         return true;
   9564       }
   9565     },
   9566 
   9567     closeNamedObservers: function() {
   9568       if (this._namedObservers) {
   9569         for (var i in this._namedObservers) {
   9570           this.closeNamedObserver(i);
   9571         }
   9572         this._namedObservers = {};
   9573       }
   9574     }
   9575 
   9576   };
   9577 
   9578   // logging
   9579   var LOG_OBSERVE = '[%s] watching [%s]';
   9580   var LOG_OBSERVED = '[%s#%s] watch: [%s] now [%s] was [%s]';
   9581   var LOG_CHANGED = '[%s#%s] propertyChanged: [%s] now [%s] was [%s]';
   9582 
   9583   // exports
   9584 
   9585   scope.api.instance.properties = properties;
   9586 
   9587 })(Polymer);
   9588 
   9589 (function(scope) {
   9590 
   9591   /**
   9592    * @class polymer-base
   9593    */
   9594 
   9595   // imports
   9596 
   9597   var log = window.WebComponents ? WebComponents.flags.log : {};
   9598 
   9599   // element api supporting mdv
   9600   var mdv = {
   9601 
   9602     /**
   9603      * Creates dom cloned from the given template, instantiating bindings
   9604      * with this element as the template model and `PolymerExpressions` as the
   9605      * binding delegate.
   9606      *
   9607      * @method instanceTemplate
   9608      * @param {Template} template source template from which to create dom.
   9609      */
   9610     instanceTemplate: function(template) {
   9611       // ensure template is decorated (lets' things like <tr template ...> work)
   9612       HTMLTemplateElement.decorate(template);
   9613       // ensure a default bindingDelegate
   9614       var syntax = this.syntax || (!template.bindingDelegate &&
   9615           this.element.syntax);
   9616       var dom = template.createInstance(this, syntax);
   9617       var observers = dom.bindings_;
   9618       for (var i = 0; i < observers.length; i++) {
   9619         this.registerObserver(observers[i]);
   9620       }
   9621       return dom;
   9622     },
   9623 
   9624     // Called by TemplateBinding/NodeBind to setup a binding to the given
   9625     // property. It's overridden here to support property bindings
   9626     // in addition to attribute bindings that are supported by default.
   9627     bind: function(name, observable, oneTime) {
   9628       var property = this.propertyForAttribute(name);
   9629       if (!property) {
   9630         // TODO(sjmiles): this mixin method must use the special form
   9631         // of `super` installed by `mixinMethod` in declaration/prototype.js
   9632         return this.mixinSuper(arguments);
   9633       } else {
   9634         // use n-way Polymer binding
   9635         var observer = this.bindProperty(property, observable, oneTime);
   9636         // NOTE: reflecting binding information is typically required only for
   9637         // tooling. It has a performance cost so it's opt-in in Node.bind.
   9638         if (Platform.enableBindingsReflection && observer) {
   9639           observer.path = observable.path_;
   9640           this._recordBinding(property, observer);
   9641         }
   9642         if (this.reflect[property]) {
   9643           this.reflectPropertyToAttribute(property);
   9644         }
   9645         return observer;
   9646       }
   9647     },
   9648 
   9649     _recordBinding: function(name, observer) {
   9650       this.bindings_ = this.bindings_ || {};
   9651       this.bindings_[name] = observer;
   9652     },
   9653 
   9654     // Called by TemplateBinding when all bindings on an element have been
   9655     // executed. This signals that all element inputs have been gathered
   9656     // and it's safe to ready the element, create shadow-root and start
   9657     // data-observation.
   9658     bindFinished: function() {
   9659       this.makeElementReady();
   9660     },
   9661 
   9662     // called at detached time to signal that an element's bindings should be
   9663     // cleaned up. This is done asynchronously so that users have the chance
   9664     // to call `cancelUnbindAll` to prevent unbinding.
   9665     asyncUnbindAll: function() {
   9666       if (!this._unbound) {
   9667         log.unbind && console.log('[%s] asyncUnbindAll', this.localName);
   9668         this._unbindAllJob = this.job(this._unbindAllJob, this.unbindAll, 0);
   9669       }
   9670     },
   9671 
   9672     /**
   9673      * This method should rarely be used and only if
   9674      * <a href="#cancelUnbindAll">`cancelUnbindAll`</a> has been called to
   9675      * prevent element unbinding. In this case, the element's bindings will
   9676      * not be automatically cleaned up and it cannot be garbage collected
   9677      * by the system. If memory pressure is a concern or a
   9678      * large amount of elements need to be managed in this way, `unbindAll`
   9679      * can be called to deactivate the element's bindings and allow its
   9680      * memory to be reclaimed.
   9681      *
   9682      * @method unbindAll
   9683      */
   9684     unbindAll: function() {
   9685       if (!this._unbound) {
   9686         this.closeObservers();
   9687         this.closeNamedObservers();
   9688         this._unbound = true;
   9689       }
   9690     },
   9691 
   9692     /**
   9693      * Call in `detached` to prevent the element from unbinding when it is
   9694      * detached from the dom. The element is unbound as a cleanup step that
   9695      * allows its memory to be reclaimed.
   9696      * If `cancelUnbindAll` is used, consider calling
   9697      * <a href="#unbindAll">`unbindAll`</a> when the element is no longer
   9698      * needed. This will allow its memory to be reclaimed.
   9699      *
   9700      * @method cancelUnbindAll
   9701      */
   9702     cancelUnbindAll: function() {
   9703       if (this._unbound) {
   9704         log.unbind && console.warn('[%s] already unbound, cannot cancel unbindAll', this.localName);
   9705         return;
   9706       }
   9707       log.unbind && console.log('[%s] cancelUnbindAll', this.localName);
   9708       if (this._unbindAllJob) {
   9709         this._unbindAllJob = this._unbindAllJob.stop();
   9710       }
   9711     }
   9712 
   9713   };
   9714 
   9715   function unbindNodeTree(node) {
   9716     forNodeTree(node, _nodeUnbindAll);
   9717   }
   9718 
   9719   function _nodeUnbindAll(node) {
   9720     node.unbindAll();
   9721   }
   9722 
   9723   function forNodeTree(node, callback) {
   9724     if (node) {
   9725       callback(node);
   9726       for (var child = node.firstChild; child; child = child.nextSibling) {
   9727         forNodeTree(child, callback);
   9728       }
   9729     }
   9730   }
   9731 
   9732   var mustachePattern = /\{\{([^{}]*)}}/;
   9733 
   9734   // exports
   9735 
   9736   scope.bindPattern = mustachePattern;
   9737   scope.api.instance.mdv = mdv;
   9738 
   9739 })(Polymer);
   9740 
   9741 (function(scope) {
   9742 
   9743   /**
   9744    * Common prototype for all Polymer Elements.
   9745    *
   9746    * @class polymer-base
   9747    * @homepage polymer.github.io
   9748    */
   9749   var base = {
   9750     /**
   9751      * Tags this object as the canonical Base prototype.
   9752      *
   9753      * @property PolymerBase
   9754      * @type boolean
   9755      * @default true
   9756      */
   9757     PolymerBase: true,
   9758 
   9759     /**
   9760      * Debounce signals.
   9761      *
   9762      * Call `job` to defer a named signal, and all subsequent matching signals,
   9763      * until a wait time has elapsed with no new signal.
   9764      *
   9765      *     debouncedClickAction: function(e) {
   9766      *       // processClick only when it's been 100ms since the last click
   9767      *       this.job('click', function() {
   9768      *        this.processClick;
   9769      *       }, 100);
   9770      *     }
   9771      *
   9772      * @method job
   9773      * @param String {String} job A string identifier for the job to debounce.
   9774      * @param Function {Function} callback A function that is called (with `this` context) when the wait time elapses.
   9775      * @param Number {Number} wait Time in milliseconds (ms) after the last signal that must elapse before invoking `callback`
   9776      * @type Handle
   9777      */
   9778     job: function(job, callback, wait) {
   9779       if (typeof job === 'string') {
   9780         var n = '___' + job;
   9781         this[n] = Polymer.job.call(this, this[n], callback, wait);
   9782       } else {
   9783         // TODO(sjmiles): suggest we deprecate this call signature
   9784         return Polymer.job.call(this, job, callback, wait);
   9785       }
   9786     },
   9787 
   9788     /**
   9789      * Invoke a superclass method.
   9790      *
   9791      * Use `super()` to invoke the most recently overridden call to the
   9792      * currently executing function.
   9793      *
   9794      * To pass arguments through, use the literal `arguments` as the parameter
   9795      * to `super()`.
   9796      *
   9797      *     nextPageAction: function(e) {
   9798      *       // invoke the superclass version of `nextPageAction`
   9799      *       this.super(arguments);
   9800      *     }
   9801      *
   9802      * To pass custom arguments, arrange them in an array.
   9803      *
   9804      *     appendSerialNo: function(value, serial) {
   9805      *       // prefix the superclass serial number with our lot # before
   9806      *       // invoking the superlcass
   9807      *       return this.super([value, this.lotNo + serial])
   9808      *     }
   9809      *
   9810      * @method super
   9811      * @type Any
   9812      * @param {args) An array of arguments to use when calling the superclass method, or null.
   9813      */
   9814     super: Polymer.super,
   9815 
   9816     /**
   9817      * Lifecycle method called when the element is instantiated.
   9818      *
   9819      * Override `created` to perform custom create-time tasks. No need to call
   9820      * super-class `created` unless you are extending another Polymer element.
   9821      * Created is called before the element creates `shadowRoot` or prepares
   9822      * data-observation.
   9823      *
   9824      * @method created
   9825      * @type void
   9826      */
   9827     created: function() {
   9828     },
   9829 
   9830     /**
   9831      * Lifecycle method called when the element has populated it's `shadowRoot`,
   9832      * prepared data-observation, and made itself ready for API interaction.
   9833      *
   9834      * @method ready
   9835      * @type void
   9836      */
   9837     ready: function() {
   9838     },
   9839 
   9840     /**
   9841      * Low-level lifecycle method called as part of standard Custom Elements
   9842      * operation. Polymer implements this method to provide basic default
   9843      * functionality. For custom create-time tasks, implement `created`
   9844      * instead, which is called immediately after `createdCallback`.
   9845      *
   9846      * @method createdCallback
   9847      */
   9848     createdCallback: function() {
   9849       if (this.templateInstance && this.templateInstance.model) {
   9850         console.warn('Attributes on ' + this.localName + ' were data bound ' +
   9851             'prior to Polymer upgrading the element. This may result in ' +
   9852             'incorrect binding types.');
   9853       }
   9854       this.created();
   9855       this.prepareElement();
   9856       if (!this.ownerDocument.isStagingDocument) {
   9857         this.makeElementReady();
   9858       }
   9859     },
   9860 
   9861     // system entry point, do not override
   9862     prepareElement: function() {
   9863       if (this._elementPrepared) {
   9864         console.warn('Element already prepared', this.localName);
   9865         return;
   9866       }
   9867       this._elementPrepared = true;
   9868       // storage for shadowRoots info
   9869       this.shadowRoots = {};
   9870       // install property observers
   9871       this.createPropertyObserver();
   9872       this.openPropertyObserver();
   9873       // install boilerplate attributes
   9874       this.copyInstanceAttributes();
   9875       // process input attributes
   9876       this.takeAttributes();
   9877       // add event listeners
   9878       this.addHostListeners();
   9879     },
   9880 
   9881     // system entry point, do not override
   9882     makeElementReady: function() {
   9883       if (this._readied) {
   9884         return;
   9885       }
   9886       this._readied = true;
   9887       this.createComputedProperties();
   9888       this.parseDeclarations(this.__proto__);
   9889       // NOTE: Support use of the `unresolved` attribute to help polyfill
   9890       // custom elements' `:unresolved` feature.
   9891       this.removeAttribute('unresolved');
   9892       // user entry point
   9893       this.ready();
   9894     },
   9895 
   9896     /**
   9897      * Low-level lifecycle method called as part of standard Custom Elements
   9898      * operation. Polymer implements this method to provide basic default
   9899      * functionality. For custom tasks in your element, implement `attributeChanged`
   9900      * instead, which is called immediately after `attributeChangedCallback`.
   9901      *
   9902      * @method attributeChangedCallback
   9903      */
   9904     attributeChangedCallback: function(name, oldValue) {
   9905       // TODO(sjmiles): adhoc filter
   9906       if (name !== 'class' && name !== 'style') {
   9907         this.attributeToProperty(name, this.getAttribute(name));
   9908       }
   9909       if (this.attributeChanged) {
   9910         this.attributeChanged.apply(this, arguments);
   9911       }
   9912     },
   9913 
   9914     /**
   9915      * Low-level lifecycle method called as part of standard Custom Elements
   9916      * operation. Polymer implements this method to provide basic default
   9917      * functionality. For custom create-time tasks, implement `attached`
   9918      * instead, which is called immediately after `attachedCallback`.
   9919      *
   9920      * @method attachedCallback
   9921      */
   9922      attachedCallback: function() {
   9923       // when the element is attached, prevent it from unbinding.
   9924       this.cancelUnbindAll();
   9925       // invoke user action
   9926       if (this.attached) {
   9927         this.attached();
   9928       }
   9929       if (!this.hasBeenAttached) {
   9930         this.hasBeenAttached = true;
   9931         if (this.domReady) {
   9932           this.async('domReady');
   9933         }
   9934       }
   9935     },
   9936 
   9937      /**
   9938      * Implement to access custom elements in dom descendants, ancestors,
   9939      * or siblings. Because custom elements upgrade in document order,
   9940      * elements accessed in `ready` or `attached` may not be upgraded. When
   9941      * `domReady` is called, all registered custom elements are guaranteed
   9942      * to have been upgraded.
   9943      *
   9944      * @method domReady
   9945      */
   9946 
   9947     /**
   9948      * Low-level lifecycle method called as part of standard Custom Elements
   9949      * operation. Polymer implements this method to provide basic default
   9950      * functionality. For custom create-time tasks, implement `detached`
   9951      * instead, which is called immediately after `detachedCallback`.
   9952      *
   9953      * @method detachedCallback
   9954      */
   9955     detachedCallback: function() {
   9956       if (!this.preventDispose) {
   9957         this.asyncUnbindAll();
   9958       }
   9959       // invoke user action
   9960       if (this.detached) {
   9961         this.detached();
   9962       }
   9963       // TODO(sorvell): bc
   9964       if (this.leftView) {
   9965         this.leftView();
   9966       }
   9967     },
   9968 
   9969     /**
   9970      * Walks the prototype-chain of this element and allows specific
   9971      * classes a chance to process static declarations.
   9972      *
   9973      * In particular, each polymer-element has it's own `template`.
   9974      * `parseDeclarations` is used to accumulate all element `template`s
   9975      * from an inheritance chain.
   9976      *
   9977      * `parseDeclaration` static methods implemented in the chain are called
   9978      * recursively, oldest first, with the `<polymer-element>` associated
   9979      * with the current prototype passed as an argument.
   9980      *
   9981      * An element may override this method to customize shadow-root generation.
   9982      *
   9983      * @method parseDeclarations
   9984      */
   9985     parseDeclarations: function(p) {
   9986       if (p && p.element) {
   9987         this.parseDeclarations(p.__proto__);
   9988         p.parseDeclaration.call(this, p.element);
   9989       }
   9990     },
   9991 
   9992     /**
   9993      * Perform init-time actions based on static information in the
   9994      * `<polymer-element>` instance argument.
   9995      *
   9996      * For example, the standard implementation locates the template associated
   9997      * with the given `<polymer-element>` and stamps it into a shadow-root to
   9998      * implement shadow inheritance.
   9999      *
   10000      * An element may override this method for custom behavior.
   10001      *
   10002      * @method parseDeclaration
   10003      */
   10004     parseDeclaration: function(elementElement) {
   10005       var template = this.fetchTemplate(elementElement);
   10006       if (template) {
   10007         var root = this.shadowFromTemplate(template);
   10008         this.shadowRoots[elementElement.name] = root;
   10009       }
   10010     },
   10011 
   10012     /**
   10013      * Given a `<polymer-element>`, find an associated template (if any) to be
   10014      * used for shadow-root generation.
   10015      *
   10016      * An element may override this method for custom behavior.
   10017      *
   10018      * @method fetchTemplate
   10019      */
   10020     fetchTemplate: function(elementElement) {
   10021       return elementElement.querySelector('template');
   10022     },
   10023 
   10024     /**
   10025      * Create a shadow-root in this host and stamp `template` as it's
   10026      * content.
   10027      *
   10028      * An element may override this method for custom behavior.
   10029      *
   10030      * @method shadowFromTemplate
   10031      */
   10032     shadowFromTemplate: function(template) {
   10033       if (template) {
   10034         // make a shadow root
   10035         var root = this.createShadowRoot();
   10036         // stamp template
   10037         // which includes parsing and applying MDV bindings before being
   10038         // inserted (to avoid {{}} in attribute values).
   10039         var dom = this.instanceTemplate(template);
   10040         // append to shadow dom
   10041         root.appendChild(dom);
   10042         // perform post-construction initialization tasks on shadow root
   10043         this.shadowRootReady(root, template);
   10044         // return the created shadow root
   10045         return root;
   10046       }
   10047     },
   10048 
   10049     // utility function that stamps a <template> into light-dom
   10050     lightFromTemplate: function(template, refNode) {
   10051       if (template) {
   10052         // TODO(sorvell): mark this element as an eventController so that
   10053         // event listeners on bound nodes inside it will be called on it.
   10054         // Note, the expectation here is that events on all descendants
   10055         // should be handled by this element.
   10056         this.eventController = this;
   10057         // stamp template
   10058         // which includes parsing and applying MDV bindings before being
   10059         // inserted (to avoid {{}} in attribute values).
   10060         var dom = this.instanceTemplate(template);
   10061         // append to shadow dom
   10062         if (refNode) {
   10063           this.insertBefore(dom, refNode);
   10064         } else {
   10065           this.appendChild(dom);
   10066         }
   10067         // perform post-construction initialization tasks on ahem, light root
   10068         this.shadowRootReady(this);
   10069         // return the created shadow root
   10070         return dom;
   10071       }
   10072     },
   10073 
   10074     shadowRootReady: function(root) {
   10075       // locate nodes with id and store references to them in this.$ hash
   10076       this.marshalNodeReferences(root);
   10077     },
   10078 
   10079     // locate nodes with id and store references to them in this.$ hash
   10080     marshalNodeReferences: function(root) {
   10081       // establish $ instance variable
   10082       var $ = this.$ = this.$ || {};
   10083       // populate $ from nodes with ID from the LOCAL tree
   10084       if (root) {
   10085         var n$ = root.querySelectorAll("[id]");
   10086         for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
   10087           $[n.id] = n;
   10088         };
   10089       }
   10090     },
   10091 
   10092     /**
   10093      * Register a one-time callback when a child-list or sub-tree mutation
   10094      * occurs on node.
   10095      *
   10096      * For persistent callbacks, call onMutation from your listener.
   10097      *
   10098      * @method onMutation
   10099      * @param Node {Node} node Node to watch for mutations.
   10100      * @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.
   10101      */
   10102     onMutation: function(node, listener) {
   10103       var observer = new MutationObserver(function(mutations) {
   10104         listener.call(this, observer, mutations);
   10105         observer.disconnect();
   10106       }.bind(this));
   10107       observer.observe(node, {childList: true, subtree: true});
   10108     }
   10109   };
   10110 
   10111   /**
   10112    * @class Polymer
   10113    */
   10114 
   10115   /**
   10116    * Returns true if the object includes <a href="#polymer-base">polymer-base</a> in it's prototype chain.
   10117    *
   10118    * @method isBase
   10119    * @param Object {Object} object Object to test.
   10120    * @type Boolean
   10121    */
   10122   function isBase(object) {
   10123     return object.hasOwnProperty('PolymerBase')
   10124   }
   10125 
   10126   // name a base constructor for dev tools
   10127 
   10128   /**
   10129    * The Polymer base-class constructor.
   10130    *
   10131    * @property Base
   10132    * @type Function
   10133    */
   10134   function PolymerBase() {};
   10135   PolymerBase.prototype = base;
   10136   base.constructor = PolymerBase;
   10137 
   10138   // exports
   10139 
   10140   scope.Base = PolymerBase;
   10141   scope.isBase = isBase;
   10142   scope.api.instance.base = base;
   10143 
   10144 })(Polymer);
   10145 
   10146 (function(scope) {
   10147 
   10148   // imports
   10149 
   10150   var log = window.WebComponents ? WebComponents.flags.log : {};
   10151   var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
   10152 
   10153   // magic words
   10154 
   10155   var STYLE_SCOPE_ATTRIBUTE = 'element';
   10156   var STYLE_CONTROLLER_SCOPE = 'controller';
   10157 
   10158   var styles = {
   10159     STYLE_SCOPE_ATTRIBUTE: STYLE_SCOPE_ATTRIBUTE,
   10160     /**
   10161      * Installs external stylesheets and <style> elements with the attribute
   10162      * polymer-scope='controller' into the scope of element. This is intended
   10163      * to be a called during custom element construction.
   10164     */
   10165     installControllerStyles: function() {
   10166       // apply controller styles, but only if they are not yet applied
   10167       var scope = this.findStyleScope();
   10168       if (scope && !this.scopeHasNamedStyle(scope, this.localName)) {
   10169         // allow inherited controller styles
   10170         var proto = getPrototypeOf(this), cssText = '';
   10171         while (proto && proto.element) {
   10172           cssText += proto.element.cssTextForScope(STYLE_CONTROLLER_SCOPE);
   10173           proto = getPrototypeOf(proto);
   10174         }
   10175         if (cssText) {
   10176           this.installScopeCssText(cssText, scope);
   10177         }
   10178       }
   10179     },
   10180     installScopeStyle: function(style, name, scope) {
   10181       var scope = scope || this.findStyleScope(), name = name || '';
   10182       if (scope && !this.scopeHasNamedStyle(scope, this.localName + name)) {
   10183         var cssText = '';
   10184         if (style instanceof Array) {
   10185           for (var i=0, l=style.length, s; (i<l) && (s=style[i]); i++) {
   10186             cssText += s.textContent + '\n\n';
   10187           }
   10188         } else {
   10189           cssText = style.textContent;
   10190         }
   10191         this.installScopeCssText(cssText, scope, name);
   10192       }
   10193     },
   10194     installScopeCssText: function(cssText, scope, name) {
   10195       scope = scope || this.findStyleScope();
   10196       name = name || '';
   10197       if (!scope) {
   10198         return;
   10199       }
   10200       if (hasShadowDOMPolyfill) {
   10201         cssText = shimCssText(cssText, scope.host);
   10202       }
   10203       var style = this.element.cssTextToScopeStyle(cssText,
   10204           STYLE_CONTROLLER_SCOPE);
   10205       Polymer.applyStyleToScope(style, scope);
   10206       // cache that this style has been applied
   10207       this.styleCacheForScope(scope)[this.localName + name] = true;
   10208     },
   10209     findStyleScope: function(node) {
   10210       // find the shadow root that contains this element
   10211       var n = node || this;
   10212       while (n.parentNode) {
   10213         n = n.parentNode;
   10214       }
   10215       return n;
   10216     },
   10217     scopeHasNamedStyle: function(scope, name) {
   10218       var cache = this.styleCacheForScope(scope);
   10219       return cache[name];
   10220     },
   10221     styleCacheForScope: function(scope) {
   10222       if (hasShadowDOMPolyfill) {
   10223         var scopeName = scope.host ? scope.host.localName : scope.localName;
   10224         return polyfillScopeStyleCache[scopeName] || (polyfillScopeStyleCache[scopeName] = {});
   10225       } else {
   10226         return scope._scopeStyles = (scope._scopeStyles || {});
   10227       }
   10228     }
   10229   };
   10230 
   10231   var polyfillScopeStyleCache = {};
   10232 
   10233   // NOTE: use raw prototype traversal so that we ensure correct traversal
   10234   // on platforms where the protoype chain is simulated via __proto__ (IE10)
   10235   function getPrototypeOf(prototype) {
   10236     return prototype.__proto__;
   10237   }
   10238 
   10239   function shimCssText(cssText, host) {
   10240     var name = '', is = false;
   10241     if (host) {
   10242       name = host.localName;
   10243       is = host.hasAttribute('is');
   10244     }
   10245     var selector = WebComponents.ShadowCSS.makeScopeSelector(name, is);
   10246     return WebComponents.ShadowCSS.shimCssText(cssText, selector);
   10247   }
   10248 
   10249   // exports
   10250 
   10251   scope.api.instance.styles = styles;
   10252 
   10253 })(Polymer);
   10254 
   10255 (function(scope) {
   10256 
   10257   // imports
   10258 
   10259   var extend = scope.extend;
   10260   var api = scope.api;
   10261 
   10262   // imperative implementation: Polymer()
   10263 
   10264   // specify an 'own' prototype for tag `name`
   10265   function element(name, prototype) {
   10266     if (typeof name !== 'string') {
   10267       var script = prototype || document._currentScript;
   10268       prototype = name;
   10269       name = script && script.parentNode && script.parentNode.getAttribute ?
   10270           script.parentNode.getAttribute('name') : '';
   10271       if (!name) {
   10272         throw 'Element name could not be inferred.';
   10273       }
   10274     }
   10275     if (getRegisteredPrototype(name)) {
   10276       throw 'Already registered (Polymer) prototype for element ' + name;
   10277     }
   10278     // cache the prototype
   10279     registerPrototype(name, prototype);
   10280     // notify the registrar waiting for 'name', if any
   10281     notifyPrototype(name);
   10282   }
   10283 
   10284   // async prototype source
   10285 
   10286   function waitingForPrototype(name, client) {
   10287     waitPrototype[name] = client;
   10288   }
   10289 
   10290   var waitPrototype = {};
   10291 
   10292   function notifyPrototype(name) {
   10293     if (waitPrototype[name]) {
   10294       waitPrototype[name].registerWhenReady();
   10295       delete waitPrototype[name];
   10296     }
   10297   }
   10298 
   10299   // utility and bookkeeping
   10300 
   10301   // maps tag names to prototypes, as registered with
   10302   // Polymer. Prototypes associated with a tag name
   10303   // using document.registerElement are available from
   10304   // HTMLElement.getPrototypeForTag().
   10305   // If an element was fully registered by Polymer, then
   10306   // Polymer.getRegisteredPrototype(name) ===
   10307   //   HTMLElement.getPrototypeForTag(name)
   10308 
   10309   var prototypesByName = {};
   10310 
   10311   function registerPrototype(name, prototype) {
   10312     return prototypesByName[name] = prototype || {};
   10313   }
   10314 
   10315   function getRegisteredPrototype(name) {
   10316     return prototypesByName[name];
   10317   }
   10318 
   10319   function instanceOfType(element, type) {
   10320     if (typeof type !== 'string') {
   10321       return false;
   10322     }
   10323     var proto = HTMLElement.getPrototypeForTag(type);
   10324     var ctor = proto && proto.constructor;
   10325     if (!ctor) {
   10326       return false;
   10327     }
   10328     if (CustomElements.instanceof) {
   10329       return CustomElements.instanceof(element, ctor);
   10330     }
   10331     return element instanceof ctor;
   10332   }
   10333 
   10334   // exports
   10335 
   10336   scope.getRegisteredPrototype = getRegisteredPrototype;
   10337   scope.waitingForPrototype = waitingForPrototype;
   10338   scope.instanceOfType = instanceOfType;
   10339 
   10340   // namespace shenanigans so we can expose our scope on the registration
   10341   // function
   10342 
   10343   // make window.Polymer reference `element()`
   10344 
   10345   window.Polymer = element;
   10346 
   10347   // TODO(sjmiles): find a way to do this that is less terrible
   10348   // copy window.Polymer properties onto `element()`
   10349 
   10350   extend(Polymer, scope);
   10351 
   10352   // Under the HTMLImports polyfill, scripts in the main document
   10353   // do not block on imports; we want to allow calls to Polymer in the main
   10354   // document. WebComponents collects those calls until we can process them, which
   10355   // we do here.
   10356 
   10357   if (WebComponents.consumeDeclarations) {
   10358     WebComponents.consumeDeclarations(function(declarations) {
   10359       if (declarations) {
   10360         for (var i=0, l=declarations.length, d; (i<l) && (d=declarations[i]); i++) {
   10361           element.apply(null, d);
   10362         }
   10363       }
   10364     });
   10365   }
   10366 
   10367 })(Polymer);
   10368 
   10369 (function(scope) {
   10370 
   10371 /**
   10372  * @class polymer-base
   10373  */
   10374 
   10375  /**
   10376   * Resolve a url path to be relative to a `base` url. If unspecified, `base`
   10377   * defaults to the element's ownerDocument url. Can be used to resolve
   10378   * paths from element's in templates loaded in HTMLImports to be relative
   10379   * to the document containing the element. Polymer automatically does this for
   10380   * url attributes in element templates; however, if a url, for
   10381   * example, contains a binding, then `resolvePath` can be used to ensure it is
   10382   * relative to the element document. For example, in an element's template,
   10383   *
   10384   *     <a href="{{resolvePath(path)}}">Resolved</a>
   10385   *
   10386   * @method resolvePath
   10387   * @param {String} url Url path to resolve.
   10388   * @param {String} base Optional base url against which to resolve, defaults
   10389   * to the element's ownerDocument url.
   10390   * returns {String} resolved url.
   10391   */
   10392 
   10393 var path = {
   10394   resolveElementPaths: function(node) {
   10395     Polymer.urlResolver.resolveDom(node);
   10396   },
   10397   addResolvePathApi: function() {
   10398     // let assetpath attribute modify the resolve path
   10399     var assetPath = this.getAttribute('assetpath') || '';
   10400     var root = new URL(assetPath, this.ownerDocument.baseURI);
   10401     this.prototype.resolvePath = function(urlPath, base) {
   10402       var u = new URL(urlPath, base || root);
   10403       return u.href;
   10404     };
   10405   }
   10406 };
   10407 
   10408 // exports
   10409 scope.api.declaration.path = path;
   10410 
   10411 })(Polymer);
   10412 
   10413 (function(scope) {
   10414 
   10415   // imports
   10416 
   10417   var log = window.WebComponents ? WebComponents.flags.log : {};
   10418   var api = scope.api.instance.styles;
   10419   var STYLE_SCOPE_ATTRIBUTE = api.STYLE_SCOPE_ATTRIBUTE;
   10420 
   10421   var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
   10422 
   10423   // magic words
   10424 
   10425   var STYLE_SELECTOR = 'style';
   10426   var STYLE_LOADABLE_MATCH = '@import';
   10427   var SHEET_SELECTOR = 'link[rel=stylesheet]';
   10428   var STYLE_GLOBAL_SCOPE = 'global';
   10429   var SCOPE_ATTR = 'polymer-scope';
   10430 
   10431   var styles = {
   10432     // returns true if resources are loading
   10433     loadStyles: function(callback) {
   10434       var template = this.fetchTemplate();
   10435       var content = template && this.templateContent();
   10436       if (content) {
   10437         this.convertSheetsToStyles(content);
   10438         var styles = this.findLoadableStyles(content);
   10439         if (styles.length) {
   10440           var templateUrl = template.ownerDocument.baseURI;
   10441           return Polymer.styleResolver.loadStyles(styles, templateUrl, callback);
   10442         }
   10443       }
   10444       if (callback) {
   10445         callback();
   10446       }
   10447     },
   10448     convertSheetsToStyles: function(root) {
   10449       var s$ = root.querySelectorAll(SHEET_SELECTOR);
   10450       for (var i=0, l=s$.length, s, c; (i<l) && (s=s$[i]); i++) {
   10451         c = createStyleElement(importRuleForSheet(s, this.ownerDocument.baseURI),
   10452             this.ownerDocument);
   10453         this.copySheetAttributes(c, s);
   10454         s.parentNode.replaceChild(c, s);
   10455       }
   10456     },
   10457     copySheetAttributes: function(style, link) {
   10458       for (var i=0, a$=link.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++) {
   10459         if (a.name !== 'rel' && a.name !== 'href') {
   10460           style.setAttribute(a.name, a.value);
   10461         }
   10462       }
   10463     },
   10464     findLoadableStyles: function(root) {
   10465       var loadables = [];
   10466       if (root) {
   10467         var s$ = root.querySelectorAll(STYLE_SELECTOR);
   10468         for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) {
   10469           if (s.textContent.match(STYLE_LOADABLE_MATCH)) {
   10470             loadables.push(s);
   10471           }
   10472         }
   10473       }
   10474       return loadables;
   10475     },
   10476     /**
   10477      * Install external stylesheets loaded in <polymer-element> elements into the
   10478      * element's template.
   10479      * @param elementElement The <element> element to style.
   10480      */
   10481     installSheets: function() {
   10482       this.cacheSheets();
   10483       this.cacheStyles();
   10484       this.installLocalSheets();
   10485       this.installGlobalStyles();
   10486     },
   10487     /**
   10488      * Remove all sheets from element and store for later use.
   10489      */
   10490     cacheSheets: function() {
   10491       this.sheets = this.findNodes(SHEET_SELECTOR);
   10492       this.sheets.forEach(function(s) {
   10493         if (s.parentNode) {
   10494           s.parentNode.removeChild(s);
   10495         }
   10496       });
   10497     },
   10498     cacheStyles: function() {
   10499       this.styles = this.findNodes(STYLE_SELECTOR + '[' + SCOPE_ATTR + ']');
   10500       this.styles.forEach(function(s) {
   10501         if (s.parentNode) {
   10502           s.parentNode.removeChild(s);
   10503         }
   10504       });
   10505     },
   10506     /**
   10507      * Takes external stylesheets loaded in an <element> element and moves
   10508      * their content into a <style> element inside the <element>'s template.
   10509      * The sheet is then removed from the <element>. This is done only so
   10510      * that if the element is loaded in the main document, the sheet does
   10511      * not become active.
   10512      * Note, ignores sheets with the attribute 'polymer-scope'.
   10513      * @param elementElement The <element> element to style.
   10514      */
   10515     installLocalSheets: function () {
   10516       var sheets = this.sheets.filter(function(s) {
   10517         return !s.hasAttribute(SCOPE_ATTR);
   10518       });
   10519       var content = this.templateContent();
   10520       if (content) {
   10521         var cssText = '';
   10522         sheets.forEach(function(sheet) {
   10523           cssText += cssTextFromSheet(sheet) + '\n';
   10524         });
   10525         if (cssText) {
   10526           var style = createStyleElement(cssText, this.ownerDocument);
   10527           content.insertBefore(style, content.firstChild);
   10528         }
   10529       }
   10530     },
   10531     findNodes: function(selector, matcher) {
   10532       var nodes = this.querySelectorAll(selector).array();
   10533       var content = this.templateContent();
   10534       if (content) {
   10535         var templateNodes = content.querySelectorAll(selector).array();
   10536         nodes = nodes.concat(templateNodes);
   10537       }
   10538       return matcher ? nodes.filter(matcher) : nodes;
   10539     },
   10540     /**
   10541      * Promotes external stylesheets and <style> elements with the attribute
   10542      * polymer-scope='global' into global scope.
   10543      * This is particularly useful for defining @keyframe rules which
   10544      * currently do not function in scoped or shadow style elements.
   10545      * (See wkb.ug/72462)
   10546      * @param elementElement The <element> element to style.
   10547     */
   10548     // TODO(sorvell): remove when wkb.ug/72462 is addressed.
   10549     installGlobalStyles: function() {
   10550       var style = this.styleForScope(STYLE_GLOBAL_SCOPE);
   10551       applyStyleToScope(style, document.head);
   10552     },
   10553     cssTextForScope: function(scopeDescriptor) {
   10554       var cssText = '';
   10555       // handle stylesheets
   10556       var selector = '[' + SCOPE_ATTR + '=' + scopeDescriptor + ']';
   10557       var matcher = function(s) {
   10558         return matchesSelector(s, selector);
   10559       };
   10560       var sheets = this.sheets.filter(matcher);
   10561       sheets.forEach(function(sheet) {
   10562         cssText += cssTextFromSheet(sheet) + '\n\n';
   10563       });
   10564       // handle cached style elements
   10565       var styles = this.styles.filter(matcher);
   10566       styles.forEach(function(style) {
   10567         cssText += style.textContent + '\n\n';
   10568       });
   10569       return cssText;
   10570     },
   10571     styleForScope: function(scopeDescriptor) {
   10572       var cssText = this.cssTextForScope(scopeDescriptor);
   10573       return this.cssTextToScopeStyle(cssText, scopeDescriptor);
   10574     },
   10575     cssTextToScopeStyle: function(cssText, scopeDescriptor) {
   10576       if (cssText) {
   10577         var style = createStyleElement(cssText);
   10578         style.setAttribute(STYLE_SCOPE_ATTRIBUTE, this.getAttribute('name') +
   10579             '-' + scopeDescriptor);
   10580         return style;
   10581       }
   10582     }
   10583   };
   10584 
   10585   function importRuleForSheet(sheet, baseUrl) {
   10586     var href = new URL(sheet.getAttribute('href'), baseUrl).href;
   10587     return '@import \'' + href + '\';';
   10588   }
   10589 
   10590   function applyStyleToScope(style, scope) {
   10591     if (style) {
   10592       if (scope === document) {
   10593         scope = document.head;
   10594       }
   10595       if (hasShadowDOMPolyfill) {
   10596         scope = document.head;
   10597       }
   10598       // TODO(sorvell): necessary for IE
   10599       // see https://connect.microsoft.com/IE/feedback/details/790212/
   10600       // cloning-a-style-element-and-adding-to-document-produces
   10601       // -unexpected-result#details
   10602       // var clone = style.cloneNode(true);
   10603       var clone = createStyleElement(style.textContent);
   10604       var attr = style.getAttribute(STYLE_SCOPE_ATTRIBUTE);
   10605       if (attr) {
   10606         clone.setAttribute(STYLE_SCOPE_ATTRIBUTE, attr);
   10607       }
   10608       // TODO(sorvell): probably too brittle; try to figure out
   10609       // where to put the element.
   10610       var refNode = scope.firstElementChild;
   10611       if (scope === document.head) {
   10612         var selector = 'style[' + STYLE_SCOPE_ATTRIBUTE + ']';
   10613         var s$ = document.head.querySelectorAll(selector);
   10614         if (s$.length) {
   10615           refNode = s$[s$.length-1].nextElementSibling;
   10616         }
   10617       }
   10618       scope.insertBefore(clone, refNode);
   10619     }
   10620   }
   10621 
   10622   function createStyleElement(cssText, scope) {
   10623     scope = scope || document;
   10624     scope = scope.createElement ? scope : scope.ownerDocument;
   10625     var style = scope.createElement('style');
   10626     style.textContent = cssText;
   10627     return style;
   10628   }
   10629 
   10630   function cssTextFromSheet(sheet) {
   10631     return (sheet && sheet.__resource) || '';
   10632   }
   10633 
   10634   function matchesSelector(node, inSelector) {
   10635     if (matches) {
   10636       return matches.call(node, inSelector);
   10637     }
   10638   }
   10639   var p = HTMLElement.prototype;
   10640   var matches = p.matches || p.matchesSelector || p.webkitMatchesSelector
   10641       || p.mozMatchesSelector;
   10642 
   10643   // exports
   10644 
   10645   scope.api.declaration.styles = styles;
   10646   scope.applyStyleToScope = applyStyleToScope;
   10647 
   10648 })(Polymer);
   10649 
   10650 (function(scope) {
   10651 
   10652   // imports
   10653 
   10654   var log = window.WebComponents ? WebComponents.flags.log : {};
   10655   var api = scope.api.instance.events;
   10656   var EVENT_PREFIX = api.EVENT_PREFIX;
   10657 
   10658   var mixedCaseEventTypes = {};
   10659   [
   10660     'webkitAnimationStart',
   10661     'webkitAnimationEnd',
   10662     'webkitTransitionEnd',
   10663     'DOMFocusOut',
   10664     'DOMFocusIn',
   10665     'DOMMouseScroll'
   10666   ].forEach(function(e) {
   10667     mixedCaseEventTypes[e.toLowerCase()] = e;
   10668   });
   10669 
   10670   // polymer-element declarative api: events feature
   10671   var events = {
   10672     parseHostEvents: function() {
   10673       // our delegates map
   10674       var delegates = this.prototype.eventDelegates;
   10675       // extract data from attributes into delegates
   10676       this.addAttributeDelegates(delegates);
   10677     },
   10678     addAttributeDelegates: function(delegates) {
   10679       // for each attribute
   10680       for (var i=0, a; a=this.attributes[i]; i++) {
   10681         // does it have magic marker identifying it as an event delegate?
   10682         if (this.hasEventPrefix(a.name)) {
   10683           // if so, add the info to delegates
   10684           delegates[this.removeEventPrefix(a.name)] = a.value.replace('{{', '')
   10685               .replace('}}', '').trim();
   10686         }
   10687       }
   10688     },
   10689     // starts with 'on-'
   10690     hasEventPrefix: function (n) {
   10691       return n && (n[0] === 'o') && (n[1] === 'n') && (n[2] === '-');
   10692     },
   10693     removeEventPrefix: function(n) {
   10694       return n.slice(prefixLength);
   10695     },
   10696     findController: function(node) {
   10697       while (node.parentNode) {
   10698         if (node.eventController) {
   10699           return node.eventController;
   10700         }
   10701         node = node.parentNode;
   10702       }
   10703       return node.host;
   10704     },
   10705     getEventHandler: function(controller, target, method) {
   10706       var events = this;
   10707       return function(e) {
   10708         if (!controller || !controller.PolymerBase) {
   10709           controller = events.findController(target);
   10710         }
   10711 
   10712         var args = [e, e.detail, e.currentTarget];
   10713         controller.dispatchMethod(controller, method, args);
   10714       };
   10715     },
   10716     prepareEventBinding: function(pathString, name, node) {
   10717       if (!this.hasEventPrefix(name))
   10718         return;
   10719 
   10720       var eventType = this.removeEventPrefix(name);
   10721       eventType = mixedCaseEventTypes[eventType] || eventType;
   10722 
   10723       var events = this;
   10724 
   10725       return function(model, node, oneTime) {
   10726         var handler = events.getEventHandler(undefined, node, pathString);
   10727         PolymerGestures.addEventListener(node, eventType, handler);
   10728 
   10729         if (oneTime)
   10730           return;
   10731 
   10732         // TODO(rafaelw): This is really pointless work. Aside from the cost
   10733         // of these allocations, NodeBind is going to setAttribute back to its
   10734         // current value. Fixing this would mean changing the TemplateBinding
   10735         // binding delegate API.
   10736         function bindingValue() {
   10737           return '{{ ' + pathString + ' }}';
   10738         }
   10739 
   10740         return {
   10741           open: bindingValue,
   10742           discardChanges: bindingValue,
   10743           close: function() {
   10744             PolymerGestures.removeEventListener(node, eventType, handler);
   10745           }
   10746         };
   10747       };
   10748     }
   10749   };
   10750 
   10751   var prefixLength = EVENT_PREFIX.length;
   10752 
   10753   // exports
   10754   scope.api.declaration.events = events;
   10755 
   10756 })(Polymer);
   10757 
   10758 (function(scope) {
   10759 
   10760   // element api
   10761 
   10762   var observationBlacklist = ['attribute'];
   10763 
   10764   var properties = {
   10765     inferObservers: function(prototype) {
   10766       // called before prototype.observe is chained to inherited object
   10767       var observe = prototype.observe, property;
   10768       for (var n in prototype) {
   10769         if (n.slice(-7) === 'Changed') {
   10770           property = n.slice(0, -7);
   10771           if (this.canObserveProperty(property)) {
   10772             if (!observe) {
   10773               observe  = (prototype.observe = {});
   10774             }
   10775             observe[property] = observe[property] || n;
   10776           }
   10777         }
   10778       }
   10779     },
   10780     canObserveProperty: function(property) {
   10781       return (observationBlacklist.indexOf(property) < 0);
   10782     },
   10783     explodeObservers: function(prototype) {
   10784       // called before prototype.observe is chained to inherited object
   10785       var o = prototype.observe;
   10786       if (o) {
   10787         var exploded = {};
   10788         for (var n in o) {
   10789           var names = n.split(' ');
   10790           for (var i=0, ni; ni=names[i]; i++) {
   10791             exploded[ni] = o[n];
   10792           }
   10793         }
   10794         prototype.observe = exploded;
   10795       }
   10796     },
   10797     optimizePropertyMaps: function(prototype) {
   10798       if (prototype.observe) {
   10799         // construct name list
   10800         var a = prototype._observeNames = [];
   10801         for (var n in prototype.observe) {
   10802           var names = n.split(' ');
   10803           for (var i=0, ni; ni=names[i]; i++) {
   10804             a.push(ni);
   10805           }
   10806         }
   10807       }
   10808       if (prototype.publish) {
   10809         // construct name list
   10810         var a = prototype._publishNames = [];
   10811         for (var n in prototype.publish) {
   10812           a.push(n);
   10813         }
   10814       }
   10815       if (prototype.computed) {
   10816         // construct name list
   10817         var a = prototype._computedNames = [];
   10818         for (var n in prototype.computed) {
   10819           a.push(n);
   10820         }
   10821       }
   10822     },
   10823     publishProperties: function(prototype, base) {
   10824       // if we have any properties to publish
   10825       var publish = prototype.publish;
   10826       if (publish) {
   10827         // transcribe `publish` entries onto own prototype
   10828         this.requireProperties(publish, prototype, base);
   10829         // warn and remove accessor names that are broken on some browsers
   10830         this.filterInvalidAccessorNames(publish);
   10831         // construct map of lower-cased property names
   10832         prototype._publishLC = this.lowerCaseMap(publish);
   10833       }
   10834       var computed = prototype.computed;
   10835       if (computed) {
   10836         // warn and remove accessor names that are broken on some browsers
   10837         this.filterInvalidAccessorNames(computed);
   10838       }
   10839     },
   10840     // Publishing/computing a property where the name might conflict with a
   10841     // browser property is not currently supported to help users of Polymer
   10842     // avoid browser bugs:
   10843     //
   10844     // https://code.google.com/p/chromium/issues/detail?id=43394
   10845     // https://bugs.webkit.org/show_bug.cgi?id=49739
   10846     //
   10847     // We can lift this restriction when those bugs are fixed.
   10848     filterInvalidAccessorNames: function(propertyNames) {
   10849       for (var name in propertyNames) {
   10850         // Check if the name is in our blacklist.
   10851         if (this.propertyNameBlacklist[name]) {
   10852           console.warn('Cannot define property "' + name + '" for element "' +
   10853             this.name + '" because it has the same name as an HTMLElement ' +
   10854             'property, and not all browsers support overriding that. ' +
   10855             'Consider giving it a different name.');
   10856           // Remove the invalid accessor from the list.
   10857           delete propertyNames[name];
   10858         }
   10859       }
   10860     },
   10861     //
   10862     // `name: value` entries in the `publish` object may need to generate
   10863     // matching properties on the prototype.
   10864     //
   10865     // Values that are objects may have a `reflect` property, which
   10866     // signals that the value describes property control metadata.
   10867     // In metadata objects, the prototype default value (if any)
   10868     // is encoded in the `value` property.
   10869     //
   10870     // publish: {
   10871     //   foo: 5,
   10872     //   bar: {value: true, reflect: true},
   10873     //   zot: {}
   10874     // }
   10875     //
   10876     // `reflect` metadata property controls whether changes to the property
   10877     // are reflected back to the attribute (default false).
   10878     //
   10879     // A value is stored on the prototype unless it's === `undefined`,
   10880     // in which case the base chain is checked for a value.
   10881     // If the basal value is also undefined, `null` is stored on the prototype.
   10882     //
   10883     // The reflection data is stored on another prototype object, `reflect`
   10884     // which also can be specified directly.
   10885     //
   10886     // reflect: {
   10887     //   foo: true
   10888     // }
   10889     //
   10890     requireProperties: function(propertyInfos, prototype, base) {
   10891       // per-prototype storage for reflected properties
   10892       prototype.reflect = prototype.reflect || {};
   10893       // ensure a prototype value for each property
   10894       // and update the property's reflect to attribute status
   10895       for (var n in propertyInfos) {
   10896         var value = propertyInfos[n];
   10897         // value has metadata if it has a `reflect` property
   10898         if (value && value.reflect !== undefined) {
   10899           prototype.reflect[n] = Boolean(value.reflect);
   10900           value = value.value;
   10901         }
   10902         // only set a value if one is specified
   10903         if (value !== undefined) {
   10904           prototype[n] = value;
   10905         }
   10906       }
   10907     },
   10908     lowerCaseMap: function(properties) {
   10909       var map = {};
   10910       for (var n in properties) {
   10911         map[n.toLowerCase()] = n;
   10912       }
   10913       return map;
   10914     },
   10915     createPropertyAccessor: function(name, ignoreWrites) {
   10916       var proto = this.prototype;
   10917 
   10918       var privateName = name + '_';
   10919       var privateObservable  = name + 'Observable_';
   10920       proto[privateName] = proto[name];
   10921 
   10922       Object.defineProperty(proto, name, {
   10923         get: function() {
   10924           var observable = this[privateObservable];
   10925           if (observable)
   10926             observable.deliver();
   10927 
   10928           return this[privateName];
   10929         },
   10930         set: function(value) {
   10931           if (ignoreWrites) {
   10932             return this[privateName];
   10933           }
   10934 
   10935           var observable = this[privateObservable];
   10936           if (observable) {
   10937             observable.setValue(value);
   10938             return;
   10939           }
   10940 
   10941           var oldValue = this[privateName];
   10942           this[privateName] = value;
   10943           this.emitPropertyChangeRecord(name, value, oldValue);
   10944 
   10945           return value;
   10946         },
   10947         configurable: true
   10948       });
   10949     },
   10950     createPropertyAccessors: function(prototype) {
   10951       var n$ = prototype._computedNames;
   10952       if (n$ && n$.length) {
   10953         for (var i=0, l=n$.length, n, fn; (i<l) && (n=n$[i]); i++) {
   10954           this.createPropertyAccessor(n, true);
   10955         }
   10956       }
   10957       var n$ = prototype._publishNames;
   10958       if (n$ && n$.length) {
   10959         for (var i=0, l=n$.length, n, fn; (i<l) && (n=n$[i]); i++) {
   10960           // If the property is computed and published, the accessor is created
   10961           // above.
   10962           if (!prototype.computed || !prototype.computed[n]) {
   10963             this.createPropertyAccessor(n);
   10964           }
   10965         }
   10966       }
   10967     },
   10968     // This list contains some property names that people commonly want to use,
   10969     // but won't work because of Chrome/Safari bugs. It isn't an exhaustive
   10970     // list. In particular it doesn't contain any property names found on
   10971     // subtypes of HTMLElement (e.g. name, value). Rather it attempts to catch
   10972     // some common cases.
   10973     propertyNameBlacklist: {
   10974       children: 1,
   10975       'class': 1,
   10976       id: 1,
   10977       hidden: 1,
   10978       style: 1,
   10979       title: 1,
   10980     }
   10981   };
   10982 
   10983   // exports
   10984 
   10985   scope.api.declaration.properties = properties;
   10986 
   10987 })(Polymer);
   10988 
   10989 (function(scope) {
   10990 
   10991   // magic words
   10992 
   10993   var ATTRIBUTES_ATTRIBUTE = 'attributes';
   10994   var ATTRIBUTES_REGEX = /\s|,/;
   10995 
   10996   // attributes api
   10997 
   10998   var attributes = {
   10999 
   11000     inheritAttributesObjects: function(prototype) {
   11001       // chain our lower-cased publish map to the inherited version
   11002       this.inheritObject(prototype, 'publishLC');
   11003       // chain our instance attributes map to the inherited version
   11004       this.inheritObject(prototype, '_instanceAttributes');
   11005     },
   11006 
   11007     publishAttributes: function(prototype, base) {
   11008       // merge names from 'attributes' attribute into the 'publish' object
   11009       var attributes = this.getAttribute(ATTRIBUTES_ATTRIBUTE);
   11010       if (attributes) {
   11011         // create a `publish` object if needed.
   11012         // the `publish` object is only relevant to this prototype, the
   11013         // publishing logic in `declaration/properties.js` is responsible for
   11014         // managing property values on the prototype chain.
   11015         // TODO(sjmiles): the `publish` object is later chained to it's
   11016         //                ancestor object, presumably this is only for
   11017         //                reflection or other non-library uses.
   11018         var publish = prototype.publish || (prototype.publish = {});
   11019         // names='a b c' or names='a,b,c'
   11020         var names = attributes.split(ATTRIBUTES_REGEX);
   11021         // record each name for publishing
   11022         for (var i=0, l=names.length, n; i<l; i++) {
   11023           // remove excess ws
   11024           n = names[i].trim();
   11025           // looks weird, but causes n to exist on `publish` if it does not;
   11026           // a more careful test would need expensive `in` operator
   11027           if (n && publish[n] === undefined) {
   11028             publish[n] = undefined;
   11029           }
   11030         }
   11031       }
   11032     },
   11033 
   11034     // record clonable attributes from <element>
   11035     accumulateInstanceAttributes: function() {
   11036       // inherit instance attributes
   11037       var clonable = this.prototype._instanceAttributes;
   11038       // merge attributes from element
   11039       var a$ = this.attributes;
   11040       for (var i=0, l=a$.length, a; (i<l) && (a=a$[i]); i++) {
   11041         if (this.isInstanceAttribute(a.name)) {
   11042           clonable[a.name] = a.value;
   11043         }
   11044       }
   11045     },
   11046 
   11047     isInstanceAttribute: function(name) {
   11048       return !this.blackList[name] && name.slice(0,3) !== 'on-';
   11049     },
   11050 
   11051     // do not clone these attributes onto instances
   11052     blackList: {
   11053       name: 1,
   11054       'extends': 1,
   11055       constructor: 1,
   11056       noscript: 1,
   11057       assetpath: 1,
   11058       'cache-csstext': 1
   11059     }
   11060 
   11061   };
   11062 
   11063   // add ATTRIBUTES_ATTRIBUTE to the blacklist
   11064   attributes.blackList[ATTRIBUTES_ATTRIBUTE] = 1;
   11065 
   11066   // exports
   11067 
   11068   scope.api.declaration.attributes = attributes;
   11069 
   11070 })(Polymer);
   11071 
   11072 (function(scope) {
   11073 
   11074   // imports
   11075   var events = scope.api.declaration.events;
   11076 
   11077   var syntax = new PolymerExpressions();
   11078   var prepareBinding = syntax.prepareBinding;
   11079 
   11080   // Polymer takes a first crack at the binding to see if it's a declarative
   11081   // event handler.
   11082   syntax.prepareBinding = function(pathString, name, node) {
   11083     return events.prepareEventBinding(pathString, name, node) ||
   11084            prepareBinding.call(syntax, pathString, name, node);
   11085   };
   11086 
   11087   // declaration api supporting mdv
   11088   var mdv = {
   11089     syntax: syntax,
   11090     fetchTemplate: function() {
   11091       return this.querySelector('template');
   11092     },
   11093     templateContent: function() {
   11094       var template = this.fetchTemplate();
   11095       return template && template.content;
   11096     },
   11097     installBindingDelegate: function(template) {
   11098       if (template) {
   11099         template.bindingDelegate = this.syntax;
   11100       }
   11101     }
   11102   };
   11103 
   11104   // exports
   11105   scope.api.declaration.mdv = mdv;
   11106 
   11107 })(Polymer);
   11108 
   11109 (function(scope) {
   11110 
   11111   // imports
   11112 
   11113   var api = scope.api;
   11114   var isBase = scope.isBase;
   11115   var extend = scope.extend;
   11116 
   11117   var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
   11118 
   11119   // prototype api
   11120 
   11121   var prototype = {
   11122 
   11123     register: function(name, extendeeName) {
   11124       // build prototype combining extendee, Polymer base, and named api
   11125       this.buildPrototype(name, extendeeName);
   11126       // register our custom element with the platform
   11127       this.registerPrototype(name, extendeeName);
   11128       // reference constructor in a global named by 'constructor' attribute
   11129       this.publishConstructor();
   11130     },
   11131 
   11132     buildPrototype: function(name, extendeeName) {
   11133       // get our custom prototype (before chaining)
   11134       var extension = scope.getRegisteredPrototype(name);
   11135       // get basal prototype
   11136       var base = this.generateBasePrototype(extendeeName);
   11137       // implement declarative features
   11138       this.desugarBeforeChaining(extension, base);
   11139       // join prototypes
   11140       this.prototype = this.chainPrototypes(extension, base);
   11141       // more declarative features
   11142       this.desugarAfterChaining(name, extendeeName);
   11143     },
   11144 
   11145     desugarBeforeChaining: function(prototype, base) {
   11146       // back reference declaration element
   11147       // TODO(sjmiles): replace `element` with `elementElement` or `declaration`
   11148       prototype.element = this;
   11149       // transcribe `attributes` declarations onto own prototype's `publish`
   11150       this.publishAttributes(prototype, base);
   11151       // `publish` properties to the prototype and to attribute watch
   11152       this.publishProperties(prototype, base);
   11153       // infer observers for `observe` list based on method names
   11154       this.inferObservers(prototype);
   11155       // desugar compound observer syntax, e.g. 'a b c'
   11156       this.explodeObservers(prototype);
   11157     },
   11158 
   11159     chainPrototypes: function(prototype, base) {
   11160       // chain various meta-data objects to inherited versions
   11161       this.inheritMetaData(prototype, base);
   11162       // chain custom api to inherited
   11163       var chained = this.chainObject(prototype, base);
   11164       // x-platform fixup
   11165       ensurePrototypeTraversal(chained);
   11166       return chained;
   11167     },
   11168 
   11169     inheritMetaData: function(prototype, base) {
   11170       // chain observe object to inherited
   11171       this.inheritObject('observe', prototype, base);
   11172       // chain publish object to inherited
   11173       this.inheritObject('publish', prototype, base);
   11174       // chain reflect object to inherited
   11175       this.inheritObject('reflect', prototype, base);
   11176       // chain our lower-cased publish map to the inherited version
   11177       this.inheritObject('_publishLC', prototype, base);
   11178       // chain our instance attributes map to the inherited version
   11179       this.inheritObject('_instanceAttributes', prototype, base);
   11180       // chain our event delegates map to the inherited version
   11181       this.inheritObject('eventDelegates', prototype, base);
   11182     },
   11183 
   11184     // implement various declarative features
   11185     desugarAfterChaining: function(name, extendee) {
   11186       // build side-chained lists to optimize iterations
   11187       this.optimizePropertyMaps(this.prototype);
   11188       this.createPropertyAccessors(this.prototype);
   11189       // install mdv delegate on template
   11190       this.installBindingDelegate(this.fetchTemplate());
   11191       // install external stylesheets as if they are inline
   11192       this.installSheets();
   11193       // adjust any paths in dom from imports
   11194       this.resolveElementPaths(this);
   11195       // compile list of attributes to copy to instances
   11196       this.accumulateInstanceAttributes();
   11197       // parse on-* delegates declared on `this` element
   11198       this.parseHostEvents();
   11199       //
   11200       // install a helper method this.resolvePath to aid in
   11201       // setting resource urls. e.g.
   11202       // this.$.image.src = this.resolvePath('images/foo.png')
   11203       this.addResolvePathApi();
   11204       // under ShadowDOMPolyfill, transforms to approximate missing CSS features
   11205       if (hasShadowDOMPolyfill) {
   11206         WebComponents.ShadowCSS.shimStyling(this.templateContent(), name,
   11207           extendee);
   11208       }
   11209       // allow custom element access to the declarative context
   11210       if (this.prototype.registerCallback) {
   11211         this.prototype.registerCallback(this);
   11212       }
   11213     },
   11214 
   11215     // if a named constructor is requested in element, map a reference
   11216     // to the constructor to the given symbol
   11217     publishConstructor: function() {
   11218       var symbol = this.getAttribute('constructor');
   11219       if (symbol) {
   11220         window[symbol] = this.ctor;
   11221       }
   11222     },
   11223 
   11224     // build prototype combining extendee, Polymer base, and named api
   11225     generateBasePrototype: function(extnds) {
   11226       var prototype = this.findBasePrototype(extnds);
   11227       if (!prototype) {
   11228         // create a prototype based on tag-name extension
   11229         var prototype = HTMLElement.getPrototypeForTag(extnds);
   11230         // insert base api in inheritance chain (if needed)
   11231         prototype = this.ensureBaseApi(prototype);
   11232         // memoize this base
   11233         memoizedBases[extnds] = prototype;
   11234       }
   11235       return prototype;
   11236     },
   11237 
   11238     findBasePrototype: function(name) {
   11239       return memoizedBases[name];
   11240     },
   11241 
   11242     // install Polymer instance api into prototype chain, as needed
   11243     ensureBaseApi: function(prototype) {
   11244       if (prototype.PolymerBase) {
   11245         return prototype;
   11246       }
   11247       var extended = Object.create(prototype);
   11248       // we need a unique copy of base api for each base prototype
   11249       // therefore we 'extend' here instead of simply chaining
   11250       api.publish(api.instance, extended);
   11251       // TODO(sjmiles): sharing methods across prototype chains is
   11252       // not supported by 'super' implementation which optimizes
   11253       // by memoizing prototype relationships.
   11254       // Probably we should have a version of 'extend' that is
   11255       // share-aware: it could study the text of each function,
   11256       // look for usage of 'super', and wrap those functions in
   11257       // closures.
   11258       // As of now, there is only one problematic method, so
   11259       // we just patch it manually.
   11260       // To avoid re-entrancy problems, the special super method
   11261       // installed is called `mixinSuper` and the mixin method
   11262       // must use this method instead of the default `super`.
   11263       this.mixinMethod(extended, prototype, api.instance.mdv, 'bind');
   11264       // return buffed-up prototype
   11265       return extended;
   11266     },
   11267 
   11268     mixinMethod: function(extended, prototype, api, name) {
   11269       var $super = function(args) {
   11270         return prototype[name].apply(this, args);
   11271       };
   11272       extended[name] = function() {
   11273         this.mixinSuper = $super;
   11274         return api[name].apply(this, arguments);
   11275       }
   11276     },
   11277 
   11278     // ensure prototype[name] inherits from a prototype.prototype[name]
   11279     inheritObject: function(name, prototype, base) {
   11280       // require an object
   11281       var source = prototype[name] || {};
   11282       // chain inherited properties onto a new object
   11283       prototype[name] = this.chainObject(source, base[name]);
   11284     },
   11285 
   11286     // register 'prototype' to custom element 'name', store constructor
   11287     registerPrototype: function(name, extendee) {
   11288       var info = {
   11289         prototype: this.prototype
   11290       }
   11291       // native element must be specified in extends
   11292       var typeExtension = this.findTypeExtension(extendee);
   11293       if (typeExtension) {
   11294         info.extends = typeExtension;
   11295       }
   11296       // register the prototype with HTMLElement for name lookup
   11297       HTMLElement.register(name, this.prototype);
   11298       // register the custom type
   11299       this.ctor = document.registerElement(name, info);
   11300     },
   11301 
   11302     findTypeExtension: function(name) {
   11303       if (name && name.indexOf('-') < 0) {
   11304         return name;
   11305       } else {
   11306         var p = this.findBasePrototype(name);
   11307         if (p.element) {
   11308           return this.findTypeExtension(p.element.extends);
   11309         }
   11310       }
   11311     }
   11312 
   11313   };
   11314 
   11315   // memoize base prototypes
   11316   var memoizedBases = {};
   11317 
   11318   // implementation of 'chainObject' depends on support for __proto__
   11319   if (Object.__proto__) {
   11320     prototype.chainObject = function(object, inherited) {
   11321       if (object && inherited && object !== inherited) {
   11322         object.__proto__ = inherited;
   11323       }
   11324       return object;
   11325     }
   11326   } else {
   11327     prototype.chainObject = function(object, inherited) {
   11328       if (object && inherited && object !== inherited) {
   11329         var chained = Object.create(inherited);
   11330         object = extend(chained, object);
   11331       }
   11332       return object;
   11333     }
   11334   }
   11335 
   11336   // On platforms that do not support __proto__ (versions of IE), the prototype
   11337   // chain of a custom element is simulated via installation of __proto__.
   11338   // Although custom elements manages this, we install it here so it's
   11339   // available during desugaring.
   11340   function ensurePrototypeTraversal(prototype) {
   11341     if (!Object.__proto__) {
   11342       var ancestor = Object.getPrototypeOf(prototype);
   11343       prototype.__proto__ = ancestor;
   11344       if (isBase(ancestor)) {
   11345         ancestor.__proto__ = Object.getPrototypeOf(ancestor);
   11346       }
   11347     }
   11348   }
   11349 
   11350   // exports
   11351 
   11352   api.declaration.prototype = prototype;
   11353 
   11354 })(Polymer);
   11355 
   11356 (function(scope) {
   11357 
   11358   /*
   11359 
   11360     Elements are added to a registration queue so that they register in
   11361     the proper order at the appropriate time. We do this for a few reasons:
   11362 
   11363     * to enable elements to load resources (like stylesheets)
   11364     asynchronously. We need to do this until the platform provides an efficient
   11365     alternative. One issue is that remote @import stylesheets are
   11366     re-fetched whenever stamped into a shadowRoot.
   11367 
   11368     * to ensure elements loaded 'at the same time' (e.g. via some set of
   11369     imports) are registered as a batch. This allows elements to be enured from
   11370     upgrade ordering as long as they query the dom tree 1 task after
   11371     upgrade (aka domReady). This is a performance tradeoff. On the one hand,
   11372     elements that could register while imports are loading are prevented from
   11373     doing so. On the other, grouping upgrades into a single task means less
   11374     incremental work (for example style recalcs),  Also, we can ensure the
   11375     document is in a known state at the single quantum of time when
   11376     elements upgrade.
   11377 
   11378   */
   11379   var queue = {
   11380 
   11381     // tell the queue to wait for an element to be ready
   11382     wait: function(element) {
   11383       if (!element.__queue) {
   11384         element.__queue = {};
   11385         elements.push(element);
   11386       }
   11387     },
   11388 
   11389     // enqueue an element to the next spot in the queue.
   11390     enqueue: function(element, check, go) {
   11391       var shouldAdd = element.__queue && !element.__queue.check;
   11392       if (shouldAdd) {
   11393         queueForElement(element).push(element);
   11394         element.__queue.check = check;
   11395         element.__queue.go = go;
   11396       }
   11397       return (this.indexOf(element) !== 0);
   11398     },
   11399 
   11400     indexOf: function(element) {
   11401       var i = queueForElement(element).indexOf(element);
   11402       if (i >= 0 && document.contains(element)) {
   11403         i += (HTMLImports.useNative || HTMLImports.ready) ?
   11404           importQueue.length : 1e9;
   11405       }
   11406       return i;
   11407     },
   11408 
   11409     // tell the queue an element is ready to be registered
   11410     go: function(element) {
   11411       var readied = this.remove(element);
   11412       if (readied) {
   11413         element.__queue.flushable = true;
   11414         this.addToFlushQueue(readied);
   11415         this.check();
   11416       }
   11417     },
   11418 
   11419     remove: function(element) {
   11420       var i = this.indexOf(element);
   11421       if (i !== 0) {
   11422         //console.warn('queue order wrong', i);
   11423         return;
   11424       }
   11425       return queueForElement(element).shift();
   11426     },
   11427 
   11428     check: function() {
   11429       // next
   11430       var element = this.nextElement();
   11431       if (element) {
   11432         element.__queue.check.call(element);
   11433       }
   11434       if (this.canReady()) {
   11435         this.ready();
   11436         return true;
   11437       }
   11438     },
   11439 
   11440     nextElement: function() {
   11441       return nextQueued();
   11442     },
   11443 
   11444     canReady: function() {
   11445       return !this.waitToReady && this.isEmpty();
   11446     },
   11447 
   11448     isEmpty: function() {
   11449       for (var i=0, l=elements.length, e; (i<l) &&
   11450           (e=elements[i]); i++) {
   11451         if (e.__queue && !e.__queue.flushable) {
   11452           return;
   11453         }
   11454       }
   11455       return true;
   11456     },
   11457 
   11458     addToFlushQueue: function(element) {
   11459       flushQueue.push(element);
   11460     },
   11461 
   11462     flush: function() {
   11463       // prevent re-entrance
   11464       if (this.flushing) {
   11465         return;
   11466       }
   11467       this.flushing = true;
   11468       var element;
   11469       while (flushQueue.length) {
   11470         element = flushQueue.shift();
   11471         element.__queue.go.call(element);
   11472         element.__queue = null;
   11473       }
   11474       this.flushing = false;
   11475     },
   11476 
   11477     ready: function() {
   11478       // TODO(sorvell): As an optimization, turn off CE polyfill upgrading
   11479       // while registering. This way we avoid having to upgrade each document
   11480       // piecemeal per registration and can instead register all elements
   11481       // and upgrade once in a batch. Without this optimization, upgrade time
   11482       // degrades significantly when SD polyfill is used. This is mainly because
   11483       // querying the document tree for elements is slow under the SD polyfill.
   11484       var polyfillWasReady = CustomElements.ready;
   11485       CustomElements.ready = false;
   11486       this.flush();
   11487       if (!CustomElements.useNative) {
   11488         CustomElements.upgradeDocumentTree(document);
   11489       }
   11490       CustomElements.ready = polyfillWasReady;
   11491       Polymer.flush();
   11492       requestAnimationFrame(this.flushReadyCallbacks);
   11493     },
   11494 
   11495     addReadyCallback: function(callback) {
   11496       if (callback) {
   11497         readyCallbacks.push(callback);
   11498       }
   11499     },
   11500 
   11501     flushReadyCallbacks: function() {
   11502       if (readyCallbacks) {
   11503         var fn;
   11504         while (readyCallbacks.length) {
   11505           fn = readyCallbacks.shift();
   11506           fn();
   11507         }
   11508       }
   11509     },
   11510 
   11511     /**
   11512     Returns a list of elements that have had polymer-elements created but
   11513     are not yet ready to register. The list is an array of element definitions.
   11514     */
   11515     waitingFor: function() {
   11516       var e$ = [];
   11517       for (var i=0, l=elements.length, e; (i<l) &&
   11518           (e=elements[i]); i++) {
   11519         if (e.__queue && !e.__queue.flushable) {
   11520           e$.push(e);
   11521         }
   11522       }
   11523       return e$;
   11524     },
   11525 
   11526     waitToReady: true
   11527 
   11528   };
   11529 
   11530   var elements = [];
   11531   var flushQueue = [];
   11532   var importQueue = [];
   11533   var mainQueue = [];
   11534   var readyCallbacks = [];
   11535 
   11536   function queueForElement(element) {
   11537     return document.contains(element) ? mainQueue : importQueue;
   11538   }
   11539 
   11540   function nextQueued() {
   11541     return importQueue.length ? importQueue[0] : mainQueue[0];
   11542   }
   11543 
   11544   function whenReady(callback) {
   11545     queue.waitToReady = true;
   11546     Polymer.endOfMicrotask(function() {
   11547       HTMLImports.whenReady(function() {
   11548         queue.addReadyCallback(callback);
   11549         queue.waitToReady = false;
   11550         queue.check();
   11551     });
   11552     });
   11553   }
   11554 
   11555   /**
   11556     Forces polymer to register any pending elements. Can be used to abort
   11557     waiting for elements that are partially defined.
   11558     @param timeout {Integer} Optional timeout in milliseconds
   11559   */
   11560   function forceReady(timeout) {
   11561     if (timeout === undefined) {
   11562       queue.ready();
   11563       return;
   11564     }
   11565     var handle = setTimeout(function() {
   11566       queue.ready();
   11567     }, timeout);
   11568     Polymer.whenReady(function() {
   11569       clearTimeout(handle);
   11570     });
   11571   }
   11572 
   11573   // exports
   11574   scope.elements = elements;
   11575   scope.waitingFor = queue.waitingFor.bind(queue);
   11576   scope.forceReady = forceReady;
   11577   scope.queue = queue;
   11578   scope.whenReady = scope.whenPolymerReady = whenReady;
   11579 })(Polymer);
   11580 
   11581 (function(scope) {
   11582 
   11583   // imports
   11584 
   11585   var extend = scope.extend;
   11586   var api = scope.api;
   11587   var queue = scope.queue;
   11588   var whenReady = scope.whenReady;
   11589   var getRegisteredPrototype = scope.getRegisteredPrototype;
   11590   var waitingForPrototype = scope.waitingForPrototype;
   11591 
   11592   // declarative implementation: <polymer-element>
   11593 
   11594   var prototype = extend(Object.create(HTMLElement.prototype), {
   11595 
   11596     createdCallback: function() {
   11597       if (this.getAttribute('name')) {
   11598         this.init();
   11599       }
   11600     },
   11601 
   11602     init: function() {
   11603       // fetch declared values
   11604       this.name = this.getAttribute('name');
   11605       this.extends = this.getAttribute('extends');
   11606       queue.wait(this);
   11607       // initiate any async resource fetches
   11608       this.loadResources();
   11609       // register when all constraints are met
   11610       this.registerWhenReady();
   11611     },
   11612 
   11613     // TODO(sorvell): we currently queue in the order the prototypes are
   11614     // registered, but we should queue in the order that polymer-elements
   11615     // are registered. We are currently blocked from doing this based on
   11616     // crbug.com/395686.
   11617     registerWhenReady: function() {
   11618      if (this.registered
   11619        || this.waitingForPrototype(this.name)
   11620        || this.waitingForQueue()
   11621        || this.waitingForResources()) {
   11622           return;
   11623       }
   11624       queue.go(this);
   11625     },
   11626 
   11627     _register: function() {
   11628       //console.log('registering', this.name);
   11629       // warn if extending from a custom element not registered via Polymer
   11630       if (isCustomTag(this.extends) && !isRegistered(this.extends)) {
   11631         console.warn('%s is attempting to extend %s, an unregistered element ' +
   11632             'or one that was not registered with Polymer.', this.name,
   11633             this.extends);
   11634       }
   11635       this.register(this.name, this.extends);
   11636       this.registered = true;
   11637     },
   11638 
   11639     waitingForPrototype: function(name) {
   11640       if (!getRegisteredPrototype(name)) {
   11641         // then wait for a prototype
   11642         waitingForPrototype(name, this);
   11643         // emulate script if user is not supplying one
   11644         this.handleNoScript(name);
   11645         // prototype not ready yet
   11646         return true;
   11647       }
   11648     },
   11649 
   11650     handleNoScript: function(name) {
   11651       // if explicitly marked as 'noscript'
   11652       if (this.hasAttribute('noscript') && !this.noscript) {
   11653         this.noscript = true;
   11654         // imperative element registration
   11655         Polymer(name);
   11656       }
   11657     },
   11658 
   11659     waitingForResources: function() {
   11660       return this._needsResources;
   11661     },
   11662 
   11663     // NOTE: Elements must be queued in proper order for inheritance/composition
   11664     // dependency resolution. Previously this was enforced for inheritance,
   11665     // and by rule for composition. It's now entirely by rule.
   11666     waitingForQueue: function() {
   11667       return queue.enqueue(this, this.registerWhenReady, this._register);
   11668     },
   11669 
   11670     loadResources: function() {
   11671       this._needsResources = true;
   11672       this.loadStyles(function() {
   11673         this._needsResources = false;
   11674         this.registerWhenReady();
   11675       }.bind(this));
   11676     }
   11677 
   11678   });
   11679 
   11680   // semi-pluggable APIs
   11681 
   11682   // TODO(sjmiles): should be fully pluggable (aka decoupled, currently
   11683   // the various plugins are allowed to depend on each other directly)
   11684   api.publish(api.declaration, prototype);
   11685 
   11686   // utility and bookkeeping
   11687 
   11688   function isRegistered(name) {
   11689     return Boolean(HTMLElement.getPrototypeForTag(name));
   11690   }
   11691 
   11692   function isCustomTag(name) {
   11693     return (name && name.indexOf('-') >= 0);
   11694   }
   11695 
   11696   // boot tasks
   11697 
   11698   whenReady(function() {
   11699     document.body.removeAttribute('unresolved');
   11700     document.dispatchEvent(
   11701       new CustomEvent('polymer-ready', {bubbles: true})
   11702     );
   11703   });
   11704 
   11705   // register polymer-element with document
   11706 
   11707   document.registerElement('polymer-element', {prototype: prototype});
   11708 
   11709 })(Polymer);
   11710 
   11711 (function(scope) {
   11712 
   11713 /**
   11714  * @class Polymer
   11715  */
   11716 
   11717 var whenReady = scope.whenReady;
   11718 
   11719 /**
   11720  * Loads the set of HTMLImports contained in `node`. Notifies when all
   11721  * the imports have loaded by calling the `callback` function argument.
   11722  * This method can be used to lazily load imports. For example, given a
   11723  * template:
   11724  *
   11725  *     <template>
   11726  *       <link rel="import" href="my-import1.html">
   11727  *       <link rel="import" href="my-import2.html">
   11728  *     </template>
   11729  *
   11730  *     Polymer.importElements(template.content, function() {
   11731  *       console.log('imports lazily loaded');
   11732  *     });
   11733  *
   11734  * @method importElements
   11735  * @param {Node} node Node containing the HTMLImports to load.
   11736  * @param {Function} callback Callback called when all imports have loaded.
   11737  */
   11738 function importElements(node, callback) {
   11739   if (node) {
   11740     document.head.appendChild(node);
   11741     whenReady(callback);
   11742   } else if (callback) {
   11743     callback();
   11744   }
   11745 }
   11746 
   11747 /**
   11748  * Loads an HTMLImport for each url specified in the `urls` array.
   11749  * Notifies when all the imports have loaded by calling the `callback`
   11750  * function argument. This method can be used to lazily load imports.
   11751  * For example,
   11752  *
   11753  *     Polymer.import(['my-import1.html', 'my-import2.html'], function() {
   11754  *       console.log('imports lazily loaded');
   11755  *     });
   11756  *
   11757  * @method import
   11758  * @param {Array} urls Array of urls to load as HTMLImports.
   11759  * @param {Function} callback Callback called when all imports have loaded.
   11760  */
   11761 function _import(urls, callback) {
   11762   if (urls && urls.length) {
   11763       var frag = document.createDocumentFragment();
   11764       for (var i=0, l=urls.length, url, link; (i<l) && (url=urls[i]); i++) {
   11765         link = document.createElement('link');
   11766         link.rel = 'import';
   11767         link.href = url;
   11768         frag.appendChild(link);
   11769       }
   11770       importElements(frag, callback);
   11771   } else if (callback) {
   11772     callback();
   11773   }
   11774 }
   11775 
   11776 // exports
   11777 scope.import = _import;
   11778 scope.importElements = importElements;
   11779 
   11780 })(Polymer);
   11781 
   11782 /**
   11783  * The `auto-binding` element extends the template element. It provides a quick
   11784  * and easy way to do data binding without the need to setup a model.
   11785  * The `auto-binding` element itself serves as the model and controller for the
   11786  * elements it contains. Both data and event handlers can be bound.
   11787  *
   11788  * The `auto-binding` element acts just like a template that is bound to
   11789  * a model. It stamps its content in the dom adjacent to itself. When the
   11790  * content is stamped, the `template-bound` event is fired.
   11791  *
   11792  * Example:
   11793  *
   11794  *     <template is="auto-binding">
   11795  *       <div>Say something: <input value="{{value}}"></div>
   11796  *       <div>You said: {{value}}</div>
   11797  *       <button on-tap="{{buttonTap}}">Tap me!</button>
   11798  *     </template>
   11799  *     <script>
   11800  *       var template = document.querySelector('template');
   11801  *       template.value = 'something';
   11802  *       template.buttonTap = function() {
   11803  *         console.log('tap!');
   11804  *       };
   11805  *     </script>
   11806  *
   11807  * @module Polymer
   11808  * @status stable
   11809 */
   11810 
   11811 (function() {
   11812 
   11813   var element = document.createElement('polymer-element');
   11814   element.setAttribute('name', 'auto-binding');
   11815   element.setAttribute('extends', 'template');
   11816   element.init();
   11817 
   11818   Polymer('auto-binding', {
   11819 
   11820     createdCallback: function() {
   11821       this.syntax = this.bindingDelegate = this.makeSyntax();
   11822       // delay stamping until polymer-ready so that auto-binding is not
   11823       // required to load last.
   11824       Polymer.whenPolymerReady(function() {
   11825         this.model = this;
   11826         this.setAttribute('bind', '');
   11827         // we don't bother with an explicit signal here, we could ust a MO
   11828         // if necessary
   11829         this.async(function() {
   11830           // note: this will marshall *all* the elements in the parentNode
   11831           // rather than just stamped ones. We'd need to use createInstance
   11832           // to fix this or something else fancier.
   11833           this.marshalNodeReferences(this.parentNode);
   11834           // template stamping is asynchronous so stamping isn't complete
   11835           // by polymer-ready; fire an event so users can use stamped elements
   11836           this.fire('template-bound');
   11837         });
   11838       }.bind(this));
   11839     },
   11840 
   11841     makeSyntax: function() {
   11842       var events = Object.create(Polymer.api.declaration.events);
   11843       var self = this;
   11844       events.findController = function() { return self.model; };
   11845 
   11846       var syntax = new PolymerExpressions();
   11847       var prepareBinding = syntax.prepareBinding;
   11848       syntax.prepareBinding = function(pathString, name, node) {
   11849         return events.prepareEventBinding(pathString, name, node) ||
   11850                prepareBinding.call(syntax, pathString, name, node);
   11851       };
   11852       return syntax;
   11853     }
   11854 
   11855   });
   11856 
   11857 })();
   11858