| OLD | NEW | 
| (Empty) |  | 
 |      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://polyme
       r.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/CON
       TRIBUTORS.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/PA
       TENTS.txt | 
 |      9  */ | 
 |     10 // @version 0.5.1 | 
 |     11 window.PolymerGestures = {}; | 
 |     12  | 
 |     13 (function(scope) { | 
 |     14   var HAS_FULL_PATH = 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         HAS_FULL_PATH = 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_FRAGME
       NT_NODE) { | 
 |     97         s = document; | 
 |     98       } | 
 |     99       return s; | 
 |    100     }, | 
 |    101     findTarget: function(inEvent) { | 
 |    102       if (HAS_FULL_PATH && 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 (HAS_FULL_PATH && 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 <= r
       ect.bottom); | 
 |    192     }, | 
 |    193     path: function(event) { | 
 |    194       var p; | 
 |    195       if (HAS_FULL_PATH && 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, in
       cluding 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.createShadowRoo
       t; | 
 |    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 prop
       erties 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         e[k] = inDict[k]; | 
 |    372       } | 
 |    373       return e; | 
 |    374     }, | 
 |    375     makePointerEvent: function(inType, inDict) { | 
 |    376       inDict = inDict || Object.create(null); | 
 |    377  | 
 |    378       var e = this.makeBaseEvent(inType, inDict); | 
 |    379       // define inherited MouseEvent properties | 
 |    380       for(var i = 0, p; i < MOUSE_PROPS.length; i++) { | 
 |    381         p = MOUSE_PROPS[i]; | 
 |    382         e[p] = inDict[p] || MOUSE_DEFAULTS[i]; | 
 |    383       } | 
 |    384       e.buttons = inDict.buttons || 0; | 
 |    385  | 
 |    386       // Spec requires that pointers without pressure specified use 0.5 for down | 
 |    387       // state and 0 for up state. | 
 |    388       var pressure = 0; | 
 |    389       if (inDict.pressure) { | 
 |    390         pressure = inDict.pressure; | 
 |    391       } else { | 
 |    392         pressure = e.buttons ? 0.5 : 0; | 
 |    393       } | 
 |    394  | 
 |    395       // add x/y properties aliased to clientX/Y | 
 |    396       e.x = e.clientX; | 
 |    397       e.y = e.clientY; | 
 |    398  | 
 |    399       // define the properties of the PointerEvent interface | 
 |    400       e.pointerId = inDict.pointerId || 0; | 
 |    401       e.width = inDict.width || 0; | 
 |    402       e.height = inDict.height || 0; | 
 |    403       e.pressure = pressure; | 
 |    404       e.tiltX = inDict.tiltX || 0; | 
 |    405       e.tiltY = inDict.tiltY || 0; | 
 |    406       e.pointerType = inDict.pointerType || ''; | 
 |    407       e.hwTimestamp = inDict.hwTimestamp || 0; | 
 |    408       e.isPrimary = inDict.isPrimary || false; | 
 |    409       e._source = inDict._source || ''; | 
 |    410       return e; | 
 |    411     } | 
 |    412   }; | 
 |    413  | 
 |    414   scope.eventFactory = eventFactory; | 
 |    415 })(window.PolymerGestures); | 
 |    416  | 
 |    417 /** | 
 |    418  * This module implements an map of pointer states | 
 |    419  */ | 
 |    420 (function(scope) { | 
 |    421   var USE_MAP = window.Map && window.Map.prototype.forEach; | 
 |    422   var POINTERS_FN = function(){ return this.size; }; | 
 |    423   function PointerMap() { | 
 |    424     if (USE_MAP) { | 
 |    425       var m = new Map(); | 
 |    426       m.pointers = POINTERS_FN; | 
 |    427       return m; | 
 |    428     } else { | 
 |    429       this.keys = []; | 
 |    430       this.values = []; | 
 |    431     } | 
 |    432   } | 
 |    433  | 
 |    434   PointerMap.prototype = { | 
 |    435     set: function(inId, inEvent) { | 
 |    436       var i = this.keys.indexOf(inId); | 
 |    437       if (i > -1) { | 
 |    438         this.values[i] = inEvent; | 
 |    439       } else { | 
 |    440         this.keys.push(inId); | 
 |    441         this.values.push(inEvent); | 
 |    442       } | 
 |    443     }, | 
 |    444     has: function(inId) { | 
 |    445       return this.keys.indexOf(inId) > -1; | 
 |    446     }, | 
 |    447     'delete': function(inId) { | 
 |    448       var i = this.keys.indexOf(inId); | 
 |    449       if (i > -1) { | 
 |    450         this.keys.splice(i, 1); | 
 |    451         this.values.splice(i, 1); | 
 |    452       } | 
 |    453     }, | 
 |    454     get: function(inId) { | 
 |    455       var i = this.keys.indexOf(inId); | 
 |    456       return this.values[i]; | 
 |    457     }, | 
 |    458     clear: function() { | 
 |    459       this.keys.length = 0; | 
 |    460       this.values.length = 0; | 
 |    461     }, | 
 |    462     // return value, key, map | 
 |    463     forEach: function(callback, thisArg) { | 
 |    464       this.values.forEach(function(v, i) { | 
 |    465         callback.call(thisArg, v, this.keys[i], this); | 
 |    466       }, this); | 
 |    467     }, | 
 |    468     pointers: function() { | 
 |    469       return this.keys.length; | 
 |    470     } | 
 |    471   }; | 
 |    472  | 
 |    473   scope.PointerMap = PointerMap; | 
 |    474 })(window.PolymerGestures); | 
 |    475  | 
 |    476 (function(scope) { | 
 |    477   var CLONE_PROPS = [ | 
 |    478     // MouseEvent | 
 |    479     'bubbles', | 
 |    480     'cancelable', | 
 |    481     'view', | 
 |    482     'detail', | 
 |    483     'screenX', | 
 |    484     'screenY', | 
 |    485     'clientX', | 
 |    486     'clientY', | 
 |    487     'ctrlKey', | 
 |    488     'altKey', | 
 |    489     'shiftKey', | 
 |    490     'metaKey', | 
 |    491     'button', | 
 |    492     'relatedTarget', | 
 |    493     // DOM Level 3 | 
 |    494     'buttons', | 
 |    495     // PointerEvent | 
 |    496     'pointerId', | 
 |    497     'width', | 
 |    498     'height', | 
 |    499     'pressure', | 
 |    500     'tiltX', | 
 |    501     'tiltY', | 
 |    502     'pointerType', | 
 |    503     'hwTimestamp', | 
 |    504     'isPrimary', | 
 |    505     // event instance | 
 |    506     'type', | 
 |    507     'target', | 
 |    508     'currentTarget', | 
 |    509     'which', | 
 |    510     'pageX', | 
 |    511     'pageY', | 
 |    512     'timeStamp', | 
 |    513     // gesture addons | 
 |    514     'preventTap', | 
 |    515     'tapPrevented', | 
 |    516     '_source' | 
 |    517   ]; | 
 |    518  | 
 |    519   var CLONE_DEFAULTS = [ | 
 |    520     // MouseEvent | 
 |    521     false, | 
 |    522     false, | 
 |    523     null, | 
 |    524     null, | 
 |    525     0, | 
 |    526     0, | 
 |    527     0, | 
 |    528     0, | 
 |    529     false, | 
 |    530     false, | 
 |    531     false, | 
 |    532     false, | 
 |    533     0, | 
 |    534     null, | 
 |    535     // DOM Level 3 | 
 |    536     0, | 
 |    537     // PointerEvent | 
 |    538     0, | 
 |    539     0, | 
 |    540     0, | 
 |    541     0, | 
 |    542     0, | 
 |    543     0, | 
 |    544     '', | 
 |    545     0, | 
 |    546     false, | 
 |    547     // event instance | 
 |    548     '', | 
 |    549     null, | 
 |    550     null, | 
 |    551     0, | 
 |    552     0, | 
 |    553     0, | 
 |    554     0, | 
 |    555     function(){}, | 
 |    556     false | 
 |    557   ]; | 
 |    558  | 
 |    559   var HAS_SVG_INSTANCE = (typeof SVGElementInstance !== 'undefined'); | 
 |    560  | 
 |    561   var eventFactory = scope.eventFactory; | 
 |    562  | 
 |    563   // set of recognizers to run for the currently handled event | 
 |    564   var currentGestures; | 
 |    565  | 
 |    566   /** | 
 |    567    * This module is for normalizing events. Mouse and Touch events will be | 
 |    568    * collected here, and fire PointerEvents that have the same semantics, no | 
 |    569    * matter the source. | 
 |    570    * Events fired: | 
 |    571    *   - pointerdown: a pointing is added | 
 |    572    *   - pointerup: a pointer is removed | 
 |    573    *   - pointermove: a pointer is moved | 
 |    574    *   - pointerover: a pointer crosses into an element | 
 |    575    *   - pointerout: a pointer leaves an element | 
 |    576    *   - pointercancel: a pointer will no longer generate events | 
 |    577    */ | 
 |    578   var dispatcher = { | 
 |    579     IS_IOS: false, | 
 |    580     pointermap: new scope.PointerMap(), | 
 |    581     requiredGestures: new scope.PointerMap(), | 
 |    582     eventMap: Object.create(null), | 
 |    583     // Scope objects for native events. | 
 |    584     // This exists for ease of testing. | 
 |    585     eventSources: Object.create(null), | 
 |    586     eventSourceList: [], | 
 |    587     gestures: [], | 
 |    588     // map gesture event -> {listeners: int, index: gestures[int]} | 
 |    589     dependencyMap: { | 
 |    590       // make sure down and up are in the map to trigger "register" | 
 |    591       down: {listeners: 0, index: -1}, | 
 |    592       up: {listeners: 0, index: -1} | 
 |    593     }, | 
 |    594     gestureQueue: [], | 
 |    595     /** | 
 |    596      * Add a new event source that will generate pointer events. | 
 |    597      * | 
 |    598      * `inSource` must contain an array of event names named `events`, and | 
 |    599      * functions with the names specified in the `events` array. | 
 |    600      * @param {string} name A name for the event source | 
 |    601      * @param {Object} source A new source of platform events. | 
 |    602      */ | 
 |    603     registerSource: function(name, source) { | 
 |    604       var s = source; | 
 |    605       var newEvents = s.events; | 
 |    606       if (newEvents) { | 
 |    607         newEvents.forEach(function(e) { | 
 |    608           if (s[e]) { | 
 |    609             this.eventMap[e] = s[e].bind(s); | 
 |    610           } | 
 |    611         }, this); | 
 |    612         this.eventSources[name] = s; | 
 |    613         this.eventSourceList.push(s); | 
 |    614       } | 
 |    615     }, | 
 |    616     registerGesture: function(name, source) { | 
 |    617       var obj = Object.create(null); | 
 |    618       obj.listeners = 0; | 
 |    619       obj.index = this.gestures.length; | 
 |    620       for (var i = 0, g; i < source.exposes.length; i++) { | 
 |    621         g = source.exposes[i].toLowerCase(); | 
 |    622         this.dependencyMap[g] = obj; | 
 |    623       } | 
 |    624       this.gestures.push(source); | 
 |    625     }, | 
 |    626     register: function(element, initial) { | 
 |    627       var l = this.eventSourceList.length; | 
 |    628       for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) { | 
 |    629         // call eventsource register | 
 |    630         es.register.call(es, element, initial); | 
 |    631       } | 
 |    632     }, | 
 |    633     unregister: function(element) { | 
 |    634       var l = this.eventSourceList.length; | 
 |    635       for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) { | 
 |    636         // call eventsource register | 
 |    637         es.unregister.call(es, element); | 
 |    638       } | 
 |    639     }, | 
 |    640     // EVENTS | 
 |    641     down: function(inEvent) { | 
 |    642       this.requiredGestures.set(inEvent.pointerId, currentGestures); | 
 |    643       this.fireEvent('down', inEvent); | 
 |    644     }, | 
 |    645     move: function(inEvent) { | 
 |    646       // pipe move events into gesture queue directly | 
 |    647       inEvent.type = 'move'; | 
 |    648       this.fillGestureQueue(inEvent); | 
 |    649     }, | 
 |    650     up: function(inEvent) { | 
 |    651       this.fireEvent('up', inEvent); | 
 |    652       this.requiredGestures.delete(inEvent.pointerId); | 
 |    653     }, | 
 |    654     cancel: function(inEvent) { | 
 |    655       inEvent.tapPrevented = true; | 
 |    656       this.fireEvent('up', inEvent); | 
 |    657       this.requiredGestures.delete(inEvent.pointerId); | 
 |    658     }, | 
 |    659     addGestureDependency: function(node, currentGestures) { | 
 |    660       var gesturesWanted = node._pgEvents; | 
 |    661       if (gesturesWanted && currentGestures) { | 
 |    662         var gk = Object.keys(gesturesWanted); | 
 |    663         for (var i = 0, r, ri, g; i < gk.length; i++) { | 
 |    664           // gesture | 
 |    665           g = gk[i]; | 
 |    666           if (gesturesWanted[g] > 0) { | 
 |    667             // lookup gesture recognizer | 
 |    668             r = this.dependencyMap[g]; | 
 |    669             // recognizer index | 
 |    670             ri = r ? r.index : -1; | 
 |    671             currentGestures[ri] = true; | 
 |    672           } | 
 |    673         } | 
 |    674       } | 
 |    675     }, | 
 |    676     // LISTENER LOGIC | 
 |    677     eventHandler: function(inEvent) { | 
 |    678       // This is used to prevent multiple dispatch of events from | 
 |    679       // platform events. This can happen when two elements in different scopes | 
 |    680       // are set up to create pointer events, which is relevant to Shadow DOM. | 
 |    681  | 
 |    682       var type = inEvent.type; | 
 |    683  | 
 |    684       // only generate the list of desired events on "down" | 
 |    685       if (type === 'touchstart' || type === 'mousedown' || type === 'pointerdown
       ' || type === 'MSPointerDown') { | 
 |    686         if (!inEvent._handledByPG) { | 
 |    687           currentGestures = {}; | 
 |    688         } | 
 |    689  | 
 |    690         // in IOS mode, there is only a listener on the document, so this is not
        re-entrant | 
 |    691         if (this.IS_IOS) { | 
 |    692           var ev = inEvent; | 
 |    693           if (type === 'touchstart') { | 
 |    694             var ct = inEvent.changedTouches[0]; | 
 |    695             // set up a fake event to give to the path builder | 
 |    696             ev = {target: inEvent.target, clientX: ct.clientX, clientY: ct.clien
       tY, path: inEvent.path}; | 
 |    697           } | 
 |    698           // use event path if available, otherwise build a path from target fin
       ding | 
 |    699           var nodes = inEvent.path || scope.targetFinding.path(ev); | 
 |    700           for (var i = 0, n; i < nodes.length; i++) { | 
 |    701             n = nodes[i]; | 
 |    702             this.addGestureDependency(n, currentGestures); | 
 |    703           } | 
 |    704         } else { | 
 |    705           this.addGestureDependency(inEvent.currentTarget, currentGestures); | 
 |    706         } | 
 |    707       } | 
 |    708  | 
 |    709       if (inEvent._handledByPG) { | 
 |    710         return; | 
 |    711       } | 
 |    712       var fn = this.eventMap && this.eventMap[type]; | 
 |    713       if (fn) { | 
 |    714         fn(inEvent); | 
 |    715       } | 
 |    716       inEvent._handledByPG = true; | 
 |    717     }, | 
 |    718     // set up event listeners | 
 |    719     listen: function(target, events) { | 
 |    720       for (var i = 0, l = events.length, e; (i < l) && (e = events[i]); i++) { | 
 |    721         this.addEvent(target, e); | 
 |    722       } | 
 |    723     }, | 
 |    724     // remove event listeners | 
 |    725     unlisten: function(target, events) { | 
 |    726       for (var i = 0, l = events.length, e; (i < l) && (e = events[i]); i++) { | 
 |    727         this.removeEvent(target, e); | 
 |    728       } | 
 |    729     }, | 
 |    730     addEvent: function(target, eventName) { | 
 |    731       target.addEventListener(eventName, this.boundHandler); | 
 |    732     }, | 
 |    733     removeEvent: function(target, eventName) { | 
 |    734       target.removeEventListener(eventName, this.boundHandler); | 
 |    735     }, | 
 |    736     // EVENT CREATION AND TRACKING | 
 |    737     /** | 
 |    738      * Creates a new Event of type `inType`, based on the information in | 
 |    739      * `inEvent`. | 
 |    740      * | 
 |    741      * @param {string} inType A string representing the type of event to create | 
 |    742      * @param {Event} inEvent A platform event with a target | 
 |    743      * @return {Event} A PointerEvent of type `inType` | 
 |    744      */ | 
 |    745     makeEvent: function(inType, inEvent) { | 
 |    746       var e = eventFactory.makePointerEvent(inType, inEvent); | 
 |    747       e.preventDefault = inEvent.preventDefault; | 
 |    748       e.tapPrevented = inEvent.tapPrevented; | 
 |    749       e._target = e._target || inEvent.target; | 
 |    750       return e; | 
 |    751     }, | 
 |    752     // make and dispatch an event in one call | 
 |    753     fireEvent: function(inType, inEvent) { | 
 |    754       var e = this.makeEvent(inType, inEvent); | 
 |    755       return this.dispatchEvent(e); | 
 |    756     }, | 
 |    757     /** | 
 |    758      * Returns a snapshot of inEvent, with writable properties. | 
 |    759      * | 
 |    760      * @param {Event} inEvent An event that contains properties to copy. | 
 |    761      * @return {Object} An object containing shallow copies of `inEvent`'s | 
 |    762      *    properties. | 
 |    763      */ | 
 |    764     cloneEvent: function(inEvent) { | 
 |    765       var eventCopy = Object.create(null), p; | 
 |    766       for (var i = 0; i < CLONE_PROPS.length; i++) { | 
 |    767         p = CLONE_PROPS[i]; | 
 |    768         eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i]; | 
 |    769         // Work around SVGInstanceElement shadow tree | 
 |    770         // Return the <use> element that is represented by the instance for Safa
       ri, Chrome, IE. | 
 |    771         // This is the behavior implemented by Firefox. | 
 |    772         if (p === 'target' || p === 'relatedTarget') { | 
 |    773           if (HAS_SVG_INSTANCE && eventCopy[p] instanceof SVGElementInstance) { | 
 |    774             eventCopy[p] = eventCopy[p].correspondingUseElement; | 
 |    775           } | 
 |    776         } | 
 |    777       } | 
 |    778       // keep the semantics of preventDefault | 
 |    779       eventCopy.preventDefault = function() { | 
 |    780         inEvent.preventDefault(); | 
 |    781       }; | 
 |    782       return eventCopy; | 
 |    783     }, | 
 |    784     /** | 
 |    785      * Dispatches the event to its target. | 
 |    786      * | 
 |    787      * @param {Event} inEvent The event to be dispatched. | 
 |    788      * @return {Boolean} True if an event handler returns true, false otherwise. | 
 |    789      */ | 
 |    790     dispatchEvent: function(inEvent) { | 
 |    791       var t = inEvent._target; | 
 |    792       if (t) { | 
 |    793         t.dispatchEvent(inEvent); | 
 |    794         // clone the event for the gesture system to process | 
 |    795         // clone after dispatch to pick up gesture prevention code | 
 |    796         var clone = this.cloneEvent(inEvent); | 
 |    797         clone.target = t; | 
 |    798         this.fillGestureQueue(clone); | 
 |    799       } | 
 |    800     }, | 
 |    801     gestureTrigger: function() { | 
 |    802       // process the gesture queue | 
 |    803       for (var i = 0, e, rg; i < this.gestureQueue.length; i++) { | 
 |    804         e = this.gestureQueue[i]; | 
 |    805         rg = e._requiredGestures; | 
 |    806         if (rg) { | 
 |    807           for (var j = 0, g, fn; j < this.gestures.length; j++) { | 
 |    808             // only run recognizer if an element in the source event's path is l
       istening for those gestures | 
 |    809             if (rg[j]) { | 
 |    810               g = this.gestures[j]; | 
 |    811               fn = g[e.type]; | 
 |    812               if (fn) { | 
 |    813                 fn.call(g, e); | 
 |    814               } | 
 |    815             } | 
 |    816           } | 
 |    817         } | 
 |    818       } | 
 |    819       this.gestureQueue.length = 0; | 
 |    820     }, | 
 |    821     fillGestureQueue: function(ev) { | 
 |    822       // only trigger the gesture queue once | 
 |    823       if (!this.gestureQueue.length) { | 
 |    824         requestAnimationFrame(this.boundGestureTrigger); | 
 |    825       } | 
 |    826       ev._requiredGestures = this.requiredGestures.get(ev.pointerId); | 
 |    827       this.gestureQueue.push(ev); | 
 |    828     } | 
 |    829   }; | 
 |    830   dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher); | 
 |    831   dispatcher.boundGestureTrigger = dispatcher.gestureTrigger.bind(dispatcher); | 
 |    832   scope.dispatcher = dispatcher; | 
 |    833  | 
 |    834   /** | 
 |    835    * Listen for `gesture` on `node` with the `handler` function | 
 |    836    * | 
 |    837    * If `handler` is the first listener for `gesture`, the underlying gesture re
       cognizer is then enabled. | 
 |    838    * | 
 |    839    * @param {Element} node | 
 |    840    * @param {string} gesture | 
 |    841    * @return Boolean `gesture` is a valid gesture | 
 |    842    */ | 
 |    843   scope.activateGesture = function(node, gesture) { | 
 |    844     var g = gesture.toLowerCase(); | 
 |    845     var dep = dispatcher.dependencyMap[g]; | 
 |    846     if (dep) { | 
 |    847       var recognizer = dispatcher.gestures[dep.index]; | 
 |    848       if (!node._pgListeners) { | 
 |    849         dispatcher.register(node); | 
 |    850         node._pgListeners = 0; | 
 |    851       } | 
 |    852       // TODO(dfreedm): re-evaluate bookkeeping to avoid using attributes | 
 |    853       if (recognizer) { | 
 |    854         var touchAction = recognizer.defaultActions && recognizer.defaultActions
       [g]; | 
 |    855         var actionNode; | 
 |    856         switch(node.nodeType) { | 
 |    857           case Node.ELEMENT_NODE: | 
 |    858             actionNode = node; | 
 |    859           break; | 
 |    860           case Node.DOCUMENT_FRAGMENT_NODE: | 
 |    861             actionNode = node.host; | 
 |    862           break; | 
 |    863           default: | 
 |    864             actionNode = null; | 
 |    865           break; | 
 |    866         } | 
 |    867         if (touchAction && actionNode && !actionNode.hasAttribute('touch-action'
       )) { | 
 |    868           actionNode.setAttribute('touch-action', touchAction); | 
 |    869         } | 
 |    870       } | 
 |    871       if (!node._pgEvents) { | 
 |    872         node._pgEvents = {}; | 
 |    873       } | 
 |    874       node._pgEvents[g] = (node._pgEvents[g] || 0) + 1; | 
 |    875       node._pgListeners++; | 
 |    876     } | 
 |    877     return Boolean(dep); | 
 |    878   }; | 
 |    879  | 
 |    880   /** | 
 |    881    * | 
 |    882    * Listen for `gesture` from `node` with `handler` function. | 
 |    883    * | 
 |    884    * @param {Element} node | 
 |    885    * @param {string} gesture | 
 |    886    * @param {Function} handler | 
 |    887    * @param {Boolean} capture | 
 |    888    */ | 
 |    889   scope.addEventListener = function(node, gesture, handler, capture) { | 
 |    890     if (handler) { | 
 |    891       scope.activateGesture(node, gesture); | 
 |    892       node.addEventListener(gesture, handler, capture); | 
 |    893     } | 
 |    894   }; | 
 |    895  | 
 |    896   /** | 
 |    897    * Tears down the gesture configuration for `node` | 
 |    898    * | 
 |    899    * If `handler` is the last listener for `gesture`, the underlying gesture rec
       ognizer is disabled. | 
 |    900    * | 
 |    901    * @param {Element} node | 
 |    902    * @param {string} gesture | 
 |    903    * @return Boolean `gesture` is a valid gesture | 
 |    904    */ | 
 |    905   scope.deactivateGesture = function(node, gesture) { | 
 |    906     var g = gesture.toLowerCase(); | 
 |    907     var dep = dispatcher.dependencyMap[g]; | 
 |    908     if (dep) { | 
 |    909       if (node._pgListeners > 0) { | 
 |    910         node._pgListeners--; | 
 |    911       } | 
 |    912       if (node._pgListeners === 0) { | 
 |    913         dispatcher.unregister(node); | 
 |    914       } | 
 |    915       if (node._pgEvents) { | 
 |    916         if (node._pgEvents[g] > 0) { | 
 |    917           node._pgEvents[g]--; | 
 |    918         } else { | 
 |    919           node._pgEvents[g] = 0; | 
 |    920         } | 
 |    921       } | 
 |    922     } | 
 |    923     return Boolean(dep); | 
 |    924   }; | 
 |    925  | 
 |    926   /** | 
 |    927    * Stop listening for `gesture` from `node` with `handler` function. | 
 |    928    * | 
 |    929    * @param {Element} node | 
 |    930    * @param {string} gesture | 
 |    931    * @param {Function} handler | 
 |    932    * @param {Boolean} capture | 
 |    933    */ | 
 |    934   scope.removeEventListener = function(node, gesture, handler, capture) { | 
 |    935     if (handler) { | 
 |    936       scope.deactivateGesture(node, gesture); | 
 |    937       node.removeEventListener(gesture, handler, capture); | 
 |    938     } | 
 |    939   }; | 
 |    940 })(window.PolymerGestures); | 
 |    941  | 
 |    942 (function(scope) { | 
 |    943   var dispatcher = scope.dispatcher; | 
 |    944   var pointermap = dispatcher.pointermap; | 
 |    945   // radius around touchend that swallows mouse events | 
 |    946   var DEDUP_DIST = 25; | 
 |    947  | 
 |    948   var WHICH_TO_BUTTONS = [0, 1, 4, 2]; | 
 |    949  | 
 |    950   var CURRENT_BUTTONS = 0; | 
 |    951  | 
 |    952   var FIREFOX_LINUX = /Linux.*Firefox\//i; | 
 |    953  | 
 |    954   var HAS_BUTTONS = (function() { | 
 |    955     // firefox on linux returns spec-incorrect values for mouseup.buttons | 
 |    956     // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.buttons#See_a
       lso | 
 |    957     // https://codereview.chromium.org/727593003/#msg16 | 
 |    958     if (FIREFOX_LINUX.test(navigator.userAgent)) { | 
 |    959       return false; | 
 |    960     } | 
 |    961     try { | 
 |    962       return new MouseEvent('test', {buttons: 1}).buttons === 1; | 
 |    963     } catch (e) { | 
 |    964       return false; | 
 |    965     } | 
 |    966   })(); | 
 |    967  | 
 |    968   // handler block for native mouse events | 
 |    969   var mouseEvents = { | 
 |    970     POINTER_ID: 1, | 
 |    971     POINTER_TYPE: 'mouse', | 
 |    972     events: [ | 
 |    973       'mousedown', | 
 |    974       'mousemove', | 
 |    975       'mouseup' | 
 |    976     ], | 
 |    977     exposes: [ | 
 |    978       'down', | 
 |    979       'up', | 
 |    980       'move' | 
 |    981     ], | 
 |    982     register: function(target) { | 
 |    983       dispatcher.listen(target, this.events); | 
 |    984     }, | 
 |    985     unregister: function(target) { | 
 |    986       if (target === document) { | 
 |    987         return; | 
 |    988       } | 
 |    989       dispatcher.unlisten(target, this.events); | 
 |    990     }, | 
 |    991     lastTouches: [], | 
 |    992     // collide with the global mouse listener | 
 |    993     isEventSimulatedFromTouch: function(inEvent) { | 
 |    994       var lts = this.lastTouches; | 
 |    995       var x = inEvent.clientX, y = inEvent.clientY; | 
 |    996       for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) { | 
 |    997         // simulated mouse events will be swallowed near a primary touchend | 
 |    998         var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y); | 
 |    999         if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) { | 
 |   1000           return true; | 
 |   1001         } | 
 |   1002       } | 
 |   1003     }, | 
 |   1004     prepareEvent: function(inEvent) { | 
 |   1005       var e = dispatcher.cloneEvent(inEvent); | 
 |   1006       e.pointerId = this.POINTER_ID; | 
 |   1007       e.isPrimary = true; | 
 |   1008       e.pointerType = this.POINTER_TYPE; | 
 |   1009       e._source = 'mouse'; | 
 |   1010       if (!HAS_BUTTONS) { | 
 |   1011         var type = inEvent.type; | 
 |   1012         var bit = WHICH_TO_BUTTONS[inEvent.which] || 0; | 
 |   1013         if (type === 'mousedown') { | 
 |   1014           CURRENT_BUTTONS |= bit; | 
 |   1015         } else if (type === 'mouseup') { | 
 |   1016           CURRENT_BUTTONS &= ~bit; | 
 |   1017         } | 
 |   1018         e.buttons = CURRENT_BUTTONS; | 
 |   1019       } | 
 |   1020       return e; | 
 |   1021     }, | 
 |   1022     mousedown: function(inEvent) { | 
 |   1023       if (!this.isEventSimulatedFromTouch(inEvent)) { | 
 |   1024         var p = pointermap.has(this.POINTER_ID); | 
 |   1025         var e = this.prepareEvent(inEvent); | 
 |   1026         e.target = scope.findTarget(inEvent); | 
 |   1027         pointermap.set(this.POINTER_ID, e.target); | 
 |   1028         dispatcher.down(e); | 
 |   1029       } | 
 |   1030     }, | 
 |   1031     mousemove: function(inEvent) { | 
 |   1032       if (!this.isEventSimulatedFromTouch(inEvent)) { | 
 |   1033         var target = pointermap.get(this.POINTER_ID); | 
 |   1034         if (target) { | 
 |   1035           var e = this.prepareEvent(inEvent); | 
 |   1036           e.target = target; | 
 |   1037           // handle case where we missed a mouseup | 
 |   1038           if ((HAS_BUTTONS ? e.buttons : e.which) === 0) { | 
 |   1039             if (!HAS_BUTTONS) { | 
 |   1040               CURRENT_BUTTONS = e.buttons = 0; | 
 |   1041             } | 
 |   1042             dispatcher.cancel(e); | 
 |   1043             this.cleanupMouse(e.buttons); | 
 |   1044           } else { | 
 |   1045             dispatcher.move(e); | 
 |   1046           } | 
 |   1047         } | 
 |   1048       } | 
 |   1049     }, | 
 |   1050     mouseup: function(inEvent) { | 
 |   1051       if (!this.isEventSimulatedFromTouch(inEvent)) { | 
 |   1052         var e = this.prepareEvent(inEvent); | 
 |   1053         e.relatedTarget = scope.findTarget(inEvent); | 
 |   1054         e.target = pointermap.get(this.POINTER_ID); | 
 |   1055         dispatcher.up(e); | 
 |   1056         this.cleanupMouse(e.buttons); | 
 |   1057       } | 
 |   1058     }, | 
 |   1059     cleanupMouse: function(buttons) { | 
 |   1060       if (buttons === 0) { | 
 |   1061         pointermap.delete(this.POINTER_ID); | 
 |   1062       } | 
 |   1063     } | 
 |   1064   }; | 
 |   1065  | 
 |   1066   scope.mouseEvents = mouseEvents; | 
 |   1067 })(window.PolymerGestures); | 
 |   1068  | 
 |   1069 (function(scope) { | 
 |   1070   var dispatcher = scope.dispatcher; | 
 |   1071   var allShadows = scope.targetFinding.allShadows.bind(scope.targetFinding); | 
 |   1072   var pointermap = dispatcher.pointermap; | 
 |   1073   var touchMap = Array.prototype.map.call.bind(Array.prototype.map); | 
 |   1074   // This should be long enough to ignore compat mouse events made by touch | 
 |   1075   var DEDUP_TIMEOUT = 2500; | 
 |   1076   var DEDUP_DIST = 25; | 
 |   1077   var CLICK_COUNT_TIMEOUT = 200; | 
 |   1078   var HYSTERESIS = 20; | 
 |   1079   var ATTRIB = 'touch-action'; | 
 |   1080   // TODO(dfreedm): disable until http://crbug.com/399765 is resolved | 
 |   1081   // var HAS_TOUCH_ACTION = ATTRIB in document.head.style; | 
 |   1082   var HAS_TOUCH_ACTION = false; | 
 |   1083  | 
 |   1084   // handler block for native touch events | 
 |   1085   var touchEvents = { | 
 |   1086     IS_IOS: false, | 
 |   1087     events: [ | 
 |   1088       'touchstart', | 
 |   1089       'touchmove', | 
 |   1090       'touchend', | 
 |   1091       'touchcancel' | 
 |   1092     ], | 
 |   1093     exposes: [ | 
 |   1094       'down', | 
 |   1095       'up', | 
 |   1096       'move' | 
 |   1097     ], | 
 |   1098     register: function(target, initial) { | 
 |   1099       if (this.IS_IOS ? initial : !initial) { | 
 |   1100         dispatcher.listen(target, this.events); | 
 |   1101       } | 
 |   1102     }, | 
 |   1103     unregister: function(target) { | 
 |   1104       if (!this.IS_IOS) { | 
 |   1105         dispatcher.unlisten(target, this.events); | 
 |   1106       } | 
 |   1107     }, | 
 |   1108     scrollTypes: { | 
 |   1109       EMITTER: 'none', | 
 |   1110       XSCROLLER: 'pan-x', | 
 |   1111       YSCROLLER: 'pan-y', | 
 |   1112     }, | 
 |   1113     touchActionToScrollType: function(touchAction) { | 
 |   1114       var t = touchAction; | 
 |   1115       var st = this.scrollTypes; | 
 |   1116       if (t === st.EMITTER) { | 
 |   1117         return 'none'; | 
 |   1118       } else if (t === st.XSCROLLER) { | 
 |   1119         return 'X'; | 
 |   1120       } else if (t === st.YSCROLLER) { | 
 |   1121         return 'Y'; | 
 |   1122       } else { | 
 |   1123         return 'XY'; | 
 |   1124       } | 
 |   1125     }, | 
 |   1126     POINTER_TYPE: 'touch', | 
 |   1127     firstTouch: null, | 
 |   1128     isPrimaryTouch: function(inTouch) { | 
 |   1129       return this.firstTouch === inTouch.identifier; | 
 |   1130     }, | 
 |   1131     setPrimaryTouch: function(inTouch) { | 
 |   1132       // set primary touch if there no pointers, or the only pointer is the mous
       e | 
 |   1133       if (pointermap.pointers() === 0 || (pointermap.pointers() === 1 && pointer
       map.has(1))) { | 
 |   1134         this.firstTouch = inTouch.identifier; | 
 |   1135         this.firstXY = {X: inTouch.clientX, Y: inTouch.clientY}; | 
 |   1136         this.firstTarget = inTouch.target; | 
 |   1137         this.scrolling = null; | 
 |   1138         this.cancelResetClickCount(); | 
 |   1139       } | 
 |   1140     }, | 
 |   1141     removePrimaryPointer: function(inPointer) { | 
 |   1142       if (inPointer.isPrimary) { | 
 |   1143         this.firstTouch = null; | 
 |   1144         this.firstXY = null; | 
 |   1145         this.resetClickCount(); | 
 |   1146       } | 
 |   1147     }, | 
 |   1148     clickCount: 0, | 
 |   1149     resetId: null, | 
 |   1150     resetClickCount: function() { | 
 |   1151       var fn = function() { | 
 |   1152         this.clickCount = 0; | 
 |   1153         this.resetId = null; | 
 |   1154       }.bind(this); | 
 |   1155       this.resetId = setTimeout(fn, CLICK_COUNT_TIMEOUT); | 
 |   1156     }, | 
 |   1157     cancelResetClickCount: function() { | 
 |   1158       if (this.resetId) { | 
 |   1159         clearTimeout(this.resetId); | 
 |   1160       } | 
 |   1161     }, | 
 |   1162     typeToButtons: function(type) { | 
 |   1163       var ret = 0; | 
 |   1164       if (type === 'touchstart' || type === 'touchmove') { | 
 |   1165         ret = 1; | 
 |   1166       } | 
 |   1167       return ret; | 
 |   1168     }, | 
 |   1169     findTarget: function(touch, id) { | 
 |   1170       if (this.currentTouchEvent.type === 'touchstart') { | 
 |   1171         if (this.isPrimaryTouch(touch)) { | 
 |   1172           var fastPath = { | 
 |   1173             clientX: touch.clientX, | 
 |   1174             clientY: touch.clientY, | 
 |   1175             path: this.currentTouchEvent.path, | 
 |   1176             target: this.currentTouchEvent.target | 
 |   1177           }; | 
 |   1178           return scope.findTarget(fastPath); | 
 |   1179         } else { | 
 |   1180           return scope.findTarget(touch); | 
 |   1181         } | 
 |   1182       } | 
 |   1183       // reuse target we found in touchstart | 
 |   1184       return pointermap.get(id); | 
 |   1185     }, | 
 |   1186     touchToPointer: function(inTouch) { | 
 |   1187       var cte = this.currentTouchEvent; | 
 |   1188       var e = dispatcher.cloneEvent(inTouch); | 
 |   1189       // Spec specifies that pointerId 1 is reserved for Mouse. | 
 |   1190       // Touch identifiers can start at 0. | 
 |   1191       // Add 2 to the touch identifier for compatibility. | 
 |   1192       var id = e.pointerId = inTouch.identifier + 2; | 
 |   1193       e.target = this.findTarget(inTouch, id); | 
 |   1194       e.bubbles = true; | 
 |   1195       e.cancelable = true; | 
 |   1196       e.detail = this.clickCount; | 
 |   1197       e.buttons = this.typeToButtons(cte.type); | 
 |   1198       e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0; | 
 |   1199       e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0; | 
 |   1200       e.pressure = inTouch.webkitForce || inTouch.force || 0.5; | 
 |   1201       e.isPrimary = this.isPrimaryTouch(inTouch); | 
 |   1202       e.pointerType = this.POINTER_TYPE; | 
 |   1203       e._source = 'touch'; | 
 |   1204       // forward touch preventDefaults | 
 |   1205       var self = this; | 
 |   1206       e.preventDefault = function() { | 
 |   1207         self.scrolling = false; | 
 |   1208         self.firstXY = null; | 
 |   1209         cte.preventDefault(); | 
 |   1210       }; | 
 |   1211       return e; | 
 |   1212     }, | 
 |   1213     processTouches: function(inEvent, inFunction) { | 
 |   1214       var tl = inEvent.changedTouches; | 
 |   1215       this.currentTouchEvent = inEvent; | 
 |   1216       for (var i = 0, t, p; i < tl.length; i++) { | 
 |   1217         t = tl[i]; | 
 |   1218         p = this.touchToPointer(t); | 
 |   1219         if (inEvent.type === 'touchstart') { | 
 |   1220           pointermap.set(p.pointerId, p.target); | 
 |   1221         } | 
 |   1222         if (pointermap.has(p.pointerId)) { | 
 |   1223           inFunction.call(this, p); | 
 |   1224         } | 
 |   1225         if (inEvent.type === 'touchend' || inEvent._cancel) { | 
 |   1226           this.cleanUpPointer(p); | 
 |   1227         } | 
 |   1228       } | 
 |   1229     }, | 
 |   1230     // For single axis scrollers, determines whether the element should emit | 
 |   1231     // pointer events or behave as a scroller | 
 |   1232     shouldScroll: function(inEvent) { | 
 |   1233       if (this.firstXY) { | 
 |   1234         var ret; | 
 |   1235         var touchAction = scope.targetFinding.findTouchAction(inEvent); | 
 |   1236         var scrollAxis = this.touchActionToScrollType(touchAction); | 
 |   1237         if (scrollAxis === 'none') { | 
 |   1238           // this element is a touch-action: none, should never scroll | 
 |   1239           ret = false; | 
 |   1240         } else if (scrollAxis === 'XY') { | 
 |   1241           // this element should always scroll | 
 |   1242           ret = true; | 
 |   1243         } else { | 
 |   1244           var t = inEvent.changedTouches[0]; | 
 |   1245           // check the intended scroll axis, and other axis | 
 |   1246           var a = scrollAxis; | 
 |   1247           var oa = scrollAxis === 'Y' ? 'X' : 'Y'; | 
 |   1248           var da = Math.abs(t['client' + a] - this.firstXY[a]); | 
 |   1249           var doa = Math.abs(t['client' + oa] - this.firstXY[oa]); | 
 |   1250           // if delta in the scroll axis > delta other axis, scroll instead of | 
 |   1251           // making events | 
 |   1252           ret = da >= doa; | 
 |   1253         } | 
 |   1254         return ret; | 
 |   1255       } | 
 |   1256     }, | 
 |   1257     findTouch: function(inTL, inId) { | 
 |   1258       for (var i = 0, l = inTL.length, t; i < l && (t = inTL[i]); i++) { | 
 |   1259         if (t.identifier === inId) { | 
 |   1260           return true; | 
 |   1261         } | 
 |   1262       } | 
 |   1263     }, | 
 |   1264     // In some instances, a touchstart can happen without a touchend. This | 
 |   1265     // leaves the pointermap in a broken state. | 
 |   1266     // Therefore, on every touchstart, we remove the touches that did not fire a | 
 |   1267     // touchend event. | 
 |   1268     // To keep state globally consistent, we fire a | 
 |   1269     // pointercancel for this "abandoned" touch | 
 |   1270     vacuumTouches: function(inEvent) { | 
 |   1271       var tl = inEvent.touches; | 
 |   1272       // pointermap.pointers() should be < tl.length here, as the touchstart has
        not | 
 |   1273       // been processed yet. | 
 |   1274       if (pointermap.pointers() >= tl.length) { | 
 |   1275         var d = []; | 
 |   1276         pointermap.forEach(function(value, key) { | 
 |   1277           // Never remove pointerId == 1, which is mouse. | 
 |   1278           // Touch identifiers are 2 smaller than their pointerId, which is the | 
 |   1279           // index in pointermap. | 
 |   1280           if (key !== 1 && !this.findTouch(tl, key - 2)) { | 
 |   1281             var p = value; | 
 |   1282             d.push(p); | 
 |   1283           } | 
 |   1284         }, this); | 
 |   1285         d.forEach(function(p) { | 
 |   1286           this.cancel(p); | 
 |   1287           pointermap.delete(p.pointerId); | 
 |   1288         }, this); | 
 |   1289       } | 
 |   1290     }, | 
 |   1291     touchstart: function(inEvent) { | 
 |   1292       this.vacuumTouches(inEvent); | 
 |   1293       this.setPrimaryTouch(inEvent.changedTouches[0]); | 
 |   1294       this.dedupSynthMouse(inEvent); | 
 |   1295       if (!this.scrolling) { | 
 |   1296         this.clickCount++; | 
 |   1297         this.processTouches(inEvent, this.down); | 
 |   1298       } | 
 |   1299     }, | 
 |   1300     down: function(inPointer) { | 
 |   1301       dispatcher.down(inPointer); | 
 |   1302     }, | 
 |   1303     touchmove: function(inEvent) { | 
 |   1304       if (HAS_TOUCH_ACTION) { | 
 |   1305         // touchevent.cancelable == false is sent when the page is scrolling und
       er native Touch Action in Chrome 36 | 
 |   1306         // https://groups.google.com/a/chromium.org/d/msg/input-dev/wHnyukcYBcA/
       b9kmtwM1jJQJ | 
 |   1307         if (inEvent.cancelable) { | 
 |   1308           this.processTouches(inEvent, this.move); | 
 |   1309         } | 
 |   1310       } else { | 
 |   1311         if (!this.scrolling) { | 
 |   1312           if (this.scrolling === null && this.shouldScroll(inEvent)) { | 
 |   1313             this.scrolling = true; | 
 |   1314           } else { | 
 |   1315             this.scrolling = false; | 
 |   1316             inEvent.preventDefault(); | 
 |   1317             this.processTouches(inEvent, this.move); | 
 |   1318           } | 
 |   1319         } else if (this.firstXY) { | 
 |   1320           var t = inEvent.changedTouches[0]; | 
 |   1321           var dx = t.clientX - this.firstXY.X; | 
 |   1322           var dy = t.clientY - this.firstXY.Y; | 
 |   1323           var dd = Math.sqrt(dx * dx + dy * dy); | 
 |   1324           if (dd >= HYSTERESIS) { | 
 |   1325             this.touchcancel(inEvent); | 
 |   1326             this.scrolling = true; | 
 |   1327             this.firstXY = null; | 
 |   1328           } | 
 |   1329         } | 
 |   1330       } | 
 |   1331     }, | 
 |   1332     move: function(inPointer) { | 
 |   1333       dispatcher.move(inPointer); | 
 |   1334     }, | 
 |   1335     touchend: function(inEvent) { | 
 |   1336       this.dedupSynthMouse(inEvent); | 
 |   1337       this.processTouches(inEvent, this.up); | 
 |   1338     }, | 
 |   1339     up: function(inPointer) { | 
 |   1340       inPointer.relatedTarget = scope.findTarget(inPointer); | 
 |   1341       dispatcher.up(inPointer); | 
 |   1342     }, | 
 |   1343     cancel: function(inPointer) { | 
 |   1344       dispatcher.cancel(inPointer); | 
 |   1345     }, | 
 |   1346     touchcancel: function(inEvent) { | 
 |   1347       inEvent._cancel = true; | 
 |   1348       this.processTouches(inEvent, this.cancel); | 
 |   1349     }, | 
 |   1350     cleanUpPointer: function(inPointer) { | 
 |   1351       pointermap['delete'](inPointer.pointerId); | 
 |   1352       this.removePrimaryPointer(inPointer); | 
 |   1353     }, | 
 |   1354     // prevent synth mouse events from creating pointer events | 
 |   1355     dedupSynthMouse: function(inEvent) { | 
 |   1356       var lts = scope.mouseEvents.lastTouches; | 
 |   1357       var t = inEvent.changedTouches[0]; | 
 |   1358       // only the primary finger will synth mouse events | 
 |   1359       if (this.isPrimaryTouch(t)) { | 
 |   1360         // remember x/y of last touch | 
 |   1361         var lt = {x: t.clientX, y: t.clientY}; | 
 |   1362         lts.push(lt); | 
 |   1363         var fn = (function(lts, lt){ | 
 |   1364           var i = lts.indexOf(lt); | 
 |   1365           if (i > -1) { | 
 |   1366             lts.splice(i, 1); | 
 |   1367           } | 
 |   1368         }).bind(null, lts, lt); | 
 |   1369         setTimeout(fn, DEDUP_TIMEOUT); | 
 |   1370       } | 
 |   1371     } | 
 |   1372   }; | 
 |   1373  | 
 |   1374   // prevent "ghost clicks" that come from elements that were removed in a touch
        handler | 
 |   1375   var STOP_PROP_FN = Event.prototype.stopImmediatePropagation || Event.prototype
       .stopPropagation; | 
 |   1376   document.addEventListener('click', function(ev) { | 
 |   1377     var x = ev.clientX, y = ev.clientY; | 
 |   1378     // check if a click is within DEDUP_DIST px radius of the touchstart | 
 |   1379     var closeTo = function(touch) { | 
 |   1380       var dx = Math.abs(x - touch.x), dy = Math.abs(y - touch.y); | 
 |   1381       return (dx <= DEDUP_DIST && dy <= DEDUP_DIST); | 
 |   1382     }; | 
 |   1383     // if click coordinates are close to touch coordinates, assume the click cam
       e from a touch | 
 |   1384     var wasTouched = scope.mouseEvents.lastTouches.some(closeTo); | 
 |   1385     // if the click came from touch, and the touchstart target is not in the pat
       h of the click event, | 
 |   1386     // then the touchstart target was probably removed, and the click should be 
       "busted" | 
 |   1387     var path = scope.targetFinding.path(ev); | 
 |   1388     if (wasTouched) { | 
 |   1389       for (var i = 0; i < path.length; i++) { | 
 |   1390         if (path[i] === touchEvents.firstTarget) { | 
 |   1391           return; | 
 |   1392         } | 
 |   1393       } | 
 |   1394       ev.preventDefault(); | 
 |   1395       STOP_PROP_FN.call(ev); | 
 |   1396     } | 
 |   1397   }, true); | 
 |   1398  | 
 |   1399   scope.touchEvents = touchEvents; | 
 |   1400 })(window.PolymerGestures); | 
 |   1401  | 
 |   1402 (function(scope) { | 
 |   1403   var dispatcher = scope.dispatcher; | 
 |   1404   var pointermap = dispatcher.pointermap; | 
 |   1405   var HAS_BITMAP_TYPE = window.MSPointerEvent && typeof window.MSPointerEvent.MS
       POINTER_TYPE_MOUSE === 'number'; | 
 |   1406   var msEvents = { | 
 |   1407     events: [ | 
 |   1408       'MSPointerDown', | 
 |   1409       'MSPointerMove', | 
 |   1410       'MSPointerUp', | 
 |   1411       'MSPointerCancel', | 
 |   1412     ], | 
 |   1413     register: function(target) { | 
 |   1414       dispatcher.listen(target, this.events); | 
 |   1415     }, | 
 |   1416     unregister: function(target) { | 
 |   1417       if (target === document) { | 
 |   1418         return; | 
 |   1419       } | 
 |   1420       dispatcher.unlisten(target, this.events); | 
 |   1421     }, | 
 |   1422     POINTER_TYPES: [ | 
 |   1423       '', | 
 |   1424       'unavailable', | 
 |   1425       'touch', | 
 |   1426       'pen', | 
 |   1427       'mouse' | 
 |   1428     ], | 
 |   1429     prepareEvent: function(inEvent) { | 
 |   1430       var e = inEvent; | 
 |   1431       e = dispatcher.cloneEvent(inEvent); | 
 |   1432       if (HAS_BITMAP_TYPE) { | 
 |   1433         e.pointerType = this.POINTER_TYPES[inEvent.pointerType]; | 
 |   1434       } | 
 |   1435       e._source = 'ms'; | 
 |   1436       return e; | 
 |   1437     }, | 
 |   1438     cleanup: function(id) { | 
 |   1439       pointermap['delete'](id); | 
 |   1440     }, | 
 |   1441     MSPointerDown: function(inEvent) { | 
 |   1442       var e = this.prepareEvent(inEvent); | 
 |   1443       e.target = scope.findTarget(inEvent); | 
 |   1444       pointermap.set(inEvent.pointerId, e.target); | 
 |   1445       dispatcher.down(e); | 
 |   1446     }, | 
 |   1447     MSPointerMove: function(inEvent) { | 
 |   1448       var target = pointermap.get(inEvent.pointerId); | 
 |   1449       if (target) { | 
 |   1450         var e = this.prepareEvent(inEvent); | 
 |   1451         e.target = target; | 
 |   1452         dispatcher.move(e); | 
 |   1453       } | 
 |   1454     }, | 
 |   1455     MSPointerUp: function(inEvent) { | 
 |   1456       var e = this.prepareEvent(inEvent); | 
 |   1457       e.relatedTarget = scope.findTarget(inEvent); | 
 |   1458       e.target = pointermap.get(e.pointerId); | 
 |   1459       dispatcher.up(e); | 
 |   1460       this.cleanup(inEvent.pointerId); | 
 |   1461     }, | 
 |   1462     MSPointerCancel: function(inEvent) { | 
 |   1463       var e = this.prepareEvent(inEvent); | 
 |   1464       e.relatedTarget = scope.findTarget(inEvent); | 
 |   1465       e.target = pointermap.get(e.pointerId); | 
 |   1466       dispatcher.cancel(e); | 
 |   1467       this.cleanup(inEvent.pointerId); | 
 |   1468     } | 
 |   1469   }; | 
 |   1470  | 
 |   1471   scope.msEvents = msEvents; | 
 |   1472 })(window.PolymerGestures); | 
 |   1473  | 
 |   1474 (function(scope) { | 
 |   1475   var dispatcher = scope.dispatcher; | 
 |   1476   var pointermap = dispatcher.pointermap; | 
 |   1477   var pointerEvents = { | 
 |   1478     events: [ | 
 |   1479       'pointerdown', | 
 |   1480       'pointermove', | 
 |   1481       'pointerup', | 
 |   1482       'pointercancel' | 
 |   1483     ], | 
 |   1484     prepareEvent: function(inEvent) { | 
 |   1485       var e = dispatcher.cloneEvent(inEvent); | 
 |   1486       e._source = 'pointer'; | 
 |   1487       return e; | 
 |   1488     }, | 
 |   1489     register: function(target) { | 
 |   1490       dispatcher.listen(target, this.events); | 
 |   1491     }, | 
 |   1492     unregister: function(target) { | 
 |   1493       if (target === document) { | 
 |   1494         return; | 
 |   1495       } | 
 |   1496       dispatcher.unlisten(target, this.events); | 
 |   1497     }, | 
 |   1498     cleanup: function(id) { | 
 |   1499       pointermap['delete'](id); | 
 |   1500     }, | 
 |   1501     pointerdown: function(inEvent) { | 
 |   1502       var e = this.prepareEvent(inEvent); | 
 |   1503       e.target = scope.findTarget(inEvent); | 
 |   1504       pointermap.set(e.pointerId, e.target); | 
 |   1505       dispatcher.down(e); | 
 |   1506     }, | 
 |   1507     pointermove: function(inEvent) { | 
 |   1508       var target = pointermap.get(inEvent.pointerId); | 
 |   1509       if (target) { | 
 |   1510         var e = this.prepareEvent(inEvent); | 
 |   1511         e.target = target; | 
 |   1512         dispatcher.move(e); | 
 |   1513       } | 
 |   1514     }, | 
 |   1515     pointerup: function(inEvent) { | 
 |   1516       var e = this.prepareEvent(inEvent); | 
 |   1517       e.relatedTarget = scope.findTarget(inEvent); | 
 |   1518       e.target = pointermap.get(e.pointerId); | 
 |   1519       dispatcher.up(e); | 
 |   1520       this.cleanup(inEvent.pointerId); | 
 |   1521     }, | 
 |   1522     pointercancel: function(inEvent) { | 
 |   1523       var e = this.prepareEvent(inEvent); | 
 |   1524       e.relatedTarget = scope.findTarget(inEvent); | 
 |   1525       e.target = pointermap.get(e.pointerId); | 
 |   1526       dispatcher.cancel(e); | 
 |   1527       this.cleanup(inEvent.pointerId); | 
 |   1528     } | 
 |   1529   }; | 
 |   1530  | 
 |   1531   scope.pointerEvents = pointerEvents; | 
 |   1532 })(window.PolymerGestures); | 
 |   1533  | 
 |   1534 /** | 
 |   1535  * This module contains the handlers for native platform events. | 
 |   1536  * From here, the dispatcher is called to create unified pointer events. | 
 |   1537  * Included are touch events (v1), mouse events, and MSPointerEvents. | 
 |   1538  */ | 
 |   1539 (function(scope) { | 
 |   1540  | 
 |   1541   var dispatcher = scope.dispatcher; | 
 |   1542   var nav = window.navigator; | 
 |   1543  | 
 |   1544   if (window.PointerEvent) { | 
 |   1545     dispatcher.registerSource('pointer', scope.pointerEvents); | 
 |   1546   } else if (nav.msPointerEnabled) { | 
 |   1547     dispatcher.registerSource('ms', scope.msEvents); | 
 |   1548   } else { | 
 |   1549     dispatcher.registerSource('mouse', scope.mouseEvents); | 
 |   1550     if (window.ontouchstart !== undefined) { | 
 |   1551       dispatcher.registerSource('touch', scope.touchEvents); | 
 |   1552     } | 
 |   1553   } | 
 |   1554  | 
 |   1555   // Work around iOS bugs https://bugs.webkit.org/show_bug.cgi?id=135628 and htt
       ps://bugs.webkit.org/show_bug.cgi?id=136506 | 
 |   1556   var ua = navigator.userAgent; | 
 |   1557   var IS_IOS = ua.match(/iPad|iPhone|iPod/) && 'ontouchstart' in window; | 
 |   1558  | 
 |   1559   dispatcher.IS_IOS = IS_IOS; | 
 |   1560   scope.touchEvents.IS_IOS = IS_IOS; | 
 |   1561  | 
 |   1562   dispatcher.register(document, true); | 
 |   1563 })(window.PolymerGestures); | 
 |   1564  | 
 |   1565 /** | 
 |   1566  * This event denotes the beginning of a series of tracking events. | 
 |   1567  * | 
 |   1568  * @module PointerGestures | 
 |   1569  * @submodule Events | 
 |   1570  * @class trackstart | 
 |   1571  */ | 
 |   1572 /** | 
 |   1573  * Pixels moved in the x direction since trackstart. | 
 |   1574  * @type Number | 
 |   1575  * @property dx | 
 |   1576  */ | 
 |   1577 /** | 
 |   1578  * Pixes moved in the y direction since trackstart. | 
 |   1579  * @type Number | 
 |   1580  * @property dy | 
 |   1581  */ | 
 |   1582 /** | 
 |   1583  * Pixels moved in the x direction since the last track. | 
 |   1584  * @type Number | 
 |   1585  * @property ddx | 
 |   1586  */ | 
 |   1587 /** | 
 |   1588  * Pixles moved in the y direction since the last track. | 
 |   1589  * @type Number | 
 |   1590  * @property ddy | 
 |   1591  */ | 
 |   1592 /** | 
 |   1593  * The clientX position of the track gesture. | 
 |   1594  * @type Number | 
 |   1595  * @property clientX | 
 |   1596  */ | 
 |   1597 /** | 
 |   1598  * The clientY position of the track gesture. | 
 |   1599  * @type Number | 
 |   1600  * @property clientY | 
 |   1601  */ | 
 |   1602 /** | 
 |   1603  * The pageX position of the track gesture. | 
 |   1604  * @type Number | 
 |   1605  * @property pageX | 
 |   1606  */ | 
 |   1607 /** | 
 |   1608  * The pageY position of the track gesture. | 
 |   1609  * @type Number | 
 |   1610  * @property pageY | 
 |   1611  */ | 
 |   1612 /** | 
 |   1613  * The screenX position of the track gesture. | 
 |   1614  * @type Number | 
 |   1615  * @property screenX | 
 |   1616  */ | 
 |   1617 /** | 
 |   1618  * The screenY position of the track gesture. | 
 |   1619  * @type Number | 
 |   1620  * @property screenY | 
 |   1621  */ | 
 |   1622 /** | 
 |   1623  * The last x axis direction of the pointer. | 
 |   1624  * @type Number | 
 |   1625  * @property xDirection | 
 |   1626  */ | 
 |   1627 /** | 
 |   1628  * The last y axis direction of the pointer. | 
 |   1629  * @type Number | 
 |   1630  * @property yDirection | 
 |   1631  */ | 
 |   1632 /** | 
 |   1633  * A shared object between all tracking events. | 
 |   1634  * @type Object | 
 |   1635  * @property trackInfo | 
 |   1636  */ | 
 |   1637 /** | 
 |   1638  * The element currently under the pointer. | 
 |   1639  * @type Element | 
 |   1640  * @property relatedTarget | 
 |   1641  */ | 
 |   1642 /** | 
 |   1643  * The type of pointer that make the track gesture. | 
 |   1644  * @type String | 
 |   1645  * @property pointerType | 
 |   1646  */ | 
 |   1647 /** | 
 |   1648  * | 
 |   1649  * This event fires for all pointer movement being tracked. | 
 |   1650  * | 
 |   1651  * @class track | 
 |   1652  * @extends trackstart | 
 |   1653  */ | 
 |   1654 /** | 
 |   1655  * This event fires when the pointer is no longer being tracked. | 
 |   1656  * | 
 |   1657  * @class trackend | 
 |   1658  * @extends trackstart | 
 |   1659  */ | 
 |   1660  | 
 |   1661  (function(scope) { | 
 |   1662    var dispatcher = scope.dispatcher; | 
 |   1663    var eventFactory = scope.eventFactory; | 
 |   1664    var pointermap = new scope.PointerMap(); | 
 |   1665    var track = { | 
 |   1666      events: [ | 
 |   1667        'down', | 
 |   1668        'move', | 
 |   1669        'up', | 
 |   1670      ], | 
 |   1671      exposes: [ | 
 |   1672       'trackstart', | 
 |   1673       'track', | 
 |   1674       'trackx', | 
 |   1675       'tracky', | 
 |   1676       'trackend' | 
 |   1677      ], | 
 |   1678      defaultActions: { | 
 |   1679        'track': 'none', | 
 |   1680        'trackx': 'pan-y', | 
 |   1681        'tracky': 'pan-x' | 
 |   1682      }, | 
 |   1683      WIGGLE_THRESHOLD: 4, | 
 |   1684      clampDir: function(inDelta) { | 
 |   1685        return inDelta > 0 ? 1 : -1; | 
 |   1686      }, | 
 |   1687      calcPositionDelta: function(inA, inB) { | 
 |   1688        var x = 0, y = 0; | 
 |   1689        if (inA && inB) { | 
 |   1690          x = inB.pageX - inA.pageX; | 
 |   1691          y = inB.pageY - inA.pageY; | 
 |   1692        } | 
 |   1693        return {x: x, y: y}; | 
 |   1694      }, | 
 |   1695      fireTrack: function(inType, inEvent, inTrackingData) { | 
 |   1696        var t = inTrackingData; | 
 |   1697        var d = this.calcPositionDelta(t.downEvent, inEvent); | 
 |   1698        var dd = this.calcPositionDelta(t.lastMoveEvent, inEvent); | 
 |   1699        if (dd.x) { | 
 |   1700          t.xDirection = this.clampDir(dd.x); | 
 |   1701        } else if (inType === 'trackx') { | 
 |   1702          return; | 
 |   1703        } | 
 |   1704        if (dd.y) { | 
 |   1705          t.yDirection = this.clampDir(dd.y); | 
 |   1706        } else if (inType === 'tracky') { | 
 |   1707          return; | 
 |   1708        } | 
 |   1709        var gestureProto = { | 
 |   1710          bubbles: true, | 
 |   1711          cancelable: true, | 
 |   1712          trackInfo: t.trackInfo, | 
 |   1713          relatedTarget: inEvent.relatedTarget, | 
 |   1714          pointerType: inEvent.pointerType, | 
 |   1715          pointerId: inEvent.pointerId, | 
 |   1716          _source: 'track' | 
 |   1717        }; | 
 |   1718        if (inType !== 'tracky') { | 
 |   1719          gestureProto.x = inEvent.x; | 
 |   1720          gestureProto.dx = d.x; | 
 |   1721          gestureProto.ddx = dd.x; | 
 |   1722          gestureProto.clientX = inEvent.clientX; | 
 |   1723          gestureProto.pageX = inEvent.pageX; | 
 |   1724          gestureProto.screenX = inEvent.screenX; | 
 |   1725          gestureProto.xDirection = t.xDirection; | 
 |   1726        } | 
 |   1727        if (inType !== 'trackx') { | 
 |   1728          gestureProto.dy = d.y; | 
 |   1729          gestureProto.ddy = dd.y; | 
 |   1730          gestureProto.y = inEvent.y; | 
 |   1731          gestureProto.clientY = inEvent.clientY; | 
 |   1732          gestureProto.pageY = inEvent.pageY; | 
 |   1733          gestureProto.screenY = inEvent.screenY; | 
 |   1734          gestureProto.yDirection = t.yDirection; | 
 |   1735        } | 
 |   1736        var e = eventFactory.makeGestureEvent(inType, gestureProto); | 
 |   1737        t.downTarget.dispatchEvent(e); | 
 |   1738      }, | 
 |   1739      down: function(inEvent) { | 
 |   1740        if (inEvent.isPrimary && (inEvent.pointerType === 'mouse' ? inEvent.butto
       ns === 1 : true)) { | 
 |   1741          var p = { | 
 |   1742            downEvent: inEvent, | 
 |   1743            downTarget: inEvent.target, | 
 |   1744            trackInfo: {}, | 
 |   1745            lastMoveEvent: null, | 
 |   1746            xDirection: 0, | 
 |   1747            yDirection: 0, | 
 |   1748            tracking: false | 
 |   1749          }; | 
 |   1750          pointermap.set(inEvent.pointerId, p); | 
 |   1751        } | 
 |   1752      }, | 
 |   1753      move: function(inEvent) { | 
 |   1754        var p = pointermap.get(inEvent.pointerId); | 
 |   1755        if (p) { | 
 |   1756          if (!p.tracking) { | 
 |   1757            var d = this.calcPositionDelta(p.downEvent, inEvent); | 
 |   1758            var move = d.x * d.x + d.y * d.y; | 
 |   1759            // start tracking only if finger moves more than WIGGLE_THRESHOLD | 
 |   1760            if (move > this.WIGGLE_THRESHOLD) { | 
 |   1761              p.tracking = true; | 
 |   1762              p.lastMoveEvent = p.downEvent; | 
 |   1763              this.fireTrack('trackstart', inEvent, p); | 
 |   1764            } | 
 |   1765          } | 
 |   1766          if (p.tracking) { | 
 |   1767            this.fireTrack('track', inEvent, p); | 
 |   1768            this.fireTrack('trackx', inEvent, p); | 
 |   1769            this.fireTrack('tracky', inEvent, p); | 
 |   1770          } | 
 |   1771          p.lastMoveEvent = inEvent; | 
 |   1772        } | 
 |   1773      }, | 
 |   1774      up: function(inEvent) { | 
 |   1775        var p = pointermap.get(inEvent.pointerId); | 
 |   1776        if (p) { | 
 |   1777          if (p.tracking) { | 
 |   1778            this.fireTrack('trackend', inEvent, p); | 
 |   1779          } | 
 |   1780          pointermap.delete(inEvent.pointerId); | 
 |   1781        } | 
 |   1782      } | 
 |   1783    }; | 
 |   1784    dispatcher.registerGesture('track', track); | 
 |   1785  })(window.PolymerGestures); | 
 |   1786  | 
 |   1787 /** | 
 |   1788  * This event is fired when a pointer is held down for 200ms. | 
 |   1789  * | 
 |   1790  * @module PointerGestures | 
 |   1791  * @submodule Events | 
 |   1792  * @class hold | 
 |   1793  */ | 
 |   1794 /** | 
 |   1795  * Type of pointer that made the holding event. | 
 |   1796  * @type String | 
 |   1797  * @property pointerType | 
 |   1798  */ | 
 |   1799 /** | 
 |   1800  * Screen X axis position of the held pointer | 
 |   1801  * @type Number | 
 |   1802  * @property clientX | 
 |   1803  */ | 
 |   1804 /** | 
 |   1805  * Screen Y axis position of the held pointer | 
 |   1806  * @type Number | 
 |   1807  * @property clientY | 
 |   1808  */ | 
 |   1809 /** | 
 |   1810  * Type of pointer that made the holding event. | 
 |   1811  * @type String | 
 |   1812  * @property pointerType | 
 |   1813  */ | 
 |   1814 /** | 
 |   1815  * This event is fired every 200ms while a pointer is held down. | 
 |   1816  * | 
 |   1817  * @class holdpulse | 
 |   1818  * @extends hold | 
 |   1819  */ | 
 |   1820 /** | 
 |   1821  * Milliseconds pointer has been held down. | 
 |   1822  * @type Number | 
 |   1823  * @property holdTime | 
 |   1824  */ | 
 |   1825 /** | 
 |   1826  * This event is fired when a held pointer is released or moved. | 
 |   1827  * | 
 |   1828  * @class release | 
 |   1829  */ | 
 |   1830  | 
 |   1831 (function(scope) { | 
 |   1832   var dispatcher = scope.dispatcher; | 
 |   1833   var eventFactory = scope.eventFactory; | 
 |   1834   var hold = { | 
 |   1835     // wait at least HOLD_DELAY ms between hold and pulse events | 
 |   1836     HOLD_DELAY: 200, | 
 |   1837     // pointer can move WIGGLE_THRESHOLD pixels before not counting as a hold | 
 |   1838     WIGGLE_THRESHOLD: 16, | 
 |   1839     events: [ | 
 |   1840       'down', | 
 |   1841       'move', | 
 |   1842       'up', | 
 |   1843     ], | 
 |   1844     exposes: [ | 
 |   1845       'hold', | 
 |   1846       'holdpulse', | 
 |   1847       'release' | 
 |   1848     ], | 
 |   1849     heldPointer: null, | 
 |   1850     holdJob: null, | 
 |   1851     pulse: function() { | 
 |   1852       var hold = Date.now() - this.heldPointer.timeStamp; | 
 |   1853       var type = this.held ? 'holdpulse' : 'hold'; | 
 |   1854       this.fireHold(type, hold); | 
 |   1855       this.held = true; | 
 |   1856     }, | 
 |   1857     cancel: function() { | 
 |   1858       clearInterval(this.holdJob); | 
 |   1859       if (this.held) { | 
 |   1860         this.fireHold('release'); | 
 |   1861       } | 
 |   1862       this.held = false; | 
 |   1863       this.heldPointer = null; | 
 |   1864       this.target = null; | 
 |   1865       this.holdJob = null; | 
 |   1866     }, | 
 |   1867     down: function(inEvent) { | 
 |   1868       if (inEvent.isPrimary && !this.heldPointer) { | 
 |   1869         this.heldPointer = inEvent; | 
 |   1870         this.target = inEvent.target; | 
 |   1871         this.holdJob = setInterval(this.pulse.bind(this), this.HOLD_DELAY); | 
 |   1872       } | 
 |   1873     }, | 
 |   1874     up: function(inEvent) { | 
 |   1875       if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) 
       { | 
 |   1876         this.cancel(); | 
 |   1877       } | 
 |   1878     }, | 
 |   1879     move: function(inEvent) { | 
 |   1880       if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) 
       { | 
 |   1881         var x = inEvent.clientX - this.heldPointer.clientX; | 
 |   1882         var y = inEvent.clientY - this.heldPointer.clientY; | 
 |   1883         if ((x * x + y * y) > this.WIGGLE_THRESHOLD) { | 
 |   1884           this.cancel(); | 
 |   1885         } | 
 |   1886       } | 
 |   1887     }, | 
 |   1888     fireHold: function(inType, inHoldTime) { | 
 |   1889       var p = { | 
 |   1890         bubbles: true, | 
 |   1891         cancelable: true, | 
 |   1892         pointerType: this.heldPointer.pointerType, | 
 |   1893         pointerId: this.heldPointer.pointerId, | 
 |   1894         x: this.heldPointer.clientX, | 
 |   1895         y: this.heldPointer.clientY, | 
 |   1896         _source: 'hold' | 
 |   1897       }; | 
 |   1898       if (inHoldTime) { | 
 |   1899         p.holdTime = inHoldTime; | 
 |   1900       } | 
 |   1901       var e = eventFactory.makeGestureEvent(inType, p); | 
 |   1902       this.target.dispatchEvent(e); | 
 |   1903     } | 
 |   1904   }; | 
 |   1905   dispatcher.registerGesture('hold', hold); | 
 |   1906 })(window.PolymerGestures); | 
 |   1907  | 
 |   1908 /** | 
 |   1909  * This event is fired when a pointer quickly goes down and up, and is used to | 
 |   1910  * denote activation. | 
 |   1911  * | 
 |   1912  * Any gesture event can prevent the tap event from being created by calling | 
 |   1913  * `event.preventTap`. | 
 |   1914  * | 
 |   1915  * Any pointer event can prevent the tap by setting the `tapPrevented` property | 
 |   1916  * on itself. | 
 |   1917  * | 
 |   1918  * @module PointerGestures | 
 |   1919  * @submodule Events | 
 |   1920  * @class tap | 
 |   1921  */ | 
 |   1922 /** | 
 |   1923  * X axis position of the tap. | 
 |   1924  * @property x | 
 |   1925  * @type Number | 
 |   1926  */ | 
 |   1927 /** | 
 |   1928  * Y axis position of the tap. | 
 |   1929  * @property y | 
 |   1930  * @type Number | 
 |   1931  */ | 
 |   1932 /** | 
 |   1933  * Type of the pointer that made the tap. | 
 |   1934  * @property pointerType | 
 |   1935  * @type String | 
 |   1936  */ | 
 |   1937 (function(scope) { | 
 |   1938   var dispatcher = scope.dispatcher; | 
 |   1939   var eventFactory = scope.eventFactory; | 
 |   1940   var pointermap = new scope.PointerMap(); | 
 |   1941   var tap = { | 
 |   1942     events: [ | 
 |   1943       'down', | 
 |   1944       'up' | 
 |   1945     ], | 
 |   1946     exposes: [ | 
 |   1947       'tap' | 
 |   1948     ], | 
 |   1949     down: function(inEvent) { | 
 |   1950       if (inEvent.isPrimary && !inEvent.tapPrevented) { | 
 |   1951         pointermap.set(inEvent.pointerId, { | 
 |   1952           target: inEvent.target, | 
 |   1953           buttons: inEvent.buttons, | 
 |   1954           x: inEvent.clientX, | 
 |   1955           y: inEvent.clientY | 
 |   1956         }); | 
 |   1957       } | 
 |   1958     }, | 
 |   1959     shouldTap: function(e, downState) { | 
 |   1960       var tap = true; | 
 |   1961       if (e.pointerType === 'mouse') { | 
 |   1962         // only allow left click to tap for mouse | 
 |   1963         tap = (e.buttons ^ 1) && (downState.buttons & 1); | 
 |   1964       } | 
 |   1965       return tap && !e.tapPrevented; | 
 |   1966     }, | 
 |   1967     up: function(inEvent) { | 
 |   1968       var start = pointermap.get(inEvent.pointerId); | 
 |   1969       if (start && this.shouldTap(inEvent, start)) { | 
 |   1970         // up.relatedTarget is target currently under finger | 
 |   1971         var t = scope.targetFinding.LCA(start.target, inEvent.relatedTarget); | 
 |   1972         if (t) { | 
 |   1973           var e = eventFactory.makeGestureEvent('tap', { | 
 |   1974             bubbles: true, | 
 |   1975             cancelable: true, | 
 |   1976             x: inEvent.clientX, | 
 |   1977             y: inEvent.clientY, | 
 |   1978             detail: inEvent.detail, | 
 |   1979             pointerType: inEvent.pointerType, | 
 |   1980             pointerId: inEvent.pointerId, | 
 |   1981             altKey: inEvent.altKey, | 
 |   1982             ctrlKey: inEvent.ctrlKey, | 
 |   1983             metaKey: inEvent.metaKey, | 
 |   1984             shiftKey: inEvent.shiftKey, | 
 |   1985             _source: 'tap' | 
 |   1986           }); | 
 |   1987           t.dispatchEvent(e); | 
 |   1988         } | 
 |   1989       } | 
 |   1990       pointermap.delete(inEvent.pointerId); | 
 |   1991     } | 
 |   1992   }; | 
 |   1993   // patch eventFactory to remove id from tap's pointermap for preventTap calls | 
 |   1994   eventFactory.preventTap = function(e) { | 
 |   1995     return function() { | 
 |   1996       e.tapPrevented = true; | 
 |   1997       pointermap.delete(e.pointerId); | 
 |   1998     }; | 
 |   1999   }; | 
 |   2000   dispatcher.registerGesture('tap', tap); | 
 |   2001 })(window.PolymerGestures); | 
 |   2002  | 
 |   2003 /* | 
 |   2004  * Basic strategy: find the farthest apart points, use as diameter of circle | 
 |   2005  * react to size change and rotation of the chord | 
 |   2006  */ | 
 |   2007  | 
 |   2008 /** | 
 |   2009  * @module pointer-gestures | 
 |   2010  * @submodule Events | 
 |   2011  * @class pinch | 
 |   2012  */ | 
 |   2013 /** | 
 |   2014  * Scale of the pinch zoom gesture | 
 |   2015  * @property scale | 
 |   2016  * @type Number | 
 |   2017  */ | 
 |   2018 /** | 
 |   2019  * Center X position of pointers causing pinch | 
 |   2020  * @property centerX | 
 |   2021  * @type Number | 
 |   2022  */ | 
 |   2023 /** | 
 |   2024  * Center Y position of pointers causing pinch | 
 |   2025  * @property centerY | 
 |   2026  * @type Number | 
 |   2027  */ | 
 |   2028  | 
 |   2029 /** | 
 |   2030  * @module pointer-gestures | 
 |   2031  * @submodule Events | 
 |   2032  * @class rotate | 
 |   2033  */ | 
 |   2034 /** | 
 |   2035  * Angle (in degrees) of rotation. Measured from starting positions of pointers. | 
 |   2036  * @property angle | 
 |   2037  * @type Number | 
 |   2038  */ | 
 |   2039 /** | 
 |   2040  * Center X position of pointers causing rotation | 
 |   2041  * @property centerX | 
 |   2042  * @type Number | 
 |   2043  */ | 
 |   2044 /** | 
 |   2045  * Center Y position of pointers causing rotation | 
 |   2046  * @property centerY | 
 |   2047  * @type Number | 
 |   2048  */ | 
 |   2049 (function(scope) { | 
 |   2050   var dispatcher = scope.dispatcher; | 
 |   2051   var eventFactory = scope.eventFactory; | 
 |   2052   var pointermap = new scope.PointerMap(); | 
 |   2053   var RAD_TO_DEG = 180 / Math.PI; | 
 |   2054   var pinch = { | 
 |   2055     events: [ | 
 |   2056       'down', | 
 |   2057       'up', | 
 |   2058       'move', | 
 |   2059       'cancel' | 
 |   2060     ], | 
 |   2061     exposes: [ | 
 |   2062       'pinchstart', | 
 |   2063       'pinch', | 
 |   2064       'pinchend', | 
 |   2065       'rotate' | 
 |   2066     ], | 
 |   2067     defaultActions: { | 
 |   2068       'pinch': 'none', | 
 |   2069       'rotate': 'none' | 
 |   2070     }, | 
 |   2071     reference: {}, | 
 |   2072     down: function(inEvent) { | 
 |   2073       pointermap.set(inEvent.pointerId, inEvent); | 
 |   2074       if (pointermap.pointers() == 2) { | 
 |   2075         var points = this.calcChord(); | 
 |   2076         var angle = this.calcAngle(points); | 
 |   2077         this.reference = { | 
 |   2078           angle: angle, | 
 |   2079           diameter: points.diameter, | 
 |   2080           target: scope.targetFinding.LCA(points.a.target, points.b.target) | 
 |   2081         }; | 
 |   2082  | 
 |   2083         this.firePinch('pinchstart', points.diameter, points); | 
 |   2084       } | 
 |   2085     }, | 
 |   2086     up: function(inEvent) { | 
 |   2087       var p = pointermap.get(inEvent.pointerId); | 
 |   2088       var num = pointermap.pointers(); | 
 |   2089       if (p) { | 
 |   2090         if (num === 2) { | 
 |   2091           // fire 'pinchend' before deleting pointer | 
 |   2092           var points = this.calcChord(); | 
 |   2093           this.firePinch('pinchend', points.diameter, points); | 
 |   2094         } | 
 |   2095         pointermap.delete(inEvent.pointerId); | 
 |   2096       } | 
 |   2097     }, | 
 |   2098     move: function(inEvent) { | 
 |   2099       if (pointermap.has(inEvent.pointerId)) { | 
 |   2100         pointermap.set(inEvent.pointerId, inEvent); | 
 |   2101         if (pointermap.pointers() > 1) { | 
 |   2102           this.calcPinchRotate(); | 
 |   2103         } | 
 |   2104       } | 
 |   2105     }, | 
 |   2106     cancel: function(inEvent) { | 
 |   2107         this.up(inEvent); | 
 |   2108     }, | 
 |   2109     firePinch: function(type, diameter, points) { | 
 |   2110       var zoom = diameter / this.reference.diameter; | 
 |   2111       var e = eventFactory.makeGestureEvent(type, { | 
 |   2112         bubbles: true, | 
 |   2113         cancelable: true, | 
 |   2114         scale: zoom, | 
 |   2115         centerX: points.center.x, | 
 |   2116         centerY: points.center.y, | 
 |   2117         _source: 'pinch' | 
 |   2118       }); | 
 |   2119       this.reference.target.dispatchEvent(e); | 
 |   2120     }, | 
 |   2121     fireRotate: function(angle, points) { | 
 |   2122       var diff = Math.round((angle - this.reference.angle) % 360); | 
 |   2123       var e = eventFactory.makeGestureEvent('rotate', { | 
 |   2124         bubbles: true, | 
 |   2125         cancelable: true, | 
 |   2126         angle: diff, | 
 |   2127         centerX: points.center.x, | 
 |   2128         centerY: points.center.y, | 
 |   2129         _source: 'pinch' | 
 |   2130       }); | 
 |   2131       this.reference.target.dispatchEvent(e); | 
 |   2132     }, | 
 |   2133     calcPinchRotate: function() { | 
 |   2134       var points = this.calcChord(); | 
 |   2135       var diameter = points.diameter; | 
 |   2136       var angle = this.calcAngle(points); | 
 |   2137       if (diameter != this.reference.diameter) { | 
 |   2138         this.firePinch('pinch', diameter, points); | 
 |   2139       } | 
 |   2140       if (angle != this.reference.angle) { | 
 |   2141         this.fireRotate(angle, points); | 
 |   2142       } | 
 |   2143     }, | 
 |   2144     calcChord: function() { | 
 |   2145       var pointers = []; | 
 |   2146       pointermap.forEach(function(p) { | 
 |   2147         pointers.push(p); | 
 |   2148       }); | 
 |   2149       var dist = 0; | 
 |   2150       // start with at least two pointers | 
 |   2151       var points = {a: pointers[0], b: pointers[1]}; | 
 |   2152       var x, y, d; | 
 |   2153       for (var i = 0; i < pointers.length; i++) { | 
 |   2154         var a = pointers[i]; | 
 |   2155         for (var j = i + 1; j < pointers.length; j++) { | 
 |   2156           var b = pointers[j]; | 
 |   2157           x = Math.abs(a.clientX - b.clientX); | 
 |   2158           y = Math.abs(a.clientY - b.clientY); | 
 |   2159           d = x + y; | 
 |   2160           if (d > dist) { | 
 |   2161             dist = d; | 
 |   2162             points = {a: a, b: b}; | 
 |   2163           } | 
 |   2164         } | 
 |   2165       } | 
 |   2166       x = Math.abs(points.a.clientX + points.b.clientX) / 2; | 
 |   2167       y = Math.abs(points.a.clientY + points.b.clientY) / 2; | 
 |   2168       points.center = { x: x, y: y }; | 
 |   2169       points.diameter = dist; | 
 |   2170       return points; | 
 |   2171     }, | 
 |   2172     calcAngle: function(points) { | 
 |   2173       var x = points.a.clientX - points.b.clientX; | 
 |   2174       var y = points.a.clientY - points.b.clientY; | 
 |   2175       return (360 + Math.atan2(y, x) * RAD_TO_DEG) % 360; | 
 |   2176     } | 
 |   2177   }; | 
 |   2178   dispatcher.registerGesture('pinch', pinch); | 
 |   2179 })(window.PolymerGestures); | 
 |   2180  | 
 |   2181 (function (global) { | 
 |   2182     'use strict'; | 
 |   2183  | 
 |   2184     var Token, | 
 |   2185         TokenName, | 
 |   2186         Syntax, | 
 |   2187         Messages, | 
 |   2188         source, | 
 |   2189         index, | 
 |   2190         length, | 
 |   2191         delegate, | 
 |   2192         lookahead, | 
 |   2193         state; | 
 |   2194  | 
 |   2195     Token = { | 
 |   2196         BooleanLiteral: 1, | 
 |   2197         EOF: 2, | 
 |   2198         Identifier: 3, | 
 |   2199         Keyword: 4, | 
 |   2200         NullLiteral: 5, | 
 |   2201         NumericLiteral: 6, | 
 |   2202         Punctuator: 7, | 
 |   2203         StringLiteral: 8 | 
 |   2204     }; | 
 |   2205  | 
 |   2206     TokenName = {}; | 
 |   2207     TokenName[Token.BooleanLiteral] = 'Boolean'; | 
 |   2208     TokenName[Token.EOF] = '<end>'; | 
 |   2209     TokenName[Token.Identifier] = 'Identifier'; | 
 |   2210     TokenName[Token.Keyword] = 'Keyword'; | 
 |   2211     TokenName[Token.NullLiteral] = 'Null'; | 
 |   2212     TokenName[Token.NumericLiteral] = 'Numeric'; | 
 |   2213     TokenName[Token.Punctuator] = 'Punctuator'; | 
 |   2214     TokenName[Token.StringLiteral] = 'String'; | 
 |   2215  | 
 |   2216     Syntax = { | 
 |   2217         ArrayExpression: 'ArrayExpression', | 
 |   2218         BinaryExpression: 'BinaryExpression', | 
 |   2219         CallExpression: 'CallExpression', | 
 |   2220         ConditionalExpression: 'ConditionalExpression', | 
 |   2221         EmptyStatement: 'EmptyStatement', | 
 |   2222         ExpressionStatement: 'ExpressionStatement', | 
 |   2223         Identifier: 'Identifier', | 
 |   2224         Literal: 'Literal', | 
 |   2225         LabeledStatement: 'LabeledStatement', | 
 |   2226         LogicalExpression: 'LogicalExpression', | 
 |   2227         MemberExpression: 'MemberExpression', | 
 |   2228         ObjectExpression: 'ObjectExpression', | 
 |   2229         Program: 'Program', | 
 |   2230         Property: 'Property', | 
 |   2231         ThisExpression: 'ThisExpression', | 
 |   2232         UnaryExpression: 'UnaryExpression' | 
 |   2233     }; | 
 |   2234  | 
 |   2235     // Error messages should be identical to V8. | 
 |   2236     Messages = { | 
 |   2237         UnexpectedToken:  'Unexpected token %0', | 
 |   2238         UnknownLabel: 'Undefined label \'%0\'', | 
 |   2239         Redeclaration: '%0 \'%1\' has already been declared' | 
 |   2240     }; | 
 |   2241  | 
 |   2242     // Ensure the condition is true, otherwise throw an error. | 
 |   2243     // This is only to have a better contract semantic, i.e. another safety net | 
 |   2244     // to catch a logic error. The condition shall be fulfilled in normal case. | 
 |   2245     // Do NOT use this to enforce a certain condition on any user input. | 
 |   2246  | 
 |   2247     function assert(condition, message) { | 
 |   2248         if (!condition) { | 
 |   2249             throw new Error('ASSERT: ' + message); | 
 |   2250         } | 
 |   2251     } | 
 |   2252  | 
 |   2253     function isDecimalDigit(ch) { | 
 |   2254         return (ch >= 48 && ch <= 57);   // 0..9 | 
 |   2255     } | 
 |   2256  | 
 |   2257  | 
 |   2258     // 7.2 White Space | 
 |   2259  | 
 |   2260     function isWhiteSpace(ch) { | 
 |   2261         return (ch === 32) ||  // space | 
 |   2262             (ch === 9) ||      // tab | 
 |   2263             (ch === 0xB) || | 
 |   2264             (ch === 0xC) || | 
 |   2265             (ch === 0xA0) || | 
 |   2266             (ch >= 0x1680 && '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u
       2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(String.fromCharCod
       e(ch)) > 0); | 
 |   2267     } | 
 |   2268  | 
 |   2269     // 7.3 Line Terminators | 
 |   2270  | 
 |   2271     function isLineTerminator(ch) { | 
 |   2272         return (ch === 10) || (ch === 13) || (ch === 0x2028) || (ch === 0x2029); | 
 |   2273     } | 
 |   2274  | 
 |   2275     // 7.6 Identifier Names and Identifiers | 
 |   2276  | 
 |   2277     function isIdentifierStart(ch) { | 
 |   2278         return (ch === 36) || (ch === 95) ||  // $ (dollar) and _ (underscore) | 
 |   2279             (ch >= 65 && ch <= 90) ||         // A..Z | 
 |   2280             (ch >= 97 && ch <= 122);          // a..z | 
 |   2281     } | 
 |   2282  | 
 |   2283     function isIdentifierPart(ch) { | 
 |   2284         return (ch === 36) || (ch === 95) ||  // $ (dollar) and _ (underscore) | 
 |   2285             (ch >= 65 && ch <= 90) ||         // A..Z | 
 |   2286             (ch >= 97 && ch <= 122) ||        // a..z | 
 |   2287             (ch >= 48 && ch <= 57);           // 0..9 | 
 |   2288     } | 
 |   2289  | 
 |   2290     // 7.6.1.1 Keywords | 
 |   2291  | 
 |   2292     function isKeyword(id) { | 
 |   2293         return (id === 'this') | 
 |   2294     } | 
 |   2295  | 
 |   2296     // 7.4 Comments | 
 |   2297  | 
 |   2298     function skipWhitespace() { | 
 |   2299         while (index < length && isWhiteSpace(source.charCodeAt(index))) { | 
 |   2300            ++index; | 
 |   2301         } | 
 |   2302     } | 
 |   2303  | 
 |   2304     function getIdentifier() { | 
 |   2305         var start, ch; | 
 |   2306  | 
 |   2307         start = index++; | 
 |   2308         while (index < length) { | 
 |   2309             ch = source.charCodeAt(index); | 
 |   2310             if (isIdentifierPart(ch)) { | 
 |   2311                 ++index; | 
 |   2312             } else { | 
 |   2313                 break; | 
 |   2314             } | 
 |   2315         } | 
 |   2316  | 
 |   2317         return source.slice(start, index); | 
 |   2318     } | 
 |   2319  | 
 |   2320     function scanIdentifier() { | 
 |   2321         var start, id, type; | 
 |   2322  | 
 |   2323         start = index; | 
 |   2324  | 
 |   2325         id = getIdentifier(); | 
 |   2326  | 
 |   2327         // There is no keyword or literal with only one character. | 
 |   2328         // Thus, it must be an identifier. | 
 |   2329         if (id.length === 1) { | 
 |   2330             type = Token.Identifier; | 
 |   2331         } else if (isKeyword(id)) { | 
 |   2332             type = Token.Keyword; | 
 |   2333         } else if (id === 'null') { | 
 |   2334             type = Token.NullLiteral; | 
 |   2335         } else if (id === 'true' || id === 'false') { | 
 |   2336             type = Token.BooleanLiteral; | 
 |   2337         } else { | 
 |   2338             type = Token.Identifier; | 
 |   2339         } | 
 |   2340  | 
 |   2341         return { | 
 |   2342             type: type, | 
 |   2343             value: id, | 
 |   2344             range: [start, index] | 
 |   2345         }; | 
 |   2346     } | 
 |   2347  | 
 |   2348  | 
 |   2349     // 7.7 Punctuators | 
 |   2350  | 
 |   2351     function scanPunctuator() { | 
 |   2352         var start = index, | 
 |   2353             code = source.charCodeAt(index), | 
 |   2354             code2, | 
 |   2355             ch1 = source[index], | 
 |   2356             ch2; | 
 |   2357  | 
 |   2358         switch (code) { | 
 |   2359  | 
 |   2360         // Check for most common single-character punctuators. | 
 |   2361         case 46:   // . dot | 
 |   2362         case 40:   // ( open bracket | 
 |   2363         case 41:   // ) close bracket | 
 |   2364         case 59:   // ; semicolon | 
 |   2365         case 44:   // , comma | 
 |   2366         case 123:  // { open curly brace | 
 |   2367         case 125:  // } close curly brace | 
 |   2368         case 91:   // [ | 
 |   2369         case 93:   // ] | 
 |   2370         case 58:   // : | 
 |   2371         case 63:   // ? | 
 |   2372             ++index; | 
 |   2373             return { | 
 |   2374                 type: Token.Punctuator, | 
 |   2375                 value: String.fromCharCode(code), | 
 |   2376                 range: [start, index] | 
 |   2377             }; | 
 |   2378  | 
 |   2379         default: | 
 |   2380             code2 = source.charCodeAt(index + 1); | 
 |   2381  | 
 |   2382             // '=' (char #61) marks an assignment or comparison operator. | 
 |   2383             if (code2 === 61) { | 
 |   2384                 switch (code) { | 
 |   2385                 case 37:  // % | 
 |   2386                 case 38:  // & | 
 |   2387                 case 42:  // *: | 
 |   2388                 case 43:  // + | 
 |   2389                 case 45:  // - | 
 |   2390                 case 47:  // / | 
 |   2391                 case 60:  // < | 
 |   2392                 case 62:  // > | 
 |   2393                 case 124: // | | 
 |   2394                     index += 2; | 
 |   2395                     return { | 
 |   2396                         type: Token.Punctuator, | 
 |   2397                         value: String.fromCharCode(code) + String.fromCharCode(c
       ode2), | 
 |   2398                         range: [start, index] | 
 |   2399                     }; | 
 |   2400  | 
 |   2401                 case 33: // ! | 
 |   2402                 case 61: // = | 
 |   2403                     index += 2; | 
 |   2404  | 
 |   2405                     // !== and === | 
 |   2406                     if (source.charCodeAt(index) === 61) { | 
 |   2407                         ++index; | 
 |   2408                     } | 
 |   2409                     return { | 
 |   2410                         type: Token.Punctuator, | 
 |   2411                         value: source.slice(start, index), | 
 |   2412                         range: [start, index] | 
 |   2413                     }; | 
 |   2414                 default: | 
 |   2415                     break; | 
 |   2416                 } | 
 |   2417             } | 
 |   2418             break; | 
 |   2419         } | 
 |   2420  | 
 |   2421         // Peek more characters. | 
 |   2422  | 
 |   2423         ch2 = source[index + 1]; | 
 |   2424  | 
 |   2425         // Other 2-character punctuators: && || | 
 |   2426  | 
 |   2427         if (ch1 === ch2 && ('&|'.indexOf(ch1) >= 0)) { | 
 |   2428             index += 2; | 
 |   2429             return { | 
 |   2430                 type: Token.Punctuator, | 
 |   2431                 value: ch1 + ch2, | 
 |   2432                 range: [start, index] | 
 |   2433             }; | 
 |   2434         } | 
 |   2435  | 
 |   2436         if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) { | 
 |   2437             ++index; | 
 |   2438             return { | 
 |   2439                 type: Token.Punctuator, | 
 |   2440                 value: ch1, | 
 |   2441                 range: [start, index] | 
 |   2442             }; | 
 |   2443         } | 
 |   2444  | 
 |   2445         throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); | 
 |   2446     } | 
 |   2447  | 
 |   2448     // 7.8.3 Numeric Literals | 
 |   2449     function scanNumericLiteral() { | 
 |   2450         var number, start, ch; | 
 |   2451  | 
 |   2452         ch = source[index]; | 
 |   2453         assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'), | 
 |   2454             'Numeric literal must start with a decimal digit or a decimal point'
       ); | 
 |   2455  | 
 |   2456         start = index; | 
 |   2457         number = ''; | 
 |   2458         if (ch !== '.') { | 
 |   2459             number = source[index++]; | 
 |   2460             ch = source[index]; | 
 |   2461  | 
 |   2462             // Hex number starts with '0x'. | 
 |   2463             // Octal number starts with '0'. | 
 |   2464             if (number === '0') { | 
 |   2465                 // decimal number starts with '0' such as '09' is illegal. | 
 |   2466                 if (ch && isDecimalDigit(ch.charCodeAt(0))) { | 
 |   2467                     throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); | 
 |   2468                 } | 
 |   2469             } | 
 |   2470  | 
 |   2471             while (isDecimalDigit(source.charCodeAt(index))) { | 
 |   2472                 number += source[index++]; | 
 |   2473             } | 
 |   2474             ch = source[index]; | 
 |   2475         } | 
 |   2476  | 
 |   2477         if (ch === '.') { | 
 |   2478             number += source[index++]; | 
 |   2479             while (isDecimalDigit(source.charCodeAt(index))) { | 
 |   2480                 number += source[index++]; | 
 |   2481             } | 
 |   2482             ch = source[index]; | 
 |   2483         } | 
 |   2484  | 
 |   2485         if (ch === 'e' || ch === 'E') { | 
 |   2486             number += source[index++]; | 
 |   2487  | 
 |   2488             ch = source[index]; | 
 |   2489             if (ch === '+' || ch === '-') { | 
 |   2490                 number += source[index++]; | 
 |   2491             } | 
 |   2492             if (isDecimalDigit(source.charCodeAt(index))) { | 
 |   2493                 while (isDecimalDigit(source.charCodeAt(index))) { | 
 |   2494                     number += source[index++]; | 
 |   2495                 } | 
 |   2496             } else { | 
 |   2497                 throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); | 
 |   2498             } | 
 |   2499         } | 
 |   2500  | 
 |   2501         if (isIdentifierStart(source.charCodeAt(index))) { | 
 |   2502             throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); | 
 |   2503         } | 
 |   2504  | 
 |   2505         return { | 
 |   2506             type: Token.NumericLiteral, | 
 |   2507             value: parseFloat(number), | 
 |   2508             range: [start, index] | 
 |   2509         }; | 
 |   2510     } | 
 |   2511  | 
 |   2512     // 7.8.4 String Literals | 
 |   2513  | 
 |   2514     function scanStringLiteral() { | 
 |   2515         var str = '', quote, start, ch, octal = false; | 
 |   2516  | 
 |   2517         quote = source[index]; | 
 |   2518         assert((quote === '\'' || quote === '"'), | 
 |   2519             'String literal must starts with a quote'); | 
 |   2520  | 
 |   2521         start = index; | 
 |   2522         ++index; | 
 |   2523  | 
 |   2524         while (index < length) { | 
 |   2525             ch = source[index++]; | 
 |   2526  | 
 |   2527             if (ch === quote) { | 
 |   2528                 quote = ''; | 
 |   2529                 break; | 
 |   2530             } else if (ch === '\\') { | 
 |   2531                 ch = source[index++]; | 
 |   2532                 if (!ch || !isLineTerminator(ch.charCodeAt(0))) { | 
 |   2533                     switch (ch) { | 
 |   2534                     case 'n': | 
 |   2535                         str += '\n'; | 
 |   2536                         break; | 
 |   2537                     case 'r': | 
 |   2538                         str += '\r'; | 
 |   2539                         break; | 
 |   2540                     case 't': | 
 |   2541                         str += '\t'; | 
 |   2542                         break; | 
 |   2543                     case 'b': | 
 |   2544                         str += '\b'; | 
 |   2545                         break; | 
 |   2546                     case 'f': | 
 |   2547                         str += '\f'; | 
 |   2548                         break; | 
 |   2549                     case 'v': | 
 |   2550                         str += '\x0B'; | 
 |   2551                         break; | 
 |   2552  | 
 |   2553                     default: | 
 |   2554                         str += ch; | 
 |   2555                         break; | 
 |   2556                     } | 
 |   2557                 } else { | 
 |   2558                     if (ch ===  '\r' && source[index] === '\n') { | 
 |   2559                         ++index; | 
 |   2560                     } | 
 |   2561                 } | 
 |   2562             } else if (isLineTerminator(ch.charCodeAt(0))) { | 
 |   2563                 break; | 
 |   2564             } else { | 
 |   2565                 str += ch; | 
 |   2566             } | 
 |   2567         } | 
 |   2568  | 
 |   2569         if (quote !== '') { | 
 |   2570             throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); | 
 |   2571         } | 
 |   2572  | 
 |   2573         return { | 
 |   2574             type: Token.StringLiteral, | 
 |   2575             value: str, | 
 |   2576             octal: octal, | 
 |   2577             range: [start, index] | 
 |   2578         }; | 
 |   2579     } | 
 |   2580  | 
 |   2581     function isIdentifierName(token) { | 
 |   2582         return token.type === Token.Identifier || | 
 |   2583             token.type === Token.Keyword || | 
 |   2584             token.type === Token.BooleanLiteral || | 
 |   2585             token.type === Token.NullLiteral; | 
 |   2586     } | 
 |   2587  | 
 |   2588     function advance() { | 
 |   2589         var ch; | 
 |   2590  | 
 |   2591         skipWhitespace(); | 
 |   2592  | 
 |   2593         if (index >= length) { | 
 |   2594             return { | 
 |   2595                 type: Token.EOF, | 
 |   2596                 range: [index, index] | 
 |   2597             }; | 
 |   2598         } | 
 |   2599  | 
 |   2600         ch = source.charCodeAt(index); | 
 |   2601  | 
 |   2602         // Very common: ( and ) and ; | 
 |   2603         if (ch === 40 || ch === 41 || ch === 58) { | 
 |   2604             return scanPunctuator(); | 
 |   2605         } | 
 |   2606  | 
 |   2607         // String literal starts with single quote (#39) or double quote (#34). | 
 |   2608         if (ch === 39 || ch === 34) { | 
 |   2609             return scanStringLiteral(); | 
 |   2610         } | 
 |   2611  | 
 |   2612         if (isIdentifierStart(ch)) { | 
 |   2613             return scanIdentifier(); | 
 |   2614         } | 
 |   2615  | 
 |   2616         // Dot (.) char #46 can also start a floating-point number, hence the ne
       ed | 
 |   2617         // to check the next character. | 
 |   2618         if (ch === 46) { | 
 |   2619             if (isDecimalDigit(source.charCodeAt(index + 1))) { | 
 |   2620                 return scanNumericLiteral(); | 
 |   2621             } | 
 |   2622             return scanPunctuator(); | 
 |   2623         } | 
 |   2624  | 
 |   2625         if (isDecimalDigit(ch)) { | 
 |   2626             return scanNumericLiteral(); | 
 |   2627         } | 
 |   2628  | 
 |   2629         return scanPunctuator(); | 
 |   2630     } | 
 |   2631  | 
 |   2632     function lex() { | 
 |   2633         var token; | 
 |   2634  | 
 |   2635         token = lookahead; | 
 |   2636         index = token.range[1]; | 
 |   2637  | 
 |   2638         lookahead = advance(); | 
 |   2639  | 
 |   2640         index = token.range[1]; | 
 |   2641  | 
 |   2642         return token; | 
 |   2643     } | 
 |   2644  | 
 |   2645     function peek() { | 
 |   2646         var pos; | 
 |   2647  | 
 |   2648         pos = index; | 
 |   2649         lookahead = advance(); | 
 |   2650         index = pos; | 
 |   2651     } | 
 |   2652  | 
 |   2653     // Throw an exception | 
 |   2654  | 
 |   2655     function throwError(token, messageFormat) { | 
 |   2656         var error, | 
 |   2657             args = Array.prototype.slice.call(arguments, 2), | 
 |   2658             msg = messageFormat.replace( | 
 |   2659                 /%(\d)/g, | 
 |   2660                 function (whole, index) { | 
 |   2661                     assert(index < args.length, 'Message reference must be in ra
       nge'); | 
 |   2662                     return args[index]; | 
 |   2663                 } | 
 |   2664             ); | 
 |   2665  | 
 |   2666         error = new Error(msg); | 
 |   2667         error.index = index; | 
 |   2668         error.description = msg; | 
 |   2669         throw error; | 
 |   2670     } | 
 |   2671  | 
 |   2672     // Throw an exception because of the token. | 
 |   2673  | 
 |   2674     function throwUnexpected(token) { | 
 |   2675         throwError(token, Messages.UnexpectedToken, token.value); | 
 |   2676     } | 
 |   2677  | 
 |   2678     // Expect the next token to match the specified punctuator. | 
 |   2679     // If not, an exception will be thrown. | 
 |   2680  | 
 |   2681     function expect(value) { | 
 |   2682         var token = lex(); | 
 |   2683         if (token.type !== Token.Punctuator || token.value !== value) { | 
 |   2684             throwUnexpected(token); | 
 |   2685         } | 
 |   2686     } | 
 |   2687  | 
 |   2688     // Return true if the next token matches the specified punctuator. | 
 |   2689  | 
 |   2690     function match(value) { | 
 |   2691         return lookahead.type === Token.Punctuator && lookahead.value === value; | 
 |   2692     } | 
 |   2693  | 
 |   2694     // Return true if the next token matches the specified keyword | 
 |   2695  | 
 |   2696     function matchKeyword(keyword) { | 
 |   2697         return lookahead.type === Token.Keyword && lookahead.value === keyword; | 
 |   2698     } | 
 |   2699  | 
 |   2700     function consumeSemicolon() { | 
 |   2701         // Catch the very common case first: immediately a semicolon (char #59). | 
 |   2702         if (source.charCodeAt(index) === 59) { | 
 |   2703             lex(); | 
 |   2704             return; | 
 |   2705         } | 
 |   2706  | 
 |   2707         skipWhitespace(); | 
 |   2708  | 
 |   2709         if (match(';')) { | 
 |   2710             lex(); | 
 |   2711             return; | 
 |   2712         } | 
 |   2713  | 
 |   2714         if (lookahead.type !== Token.EOF && !match('}')) { | 
 |   2715             throwUnexpected(lookahead); | 
 |   2716         } | 
 |   2717     } | 
 |   2718  | 
 |   2719     // 11.1.4 Array Initialiser | 
 |   2720  | 
 |   2721     function parseArrayInitialiser() { | 
 |   2722         var elements = []; | 
 |   2723  | 
 |   2724         expect('['); | 
 |   2725  | 
 |   2726         while (!match(']')) { | 
 |   2727             if (match(',')) { | 
 |   2728                 lex(); | 
 |   2729                 elements.push(null); | 
 |   2730             } else { | 
 |   2731                 elements.push(parseExpression()); | 
 |   2732  | 
 |   2733                 if (!match(']')) { | 
 |   2734                     expect(','); | 
 |   2735                 } | 
 |   2736             } | 
 |   2737         } | 
 |   2738  | 
 |   2739         expect(']'); | 
 |   2740  | 
 |   2741         return delegate.createArrayExpression(elements); | 
 |   2742     } | 
 |   2743  | 
 |   2744     // 11.1.5 Object Initialiser | 
 |   2745  | 
 |   2746     function parseObjectPropertyKey() { | 
 |   2747         var token; | 
 |   2748  | 
 |   2749         skipWhitespace(); | 
 |   2750         token = lex(); | 
 |   2751  | 
 |   2752         // Note: This function is called only from parseObjectProperty(), where | 
 |   2753         // EOF and Punctuator tokens are already filtered out. | 
 |   2754         if (token.type === Token.StringLiteral || token.type === Token.NumericLi
       teral) { | 
 |   2755             return delegate.createLiteral(token); | 
 |   2756         } | 
 |   2757  | 
 |   2758         return delegate.createIdentifier(token.value); | 
 |   2759     } | 
 |   2760  | 
 |   2761     function parseObjectProperty() { | 
 |   2762         var token, key; | 
 |   2763  | 
 |   2764         token = lookahead; | 
 |   2765         skipWhitespace(); | 
 |   2766  | 
 |   2767         if (token.type === Token.EOF || token.type === Token.Punctuator) { | 
 |   2768             throwUnexpected(token); | 
 |   2769         } | 
 |   2770  | 
 |   2771         key = parseObjectPropertyKey(); | 
 |   2772         expect(':'); | 
 |   2773         return delegate.createProperty('init', key, parseExpression()); | 
 |   2774     } | 
 |   2775  | 
 |   2776     function parseObjectInitialiser() { | 
 |   2777         var properties = []; | 
 |   2778  | 
 |   2779         expect('{'); | 
 |   2780  | 
 |   2781         while (!match('}')) { | 
 |   2782             properties.push(parseObjectProperty()); | 
 |   2783  | 
 |   2784             if (!match('}')) { | 
 |   2785                 expect(','); | 
 |   2786             } | 
 |   2787         } | 
 |   2788  | 
 |   2789         expect('}'); | 
 |   2790  | 
 |   2791         return delegate.createObjectExpression(properties); | 
 |   2792     } | 
 |   2793  | 
 |   2794     // 11.1.6 The Grouping Operator | 
 |   2795  | 
 |   2796     function parseGroupExpression() { | 
 |   2797         var expr; | 
 |   2798  | 
 |   2799         expect('('); | 
 |   2800  | 
 |   2801         expr = parseExpression(); | 
 |   2802  | 
 |   2803         expect(')'); | 
 |   2804  | 
 |   2805         return expr; | 
 |   2806     } | 
 |   2807  | 
 |   2808  | 
 |   2809     // 11.1 Primary Expressions | 
 |   2810  | 
 |   2811     function parsePrimaryExpression() { | 
 |   2812         var type, token, expr; | 
 |   2813  | 
 |   2814         if (match('(')) { | 
 |   2815             return parseGroupExpression(); | 
 |   2816         } | 
 |   2817  | 
 |   2818         type = lookahead.type; | 
 |   2819  | 
 |   2820         if (type === Token.Identifier) { | 
 |   2821             expr = delegate.createIdentifier(lex().value); | 
 |   2822         } else if (type === Token.StringLiteral || type === Token.NumericLiteral
       ) { | 
 |   2823             expr = delegate.createLiteral(lex()); | 
 |   2824         } else if (type === Token.Keyword) { | 
 |   2825             if (matchKeyword('this')) { | 
 |   2826                 lex(); | 
 |   2827                 expr = delegate.createThisExpression(); | 
 |   2828             } | 
 |   2829         } else if (type === Token.BooleanLiteral) { | 
 |   2830             token = lex(); | 
 |   2831             token.value = (token.value === 'true'); | 
 |   2832             expr = delegate.createLiteral(token); | 
 |   2833         } else if (type === Token.NullLiteral) { | 
 |   2834             token = lex(); | 
 |   2835             token.value = null; | 
 |   2836             expr = delegate.createLiteral(token); | 
 |   2837         } else if (match('[')) { | 
 |   2838             expr = parseArrayInitialiser(); | 
 |   2839         } else if (match('{')) { | 
 |   2840             expr = parseObjectInitialiser(); | 
 |   2841         } | 
 |   2842  | 
 |   2843         if (expr) { | 
 |   2844             return expr; | 
 |   2845         } | 
 |   2846  | 
 |   2847         throwUnexpected(lex()); | 
 |   2848     } | 
 |   2849  | 
 |   2850     // 11.2 Left-Hand-Side Expressions | 
 |   2851  | 
 |   2852     function parseArguments() { | 
 |   2853         var args = []; | 
 |   2854  | 
 |   2855         expect('('); | 
 |   2856  | 
 |   2857         if (!match(')')) { | 
 |   2858             while (index < length) { | 
 |   2859                 args.push(parseExpression()); | 
 |   2860                 if (match(')')) { | 
 |   2861                     break; | 
 |   2862                 } | 
 |   2863                 expect(','); | 
 |   2864             } | 
 |   2865         } | 
 |   2866  | 
 |   2867         expect(')'); | 
 |   2868  | 
 |   2869         return args; | 
 |   2870     } | 
 |   2871  | 
 |   2872     function parseNonComputedProperty() { | 
 |   2873         var token; | 
 |   2874  | 
 |   2875         token = lex(); | 
 |   2876  | 
 |   2877         if (!isIdentifierName(token)) { | 
 |   2878             throwUnexpected(token); | 
 |   2879         } | 
 |   2880  | 
 |   2881         return delegate.createIdentifier(token.value); | 
 |   2882     } | 
 |   2883  | 
 |   2884     function parseNonComputedMember() { | 
 |   2885         expect('.'); | 
 |   2886  | 
 |   2887         return parseNonComputedProperty(); | 
 |   2888     } | 
 |   2889  | 
 |   2890     function parseComputedMember() { | 
 |   2891         var expr; | 
 |   2892  | 
 |   2893         expect('['); | 
 |   2894  | 
 |   2895         expr = parseExpression(); | 
 |   2896  | 
 |   2897         expect(']'); | 
 |   2898  | 
 |   2899         return expr; | 
 |   2900     } | 
 |   2901  | 
 |   2902     function parseLeftHandSideExpression() { | 
 |   2903         var expr, args, property; | 
 |   2904  | 
 |   2905         expr = parsePrimaryExpression(); | 
 |   2906  | 
 |   2907         while (true) { | 
 |   2908             if (match('[')) { | 
 |   2909                 property = parseComputedMember(); | 
 |   2910                 expr = delegate.createMemberExpression('[', expr, property); | 
 |   2911             } else if (match('.')) { | 
 |   2912                 property = parseNonComputedMember(); | 
 |   2913                 expr = delegate.createMemberExpression('.', expr, property); | 
 |   2914             } else if (match('(')) { | 
 |   2915                 args = parseArguments(); | 
 |   2916                 expr = delegate.createCallExpression(expr, args); | 
 |   2917             } else { | 
 |   2918                 break; | 
 |   2919             } | 
 |   2920         } | 
 |   2921  | 
 |   2922         return expr; | 
 |   2923     } | 
 |   2924  | 
 |   2925     // 11.3 Postfix Expressions | 
 |   2926  | 
 |   2927     var parsePostfixExpression = parseLeftHandSideExpression; | 
 |   2928  | 
 |   2929     // 11.4 Unary Operators | 
 |   2930  | 
 |   2931     function parseUnaryExpression() { | 
 |   2932         var token, expr; | 
 |   2933  | 
 |   2934         if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyw
       ord) { | 
 |   2935             expr = parsePostfixExpression(); | 
 |   2936         } else if (match('+') || match('-') || match('!')) { | 
 |   2937             token = lex(); | 
 |   2938             expr = parseUnaryExpression(); | 
 |   2939             expr = delegate.createUnaryExpression(token.value, expr); | 
 |   2940         } else if (matchKeyword('delete') || matchKeyword('void') || matchKeywor
       d('typeof')) { | 
 |   2941             throwError({}, Messages.UnexpectedToken); | 
 |   2942         } else { | 
 |   2943             expr = parsePostfixExpression(); | 
 |   2944         } | 
 |   2945  | 
 |   2946         return expr; | 
 |   2947     } | 
 |   2948  | 
 |   2949     function binaryPrecedence(token) { | 
 |   2950         var prec = 0; | 
 |   2951  | 
 |   2952         if (token.type !== Token.Punctuator && token.type !== Token.Keyword) { | 
 |   2953             return 0; | 
 |   2954         } | 
 |   2955  | 
 |   2956         switch (token.value) { | 
 |   2957         case '||': | 
 |   2958             prec = 1; | 
 |   2959             break; | 
 |   2960  | 
 |   2961         case '&&': | 
 |   2962             prec = 2; | 
 |   2963             break; | 
 |   2964  | 
 |   2965         case '==': | 
 |   2966         case '!=': | 
 |   2967         case '===': | 
 |   2968         case '!==': | 
 |   2969             prec = 6; | 
 |   2970             break; | 
 |   2971  | 
 |   2972         case '<': | 
 |   2973         case '>': | 
 |   2974         case '<=': | 
 |   2975         case '>=': | 
 |   2976         case 'instanceof': | 
 |   2977             prec = 7; | 
 |   2978             break; | 
 |   2979  | 
 |   2980         case 'in': | 
 |   2981             prec = 7; | 
 |   2982             break; | 
 |   2983  | 
 |   2984         case '+': | 
 |   2985         case '-': | 
 |   2986             prec = 9; | 
 |   2987             break; | 
 |   2988  | 
 |   2989         case '*': | 
 |   2990         case '/': | 
 |   2991         case '%': | 
 |   2992             prec = 11; | 
 |   2993             break; | 
 |   2994  | 
 |   2995         default: | 
 |   2996             break; | 
 |   2997         } | 
 |   2998  | 
 |   2999         return prec; | 
 |   3000     } | 
 |   3001  | 
 |   3002     // 11.5 Multiplicative Operators | 
 |   3003     // 11.6 Additive Operators | 
 |   3004     // 11.7 Bitwise Shift Operators | 
 |   3005     // 11.8 Relational Operators | 
 |   3006     // 11.9 Equality Operators | 
 |   3007     // 11.10 Binary Bitwise Operators | 
 |   3008     // 11.11 Binary Logical Operators | 
 |   3009  | 
 |   3010     function parseBinaryExpression() { | 
 |   3011         var expr, token, prec, stack, right, operator, left, i; | 
 |   3012  | 
 |   3013         left = parseUnaryExpression(); | 
 |   3014  | 
 |   3015         token = lookahead; | 
 |   3016         prec = binaryPrecedence(token); | 
 |   3017         if (prec === 0) { | 
 |   3018             return left; | 
 |   3019         } | 
 |   3020         token.prec = prec; | 
 |   3021         lex(); | 
 |   3022  | 
 |   3023         right = parseUnaryExpression(); | 
 |   3024  | 
 |   3025         stack = [left, token, right]; | 
 |   3026  | 
 |   3027         while ((prec = binaryPrecedence(lookahead)) > 0) { | 
 |   3028  | 
 |   3029             // Reduce: make a binary expression from the three topmost entries. | 
 |   3030             while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec))
        { | 
 |   3031                 right = stack.pop(); | 
 |   3032                 operator = stack.pop().value; | 
 |   3033                 left = stack.pop(); | 
 |   3034                 expr = delegate.createBinaryExpression(operator, left, right); | 
 |   3035                 stack.push(expr); | 
 |   3036             } | 
 |   3037  | 
 |   3038             // Shift. | 
 |   3039             token = lex(); | 
 |   3040             token.prec = prec; | 
 |   3041             stack.push(token); | 
 |   3042             expr = parseUnaryExpression(); | 
 |   3043             stack.push(expr); | 
 |   3044         } | 
 |   3045  | 
 |   3046         // Final reduce to clean-up the stack. | 
 |   3047         i = stack.length - 1; | 
 |   3048         expr = stack[i]; | 
 |   3049         while (i > 1) { | 
 |   3050             expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i -
        2], expr); | 
 |   3051             i -= 2; | 
 |   3052         } | 
 |   3053  | 
 |   3054         return expr; | 
 |   3055     } | 
 |   3056  | 
 |   3057  | 
 |   3058     // 11.12 Conditional Operator | 
 |   3059  | 
 |   3060     function parseConditionalExpression() { | 
 |   3061         var expr, consequent, alternate; | 
 |   3062  | 
 |   3063         expr = parseBinaryExpression(); | 
 |   3064  | 
 |   3065         if (match('?')) { | 
 |   3066             lex(); | 
 |   3067             consequent = parseConditionalExpression(); | 
 |   3068             expect(':'); | 
 |   3069             alternate = parseConditionalExpression(); | 
 |   3070  | 
 |   3071             expr = delegate.createConditionalExpression(expr, consequent, altern
       ate); | 
 |   3072         } | 
 |   3073  | 
 |   3074         return expr; | 
 |   3075     } | 
 |   3076  | 
 |   3077     // Simplification since we do not support AssignmentExpression. | 
 |   3078     var parseExpression = parseConditionalExpression; | 
 |   3079  | 
 |   3080     // Polymer Syntax extensions | 
 |   3081  | 
 |   3082     // Filter :: | 
 |   3083     //   Identifier | 
 |   3084     //   Identifier "(" ")" | 
 |   3085     //   Identifier "(" FilterArguments ")" | 
 |   3086  | 
 |   3087     function parseFilter() { | 
 |   3088         var identifier, args; | 
 |   3089  | 
 |   3090         identifier = lex(); | 
 |   3091  | 
 |   3092         if (identifier.type !== Token.Identifier) { | 
 |   3093             throwUnexpected(identifier); | 
 |   3094         } | 
 |   3095  | 
 |   3096         args = match('(') ? parseArguments() : []; | 
 |   3097  | 
 |   3098         return delegate.createFilter(identifier.value, args); | 
 |   3099     } | 
 |   3100  | 
 |   3101     // Filters :: | 
 |   3102     //   "|" Filter | 
 |   3103     //   Filters "|" Filter | 
 |   3104  | 
 |   3105     function parseFilters() { | 
 |   3106         while (match('|')) { | 
 |   3107             lex(); | 
 |   3108             parseFilter(); | 
 |   3109         } | 
 |   3110     } | 
 |   3111  | 
 |   3112     // TopLevel :: | 
 |   3113     //   LabelledExpressions | 
 |   3114     //   AsExpression | 
 |   3115     //   InExpression | 
 |   3116     //   FilterExpression | 
 |   3117  | 
 |   3118     // AsExpression :: | 
 |   3119     //   FilterExpression as Identifier | 
 |   3120  | 
 |   3121     // InExpression :: | 
 |   3122     //   Identifier, Identifier in FilterExpression | 
 |   3123     //   Identifier in FilterExpression | 
 |   3124  | 
 |   3125     // FilterExpression :: | 
 |   3126     //   Expression | 
 |   3127     //   Expression Filters | 
 |   3128  | 
 |   3129     function parseTopLevel() { | 
 |   3130         skipWhitespace(); | 
 |   3131         peek(); | 
 |   3132  | 
 |   3133         var expr = parseExpression(); | 
 |   3134         if (expr) { | 
 |   3135             if (lookahead.value === ',' || lookahead.value == 'in' && | 
 |   3136                        expr.type === Syntax.Identifier) { | 
 |   3137                 parseInExpression(expr); | 
 |   3138             } else { | 
 |   3139                 parseFilters(); | 
 |   3140                 if (lookahead.value === 'as') { | 
 |   3141                     parseAsExpression(expr); | 
 |   3142                 } else { | 
 |   3143                     delegate.createTopLevel(expr); | 
 |   3144                 } | 
 |   3145             } | 
 |   3146         } | 
 |   3147  | 
 |   3148         if (lookahead.type !== Token.EOF) { | 
 |   3149             throwUnexpected(lookahead); | 
 |   3150         } | 
 |   3151     } | 
 |   3152  | 
 |   3153     function parseAsExpression(expr) { | 
 |   3154         lex();  // as | 
 |   3155         var identifier = lex().value; | 
 |   3156         delegate.createAsExpression(expr, identifier); | 
 |   3157     } | 
 |   3158  | 
 |   3159     function parseInExpression(identifier) { | 
 |   3160         var indexName; | 
 |   3161         if (lookahead.value === ',') { | 
 |   3162             lex(); | 
 |   3163             if (lookahead.type !== Token.Identifier) | 
 |   3164                 throwUnexpected(lookahead); | 
 |   3165             indexName = lex().value; | 
 |   3166         } | 
 |   3167  | 
 |   3168         lex();  // in | 
 |   3169         var expr = parseExpression(); | 
 |   3170         parseFilters(); | 
 |   3171         delegate.createInExpression(identifier.name, indexName, expr); | 
 |   3172     } | 
 |   3173  | 
 |   3174     function parse(code, inDelegate) { | 
 |   3175         delegate = inDelegate; | 
 |   3176         source = code; | 
 |   3177         index = 0; | 
 |   3178         length = source.length; | 
 |   3179         lookahead = null; | 
 |   3180         state = { | 
 |   3181             labelSet: {} | 
 |   3182         }; | 
 |   3183  | 
 |   3184         return parseTopLevel(); | 
 |   3185     } | 
 |   3186  | 
 |   3187     global.esprima = { | 
 |   3188         parse: parse | 
 |   3189     }; | 
 |   3190 })(this); | 
 |   3191  | 
 |   3192 // Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | 
 |   3193 // This code may only be used under the BSD style license found at http://polyme
       r.github.io/LICENSE.txt | 
 |   3194 // The complete set of authors may be found at http://polymer.github.io/AUTHORS.
       txt | 
 |   3195 // The complete set of contributors may be found at http://polymer.github.io/CON
       TRIBUTORS.txt | 
 |   3196 // Code distributed by Google as part of the polymer project is also | 
 |   3197 // subject to an additional IP rights grant found at http://polymer.github.io/PA
       TENTS.txt | 
 |   3198  | 
 |   3199 (function (global) { | 
 |   3200   'use strict'; | 
 |   3201  | 
 |   3202   function prepareBinding(expressionText, name, node, filterRegistry) { | 
 |   3203     var expression; | 
 |   3204     try { | 
 |   3205       expression = getExpression(expressionText); | 
 |   3206       if (expression.scopeIdent && | 
 |   3207           (node.nodeType !== Node.ELEMENT_NODE || | 
 |   3208            node.tagName !== 'TEMPLATE' || | 
 |   3209            (name !== 'bind' && name !== 'repeat'))) { | 
 |   3210         throw Error('as and in can only be used within <template bind/repeat>'); | 
 |   3211       } | 
 |   3212     } catch (ex) { | 
 |   3213       console.error('Invalid expression syntax: ' + expressionText, ex); | 
 |   3214       return; | 
 |   3215     } | 
 |   3216  | 
 |   3217     return function(model, node, oneTime) { | 
 |   3218       var binding = expression.getBinding(model, filterRegistry, oneTime); | 
 |   3219       if (expression.scopeIdent && binding) { | 
 |   3220         node.polymerExpressionScopeIdent_ = expression.scopeIdent; | 
 |   3221         if (expression.indexIdent) | 
 |   3222           node.polymerExpressionIndexIdent_ = expression.indexIdent; | 
 |   3223       } | 
 |   3224  | 
 |   3225       return binding; | 
 |   3226     } | 
 |   3227   } | 
 |   3228  | 
 |   3229   // TODO(rafaelw): Implement simple LRU. | 
 |   3230   var expressionParseCache = Object.create(null); | 
 |   3231  | 
 |   3232   function getExpression(expressionText) { | 
 |   3233     var expression = expressionParseCache[expressionText]; | 
 |   3234     if (!expression) { | 
 |   3235       var delegate = new ASTDelegate(); | 
 |   3236       esprima.parse(expressionText, delegate); | 
 |   3237       expression = new Expression(delegate); | 
 |   3238       expressionParseCache[expressionText] = expression; | 
 |   3239     } | 
 |   3240     return expression; | 
 |   3241   } | 
 |   3242  | 
 |   3243   function Literal(value) { | 
 |   3244     this.value = value; | 
 |   3245     this.valueFn_ = undefined; | 
 |   3246   } | 
 |   3247  | 
 |   3248   Literal.prototype = { | 
 |   3249     valueFn: function() { | 
 |   3250       if (!this.valueFn_) { | 
 |   3251         var value = this.value; | 
 |   3252         this.valueFn_ = function() { | 
 |   3253           return value; | 
 |   3254         } | 
 |   3255       } | 
 |   3256  | 
 |   3257       return this.valueFn_; | 
 |   3258     } | 
 |   3259   } | 
 |   3260  | 
 |   3261   function IdentPath(name) { | 
 |   3262     this.name = name; | 
 |   3263     this.path = Path.get(name); | 
 |   3264   } | 
 |   3265  | 
 |   3266   IdentPath.prototype = { | 
 |   3267     valueFn: function() { | 
 |   3268       if (!this.valueFn_) { | 
 |   3269         var name = this.name; | 
 |   3270         var path = this.path; | 
 |   3271         this.valueFn_ = function(model, observer) { | 
 |   3272           if (observer) | 
 |   3273             observer.addPath(model, path); | 
 |   3274  | 
 |   3275           return path.getValueFrom(model); | 
 |   3276         } | 
 |   3277       } | 
 |   3278  | 
 |   3279       return this.valueFn_; | 
 |   3280     }, | 
 |   3281  | 
 |   3282     setValue: function(model, newValue) { | 
 |   3283       if (this.path.length == 1) | 
 |   3284         model = findScope(model, this.path[0]); | 
 |   3285  | 
 |   3286       return this.path.setValueFrom(model, newValue); | 
 |   3287     } | 
 |   3288   }; | 
 |   3289  | 
 |   3290   function MemberExpression(object, property, accessor) { | 
 |   3291     this.computed = accessor == '['; | 
 |   3292  | 
 |   3293     this.dynamicDeps = typeof object == 'function' || | 
 |   3294                        object.dynamicDeps || | 
 |   3295                        (this.computed && !(property instanceof Literal)); | 
 |   3296  | 
 |   3297     this.simplePath = | 
 |   3298         !this.dynamicDeps && | 
 |   3299         (property instanceof IdentPath || property instanceof Literal) && | 
 |   3300         (object instanceof MemberExpression || object instanceof IdentPath); | 
 |   3301  | 
 |   3302     this.object = this.simplePath ? object : getFn(object); | 
 |   3303     this.property = !this.computed || this.simplePath ? | 
 |   3304         property : getFn(property); | 
 |   3305   } | 
 |   3306  | 
 |   3307   MemberExpression.prototype = { | 
 |   3308     get fullPath() { | 
 |   3309       if (!this.fullPath_) { | 
 |   3310  | 
 |   3311         var parts = this.object instanceof MemberExpression ? | 
 |   3312             this.object.fullPath.slice() : [this.object.name]; | 
 |   3313         parts.push(this.property instanceof IdentPath ? | 
 |   3314             this.property.name : this.property.value); | 
 |   3315         this.fullPath_ = Path.get(parts); | 
 |   3316       } | 
 |   3317  | 
 |   3318       return this.fullPath_; | 
 |   3319     }, | 
 |   3320  | 
 |   3321     valueFn: function() { | 
 |   3322       if (!this.valueFn_) { | 
 |   3323         var object = this.object; | 
 |   3324  | 
 |   3325         if (this.simplePath) { | 
 |   3326           var path = this.fullPath; | 
 |   3327  | 
 |   3328           this.valueFn_ = function(model, observer) { | 
 |   3329             if (observer) | 
 |   3330               observer.addPath(model, path); | 
 |   3331  | 
 |   3332             return path.getValueFrom(model); | 
 |   3333           }; | 
 |   3334         } else if (!this.computed) { | 
 |   3335           var path = Path.get(this.property.name); | 
 |   3336  | 
 |   3337           this.valueFn_ = function(model, observer, filterRegistry) { | 
 |   3338             var context = object(model, observer, filterRegistry); | 
 |   3339  | 
 |   3340             if (observer) | 
 |   3341               observer.addPath(context, path); | 
 |   3342  | 
 |   3343             return path.getValueFrom(context); | 
 |   3344           } | 
 |   3345         } else { | 
 |   3346           // Computed property. | 
 |   3347           var property = this.property; | 
 |   3348  | 
 |   3349           this.valueFn_ = function(model, observer, filterRegistry) { | 
 |   3350             var context = object(model, observer, filterRegistry); | 
 |   3351             var propName = property(model, observer, filterRegistry); | 
 |   3352             if (observer) | 
 |   3353               observer.addPath(context, [propName]); | 
 |   3354  | 
 |   3355             return context ? context[propName] : undefined; | 
 |   3356           }; | 
 |   3357         } | 
 |   3358       } | 
 |   3359       return this.valueFn_; | 
 |   3360     }, | 
 |   3361  | 
 |   3362     setValue: function(model, newValue) { | 
 |   3363       if (this.simplePath) { | 
 |   3364         this.fullPath.setValueFrom(model, newValue); | 
 |   3365         return newValue; | 
 |   3366       } | 
 |   3367  | 
 |   3368       var object = this.object(model); | 
 |   3369       var propName = this.property instanceof IdentPath ? this.property.name : | 
 |   3370           this.property(model); | 
 |   3371       return object[propName] = newValue; | 
 |   3372     } | 
 |   3373   }; | 
 |   3374  | 
 |   3375   function Filter(name, args) { | 
 |   3376     this.name = name; | 
 |   3377     this.args = []; | 
 |   3378     for (var i = 0; i < args.length; i++) { | 
 |   3379       this.args[i] = getFn(args[i]); | 
 |   3380     } | 
 |   3381   } | 
 |   3382  | 
 |   3383   Filter.prototype = { | 
 |   3384     transform: function(model, observer, filterRegistry, toModelDirection, | 
 |   3385                         initialArgs) { | 
 |   3386       var fn = filterRegistry[this.name]; | 
 |   3387       var context = model; | 
 |   3388       if (fn) { | 
 |   3389         context = undefined; | 
 |   3390       } else { | 
 |   3391         fn = context[this.name]; | 
 |   3392         if (!fn) { | 
 |   3393           console.error('Cannot find function or filter: ' + this.name); | 
 |   3394           return; | 
 |   3395         } | 
 |   3396       } | 
 |   3397  | 
 |   3398       // If toModelDirection is falsey, then the "normal" (dom-bound) direction | 
 |   3399       // is used. Otherwise, it looks for a 'toModel' property function on the | 
 |   3400       // object. | 
 |   3401       if (toModelDirection) { | 
 |   3402         fn = fn.toModel; | 
 |   3403       } else if (typeof fn.toDOM == 'function') { | 
 |   3404         fn = fn.toDOM; | 
 |   3405       } | 
 |   3406  | 
 |   3407       if (typeof fn != 'function') { | 
 |   3408         console.error('Cannot find function or filter: ' + this.name); | 
 |   3409         return; | 
 |   3410       } | 
 |   3411  | 
 |   3412       var args = initialArgs || []; | 
 |   3413       for (var i = 0; i < this.args.length; i++) { | 
 |   3414         args.push(getFn(this.args[i])(model, observer, filterRegistry)); | 
 |   3415       } | 
 |   3416  | 
 |   3417       return fn.apply(context, args); | 
 |   3418     } | 
 |   3419   }; | 
 |   3420  | 
 |   3421   function notImplemented() { throw Error('Not Implemented'); } | 
 |   3422  | 
 |   3423   var unaryOperators = { | 
 |   3424     '+': function(v) { return +v; }, | 
 |   3425     '-': function(v) { return -v; }, | 
 |   3426     '!': function(v) { return !v; } | 
 |   3427   }; | 
 |   3428  | 
 |   3429   var binaryOperators = { | 
 |   3430     '+': function(l, r) { return l+r; }, | 
 |   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   }; | 
 |   3446  | 
 |   3447   function getFn(arg) { | 
 |   3448     return typeof arg == 'function' ? arg : arg.valueFn(); | 
 |   3449   } | 
 |   3450  | 
 |   3451   function ASTDelegate() { | 
 |   3452     this.expression = null; | 
 |   3453     this.filters = []; | 
 |   3454     this.deps = {}; | 
 |   3455     this.currentPath = undefined; | 
 |   3456     this.scopeIdent = undefined; | 
 |   3457     this.indexIdent = undefined; | 
 |   3458     this.dynamicDeps = false; | 
 |   3459   } | 
 |   3460  | 
 |   3461   ASTDelegate.prototype = { | 
 |   3462     createUnaryExpression: function(op, argument) { | 
 |   3463       if (!unaryOperators[op]) | 
 |   3464         throw Error('Disallowed operator: ' + op); | 
 |   3465  | 
 |   3466       argument = getFn(argument); | 
 |   3467  | 
 |   3468       return function(model, observer, filterRegistry) { | 
 |   3469         return unaryOperators[op](argument(model, observer, filterRegistry)); | 
 |   3470       }; | 
 |   3471     }, | 
 |   3472  | 
 |   3473     createBinaryExpression: function(op, left, right) { | 
 |   3474       if (!binaryOperators[op]) | 
 |   3475         throw Error('Disallowed operator: ' + op); | 
 |   3476  | 
 |   3477       left = getFn(left); | 
 |   3478       right = getFn(right); | 
 |   3479  | 
 |   3480       switch (op) { | 
 |   3481         case '||': | 
 |   3482           this.dynamicDeps = true; | 
 |   3483           return function(model, observer, filterRegistry) { | 
 |   3484             return left(model, observer, filterRegistry) || | 
 |   3485                 right(model, observer, filterRegistry); | 
 |   3486           }; | 
 |   3487         case '&&': | 
 |   3488           this.dynamicDeps = true; | 
 |   3489           return function(model, observer, filterRegistry) { | 
 |   3490             return left(model, observer, filterRegistry) && | 
 |   3491                 right(model, observer, filterRegistry); | 
 |   3492           }; | 
 |   3493       } | 
 |   3494  | 
 |   3495       return function(model, observer, filterRegistry) { | 
 |   3496         return binaryOperators[op](left(model, observer, filterRegistry), | 
 |   3497                                    right(model, observer, filterRegistry)); | 
 |   3498       }; | 
 |   3499     }, | 
 |   3500  | 
 |   3501     createConditionalExpression: function(test, consequent, alternate) { | 
 |   3502       test = getFn(test); | 
 |   3503       consequent = getFn(consequent); | 
 |   3504       alternate = getFn(alternate); | 
 |   3505  | 
 |   3506       this.dynamicDeps = true; | 
 |   3507  | 
 |   3508       return function(model, observer, filterRegistry) { | 
 |   3509         return test(model, observer, filterRegistry) ? | 
 |   3510             consequent(model, observer, filterRegistry) : | 
 |   3511             alternate(model, observer, filterRegistry); | 
 |   3512       } | 
 |   3513     }, | 
 |   3514  | 
 |   3515     createIdentifier: function(name) { | 
 |   3516       var ident = new IdentPath(name); | 
 |   3517       ident.type = 'Identifier'; | 
 |   3518       return ident; | 
 |   3519     }, | 
 |   3520  | 
 |   3521     createMemberExpression: function(accessor, object, property) { | 
 |   3522       var ex = new MemberExpression(object, property, accessor); | 
 |   3523       if (ex.dynamicDeps) | 
 |   3524         this.dynamicDeps = true; | 
 |   3525       return ex; | 
 |   3526     }, | 
 |   3527  | 
 |   3528     createCallExpression: function(expression, args) { | 
 |   3529       if (!(expression instanceof IdentPath)) | 
 |   3530         throw Error('Only identifier function invocations are allowed'); | 
 |   3531  | 
 |   3532       var filter = new Filter(expression.name, args); | 
 |   3533  | 
 |   3534       return function(model, observer, filterRegistry) { | 
 |   3535         return filter.transform(model, observer, filterRegistry, false); | 
 |   3536       }; | 
 |   3537     }, | 
 |   3538  | 
 |   3539     createLiteral: function(token) { | 
 |   3540       return new Literal(token.value); | 
 |   3541     }, | 
 |   3542  | 
 |   3543     createArrayExpression: function(elements) { | 
 |   3544       for (var i = 0; i < elements.length; i++) | 
 |   3545         elements[i] = getFn(elements[i]); | 
 |   3546  | 
 |   3547       return function(model, observer, filterRegistry) { | 
 |   3548         var arr = [] | 
 |   3549         for (var i = 0; i < elements.length; i++) | 
 |   3550           arr.push(elements[i](model, observer, filterRegistry)); | 
 |   3551         return arr; | 
 |   3552       } | 
 |   3553     }, | 
 |   3554  | 
 |   3555     createProperty: function(kind, key, value) { | 
 |   3556       return { | 
 |   3557         key: key instanceof IdentPath ? key.name : key.value, | 
 |   3558         value: value | 
 |   3559       }; | 
 |   3560     }, | 
 |   3561  | 
 |   3562     createObjectExpression: function(properties) { | 
 |   3563       for (var i = 0; i < properties.length; i++) | 
 |   3564         properties[i].value = getFn(properties[i].value); | 
 |   3565  | 
 |   3566       return function(model, observer, filterRegistry) { | 
 |   3567         var obj = {}; | 
 |   3568         for (var i = 0; i < properties.length; i++) | 
 |   3569           obj[properties[i].key] = | 
 |   3570               properties[i].value(model, observer, filterRegistry); | 
 |   3571         return obj; | 
 |   3572       } | 
 |   3573     }, | 
 |   3574  | 
 |   3575     createFilter: function(name, args) { | 
 |   3576       this.filters.push(new Filter(name, args)); | 
 |   3577     }, | 
 |   3578  | 
 |   3579     createAsExpression: function(expression, scopeIdent) { | 
 |   3580       this.expression = expression; | 
 |   3581       this.scopeIdent = scopeIdent; | 
 |   3582     }, | 
 |   3583  | 
 |   3584     createInExpression: function(scopeIdent, indexIdent, expression) { | 
 |   3585       this.expression = expression; | 
 |   3586       this.scopeIdent = scopeIdent; | 
 |   3587       this.indexIdent = indexIdent; | 
 |   3588     }, | 
 |   3589  | 
 |   3590     createTopLevel: function(expression) { | 
 |   3591       this.expression = expression; | 
 |   3592     }, | 
 |   3593  | 
 |   3594     createThisExpression: notImplemented | 
 |   3595   } | 
 |   3596  | 
 |   3597   function ConstantObservable(value) { | 
 |   3598     this.value_ = value; | 
 |   3599   } | 
 |   3600  | 
 |   3601   ConstantObservable.prototype = { | 
 |   3602     open: function() { return this.value_; }, | 
 |   3603     discardChanges: function() { return this.value_; }, | 
 |   3604     deliver: function() {}, | 
 |   3605     close: function() {}, | 
 |   3606   } | 
 |   3607  | 
 |   3608   function Expression(delegate) { | 
 |   3609     this.scopeIdent = delegate.scopeIdent; | 
 |   3610     this.indexIdent = delegate.indexIdent; | 
 |   3611  | 
 |   3612     if (!delegate.expression) | 
 |   3613       throw Error('No expression found.'); | 
 |   3614  | 
 |   3615     this.expression = delegate.expression; | 
 |   3616     getFn(this.expression); // forces enumeration of path dependencies | 
 |   3617  | 
 |   3618     this.filters = delegate.filters; | 
 |   3619     this.dynamicDeps = delegate.dynamicDeps; | 
 |   3620   } | 
 |   3621  | 
 |   3622   Expression.prototype = { | 
 |   3623     getBinding: function(model, filterRegistry, oneTime) { | 
 |   3624       if (oneTime) | 
 |   3625         return this.getValue(model, undefined, filterRegistry); | 
 |   3626  | 
 |   3627       var observer = new CompoundObserver(); | 
 |   3628       // captures deps. | 
 |   3629       var firstValue = this.getValue(model, observer, filterRegistry); | 
 |   3630       var firstTime = true; | 
 |   3631       var self = this; | 
 |   3632  | 
 |   3633       function valueFn() { | 
 |   3634         // deps cannot have changed on first value retrieval. | 
 |   3635         if (firstTime) { | 
 |   3636           firstTime = false; | 
 |   3637           return firstValue; | 
 |   3638         } | 
 |   3639  | 
 |   3640         if (self.dynamicDeps) | 
 |   3641           observer.startReset(); | 
 |   3642  | 
 |   3643         var value = self.getValue(model, | 
 |   3644                                   self.dynamicDeps ? observer : undefined, | 
 |   3645                                   filterRegistry); | 
 |   3646         if (self.dynamicDeps) | 
 |   3647           observer.finishReset(); | 
 |   3648  | 
 |   3649         return value; | 
 |   3650       } | 
 |   3651  | 
 |   3652       function setValueFn(newValue) { | 
 |   3653         self.setValue(model, newValue, filterRegistry); | 
 |   3654         return newValue; | 
 |   3655       } | 
 |   3656  | 
 |   3657       return new ObserverTransform(observer, valueFn, setValueFn, true); | 
 |   3658     }, | 
 |   3659  | 
 |   3660     getValue: function(model, observer, filterRegistry) { | 
 |   3661       var value = getFn(this.expression)(model, observer, filterRegistry); | 
 |   3662       for (var i = 0; i < this.filters.length; i++) { | 
 |   3663         value = this.filters[i].transform(model, observer, filterRegistry, | 
 |   3664             false, [value]); | 
 |   3665       } | 
 |   3666  | 
 |   3667       return value; | 
 |   3668     }, | 
 |   3669  | 
 |   3670     setValue: function(model, newValue, filterRegistry) { | 
 |   3671       var count = this.filters ? this.filters.length : 0; | 
 |   3672       while (count-- > 0) { | 
 |   3673         newValue = this.filters[count].transform(model, undefined, | 
 |   3674             filterRegistry, true, [newValue]); | 
 |   3675       } | 
 |   3676  | 
 |   3677       if (this.expression.setValue) | 
 |   3678         return this.expression.setValue(model, newValue); | 
 |   3679     } | 
 |   3680   } | 
 |   3681  | 
 |   3682   /** | 
 |   3683    * Converts a style property name to a css property name. For example: | 
 |   3684    * "WebkitUserSelect" to "-webkit-user-select" | 
 |   3685    */ | 
 |   3686   function convertStylePropertyName(name) { | 
 |   3687     return String(name).replace(/[A-Z]/g, function(c) { | 
 |   3688       return '-' + c.toLowerCase(); | 
 |   3689     }); | 
 |   3690   } | 
 |   3691  | 
 |   3692   var parentScopeName = '@' + Math.random().toString(36).slice(2); | 
 |   3693  | 
 |   3694   // Single ident paths must bind directly to the appropriate scope object. | 
 |   3695   // I.e. Pushed values in two-bindings need to be assigned to the actual model | 
 |   3696   // object. | 
 |   3697   function findScope(model, prop) { | 
 |   3698     while (model[parentScopeName] && | 
 |   3699            !Object.prototype.hasOwnProperty.call(model, prop)) { | 
 |   3700       model = model[parentScopeName]; | 
 |   3701     } | 
 |   3702  | 
 |   3703     return model; | 
 |   3704   } | 
 |   3705  | 
 |   3706   function isLiteralExpression(pathString) { | 
 |   3707     switch (pathString) { | 
 |   3708       case '': | 
 |   3709         return false; | 
 |   3710  | 
 |   3711       case 'false': | 
 |   3712       case 'null': | 
 |   3713       case 'true': | 
 |   3714         return true; | 
 |   3715     } | 
 |   3716  | 
 |   3717     if (!isNaN(Number(pathString))) | 
 |   3718       return true; | 
 |   3719  | 
 |   3720     return false; | 
 |   3721   }; | 
 |   3722  | 
 |   3723   function PolymerExpressions() {} | 
 |   3724  | 
 |   3725   PolymerExpressions.prototype = { | 
 |   3726     // "built-in" filters | 
 |   3727     styleObject: function(value) { | 
 |   3728       var parts = []; | 
 |   3729       for (var key in value) { | 
 |   3730         parts.push(convertStylePropertyName(key) + ': ' + value[key]); | 
 |   3731       } | 
 |   3732       return parts.join('; '); | 
 |   3733     }, | 
 |   3734  | 
 |   3735     tokenList: function(value) { | 
 |   3736       var tokens = []; | 
 |   3737       for (var key in value) { | 
 |   3738         if (value[key]) | 
 |   3739           tokens.push(key); | 
 |   3740       } | 
 |   3741       return tokens.join(' '); | 
 |   3742     }, | 
 |   3743  | 
 |   3744     // binding delegate API | 
 |   3745     prepareInstancePositionChanged: function(template) { | 
 |   3746       var indexIdent = template.polymerExpressionIndexIdent_; | 
 |   3747       if (!indexIdent) | 
 |   3748         return; | 
 |   3749  | 
 |   3750       return function(templateInstance, index) { | 
 |   3751         templateInstance.model[indexIdent] = index; | 
 |   3752       }; | 
 |   3753     }, | 
 |   3754  | 
 |   3755     prepareBinding: function(pathString, name, node) { | 
 |   3756       var path = Path.get(pathString); | 
 |   3757  | 
 |   3758       if (!isLiteralExpression(pathString) && path.valid) { | 
 |   3759         if (path.length == 1) { | 
 |   3760           return function(model, node, oneTime) { | 
 |   3761             if (oneTime) | 
 |   3762               return path.getValueFrom(model); | 
 |   3763  | 
 |   3764             var scope = findScope(model, path[0]); | 
 |   3765             return new PathObserver(scope, path); | 
 |   3766           }; | 
 |   3767         } | 
 |   3768         return; // bail out early if pathString is simple path. | 
 |   3769       } | 
 |   3770  | 
 |   3771       return prepareBinding(pathString, name, node, this); | 
 |   3772     }, | 
 |   3773  | 
 |   3774     prepareInstanceModel: function(template) { | 
 |   3775       var scopeName = template.polymerExpressionScopeIdent_; | 
 |   3776       if (!scopeName) | 
 |   3777         return; | 
 |   3778  | 
 |   3779       var parentScope = template.templateInstance ? | 
 |   3780           template.templateInstance.model : | 
 |   3781           template.model; | 
 |   3782  | 
 |   3783       var indexName = template.polymerExpressionIndexIdent_; | 
 |   3784  | 
 |   3785       return function(model) { | 
 |   3786         return createScopeObject(parentScope, model, scopeName, indexName); | 
 |   3787       }; | 
 |   3788     } | 
 |   3789   }; | 
 |   3790  | 
 |   3791   var createScopeObject = ('__proto__' in {}) ? | 
 |   3792     function(parentScope, model, scopeName, indexName) { | 
 |   3793       var scope = {}; | 
 |   3794       scope[scopeName] = model; | 
 |   3795       scope[indexName] = undefined; | 
 |   3796       scope[parentScopeName] = parentScope; | 
 |   3797       scope.__proto__ = parentScope; | 
 |   3798       return scope; | 
 |   3799     } : | 
 |   3800     function(parentScope, model, scopeName, indexName) { | 
 |   3801       var scope = Object.create(parentScope); | 
 |   3802       Object.defineProperty(scope, scopeName, | 
 |   3803           { value: model, configurable: true, writable: true }); | 
 |   3804       Object.defineProperty(scope, indexName, | 
 |   3805           { value: undefined, configurable: true, writable: true }); | 
 |   3806       Object.defineProperty(scope, parentScopeName, | 
 |   3807           { value: parentScope, configurable: true, writable: true }); | 
 |   3808       return scope; | 
 |   3809     }; | 
 |   3810  | 
 |   3811   global.PolymerExpressions = PolymerExpressions; | 
 |   3812   PolymerExpressions.getExpression = getExpression; | 
 |   3813 })(this); | 
 |   3814  | 
 |   3815 Polymer = { | 
 |   3816   version: '0.5.1' | 
 |   3817 }; | 
 |   3818  | 
 |   3819 // TODO(sorvell): this ensures Polymer is an object and not a function | 
 |   3820 // Platform is currently defining it as a function to allow for async loading | 
 |   3821 // of polymer; once we refine the loading process this likely goes away. | 
 |   3822 if (typeof window.Polymer === 'function') { | 
 |   3823   Polymer = {}; | 
 |   3824 } | 
 |   3825  | 
 |   3826  | 
 |   3827 (function(scope) { | 
 |   3828  | 
 |   3829   function withDependencies(task, depends) { | 
 |   3830     depends = depends || []; | 
 |   3831     if (!depends.map) { | 
 |   3832       depends = [depends]; | 
 |   3833     } | 
 |   3834     return task.apply(this, depends.map(marshal)); | 
 |   3835   } | 
 |   3836  | 
 |   3837   function module(name, dependsOrFactory, moduleFactory) { | 
 |   3838     var module; | 
 |   3839     switch (arguments.length) { | 
 |   3840       case 0: | 
 |   3841         return; | 
 |   3842       case 1: | 
 |   3843         module = null; | 
 |   3844         break; | 
 |   3845       case 2: | 
 |   3846         // dependsOrFactory is `factory` in this case | 
 |   3847         module = dependsOrFactory.apply(this); | 
 |   3848         break; | 
 |   3849       default: | 
 |   3850         // dependsOrFactory is `depends` in this case | 
 |   3851         module = withDependencies(moduleFactory, dependsOrFactory); | 
 |   3852         break; | 
 |   3853     } | 
 |   3854     modules[name] = module; | 
 |   3855   }; | 
 |   3856  | 
 |   3857   function marshal(name) { | 
 |   3858     return modules[name]; | 
 |   3859   } | 
 |   3860  | 
 |   3861   var modules = {}; | 
 |   3862  | 
 |   3863   function using(depends, task) { | 
 |   3864     HTMLImports.whenImportsReady(function() { | 
 |   3865       withDependencies(task, depends); | 
 |   3866     }); | 
 |   3867   }; | 
 |   3868  | 
 |   3869   // exports | 
 |   3870  | 
 |   3871   scope.marshal = marshal; | 
 |   3872   // `module` confuses commonjs detectors | 
 |   3873   scope.modularize = module; | 
 |   3874   scope.using = using; | 
 |   3875  | 
 |   3876 })(window); | 
 |   3877  | 
 |   3878 /* | 
 |   3879         Build only script. | 
 |   3880  | 
 |   3881   Ensures scripts needed for basic x-platform compatibility | 
 |   3882   will be run when platform.js is not loaded. | 
 |   3883  */ | 
 |   3884 if (!window.WebComponents) { | 
 |   3885  | 
 |   3886 /* | 
 |   3887         On supported platforms, platform.js is not needed. To retain compatibili
       ty | 
 |   3888         with the polyfills, we stub out minimal functionality. | 
 |   3889  */ | 
 |   3890 if (!window.WebComponents) { | 
 |   3891  | 
 |   3892   WebComponents = { | 
 |   3893         flush: function() {}, | 
 |   3894     flags: {log: {}} | 
 |   3895   }; | 
 |   3896  | 
 |   3897   Platform = WebComponents; | 
 |   3898  | 
 |   3899   CustomElements = { | 
 |   3900         useNative: true, | 
 |   3901     ready: true, | 
 |   3902     takeRecords: function() {}, | 
 |   3903     instanceof: function(obj, base) { | 
 |   3904       return obj instanceof base; | 
 |   3905     } | 
 |   3906   }; | 
 |   3907    | 
 |   3908   HTMLImports = { | 
 |   3909         useNative: true | 
 |   3910   }; | 
 |   3911  | 
 |   3912    | 
 |   3913   addEventListener('HTMLImportsLoaded', function() { | 
 |   3914     document.dispatchEvent( | 
 |   3915       new CustomEvent('WebComponentsReady', {bubbles: true}) | 
 |   3916     ); | 
 |   3917   }); | 
 |   3918  | 
 |   3919  | 
 |   3920   // ShadowDOM | 
 |   3921   ShadowDOMPolyfill = null; | 
 |   3922   wrap = unwrap = function(n){ | 
 |   3923     return n; | 
 |   3924   }; | 
 |   3925  | 
 |   3926 } | 
 |   3927  | 
 |   3928 /* | 
 |   3929   Create polyfill scope and feature detect native support. | 
 |   3930 */ | 
 |   3931 window.HTMLImports = window.HTMLImports || {flags:{}}; | 
 |   3932  | 
 |   3933 (function(scope) { | 
 |   3934  | 
 |   3935 /** | 
 |   3936   Basic setup and simple module executer. We collect modules and then execute | 
 |   3937   the code later, only if it's necessary for polyfilling. | 
 |   3938 */ | 
 |   3939 var IMPORT_LINK_TYPE = 'import'; | 
 |   3940 var useNative = Boolean(IMPORT_LINK_TYPE in document.createElement('link')); | 
 |   3941  | 
 |   3942 /** | 
 |   3943   Support `currentScript` on all browsers as `document._currentScript.` | 
 |   3944  | 
 |   3945   NOTE: We cannot polyfill `document.currentScript` because it's not possible | 
 |   3946   both to override and maintain the ability to capture the native value. | 
 |   3947   Therefore we choose to expose `_currentScript` both when native imports | 
 |   3948   and the polyfill are in use. | 
 |   3949 */ | 
 |   3950 // NOTE: ShadowDOMPolyfill intrusion. | 
 |   3951 var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill); | 
 |   3952 var wrap = function(node) { | 
 |   3953   return hasShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) : node; | 
 |   3954 }; | 
 |   3955 var rootDocument = wrap(document); | 
 |   3956  | 
 |   3957 var currentScriptDescriptor = { | 
 |   3958   get: function() { | 
 |   3959     var script = HTMLImports.currentScript || document.currentScript || | 
 |   3960         // NOTE: only works when called in synchronously executing code. | 
 |   3961         // readyState should check if `loading` but IE10 is | 
 |   3962         // interactive when scripts run so we cheat. | 
 |   3963         (document.readyState !== 'complete' ? | 
 |   3964         document.scripts[document.scripts.length - 1] : null); | 
 |   3965     return wrap(script); | 
 |   3966   }, | 
 |   3967   configurable: true | 
 |   3968 }; | 
 |   3969  | 
 |   3970 Object.defineProperty(document, '_currentScript', currentScriptDescriptor); | 
 |   3971 Object.defineProperty(rootDocument, '_currentScript', currentScriptDescriptor); | 
 |   3972  | 
 |   3973 /** | 
 |   3974   Add support for the `HTMLImportsLoaded` event and the `HTMLImports.whenReady` | 
 |   3975   method. This api is necessary because unlike the native implementation, | 
 |   3976   script elements do not force imports to resolve. Instead, users should wrap | 
 |   3977   code in either an `HTMLImportsLoaded` hander or after load time in an | 
 |   3978   `HTMLImports.whenReady(callback)` call. | 
 |   3979  | 
 |   3980   NOTE: This module also supports these apis under the native implementation. | 
 |   3981   Therefore, if this file is loaded, the same code can be used under both | 
 |   3982   the polyfill and native implementation. | 
 |   3983  */ | 
 |   3984  | 
 |   3985 var isIE = /Trident/.test(navigator.userAgent); | 
 |   3986  | 
 |   3987 // call a callback when all HTMLImports in the document at call time | 
 |   3988 // (or at least document ready) have loaded. | 
 |   3989 // 1. ensure the document is in a ready state (has dom), then | 
 |   3990 // 2. watch for loading of imports and call callback when done | 
 |   3991 function whenReady(callback, doc) { | 
 |   3992   doc = doc || rootDocument; | 
 |   3993   // if document is loading, wait and try again | 
 |   3994   whenDocumentReady(function() { | 
 |   3995     watchImportsLoad(callback, doc); | 
 |   3996   }, doc); | 
 |   3997 } | 
 |   3998  | 
 |   3999 // call the callback when the document is in a ready state (has dom) | 
 |   4000 var requiredReadyState = isIE ? 'complete' : 'interactive'; | 
 |   4001 var READY_EVENT = 'readystatechange'; | 
 |   4002 function isDocumentReady(doc) { | 
 |   4003   return (doc.readyState === 'complete' || | 
 |   4004       doc.readyState === requiredReadyState); | 
 |   4005 } | 
 |   4006  | 
 |   4007 // call <callback> when we ensure the document is in a ready state | 
 |   4008 function whenDocumentReady(callback, doc) { | 
 |   4009   if (!isDocumentReady(doc)) { | 
 |   4010     var checkReady = function() { | 
 |   4011       if (doc.readyState === 'complete' || | 
 |   4012           doc.readyState === requiredReadyState) { | 
 |   4013         doc.removeEventListener(READY_EVENT, checkReady); | 
 |   4014         whenDocumentReady(callback, doc); | 
 |   4015       } | 
 |   4016     }; | 
 |   4017     doc.addEventListener(READY_EVENT, checkReady); | 
 |   4018   } else if (callback) { | 
 |   4019     callback(); | 
 |   4020   } | 
 |   4021 } | 
 |   4022  | 
 |   4023 function markTargetLoaded(event) { | 
 |   4024   event.target.__loaded = true; | 
 |   4025 } | 
 |   4026  | 
 |   4027 // call <callback> when we ensure all imports have loaded | 
 |   4028 function watchImportsLoad(callback, doc) { | 
 |   4029   var imports = doc.querySelectorAll('link[rel=import]'); | 
 |   4030   var loaded = 0, l = imports.length; | 
 |   4031   function checkDone(d) { | 
 |   4032     if ((loaded == l) && callback) { | 
 |   4033        callback(); | 
 |   4034     } | 
 |   4035   } | 
 |   4036   function loadedImport(e) { | 
 |   4037     markTargetLoaded(e); | 
 |   4038     loaded++; | 
 |   4039     checkDone(); | 
 |   4040   } | 
 |   4041   if (l) { | 
 |   4042     for (var i=0, imp; (i<l) && (imp=imports[i]); i++) { | 
 |   4043       if (isImportLoaded(imp)) { | 
 |   4044         loadedImport.call(imp, {target: imp}); | 
 |   4045       } else { | 
 |   4046         imp.addEventListener('load', loadedImport); | 
 |   4047         imp.addEventListener('error', loadedImport); | 
 |   4048       } | 
 |   4049     } | 
 |   4050   } else { | 
 |   4051     checkDone(); | 
 |   4052   } | 
 |   4053 } | 
 |   4054  | 
 |   4055 // NOTE: test for native imports loading is based on explicitly watching | 
 |   4056 // all imports (see below). | 
 |   4057 // However, we cannot rely on this entirely without watching the entire document | 
 |   4058 // for import links. For perf reasons, currently only head is watched. | 
 |   4059 // Instead, we fallback to checking if the import property is available | 
 |   4060 // and the document is not itself loading. | 
 |   4061 function isImportLoaded(link) { | 
 |   4062   return useNative ? link.__loaded || | 
 |   4063       (link.import && link.import.readyState !== 'loading') : | 
 |   4064       link.__importParsed; | 
 |   4065 } | 
 |   4066  | 
 |   4067 // TODO(sorvell): Workaround for | 
 |   4068 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=25007, should be removed when | 
 |   4069 // this bug is addressed. | 
 |   4070 // (1) Install a mutation observer to see when HTMLImports have loaded | 
 |   4071 // (2) if this script is run during document load it will watch any existing | 
 |   4072 // imports for loading. | 
 |   4073 // | 
 |   4074 // NOTE: The workaround has restricted functionality: (1) it's only compatible | 
 |   4075 // with imports that are added to document.head since the mutation observer | 
 |   4076 // watches only head for perf reasons, (2) it requires this script | 
 |   4077 // to run before any imports have completed loading. | 
 |   4078 if (useNative) { | 
 |   4079   new MutationObserver(function(mxns) { | 
 |   4080     for (var i=0, l=mxns.length, m; (i < l) && (m=mxns[i]); i++) { | 
 |   4081       if (m.addedNodes) { | 
 |   4082         handleImports(m.addedNodes); | 
 |   4083       } | 
 |   4084     } | 
 |   4085   }).observe(document.head, {childList: true}); | 
 |   4086  | 
 |   4087   function handleImports(nodes) { | 
 |   4088     for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) { | 
 |   4089       if (isImport(n)) { | 
 |   4090         handleImport(n); | 
 |   4091       } | 
 |   4092     } | 
 |   4093   } | 
 |   4094  | 
 |   4095   function isImport(element) { | 
 |   4096     return element.localName === 'link' && element.rel === 'import'; | 
 |   4097   } | 
 |   4098  | 
 |   4099   function handleImport(element) { | 
 |   4100     var loaded = element.import; | 
 |   4101     if (loaded) { | 
 |   4102       markTargetLoaded({target: element}); | 
 |   4103     } else { | 
 |   4104       element.addEventListener('load', markTargetLoaded); | 
 |   4105       element.addEventListener('error', markTargetLoaded); | 
 |   4106     } | 
 |   4107   } | 
 |   4108  | 
 |   4109   // make sure to catch any imports that are in the process of loading | 
 |   4110   // when this script is run. | 
 |   4111   (function() { | 
 |   4112     if (document.readyState === 'loading') { | 
 |   4113       var imports = document.querySelectorAll('link[rel=import]'); | 
 |   4114       for (var i=0, l=imports.length, imp; (i<l) && (imp=imports[i]); i++) { | 
 |   4115         handleImport(imp); | 
 |   4116       } | 
 |   4117     } | 
 |   4118   })(); | 
 |   4119  | 
 |   4120 } | 
 |   4121  | 
 |   4122 // Fire the 'HTMLImportsLoaded' event when imports in document at load time | 
 |   4123 // have loaded. This event is required to simulate the script blocking | 
 |   4124 // behavior of native imports. A main document script that needs to be sure | 
 |   4125 // imports have loaded should wait for this event. | 
 |   4126 whenReady(function() { | 
 |   4127   HTMLImports.ready = true; | 
 |   4128   HTMLImports.readyTime = new Date().getTime(); | 
 |   4129   rootDocument.dispatchEvent( | 
 |   4130     new CustomEvent('HTMLImportsLoaded', {bubbles: true}) | 
 |   4131   ); | 
 |   4132 }); | 
 |   4133  | 
 |   4134 // exports | 
 |   4135 scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE; | 
 |   4136 scope.useNative = useNative; | 
 |   4137 scope.rootDocument = rootDocument; | 
 |   4138 scope.whenReady = whenReady; | 
 |   4139 scope.isIE = isIE; | 
 |   4140  | 
 |   4141 })(HTMLImports); | 
 |   4142  | 
 |   4143 (function(scope) { | 
 |   4144  | 
 |   4145   // TODO(sorvell): It's desireable to provide a default stylesheet  | 
 |   4146   // that's convenient for styling unresolved elements, but | 
 |   4147   // it's cumbersome to have to include this manually in every page. | 
 |   4148   // It would make sense to put inside some HTMLImport but  | 
 |   4149   // the HTMLImports polyfill does not allow loading of stylesheets  | 
 |   4150   // that block rendering. Therefore this injection is tolerated here. | 
 |   4151   var style = document.createElement('style'); | 
 |   4152   style.textContent = '' | 
 |   4153       + 'body {' | 
 |   4154       + 'transition: opacity ease-in 0.2s;'  | 
 |   4155       + ' } \n' | 
 |   4156       + 'body[unresolved] {' | 
 |   4157       + 'opacity: 0; display: block; overflow: hidden;'  | 
 |   4158       + ' } \n' | 
 |   4159       ; | 
 |   4160   var head = document.querySelector('head'); | 
 |   4161   head.insertBefore(style, head.firstChild); | 
 |   4162  | 
 |   4163 })(Platform); | 
 |   4164  | 
 |   4165 /* | 
 |   4166         Build only script. | 
 |   4167  | 
 |   4168   Ensures scripts needed for basic x-platform compatibility | 
 |   4169   will be run when platform.js is not loaded. | 
 |   4170  */ | 
 |   4171 } | 
 |   4172 (function(global) { | 
 |   4173   'use strict'; | 
 |   4174  | 
 |   4175   var testingExposeCycleCount = global.testingExposeCycleCount; | 
 |   4176  | 
 |   4177   // Detect and do basic sanity checking on Object/Array.observe. | 
 |   4178   function detectObjectObserve() { | 
 |   4179     if (typeof Object.observe !== 'function' || | 
 |   4180         typeof Array.observe !== 'function') { | 
 |   4181       return false; | 
 |   4182     } | 
 |   4183  | 
 |   4184     var records = []; | 
 |   4185  | 
 |   4186     function callback(recs) { | 
 |   4187       records = recs; | 
 |   4188     } | 
 |   4189  | 
 |   4190     var test = {}; | 
 |   4191     var arr = []; | 
 |   4192     Object.observe(test, callback); | 
 |   4193     Array.observe(arr, callback); | 
 |   4194     test.id = 1; | 
 |   4195     test.id = 2; | 
 |   4196     delete test.id; | 
 |   4197     arr.push(1, 2); | 
 |   4198     arr.length = 0; | 
 |   4199  | 
 |   4200     Object.deliverChangeRecords(callback); | 
 |   4201     if (records.length !== 5) | 
 |   4202       return false; | 
 |   4203  | 
 |   4204     if (records[0].type != 'add' || | 
 |   4205         records[1].type != 'update' || | 
 |   4206         records[2].type != 'delete' || | 
 |   4207         records[3].type != 'splice' || | 
 |   4208         records[4].type != 'splice') { | 
 |   4209       return false; | 
 |   4210     } | 
 |   4211  | 
 |   4212     Object.unobserve(test, callback); | 
 |   4213     Array.unobserve(arr, callback); | 
 |   4214  | 
 |   4215     return true; | 
 |   4216   } | 
 |   4217  | 
 |   4218   var hasObserve = detectObjectObserve(); | 
 |   4219  | 
 |   4220   function detectEval() { | 
 |   4221     // Don't test for eval if we're running in a Chrome App environment. | 
 |   4222     // We check for APIs set that only exist in a Chrome App context. | 
 |   4223     if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) { | 
 |   4224       return false; | 
 |   4225     } | 
 |   4226  | 
 |   4227     // Firefox OS Apps do not allow eval. This feature detection is very hacky | 
 |   4228     // but even if some other platform adds support for this function this code | 
 |   4229     // will continue to work. | 
 |   4230     if (typeof navigator != 'undefined' && navigator.getDeviceStorage) { | 
 |   4231       return false; | 
 |   4232     } | 
 |   4233  | 
 |   4234     try { | 
 |   4235       var f = new Function('', 'return true;'); | 
 |   4236       return f(); | 
 |   4237     } catch (ex) { | 
 |   4238       return false; | 
 |   4239     } | 
 |   4240   } | 
 |   4241  | 
 |   4242   var hasEval = detectEval(); | 
 |   4243  | 
 |   4244   function isIndex(s) { | 
 |   4245     return +s === s >>> 0 && s !== ''; | 
 |   4246   } | 
 |   4247  | 
 |   4248   function toNumber(s) { | 
 |   4249     return +s; | 
 |   4250   } | 
 |   4251  | 
 |   4252   function isObject(obj) { | 
 |   4253     return obj === Object(obj); | 
 |   4254   } | 
 |   4255  | 
 |   4256   var numberIsNaN = global.Number.isNaN || function(value) { | 
 |   4257     return typeof value === 'number' && global.isNaN(value); | 
 |   4258   } | 
 |   4259  | 
 |   4260   function areSameValue(left, right) { | 
 |   4261     if (left === right) | 
 |   4262       return left !== 0 || 1 / left === 1 / right; | 
 |   4263     if (numberIsNaN(left) && numberIsNaN(right)) | 
 |   4264       return true; | 
 |   4265  | 
 |   4266     return left !== left && right !== right; | 
 |   4267   } | 
 |   4268  | 
 |   4269   var createObject = ('__proto__' in {}) ? | 
 |   4270     function(obj) { return obj; } : | 
 |   4271     function(obj) { | 
 |   4272       var proto = obj.__proto__; | 
 |   4273       if (!proto) | 
 |   4274         return obj; | 
 |   4275       var newObject = Object.create(proto); | 
 |   4276       Object.getOwnPropertyNames(obj).forEach(function(name) { | 
 |   4277         Object.defineProperty(newObject, name, | 
 |   4278                              Object.getOwnPropertyDescriptor(obj, name)); | 
 |   4279       }); | 
 |   4280       return newObject; | 
 |   4281     }; | 
 |   4282  | 
 |   4283   var identStart = '[\$_a-zA-Z]'; | 
 |   4284   var identPart = '[\$_a-zA-Z0-9]'; | 
 |   4285   var identRegExp = new RegExp('^' + identStart + '+' + identPart + '*' + '$'); | 
 |   4286  | 
 |   4287   function getPathCharType(char) { | 
 |   4288     if (char === undefined) | 
 |   4289       return 'eof'; | 
 |   4290  | 
 |   4291     var code = char.charCodeAt(0); | 
 |   4292  | 
 |   4293     switch(code) { | 
 |   4294       case 0x5B: // [ | 
 |   4295       case 0x5D: // ] | 
 |   4296       case 0x2E: // . | 
 |   4297       case 0x22: // " | 
 |   4298       case 0x27: // ' | 
 |   4299       case 0x30: // 0 | 
 |   4300         return char; | 
 |   4301  | 
 |   4302       case 0x5F: // _ | 
 |   4303       case 0x24: // $ | 
 |   4304         return 'ident'; | 
 |   4305  | 
 |   4306       case 0x20: // Space | 
 |   4307       case 0x09: // Tab | 
 |   4308       case 0x0A: // Newline | 
 |   4309       case 0x0D: // Return | 
 |   4310       case 0xA0:  // No-break space | 
 |   4311       case 0xFEFF:  // Byte Order Mark | 
 |   4312       case 0x2028:  // Line Separator | 
 |   4313       case 0x2029:  // Paragraph Separator | 
 |   4314         return 'ws'; | 
 |   4315     } | 
 |   4316  | 
 |   4317     // a-z, A-Z | 
 |   4318     if ((0x61 <= code && code <= 0x7A) || (0x41 <= code && code <= 0x5A)) | 
 |   4319       return 'ident'; | 
 |   4320  | 
 |   4321     // 1-9 | 
 |   4322     if (0x31 <= code && code <= 0x39) | 
 |   4323       return 'number'; | 
 |   4324  | 
 |   4325     return 'else'; | 
 |   4326   } | 
 |   4327  | 
 |   4328   var pathStateMachine = { | 
 |   4329     'beforePath': { | 
 |   4330       'ws': ['beforePath'], | 
 |   4331       'ident': ['inIdent', 'append'], | 
 |   4332       '[': ['beforeElement'], | 
 |   4333       'eof': ['afterPath'] | 
 |   4334     }, | 
 |   4335  | 
 |   4336     'inPath': { | 
 |   4337       'ws': ['inPath'], | 
 |   4338       '.': ['beforeIdent'], | 
 |   4339       '[': ['beforeElement'], | 
 |   4340       'eof': ['afterPath'] | 
 |   4341     }, | 
 |   4342  | 
 |   4343     'beforeIdent': { | 
 |   4344       'ws': ['beforeIdent'], | 
 |   4345       'ident': ['inIdent', 'append'] | 
 |   4346     }, | 
 |   4347  | 
 |   4348     'inIdent': { | 
 |   4349       'ident': ['inIdent', 'append'], | 
 |   4350       '0': ['inIdent', 'append'], | 
 |   4351       'number': ['inIdent', 'append'], | 
 |   4352       'ws': ['inPath', 'push'], | 
 |   4353       '.': ['beforeIdent', 'push'], | 
 |   4354       '[': ['beforeElement', 'push'], | 
 |   4355       'eof': ['afterPath', 'push'] | 
 |   4356     }, | 
 |   4357  | 
 |   4358     'beforeElement': { | 
 |   4359       'ws': ['beforeElement'], | 
 |   4360       '0': ['afterZero', 'append'], | 
 |   4361       'number': ['inIndex', 'append'], | 
 |   4362       "'": ['inSingleQuote', 'append', ''], | 
 |   4363       '"': ['inDoubleQuote', 'append', ''] | 
 |   4364     }, | 
 |   4365  | 
 |   4366     'afterZero': { | 
 |   4367       'ws': ['afterElement', 'push'], | 
 |   4368       ']': ['inPath', 'push'] | 
 |   4369     }, | 
 |   4370  | 
 |   4371     'inIndex': { | 
 |   4372       '0': ['inIndex', 'append'], | 
 |   4373       'number': ['inIndex', 'append'], | 
 |   4374       'ws': ['afterElement'], | 
 |   4375       ']': ['inPath', 'push'] | 
 |   4376     }, | 
 |   4377  | 
 |   4378     'inSingleQuote': { | 
 |   4379       "'": ['afterElement'], | 
 |   4380       'eof': ['error'], | 
 |   4381       'else': ['inSingleQuote', 'append'] | 
 |   4382     }, | 
 |   4383  | 
 |   4384     'inDoubleQuote': { | 
 |   4385       '"': ['afterElement'], | 
 |   4386       'eof': ['error'], | 
 |   4387       'else': ['inDoubleQuote', 'append'] | 
 |   4388     }, | 
 |   4389  | 
 |   4390     'afterElement': { | 
 |   4391       'ws': ['afterElement'], | 
 |   4392       ']': ['inPath', 'push'] | 
 |   4393     } | 
 |   4394   } | 
 |   4395  | 
 |   4396   function noop() {} | 
 |   4397  | 
 |   4398   function parsePath(path) { | 
 |   4399     var keys = []; | 
 |   4400     var index = -1; | 
 |   4401     var c, newChar, key, type, transition, action, typeMap, mode = 'beforePath'; | 
 |   4402  | 
 |   4403     var actions = { | 
 |   4404       push: function() { | 
 |   4405         if (key === undefined) | 
 |   4406           return; | 
 |   4407  | 
 |   4408         keys.push(key); | 
 |   4409         key = undefined; | 
 |   4410       }, | 
 |   4411  | 
 |   4412       append: function() { | 
 |   4413         if (key === undefined) | 
 |   4414           key = newChar | 
 |   4415         else | 
 |   4416           key += newChar; | 
 |   4417       } | 
 |   4418     }; | 
 |   4419  | 
 |   4420     function maybeUnescapeQuote() { | 
 |   4421       if (index >= path.length) | 
 |   4422         return; | 
 |   4423  | 
 |   4424       var nextChar = path[index + 1]; | 
 |   4425       if ((mode == 'inSingleQuote' && nextChar == "'") || | 
 |   4426           (mode == 'inDoubleQuote' && nextChar == '"')) { | 
 |   4427         index++; | 
 |   4428         newChar = nextChar; | 
 |   4429         actions.append(); | 
 |   4430         return true; | 
 |   4431       } | 
 |   4432     } | 
 |   4433  | 
 |   4434     while (mode) { | 
 |   4435       index++; | 
 |   4436       c = path[index]; | 
 |   4437  | 
 |   4438       if (c == '\\' && maybeUnescapeQuote(mode)) | 
 |   4439         continue; | 
 |   4440  | 
 |   4441       type = getPathCharType(c); | 
 |   4442       typeMap = pathStateMachine[mode]; | 
 |   4443       transition = typeMap[type] || typeMap['else'] || 'error'; | 
 |   4444  | 
 |   4445       if (transition == 'error') | 
 |   4446         return; // parse error; | 
 |   4447  | 
 |   4448       mode = transition[0]; | 
 |   4449       action = actions[transition[1]] || noop; | 
 |   4450       newChar = transition[2] === undefined ? c : transition[2]; | 
 |   4451       action(); | 
 |   4452  | 
 |   4453       if (mode === 'afterPath') { | 
 |   4454         return keys; | 
 |   4455       } | 
 |   4456     } | 
 |   4457  | 
 |   4458     return; // parse error | 
 |   4459   } | 
 |   4460  | 
 |   4461   function isIdent(s) { | 
 |   4462     return identRegExp.test(s); | 
 |   4463   } | 
 |   4464  | 
 |   4465   var constructorIsPrivate = {}; | 
 |   4466  | 
 |   4467   function Path(parts, privateToken) { | 
 |   4468     if (privateToken !== constructorIsPrivate) | 
 |   4469       throw Error('Use Path.get to retrieve path objects'); | 
 |   4470  | 
 |   4471     for (var i = 0; i < parts.length; i++) { | 
 |   4472       this.push(String(parts[i])); | 
 |   4473     } | 
 |   4474  | 
 |   4475     if (hasEval && this.length) { | 
 |   4476       this.getValueFrom = this.compiledGetValueFromFn(); | 
 |   4477     } | 
 |   4478   } | 
 |   4479  | 
 |   4480   // TODO(rafaelw): Make simple LRU cache | 
 |   4481   var pathCache = {}; | 
 |   4482  | 
 |   4483   function getPath(pathString) { | 
 |   4484     if (pathString instanceof Path) | 
 |   4485       return pathString; | 
 |   4486  | 
 |   4487     if (pathString == null || pathString.length == 0) | 
 |   4488       pathString = ''; | 
 |   4489  | 
 |   4490     if (typeof pathString != 'string') { | 
 |   4491       if (isIndex(pathString.length)) { | 
 |   4492         // Constructed with array-like (pre-parsed) keys | 
 |   4493         return new Path(pathString, constructorIsPrivate); | 
 |   4494       } | 
 |   4495  | 
 |   4496       pathString = String(pathString); | 
 |   4497     } | 
 |   4498  | 
 |   4499     var path = pathCache[pathString]; | 
 |   4500     if (path) | 
 |   4501       return path; | 
 |   4502  | 
 |   4503     var parts = parsePath(pathString); | 
 |   4504     if (!parts) | 
 |   4505       return invalidPath; | 
 |   4506  | 
 |   4507     var path = new Path(parts, constructorIsPrivate); | 
 |   4508     pathCache[pathString] = path; | 
 |   4509     return path; | 
 |   4510   } | 
 |   4511  | 
 |   4512   Path.get = getPath; | 
 |   4513  | 
 |   4514   function formatAccessor(key) { | 
 |   4515     if (isIndex(key)) { | 
 |   4516       return '[' + key + ']'; | 
 |   4517     } else { | 
 |   4518       return '["' + key.replace(/"/g, '\\"') + '"]'; | 
 |   4519     } | 
 |   4520   } | 
 |   4521  | 
 |   4522   Path.prototype = createObject({ | 
 |   4523     __proto__: [], | 
 |   4524     valid: true, | 
 |   4525  | 
 |   4526     toString: function() { | 
 |   4527       var pathString = ''; | 
 |   4528       for (var i = 0; i < this.length; i++) { | 
 |   4529         var key = this[i]; | 
 |   4530         if (isIdent(key)) { | 
 |   4531           pathString += i ? '.' + key : key; | 
 |   4532         } else { | 
 |   4533           pathString += formatAccessor(key); | 
 |   4534         } | 
 |   4535       } | 
 |   4536  | 
 |   4537       return pathString; | 
 |   4538     }, | 
 |   4539  | 
 |   4540     getValueFrom: function(obj, directObserver) { | 
 |   4541       for (var i = 0; i < this.length; i++) { | 
 |   4542         if (obj == null) | 
 |   4543           return; | 
 |   4544         obj = obj[this[i]]; | 
 |   4545       } | 
 |   4546       return obj; | 
 |   4547     }, | 
 |   4548  | 
 |   4549     iterateObjects: function(obj, observe) { | 
 |   4550       for (var i = 0; i < this.length; i++) { | 
 |   4551         if (i) | 
 |   4552           obj = obj[this[i - 1]]; | 
 |   4553         if (!isObject(obj)) | 
 |   4554           return; | 
 |   4555         observe(obj, this[i]); | 
 |   4556       } | 
 |   4557     }, | 
 |   4558  | 
 |   4559     compiledGetValueFromFn: function() { | 
 |   4560       var str = ''; | 
 |   4561       var pathString = 'obj'; | 
 |   4562       str += 'if (obj != null'; | 
 |   4563       var i = 0; | 
 |   4564       var key; | 
 |   4565       for (; i < (this.length - 1); i++) { | 
 |   4566         key = this[i]; | 
 |   4567         pathString += isIdent(key) ? '.' + key : formatAccessor(key); | 
 |   4568         str += ' &&\n     ' + pathString + ' != null'; | 
 |   4569       } | 
 |   4570       str += ')\n'; | 
 |   4571  | 
 |   4572       var key = this[i]; | 
 |   4573       pathString += isIdent(key) ? '.' + key : formatAccessor(key); | 
 |   4574  | 
 |   4575       str += '  return ' + pathString + ';\nelse\n  return undefined;'; | 
 |   4576       return new Function('obj', str); | 
 |   4577     }, | 
 |   4578  | 
 |   4579     setValueFrom: function(obj, value) { | 
 |   4580       if (!this.length) | 
 |   4581         return false; | 
 |   4582  | 
 |   4583       for (var i = 0; i < this.length - 1; i++) { | 
 |   4584         if (!isObject(obj)) | 
 |   4585           return false; | 
 |   4586         obj = obj[this[i]]; | 
 |   4587       } | 
 |   4588  | 
 |   4589       if (!isObject(obj)) | 
 |   4590         return false; | 
 |   4591  | 
 |   4592       obj[this[i]] = value; | 
 |   4593       return true; | 
 |   4594     } | 
 |   4595   }); | 
 |   4596  | 
 |   4597   var invalidPath = new Path('', constructorIsPrivate); | 
 |   4598   invalidPath.valid = false; | 
 |   4599   invalidPath.getValueFrom = invalidPath.setValueFrom = function() {}; | 
 |   4600  | 
 |   4601   var MAX_DIRTY_CHECK_CYCLES = 1000; | 
 |   4602  | 
 |   4603   function dirtyCheck(observer) { | 
 |   4604     var cycles = 0; | 
 |   4605     while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check_()) { | 
 |   4606       cycles++; | 
 |   4607     } | 
 |   4608     if (testingExposeCycleCount) | 
 |   4609       global.dirtyCheckCycleCount = cycles; | 
 |   4610  | 
 |   4611     return cycles > 0; | 
 |   4612   } | 
 |   4613  | 
 |   4614   function objectIsEmpty(object) { | 
 |   4615     for (var prop in object) | 
 |   4616       return false; | 
 |   4617     return true; | 
 |   4618   } | 
 |   4619  | 
 |   4620   function diffIsEmpty(diff) { | 
 |   4621     return objectIsEmpty(diff.added) && | 
 |   4622            objectIsEmpty(diff.removed) && | 
 |   4623            objectIsEmpty(diff.changed); | 
 |   4624   } | 
 |   4625  | 
 |   4626   function diffObjectFromOldObject(object, oldObject) { | 
 |   4627     var added = {}; | 
 |   4628     var removed = {}; | 
 |   4629     var changed = {}; | 
 |   4630  | 
 |   4631     for (var prop in oldObject) { | 
 |   4632       var newValue = object[prop]; | 
 |   4633  | 
 |   4634       if (newValue !== undefined && newValue === oldObject[prop]) | 
 |   4635         continue; | 
 |   4636  | 
 |   4637       if (!(prop in object)) { | 
 |   4638         removed[prop] = undefined; | 
 |   4639         continue; | 
 |   4640       } | 
 |   4641  | 
 |   4642       if (newValue !== oldObject[prop]) | 
 |   4643         changed[prop] = newValue; | 
 |   4644     } | 
 |   4645  | 
 |   4646     for (var prop in object) { | 
 |   4647       if (prop in oldObject) | 
 |   4648         continue; | 
 |   4649  | 
 |   4650       added[prop] = object[prop]; | 
 |   4651     } | 
 |   4652  | 
 |   4653     if (Array.isArray(object) && object.length !== oldObject.length) | 
 |   4654       changed.length = object.length; | 
 |   4655  | 
 |   4656     return { | 
 |   4657       added: added, | 
 |   4658       removed: removed, | 
 |   4659       changed: changed | 
 |   4660     }; | 
 |   4661   } | 
 |   4662  | 
 |   4663   var eomTasks = []; | 
 |   4664   function runEOMTasks() { | 
 |   4665     if (!eomTasks.length) | 
 |   4666       return false; | 
 |   4667  | 
 |   4668     for (var i = 0; i < eomTasks.length; i++) { | 
 |   4669       eomTasks[i](); | 
 |   4670     } | 
 |   4671     eomTasks.length = 0; | 
 |   4672     return true; | 
 |   4673   } | 
 |   4674  | 
 |   4675   var runEOM = hasObserve ? (function(){ | 
 |   4676     return function(fn) { | 
 |   4677       return Promise.resolve().then(fn); | 
 |   4678     } | 
 |   4679   })() : | 
 |   4680   (function() { | 
 |   4681     return function(fn) { | 
 |   4682       eomTasks.push(fn); | 
 |   4683     }; | 
 |   4684   })(); | 
 |   4685  | 
 |   4686   var observedObjectCache = []; | 
 |   4687  | 
 |   4688   function newObservedObject() { | 
 |   4689     var observer; | 
 |   4690     var object; | 
 |   4691     var discardRecords = false; | 
 |   4692     var first = true; | 
 |   4693  | 
 |   4694     function callback(records) { | 
 |   4695       if (observer && observer.state_ === OPENED && !discardRecords) | 
 |   4696         observer.check_(records); | 
 |   4697     } | 
 |   4698  | 
 |   4699     return { | 
 |   4700       open: function(obs) { | 
 |   4701         if (observer) | 
 |   4702           throw Error('ObservedObject in use'); | 
 |   4703  | 
 |   4704         if (!first) | 
 |   4705           Object.deliverChangeRecords(callback); | 
 |   4706  | 
 |   4707         observer = obs; | 
 |   4708         first = false; | 
 |   4709       }, | 
 |   4710       observe: function(obj, arrayObserve) { | 
 |   4711         object = obj; | 
 |   4712         if (arrayObserve) | 
 |   4713           Array.observe(object, callback); | 
 |   4714         else | 
 |   4715           Object.observe(object, callback); | 
 |   4716       }, | 
 |   4717       deliver: function(discard) { | 
 |   4718         discardRecords = discard; | 
 |   4719         Object.deliverChangeRecords(callback); | 
 |   4720         discardRecords = false; | 
 |   4721       }, | 
 |   4722       close: function() { | 
 |   4723         observer = undefined; | 
 |   4724         Object.unobserve(object, callback); | 
 |   4725         observedObjectCache.push(this); | 
 |   4726       } | 
 |   4727     }; | 
 |   4728   } | 
 |   4729  | 
 |   4730   /* | 
 |   4731    * The observedSet abstraction is a perf optimization which reduces the total | 
 |   4732    * number of Object.observe observations of a set of objects. The idea is that | 
 |   4733    * groups of Observers will have some object dependencies in common and this | 
 |   4734    * observed set ensures that each object in the transitive closure of | 
 |   4735    * dependencies is only observed once. The observedSet acts as a write barrier | 
 |   4736    * such that whenever any change comes through, all Observers are checked for | 
 |   4737    * changed values. | 
 |   4738    * | 
 |   4739    * Note that this optimization is explicitly moving work from setup-time to | 
 |   4740    * change-time. | 
 |   4741    * | 
 |   4742    * TODO(rafaelw): Implement "garbage collection". In order to move work off | 
 |   4743    * the critical path, when Observers are closed, their observed objects are | 
 |   4744    * not Object.unobserve(d). As a result, it's possible that if the observedSet | 
 |   4745    * is kept open, but some Observers have been closed, it could cause "leaks" | 
 |   4746    * (prevent otherwise collectable objects from being collected). At some | 
 |   4747    * point, we should implement incremental "gc" which keeps a list of | 
 |   4748    * observedSets which may need clean-up and does small amounts of cleanup on a | 
 |   4749    * timeout until all is clean. | 
 |   4750    */ | 
 |   4751  | 
 |   4752   function getObservedObject(observer, object, arrayObserve) { | 
 |   4753     var dir = observedObjectCache.pop() || newObservedObject(); | 
 |   4754     dir.open(observer); | 
 |   4755     dir.observe(object, arrayObserve); | 
 |   4756     return dir; | 
 |   4757   } | 
 |   4758  | 
 |   4759   var observedSetCache = []; | 
 |   4760  | 
 |   4761   function newObservedSet() { | 
 |   4762     var observerCount = 0; | 
 |   4763     var observers = []; | 
 |   4764     var objects = []; | 
 |   4765     var rootObj; | 
 |   4766     var rootObjProps; | 
 |   4767  | 
 |   4768     function observe(obj, prop) { | 
 |   4769       if (!obj) | 
 |   4770         return; | 
 |   4771  | 
 |   4772       if (obj === rootObj) | 
 |   4773         rootObjProps[prop] = true; | 
 |   4774  | 
 |   4775       if (objects.indexOf(obj) < 0) { | 
 |   4776         objects.push(obj); | 
 |   4777         Object.observe(obj, callback); | 
 |   4778       } | 
 |   4779  | 
 |   4780       observe(Object.getPrototypeOf(obj), prop); | 
 |   4781     } | 
 |   4782  | 
 |   4783     function allRootObjNonObservedProps(recs) { | 
 |   4784       for (var i = 0; i < recs.length; i++) { | 
 |   4785         var rec = recs[i]; | 
 |   4786         if (rec.object !== rootObj || | 
 |   4787             rootObjProps[rec.name] || | 
 |   4788             rec.type === 'setPrototype') { | 
 |   4789           return false; | 
 |   4790         } | 
 |   4791       } | 
 |   4792       return true; | 
 |   4793     } | 
 |   4794  | 
 |   4795     function callback(recs) { | 
 |   4796       if (allRootObjNonObservedProps(recs)) | 
 |   4797         return; | 
 |   4798  | 
 |   4799       var observer; | 
 |   4800       for (var i = 0; i < observers.length; i++) { | 
 |   4801         observer = observers[i]; | 
 |   4802         if (observer.state_ == OPENED) { | 
 |   4803           observer.iterateObjects_(observe); | 
 |   4804         } | 
 |   4805       } | 
 |   4806  | 
 |   4807       for (var i = 0; i < observers.length; i++) { | 
 |   4808         observer = observers[i]; | 
 |   4809         if (observer.state_ == OPENED) { | 
 |   4810           observer.check_(); | 
 |   4811         } | 
 |   4812       } | 
 |   4813     } | 
 |   4814  | 
 |   4815     var record = { | 
 |   4816       objects: objects, | 
 |   4817       get rootObject() { return rootObj; }, | 
 |   4818       set rootObject(value) { | 
 |   4819         rootObj = value; | 
 |   4820         rootObjProps = {}; | 
 |   4821       }, | 
 |   4822       open: function(obs, object) { | 
 |   4823         observers.push(obs); | 
 |   4824         observerCount++; | 
 |   4825         obs.iterateObjects_(observe); | 
 |   4826       }, | 
 |   4827       close: function(obs) { | 
 |   4828         observerCount--; | 
 |   4829         if (observerCount > 0) { | 
 |   4830           return; | 
 |   4831         } | 
 |   4832  | 
 |   4833         for (var i = 0; i < objects.length; i++) { | 
 |   4834           Object.unobserve(objects[i], callback); | 
 |   4835           Observer.unobservedCount++; | 
 |   4836         } | 
 |   4837  | 
 |   4838         observers.length = 0; | 
 |   4839         objects.length = 0; | 
 |   4840         rootObj = undefined; | 
 |   4841         rootObjProps = undefined; | 
 |   4842         observedSetCache.push(this); | 
 |   4843         if (lastObservedSet === this) | 
 |   4844           lastObservedSet = null; | 
 |   4845       }, | 
 |   4846     }; | 
 |   4847  | 
 |   4848     return record; | 
 |   4849   } | 
 |   4850  | 
 |   4851   var lastObservedSet; | 
 |   4852  | 
 |   4853   function getObservedSet(observer, obj) { | 
 |   4854     if (!lastObservedSet || lastObservedSet.rootObject !== obj) { | 
 |   4855       lastObservedSet = observedSetCache.pop() || newObservedSet(); | 
 |   4856       lastObservedSet.rootObject = obj; | 
 |   4857     } | 
 |   4858     lastObservedSet.open(observer, obj); | 
 |   4859     return lastObservedSet; | 
 |   4860   } | 
 |   4861  | 
 |   4862   var UNOPENED = 0; | 
 |   4863   var OPENED = 1; | 
 |   4864   var CLOSED = 2; | 
 |   4865   var RESETTING = 3; | 
 |   4866  | 
 |   4867   var nextObserverId = 1; | 
 |   4868  | 
 |   4869   function Observer() { | 
 |   4870     this.state_ = UNOPENED; | 
 |   4871     this.callback_ = undefined; | 
 |   4872     this.target_ = undefined; // TODO(rafaelw): Should be WeakRef | 
 |   4873     this.directObserver_ = undefined; | 
 |   4874     this.value_ = undefined; | 
 |   4875     this.id_ = nextObserverId++; | 
 |   4876   } | 
 |   4877  | 
 |   4878   Observer.prototype = { | 
 |   4879     open: function(callback, target) { | 
 |   4880       if (this.state_ != UNOPENED) | 
 |   4881         throw Error('Observer has already been opened.'); | 
 |   4882  | 
 |   4883       addToAll(this); | 
 |   4884       this.callback_ = callback; | 
 |   4885       this.target_ = target; | 
 |   4886       this.connect_(); | 
 |   4887       this.state_ = OPENED; | 
 |   4888       return this.value_; | 
 |   4889     }, | 
 |   4890  | 
 |   4891     close: function() { | 
 |   4892       if (this.state_ != OPENED) | 
 |   4893         return; | 
 |   4894  | 
 |   4895       removeFromAll(this); | 
 |   4896       this.disconnect_(); | 
 |   4897       this.value_ = undefined; | 
 |   4898       this.callback_ = undefined; | 
 |   4899       this.target_ = undefined; | 
 |   4900       this.state_ = CLOSED; | 
 |   4901     }, | 
 |   4902  | 
 |   4903     deliver: function() { | 
 |   4904       if (this.state_ != OPENED) | 
 |   4905         return; | 
 |   4906  | 
 |   4907       dirtyCheck(this); | 
 |   4908     }, | 
 |   4909  | 
 |   4910     report_: function(changes) { | 
 |   4911       try { | 
 |   4912         this.callback_.apply(this.target_, changes); | 
 |   4913       } catch (ex) { | 
 |   4914         Observer._errorThrownDuringCallback = true; | 
 |   4915         console.error('Exception caught during observer callback: ' + | 
 |   4916                        (ex.stack || ex)); | 
 |   4917       } | 
 |   4918     }, | 
 |   4919  | 
 |   4920     discardChanges: function() { | 
 |   4921       this.check_(undefined, true); | 
 |   4922       return this.value_; | 
 |   4923     } | 
 |   4924   } | 
 |   4925  | 
 |   4926   var collectObservers = !hasObserve; | 
 |   4927   var allObservers; | 
 |   4928   Observer._allObserversCount = 0; | 
 |   4929  | 
 |   4930   if (collectObservers) { | 
 |   4931     allObservers = []; | 
 |   4932   } | 
 |   4933  | 
 |   4934   function addToAll(observer) { | 
 |   4935     Observer._allObserversCount++; | 
 |   4936     if (!collectObservers) | 
 |   4937       return; | 
 |   4938  | 
 |   4939     allObservers.push(observer); | 
 |   4940   } | 
 |   4941  | 
 |   4942   function removeFromAll(observer) { | 
 |   4943     Observer._allObserversCount--; | 
 |   4944   } | 
 |   4945  | 
 |   4946   var runningMicrotaskCheckpoint = false; | 
 |   4947  | 
 |   4948   global.Platform = global.Platform || {}; | 
 |   4949  | 
 |   4950   global.Platform.performMicrotaskCheckpoint = function() { | 
 |   4951     if (runningMicrotaskCheckpoint) | 
 |   4952       return; | 
 |   4953  | 
 |   4954     if (!collectObservers) | 
 |   4955       return; | 
 |   4956  | 
 |   4957     runningMicrotaskCheckpoint = true; | 
 |   4958  | 
 |   4959     var cycles = 0; | 
 |   4960     var anyChanged, toCheck; | 
 |   4961  | 
 |   4962     do { | 
 |   4963       cycles++; | 
 |   4964       toCheck = allObservers; | 
 |   4965       allObservers = []; | 
 |   4966       anyChanged = false; | 
 |   4967  | 
 |   4968       for (var i = 0; i < toCheck.length; i++) { | 
 |   4969         var observer = toCheck[i]; | 
 |   4970         if (observer.state_ != OPENED) | 
 |   4971           continue; | 
 |   4972  | 
 |   4973         if (observer.check_()) | 
 |   4974           anyChanged = true; | 
 |   4975  | 
 |   4976         allObservers.push(observer); | 
 |   4977       } | 
 |   4978       if (runEOMTasks()) | 
 |   4979         anyChanged = true; | 
 |   4980     } while (cycles < MAX_DIRTY_CHECK_CYCLES && anyChanged); | 
 |   4981  | 
 |   4982     if (testingExposeCycleCount) | 
 |   4983       global.dirtyCheckCycleCount = cycles; | 
 |   4984  | 
 |   4985     runningMicrotaskCheckpoint = false; | 
 |   4986   }; | 
 |   4987  | 
 |   4988   if (collectObservers) { | 
 |   4989     global.Platform.clearObservers = function() { | 
 |   4990       allObservers = []; | 
 |   4991     }; | 
 |   4992   } | 
 |   4993  | 
 |   4994   function ObjectObserver(object) { | 
 |   4995     Observer.call(this); | 
 |   4996     this.value_ = object; | 
 |   4997     this.oldObject_ = undefined; | 
 |   4998   } | 
 |   4999  | 
 |   5000   ObjectObserver.prototype = createObject({ | 
 |   5001     __proto__: Observer.prototype, | 
 |   5002  | 
 |   5003     arrayObserve: false, | 
 |   5004  | 
 |   5005     connect_: function(callback, target) { | 
 |   5006       if (hasObserve) { | 
 |   5007         this.directObserver_ = getObservedObject(this, this.value_, | 
 |   5008                                                  this.arrayObserve); | 
 |   5009       } else { | 
 |   5010         this.oldObject_ = this.copyObject(this.value_); | 
 |   5011       } | 
 |   5012  | 
 |   5013     }, | 
 |   5014  | 
 |   5015     copyObject: function(object) { | 
 |   5016       var copy = Array.isArray(object) ? [] : {}; | 
 |   5017       for (var prop in object) { | 
 |   5018         copy[prop] = object[prop]; | 
 |   5019       }; | 
 |   5020       if (Array.isArray(object)) | 
 |   5021         copy.length = object.length; | 
 |   5022       return copy; | 
 |   5023     }, | 
 |   5024  | 
 |   5025     check_: function(changeRecords, skipChanges) { | 
 |   5026       var diff; | 
 |   5027       var oldValues; | 
 |   5028       if (hasObserve) { | 
 |   5029         if (!changeRecords) | 
 |   5030           return false; | 
 |   5031  | 
 |   5032         oldValues = {}; | 
 |   5033         diff = diffObjectFromChangeRecords(this.value_, changeRecords, | 
 |   5034                                            oldValues); | 
 |   5035       } else { | 
 |   5036         oldValues = this.oldObject_; | 
 |   5037         diff = diffObjectFromOldObject(this.value_, this.oldObject_); | 
 |   5038       } | 
 |   5039  | 
 |   5040       if (diffIsEmpty(diff)) | 
 |   5041         return false; | 
 |   5042  | 
 |   5043       if (!hasObserve) | 
 |   5044         this.oldObject_ = this.copyObject(this.value_); | 
 |   5045  | 
 |   5046       this.report_([ | 
 |   5047         diff.added || {}, | 
 |   5048         diff.removed || {}, | 
 |   5049         diff.changed || {}, | 
 |   5050         function(property) { | 
 |   5051           return oldValues[property]; | 
 |   5052         } | 
 |   5053       ]); | 
 |   5054  | 
 |   5055       return true; | 
 |   5056     }, | 
 |   5057  | 
 |   5058     disconnect_: function() { | 
 |   5059       if (hasObserve) { | 
 |   5060         this.directObserver_.close(); | 
 |   5061         this.directObserver_ = undefined; | 
 |   5062       } else { | 
 |   5063         this.oldObject_ = undefined; | 
 |   5064       } | 
 |   5065     }, | 
 |   5066  | 
 |   5067     deliver: function() { | 
 |   5068       if (this.state_ != OPENED) | 
 |   5069         return; | 
 |   5070  | 
 |   5071       if (hasObserve) | 
 |   5072         this.directObserver_.deliver(false); | 
 |   5073       else | 
 |   5074         dirtyCheck(this); | 
 |   5075     }, | 
 |   5076  | 
 |   5077     discardChanges: function() { | 
 |   5078       if (this.directObserver_) | 
 |   5079         this.directObserver_.deliver(true); | 
 |   5080       else | 
 |   5081         this.oldObject_ = this.copyObject(this.value_); | 
 |   5082  | 
 |   5083       return this.value_; | 
 |   5084     } | 
 |   5085   }); | 
 |   5086  | 
 |   5087   function ArrayObserver(array) { | 
 |   5088     if (!Array.isArray(array)) | 
 |   5089       throw Error('Provided object is not an Array'); | 
 |   5090     ObjectObserver.call(this, array); | 
 |   5091   } | 
 |   5092  | 
 |   5093   ArrayObserver.prototype = createObject({ | 
 |   5094  | 
 |   5095     __proto__: ObjectObserver.prototype, | 
 |   5096  | 
 |   5097     arrayObserve: true, | 
 |   5098  | 
 |   5099     copyObject: function(arr) { | 
 |   5100       return arr.slice(); | 
 |   5101     }, | 
 |   5102  | 
 |   5103     check_: function(changeRecords) { | 
 |   5104       var splices; | 
 |   5105       if (hasObserve) { | 
 |   5106         if (!changeRecords) | 
 |   5107           return false; | 
 |   5108         splices = projectArraySplices(this.value_, changeRecords); | 
 |   5109       } else { | 
 |   5110         splices = calcSplices(this.value_, 0, this.value_.length, | 
 |   5111                               this.oldObject_, 0, this.oldObject_.length); | 
 |   5112       } | 
 |   5113  | 
 |   5114       if (!splices || !splices.length) | 
 |   5115         return false; | 
 |   5116  | 
 |   5117       if (!hasObserve) | 
 |   5118         this.oldObject_ = this.copyObject(this.value_); | 
 |   5119  | 
 |   5120       this.report_([splices]); | 
 |   5121       return true; | 
 |   5122     } | 
 |   5123   }); | 
 |   5124  | 
 |   5125   ArrayObserver.applySplices = function(previous, current, splices) { | 
 |   5126     splices.forEach(function(splice) { | 
 |   5127       var spliceArgs = [splice.index, splice.removed.length]; | 
 |   5128       var addIndex = splice.index; | 
 |   5129       while (addIndex < splice.index + splice.addedCount) { | 
 |   5130         spliceArgs.push(current[addIndex]); | 
 |   5131         addIndex++; | 
 |   5132       } | 
 |   5133  | 
 |   5134       Array.prototype.splice.apply(previous, spliceArgs); | 
 |   5135     }); | 
 |   5136   }; | 
 |   5137  | 
 |   5138   function PathObserver(object, path) { | 
 |   5139     Observer.call(this); | 
 |   5140  | 
 |   5141     this.object_ = object; | 
 |   5142     this.path_ = getPath(path); | 
 |   5143     this.directObserver_ = undefined; | 
 |   5144   } | 
 |   5145  | 
 |   5146   PathObserver.prototype = createObject({ | 
 |   5147     __proto__: Observer.prototype, | 
 |   5148  | 
 |   5149     get path() { | 
 |   5150       return this.path_; | 
 |   5151     }, | 
 |   5152  | 
 |   5153     connect_: function() { | 
 |   5154       if (hasObserve) | 
 |   5155         this.directObserver_ = getObservedSet(this, this.object_); | 
 |   5156  | 
 |   5157       this.check_(undefined, true); | 
 |   5158     }, | 
 |   5159  | 
 |   5160     disconnect_: function() { | 
 |   5161       this.value_ = undefined; | 
 |   5162  | 
 |   5163       if (this.directObserver_) { | 
 |   5164         this.directObserver_.close(this); | 
 |   5165         this.directObserver_ = undefined; | 
 |   5166       } | 
 |   5167     }, | 
 |   5168  | 
 |   5169     iterateObjects_: function(observe) { | 
 |   5170       this.path_.iterateObjects(this.object_, observe); | 
 |   5171     }, | 
 |   5172  | 
 |   5173     check_: function(changeRecords, skipChanges) { | 
 |   5174       var oldValue = this.value_; | 
 |   5175       this.value_ = this.path_.getValueFrom(this.object_); | 
 |   5176       if (skipChanges || areSameValue(this.value_, oldValue)) | 
 |   5177         return false; | 
 |   5178  | 
 |   5179       this.report_([this.value_, oldValue, this]); | 
 |   5180       return true; | 
 |   5181     }, | 
 |   5182  | 
 |   5183     setValue: function(newValue) { | 
 |   5184       if (this.path_) | 
 |   5185         this.path_.setValueFrom(this.object_, newValue); | 
 |   5186     } | 
 |   5187   }); | 
 |   5188  | 
 |   5189   function CompoundObserver(reportChangesOnOpen) { | 
 |   5190     Observer.call(this); | 
 |   5191  | 
 |   5192     this.reportChangesOnOpen_ = reportChangesOnOpen; | 
 |   5193     this.value_ = []; | 
 |   5194     this.directObserver_ = undefined; | 
 |   5195     this.observed_ = []; | 
 |   5196   } | 
 |   5197  | 
 |   5198   var observerSentinel = {}; | 
 |   5199  | 
 |   5200   CompoundObserver.prototype = createObject({ | 
 |   5201     __proto__: Observer.prototype, | 
 |   5202  | 
 |   5203     connect_: function() { | 
 |   5204       if (hasObserve) { | 
 |   5205         var object; | 
 |   5206         var needsDirectObserver = false; | 
 |   5207         for (var i = 0; i < this.observed_.length; i += 2) { | 
 |   5208           object = this.observed_[i] | 
 |   5209           if (object !== observerSentinel) { | 
 |   5210             needsDirectObserver = true; | 
 |   5211             break; | 
 |   5212           } | 
 |   5213         } | 
 |   5214  | 
 |   5215         if (needsDirectObserver) | 
 |   5216           this.directObserver_ = getObservedSet(this, object); | 
 |   5217       } | 
 |   5218  | 
 |   5219       this.check_(undefined, !this.reportChangesOnOpen_); | 
 |   5220     }, | 
 |   5221  | 
 |   5222     disconnect_: function() { | 
 |   5223       for (var i = 0; i < this.observed_.length; i += 2) { | 
 |   5224         if (this.observed_[i] === observerSentinel) | 
 |   5225           this.observed_[i + 1].close(); | 
 |   5226       } | 
 |   5227       this.observed_.length = 0; | 
 |   5228       this.value_.length = 0; | 
 |   5229  | 
 |   5230       if (this.directObserver_) { | 
 |   5231         this.directObserver_.close(this); | 
 |   5232         this.directObserver_ = undefined; | 
 |   5233       } | 
 |   5234     }, | 
 |   5235  | 
 |   5236     addPath: function(object, path) { | 
 |   5237       if (this.state_ != UNOPENED && this.state_ != RESETTING) | 
 |   5238         throw Error('Cannot add paths once started.'); | 
 |   5239  | 
 |   5240       var path = getPath(path); | 
 |   5241       this.observed_.push(object, path); | 
 |   5242       if (!this.reportChangesOnOpen_) | 
 |   5243         return; | 
 |   5244       var index = this.observed_.length / 2 - 1; | 
 |   5245       this.value_[index] = path.getValueFrom(object); | 
 |   5246     }, | 
 |   5247  | 
 |   5248     addObserver: function(observer) { | 
 |   5249       if (this.state_ != UNOPENED && this.state_ != RESETTING) | 
 |   5250         throw Error('Cannot add observers once started.'); | 
 |   5251  | 
 |   5252       this.observed_.push(observerSentinel, observer); | 
 |   5253       if (!this.reportChangesOnOpen_) | 
 |   5254         return; | 
 |   5255       var index = this.observed_.length / 2 - 1; | 
 |   5256       this.value_[index] = observer.open(this.deliver, this); | 
 |   5257     }, | 
 |   5258  | 
 |   5259     startReset: function() { | 
 |   5260       if (this.state_ != OPENED) | 
 |   5261         throw Error('Can only reset while open'); | 
 |   5262  | 
 |   5263       this.state_ = RESETTING; | 
 |   5264       this.disconnect_(); | 
 |   5265     }, | 
 |   5266  | 
 |   5267     finishReset: function() { | 
 |   5268       if (this.state_ != RESETTING) | 
 |   5269         throw Error('Can only finishReset after startReset'); | 
 |   5270       this.state_ = OPENED; | 
 |   5271       this.connect_(); | 
 |   5272  | 
 |   5273       return this.value_; | 
 |   5274     }, | 
 |   5275  | 
 |   5276     iterateObjects_: function(observe) { | 
 |   5277       var object; | 
 |   5278       for (var i = 0; i < this.observed_.length; i += 2) { | 
 |   5279         object = this.observed_[i] | 
 |   5280         if (object !== observerSentinel) | 
 |   5281           this.observed_[i + 1].iterateObjects(object, observe) | 
 |   5282       } | 
 |   5283     }, | 
 |   5284  | 
 |   5285     check_: function(changeRecords, skipChanges) { | 
 |   5286       var oldValues; | 
 |   5287       for (var i = 0; i < this.observed_.length; i += 2) { | 
 |   5288         var object = this.observed_[i]; | 
 |   5289         var path = this.observed_[i+1]; | 
 |   5290         var value; | 
 |   5291         if (object === observerSentinel) { | 
 |   5292           var observable = path; | 
 |   5293           value = this.state_ === UNOPENED ? | 
 |   5294               observable.open(this.deliver, this) : | 
 |   5295               observable.discardChanges(); | 
 |   5296         } else { | 
 |   5297           value = path.getValueFrom(object); | 
 |   5298         } | 
 |   5299  | 
 |   5300         if (skipChanges) { | 
 |   5301           this.value_[i / 2] = value; | 
 |   5302           continue; | 
 |   5303         } | 
 |   5304  | 
 |   5305         if (areSameValue(value, this.value_[i / 2])) | 
 |   5306           continue; | 
 |   5307  | 
 |   5308         oldValues = oldValues || []; | 
 |   5309         oldValues[i / 2] = this.value_[i / 2]; | 
 |   5310         this.value_[i / 2] = value; | 
 |   5311       } | 
 |   5312  | 
 |   5313       if (!oldValues) | 
 |   5314         return false; | 
 |   5315  | 
 |   5316       // TODO(rafaelw): Having observed_ as the third callback arg here is | 
 |   5317       // pretty lame API. Fix. | 
 |   5318       this.report_([this.value_, oldValues, this.observed_]); | 
 |   5319       return true; | 
 |   5320     } | 
 |   5321   }); | 
 |   5322  | 
 |   5323   function identFn(value) { return value; } | 
 |   5324  | 
 |   5325   function ObserverTransform(observable, getValueFn, setValueFn, | 
 |   5326                              dontPassThroughSet) { | 
 |   5327     this.callback_ = undefined; | 
 |   5328     this.target_ = undefined; | 
 |   5329     this.value_ = undefined; | 
 |   5330     this.observable_ = observable; | 
 |   5331     this.getValueFn_ = getValueFn || identFn; | 
 |   5332     this.setValueFn_ = setValueFn || identFn; | 
 |   5333     // TODO(rafaelw): This is a temporary hack. PolymerExpressions needs this | 
 |   5334     // at the moment because of a bug in it's dependency tracking. | 
 |   5335     this.dontPassThroughSet_ = dontPassThroughSet; | 
 |   5336   } | 
 |   5337  | 
 |   5338   ObserverTransform.prototype = { | 
 |   5339     open: function(callback, target) { | 
 |   5340       this.callback_ = callback; | 
 |   5341       this.target_ = target; | 
 |   5342       this.value_ = | 
 |   5343           this.getValueFn_(this.observable_.open(this.observedCallback_, this)); | 
 |   5344       return this.value_; | 
 |   5345     }, | 
 |   5346  | 
 |   5347     observedCallback_: function(value) { | 
 |   5348       value = this.getValueFn_(value); | 
 |   5349       if (areSameValue(value, this.value_)) | 
 |   5350         return; | 
 |   5351       var oldValue = this.value_; | 
 |   5352       this.value_ = value; | 
 |   5353       this.callback_.call(this.target_, this.value_, oldValue); | 
 |   5354     }, | 
 |   5355  | 
 |   5356     discardChanges: function() { | 
 |   5357       this.value_ = this.getValueFn_(this.observable_.discardChanges()); | 
 |   5358       return this.value_; | 
 |   5359     }, | 
 |   5360  | 
 |   5361     deliver: function() { | 
 |   5362       return this.observable_.deliver(); | 
 |   5363     }, | 
 |   5364  | 
 |   5365     setValue: function(value) { | 
 |   5366       value = this.setValueFn_(value); | 
 |   5367       if (!this.dontPassThroughSet_ && this.observable_.setValue) | 
 |   5368         return this.observable_.setValue(value); | 
 |   5369     }, | 
 |   5370  | 
 |   5371     close: function() { | 
 |   5372       if (this.observable_) | 
 |   5373         this.observable_.close(); | 
 |   5374       this.callback_ = undefined; | 
 |   5375       this.target_ = undefined; | 
 |   5376       this.observable_ = undefined; | 
 |   5377       this.value_ = undefined; | 
 |   5378       this.getValueFn_ = undefined; | 
 |   5379       this.setValueFn_ = undefined; | 
 |   5380     } | 
 |   5381   } | 
 |   5382  | 
 |   5383   var expectedRecordTypes = { | 
 |   5384     add: true, | 
 |   5385     update: true, | 
 |   5386     delete: true | 
 |   5387   }; | 
 |   5388  | 
 |   5389   function diffObjectFromChangeRecords(object, changeRecords, oldValues) { | 
 |   5390     var added = {}; | 
 |   5391     var removed = {}; | 
 |   5392  | 
 |   5393     for (var i = 0; i < changeRecords.length; i++) { | 
 |   5394       var record = changeRecords[i]; | 
 |   5395       if (!expectedRecordTypes[record.type]) { | 
 |   5396         console.error('Unknown changeRecord type: ' + record.type); | 
 |   5397         console.error(record); | 
 |   5398         continue; | 
 |   5399       } | 
 |   5400  | 
 |   5401       if (!(record.name in oldValues)) | 
 |   5402         oldValues[record.name] = record.oldValue; | 
 |   5403  | 
 |   5404       if (record.type == 'update') | 
 |   5405         continue; | 
 |   5406  | 
 |   5407       if (record.type == 'add') { | 
 |   5408         if (record.name in removed) | 
 |   5409           delete removed[record.name]; | 
 |   5410         else | 
 |   5411           added[record.name] = true; | 
 |   5412  | 
 |   5413         continue; | 
 |   5414       } | 
 |   5415  | 
 |   5416       // type = 'delete' | 
 |   5417       if (record.name in added) { | 
 |   5418         delete added[record.name]; | 
 |   5419         delete oldValues[record.name]; | 
 |   5420       } else { | 
 |   5421         removed[record.name] = true; | 
 |   5422       } | 
 |   5423     } | 
 |   5424  | 
 |   5425     for (var prop in added) | 
 |   5426       added[prop] = object[prop]; | 
 |   5427  | 
 |   5428     for (var prop in removed) | 
 |   5429       removed[prop] = undefined; | 
 |   5430  | 
 |   5431     var changed = {}; | 
 |   5432     for (var prop in oldValues) { | 
 |   5433       if (prop in added || prop in removed) | 
 |   5434         continue; | 
 |   5435  | 
 |   5436       var newValue = object[prop]; | 
 |   5437       if (oldValues[prop] !== newValue) | 
 |   5438         changed[prop] = newValue; | 
 |   5439     } | 
 |   5440  | 
 |   5441     return { | 
 |   5442       added: added, | 
 |   5443       removed: removed, | 
 |   5444       changed: changed | 
 |   5445     }; | 
 |   5446   } | 
 |   5447  | 
 |   5448   function newSplice(index, removed, addedCount) { | 
 |   5449     return { | 
 |   5450       index: index, | 
 |   5451       removed: removed, | 
 |   5452       addedCount: addedCount | 
 |   5453     }; | 
 |   5454   } | 
 |   5455  | 
 |   5456   var EDIT_LEAVE = 0; | 
 |   5457   var EDIT_UPDATE = 1; | 
 |   5458   var EDIT_ADD = 2; | 
 |   5459   var EDIT_DELETE = 3; | 
 |   5460  | 
 |   5461   function ArraySplice() {} | 
 |   5462  | 
 |   5463   ArraySplice.prototype = { | 
 |   5464  | 
 |   5465     // Note: This function is *based* on the computation of the Levenshtein | 
 |   5466     // "edit" distance. The one change is that "updates" are treated as two | 
 |   5467     // edits - not one. With Array splices, an update is really a delete | 
 |   5468     // followed by an add. By retaining this, we optimize for "keeping" the | 
 |   5469     // maximum array items in the original array. For example: | 
 |   5470     // | 
 |   5471     //   'xxxx123' -> '123yyyy' | 
 |   5472     // | 
 |   5473     // With 1-edit updates, the shortest path would be just to update all seven | 
 |   5474     // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This | 
 |   5475     // leaves the substring '123' intact. | 
 |   5476     calcEditDistances: function(current, currentStart, currentEnd, | 
 |   5477                                 old, oldStart, oldEnd) { | 
 |   5478       // "Deletion" columns | 
 |   5479       var rowCount = oldEnd - oldStart + 1; | 
 |   5480       var columnCount = currentEnd - currentStart + 1; | 
 |   5481       var distances = new Array(rowCount); | 
 |   5482  | 
 |   5483       // "Addition" rows. Initialize null column. | 
 |   5484       for (var i = 0; i < rowCount; i++) { | 
 |   5485         distances[i] = new Array(columnCount); | 
 |   5486         distances[i][0] = i; | 
 |   5487       } | 
 |   5488  | 
 |   5489       // Initialize null row | 
 |   5490       for (var j = 0; j < columnCount; j++) | 
 |   5491         distances[0][j] = j; | 
 |   5492  | 
 |   5493       for (var i = 1; i < rowCount; i++) { | 
 |   5494         for (var j = 1; j < columnCount; j++) { | 
 |   5495           if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1])) | 
 |   5496             distances[i][j] = distances[i - 1][j - 1]; | 
 |   5497           else { | 
 |   5498             var north = distances[i - 1][j] + 1; | 
 |   5499             var west = distances[i][j - 1] + 1; | 
 |   5500             distances[i][j] = north < west ? north : west; | 
 |   5501           } | 
 |   5502         } | 
 |   5503       } | 
 |   5504  | 
 |   5505       return distances; | 
 |   5506     }, | 
 |   5507  | 
 |   5508     // This starts at the final weight, and walks "backward" by finding | 
 |   5509     // the minimum previous weight recursively until the origin of the weight | 
 |   5510     // matrix. | 
 |   5511     spliceOperationsFromEditDistances: function(distances) { | 
 |   5512       var i = distances.length - 1; | 
 |   5513       var j = distances[0].length - 1; | 
 |   5514       var current = distances[i][j]; | 
 |   5515       var edits = []; | 
 |   5516       while (i > 0 || j > 0) { | 
 |   5517         if (i == 0) { | 
 |   5518           edits.push(EDIT_ADD); | 
 |   5519           j--; | 
 |   5520           continue; | 
 |   5521         } | 
 |   5522         if (j == 0) { | 
 |   5523           edits.push(EDIT_DELETE); | 
 |   5524           i--; | 
 |   5525           continue; | 
 |   5526         } | 
 |   5527         var northWest = distances[i - 1][j - 1]; | 
 |   5528         var west = distances[i - 1][j]; | 
 |   5529         var north = distances[i][j - 1]; | 
 |   5530  | 
 |   5531         var min; | 
 |   5532         if (west < north) | 
 |   5533           min = west < northWest ? west : northWest; | 
 |   5534         else | 
 |   5535           min = north < northWest ? north : northWest; | 
 |   5536  | 
 |   5537         if (min == northWest) { | 
 |   5538           if (northWest == current) { | 
 |   5539             edits.push(EDIT_LEAVE); | 
 |   5540           } else { | 
 |   5541             edits.push(EDIT_UPDATE); | 
 |   5542             current = northWest; | 
 |   5543           } | 
 |   5544           i--; | 
 |   5545           j--; | 
 |   5546         } else if (min == west) { | 
 |   5547           edits.push(EDIT_DELETE); | 
 |   5548           i--; | 
 |   5549           current = west; | 
 |   5550         } else { | 
 |   5551           edits.push(EDIT_ADD); | 
 |   5552           j--; | 
 |   5553           current = north; | 
 |   5554         } | 
 |   5555       } | 
 |   5556  | 
 |   5557       edits.reverse(); | 
 |   5558       return edits; | 
 |   5559     }, | 
 |   5560  | 
 |   5561     /** | 
 |   5562      * Splice Projection functions: | 
 |   5563      * | 
 |   5564      * A splice map is a representation of how a previous array of items | 
 |   5565      * was transformed into a new array of items. Conceptually it is a list of | 
 |   5566      * tuples of | 
 |   5567      * | 
 |   5568      *   <index, removed, addedCount> | 
 |   5569      * | 
 |   5570      * which are kept in ascending index order of. The tuple represents that at | 
 |   5571      * the |index|, |removed| sequence of items were removed, and counting forwa
       rd | 
 |   5572      * from |index|, |addedCount| items were added. | 
 |   5573      */ | 
 |   5574  | 
 |   5575     /** | 
 |   5576      * Lacking individual splice mutation information, the minimal set of | 
 |   5577      * splices can be synthesized given the previous state and final state of an | 
 |   5578      * array. The basic approach is to calculate the edit distance matrix and | 
 |   5579      * choose the shortest path through it. | 
 |   5580      * | 
 |   5581      * Complexity: O(l * p) | 
 |   5582      *   l: The length of the current array | 
 |   5583      *   p: The length of the old array | 
 |   5584      */ | 
 |   5585     calcSplices: function(current, currentStart, currentEnd, | 
 |   5586                           old, oldStart, oldEnd) { | 
 |   5587       var prefixCount = 0; | 
 |   5588       var suffixCount = 0; | 
 |   5589  | 
 |   5590       var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart); | 
 |   5591       if (currentStart == 0 && oldStart == 0) | 
 |   5592         prefixCount = this.sharedPrefix(current, old, minLength); | 
 |   5593  | 
 |   5594       if (currentEnd == current.length && oldEnd == old.length) | 
 |   5595         suffixCount = this.sharedSuffix(current, old, minLength - prefixCount); | 
 |   5596  | 
 |   5597       currentStart += prefixCount; | 
 |   5598       oldStart += prefixCount; | 
 |   5599       currentEnd -= suffixCount; | 
 |   5600       oldEnd -= suffixCount; | 
 |   5601  | 
 |   5602       if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0) | 
 |   5603         return []; | 
 |   5604  | 
 |   5605       if (currentStart == currentEnd) { | 
 |   5606         var splice = newSplice(currentStart, [], 0); | 
 |   5607         while (oldStart < oldEnd) | 
 |   5608           splice.removed.push(old[oldStart++]); | 
 |   5609  | 
 |   5610         return [ splice ]; | 
 |   5611       } else if (oldStart == oldEnd) | 
 |   5612         return [ newSplice(currentStart, [], currentEnd - currentStart) ]; | 
 |   5613  | 
 |   5614       var ops = this.spliceOperationsFromEditDistances( | 
 |   5615           this.calcEditDistances(current, currentStart, currentEnd, | 
 |   5616                                  old, oldStart, oldEnd)); | 
 |   5617  | 
 |   5618       var splice = undefined; | 
 |   5619       var splices = []; | 
 |   5620       var index = currentStart; | 
 |   5621       var oldIndex = oldStart; | 
 |   5622       for (var i = 0; i < ops.length; i++) { | 
 |   5623         switch(ops[i]) { | 
 |   5624           case EDIT_LEAVE: | 
 |   5625             if (splice) { | 
 |   5626               splices.push(splice); | 
 |   5627               splice = undefined; | 
 |   5628             } | 
 |   5629  | 
 |   5630             index++; | 
 |   5631             oldIndex++; | 
 |   5632             break; | 
 |   5633           case EDIT_UPDATE: | 
 |   5634             if (!splice) | 
 |   5635               splice = newSplice(index, [], 0); | 
 |   5636  | 
 |   5637             splice.addedCount++; | 
 |   5638             index++; | 
 |   5639  | 
 |   5640             splice.removed.push(old[oldIndex]); | 
 |   5641             oldIndex++; | 
 |   5642             break; | 
 |   5643           case EDIT_ADD: | 
 |   5644             if (!splice) | 
 |   5645               splice = newSplice(index, [], 0); | 
 |   5646  | 
 |   5647             splice.addedCount++; | 
 |   5648             index++; | 
 |   5649             break; | 
 |   5650           case EDIT_DELETE: | 
 |   5651             if (!splice) | 
 |   5652               splice = newSplice(index, [], 0); | 
 |   5653  | 
 |   5654             splice.removed.push(old[oldIndex]); | 
 |   5655             oldIndex++; | 
 |   5656             break; | 
 |   5657         } | 
 |   5658       } | 
 |   5659  | 
 |   5660       if (splice) { | 
 |   5661         splices.push(splice); | 
 |   5662       } | 
 |   5663       return splices; | 
 |   5664     }, | 
 |   5665  | 
 |   5666     sharedPrefix: function(current, old, searchLength) { | 
 |   5667       for (var i = 0; i < searchLength; i++) | 
 |   5668         if (!this.equals(current[i], old[i])) | 
 |   5669           return i; | 
 |   5670       return searchLength; | 
 |   5671     }, | 
 |   5672  | 
 |   5673     sharedSuffix: function(current, old, searchLength) { | 
 |   5674       var index1 = current.length; | 
 |   5675       var index2 = old.length; | 
 |   5676       var count = 0; | 
 |   5677       while (count < searchLength && this.equals(current[--index1], old[--index2
       ])) | 
 |   5678         count++; | 
 |   5679  | 
 |   5680       return count; | 
 |   5681     }, | 
 |   5682  | 
 |   5683     calculateSplices: function(current, previous) { | 
 |   5684       return this.calcSplices(current, 0, current.length, previous, 0, | 
 |   5685                               previous.length); | 
 |   5686     }, | 
 |   5687  | 
 |   5688     equals: function(currentValue, previousValue) { | 
 |   5689       return currentValue === previousValue; | 
 |   5690     } | 
 |   5691   }; | 
 |   5692  | 
 |   5693   var arraySplice = new ArraySplice(); | 
 |   5694  | 
 |   5695   function calcSplices(current, currentStart, currentEnd, | 
 |   5696                        old, oldStart, oldEnd) { | 
 |   5697     return arraySplice.calcSplices(current, currentStart, currentEnd, | 
 |   5698                                    old, oldStart, oldEnd); | 
 |   5699   } | 
 |   5700  | 
 |   5701   function intersect(start1, end1, start2, end2) { | 
 |   5702     // Disjoint | 
 |   5703     if (end1 < start2 || end2 < start1) | 
 |   5704       return -1; | 
 |   5705  | 
 |   5706     // Adjacent | 
 |   5707     if (end1 == start2 || end2 == start1) | 
 |   5708       return 0; | 
 |   5709  | 
 |   5710     // Non-zero intersect, span1 first | 
 |   5711     if (start1 < start2) { | 
 |   5712       if (end1 < end2) | 
 |   5713         return end1 - start2; // Overlap | 
 |   5714       else | 
 |   5715         return end2 - start2; // Contained | 
 |   5716     } else { | 
 |   5717       // Non-zero intersect, span2 first | 
 |   5718       if (end2 < end1) | 
 |   5719         return end2 - start1; // Overlap | 
 |   5720       else | 
 |   5721         return end1 - start1; // Contained | 
 |   5722     } | 
 |   5723   } | 
 |   5724  | 
 |   5725   function mergeSplice(splices, index, removed, addedCount) { | 
 |   5726  | 
 |   5727     var splice = newSplice(index, removed, addedCount); | 
 |   5728  | 
 |   5729     var inserted = false; | 
 |   5730     var insertionOffset = 0; | 
 |   5731  | 
 |   5732     for (var i = 0; i < splices.length; i++) { | 
 |   5733       var current = splices[i]; | 
 |   5734       current.index += insertionOffset; | 
 |   5735  | 
 |   5736       if (inserted) | 
 |   5737         continue; | 
 |   5738  | 
 |   5739       var intersectCount = intersect(splice.index, | 
 |   5740                                      splice.index + splice.removed.length, | 
 |   5741                                      current.index, | 
 |   5742                                      current.index + current.addedCount); | 
 |   5743  | 
 |   5744       if (intersectCount >= 0) { | 
 |   5745         // Merge the two splices | 
 |   5746  | 
 |   5747         splices.splice(i, 1); | 
 |   5748         i--; | 
 |   5749  | 
 |   5750         insertionOffset -= current.addedCount - current.removed.length; | 
 |   5751  | 
 |   5752         splice.addedCount += current.addedCount - intersectCount; | 
 |   5753         var deleteCount = splice.removed.length + | 
 |   5754                           current.removed.length - intersectCount; | 
 |   5755  | 
 |   5756         if (!splice.addedCount && !deleteCount) { | 
 |   5757           // merged splice is a noop. discard. | 
 |   5758           inserted = true; | 
 |   5759         } else { | 
 |   5760           var removed = current.removed; | 
 |   5761  | 
 |   5762           if (splice.index < current.index) { | 
 |   5763             // some prefix of splice.removed is prepended to current.removed. | 
 |   5764             var prepend = splice.removed.slice(0, current.index - splice.index); | 
 |   5765             Array.prototype.push.apply(prepend, removed); | 
 |   5766             removed = prepend; | 
 |   5767           } | 
 |   5768  | 
 |   5769           if (splice.index + splice.removed.length > current.index + current.add
       edCount) { | 
 |   5770             // some suffix of splice.removed is appended to current.removed. | 
 |   5771             var append = splice.removed.slice(current.index + current.addedCount
        - splice.index); | 
 |   5772             Array.prototype.push.apply(removed, append); | 
 |   5773           } | 
 |   5774  | 
 |   5775           splice.removed = removed; | 
 |   5776           if (current.index < splice.index) { | 
 |   5777             splice.index = current.index; | 
 |   5778           } | 
 |   5779         } | 
 |   5780       } else if (splice.index < current.index) { | 
 |   5781         // Insert splice here. | 
 |   5782  | 
 |   5783         inserted = true; | 
 |   5784  | 
 |   5785         splices.splice(i, 0, splice); | 
 |   5786         i++; | 
 |   5787  | 
 |   5788         var offset = splice.addedCount - splice.removed.length | 
 |   5789         current.index += offset; | 
 |   5790         insertionOffset += offset; | 
 |   5791       } | 
 |   5792     } | 
 |   5793  | 
 |   5794     if (!inserted) | 
 |   5795       splices.push(splice); | 
 |   5796   } | 
 |   5797  | 
 |   5798   function createInitialSplices(array, changeRecords) { | 
 |   5799     var splices = []; | 
 |   5800  | 
 |   5801     for (var i = 0; i < changeRecords.length; i++) { | 
 |   5802       var record = changeRecords[i]; | 
 |   5803       switch(record.type) { | 
 |   5804         case 'splice': | 
 |   5805           mergeSplice(splices, record.index, record.removed.slice(), record.adde
       dCount); | 
 |   5806           break; | 
 |   5807         case 'add': | 
 |   5808         case 'update': | 
 |   5809         case 'delete': | 
 |   5810           if (!isIndex(record.name)) | 
 |   5811             continue; | 
 |   5812           var index = toNumber(record.name); | 
 |   5813           if (index < 0) | 
 |   5814             continue; | 
 |   5815           mergeSplice(splices, index, [record.oldValue], 1); | 
 |   5816           break; | 
 |   5817         default: | 
 |   5818           console.error('Unexpected record type: ' + JSON.stringify(record)); | 
 |   5819           break; | 
 |   5820       } | 
 |   5821     } | 
 |   5822  | 
 |   5823     return splices; | 
 |   5824   } | 
 |   5825  | 
 |   5826   function projectArraySplices(array, changeRecords) { | 
 |   5827     var splices = []; | 
 |   5828  | 
 |   5829     createInitialSplices(array, changeRecords).forEach(function(splice) { | 
 |   5830       if (splice.addedCount == 1 && splice.removed.length == 1) { | 
 |   5831         if (splice.removed[0] !== array[splice.index]) | 
 |   5832           splices.push(splice); | 
 |   5833  | 
 |   5834         return | 
 |   5835       }; | 
 |   5836  | 
 |   5837       splices = splices.concat(calcSplices(array, splice.index, splice.index + s
       plice.addedCount, | 
 |   5838                                            splice.removed, 0, splice.removed.len
       gth)); | 
 |   5839     }); | 
 |   5840  | 
 |   5841     return splices; | 
 |   5842   } | 
 |   5843  | 
 |   5844   // Export the observe-js object for **Node.js**, with | 
 |   5845   // backwards-compatibility for the old `require()` API. If we're in | 
 |   5846   // the browser, export as a global object. | 
 |   5847  | 
 |   5848   var expose = global; | 
 |   5849  | 
 |   5850   if (typeof exports !== 'undefined') { | 
 |   5851     if (typeof module !== 'undefined' && module.exports) { | 
 |   5852       expose = exports = module.exports; | 
 |   5853     } | 
 |   5854     expose = exports; | 
 |   5855   }  | 
 |   5856  | 
 |   5857   expose.Observer = Observer; | 
 |   5858   expose.Observer.runEOM_ = runEOM; | 
 |   5859   expose.Observer.observerSentinel_ = observerSentinel; // for testing. | 
 |   5860   expose.Observer.hasObjectObserve = hasObserve; | 
 |   5861   expose.ArrayObserver = ArrayObserver; | 
 |   5862   expose.ArrayObserver.calculateSplices = function(current, previous) { | 
 |   5863     return arraySplice.calculateSplices(current, previous); | 
 |   5864   }; | 
 |   5865  | 
 |   5866   expose.ArraySplice = ArraySplice; | 
 |   5867   expose.ObjectObserver = ObjectObserver; | 
 |   5868   expose.PathObserver = PathObserver; | 
 |   5869   expose.CompoundObserver = CompoundObserver; | 
 |   5870   expose.Path = Path; | 
 |   5871   expose.ObserverTransform = ObserverTransform; | 
 |   5872    | 
 |   5873 })(typeof global !== 'undefined' && global && typeof module !== 'undefined' && m
       odule ? global : this || window); | 
 |   5874  | 
 |   5875 // Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | 
 |   5876 // This code may only be used under the BSD style license found at http://polyme
       r.github.io/LICENSE.txt | 
 |   5877 // The complete set of authors may be found at http://polymer.github.io/AUTHORS.
       txt | 
 |   5878 // The complete set of contributors may be found at http://polymer.github.io/CON
       TRIBUTORS.txt | 
 |   5879 // Code distributed by Google as part of the polymer project is also | 
 |   5880 // subject to an additional IP rights grant found at http://polymer.github.io/PA
       TENTS.txt | 
 |   5881  | 
 |   5882 (function(global) { | 
 |   5883   'use strict'; | 
 |   5884  | 
 |   5885   var filter = Array.prototype.filter.call.bind(Array.prototype.filter); | 
 |   5886  | 
 |   5887   function getTreeScope(node) { | 
 |   5888     while (node.parentNode) { | 
 |   5889       node = node.parentNode; | 
 |   5890     } | 
 |   5891  | 
 |   5892     return typeof node.getElementById === 'function' ? node : null; | 
 |   5893   } | 
 |   5894  | 
 |   5895   Node.prototype.bind = function(name, observable) { | 
 |   5896     console.error('Unhandled binding to Node: ', this, name, observable); | 
 |   5897   }; | 
 |   5898  | 
 |   5899   Node.prototype.bindFinished = function() {}; | 
 |   5900  | 
 |   5901   function updateBindings(node, name, binding) { | 
 |   5902     var bindings = node.bindings_; | 
 |   5903     if (!bindings) | 
 |   5904       bindings = node.bindings_ = {}; | 
 |   5905  | 
 |   5906     if (bindings[name]) | 
 |   5907       binding[name].close(); | 
 |   5908  | 
 |   5909     return bindings[name] = binding; | 
 |   5910   } | 
 |   5911  | 
 |   5912   function returnBinding(node, name, binding) { | 
 |   5913     return binding; | 
 |   5914   } | 
 |   5915  | 
 |   5916   function sanitizeValue(value) { | 
 |   5917     return value == null ? '' : value; | 
 |   5918   } | 
 |   5919  | 
 |   5920   function updateText(node, value) { | 
 |   5921     node.data = sanitizeValue(value); | 
 |   5922   } | 
 |   5923  | 
 |   5924   function textBinding(node) { | 
 |   5925     return function(value) { | 
 |   5926       return updateText(node, value); | 
 |   5927     }; | 
 |   5928   } | 
 |   5929  | 
 |   5930   var maybeUpdateBindings = returnBinding; | 
 |   5931  | 
 |   5932   Object.defineProperty(Platform, 'enableBindingsReflection', { | 
 |   5933     get: function() { | 
 |   5934       return maybeUpdateBindings === updateBindings; | 
 |   5935     }, | 
 |   5936     set: function(enable) { | 
 |   5937       maybeUpdateBindings = enable ? updateBindings : returnBinding; | 
 |   5938       return enable; | 
 |   5939     }, | 
 |   5940     configurable: true | 
 |   5941   }); | 
 |   5942  | 
 |   5943   Text.prototype.bind = function(name, value, oneTime) { | 
 |   5944     if (name !== 'textContent') | 
 |   5945       return Node.prototype.bind.call(this, name, value, oneTime); | 
 |   5946  | 
 |   5947     if (oneTime) | 
 |   5948       return updateText(this, value); | 
 |   5949  | 
 |   5950     var observable = value; | 
 |   5951     updateText(this, observable.open(textBinding(this))); | 
 |   5952     return maybeUpdateBindings(this, name, observable); | 
 |   5953   } | 
 |   5954  | 
 |   5955   function updateAttribute(el, name, conditional, value) { | 
 |   5956     if (conditional) { | 
 |   5957       if (value) | 
 |   5958         el.setAttribute(name, ''); | 
 |   5959       else | 
 |   5960         el.removeAttribute(name); | 
 |   5961       return; | 
 |   5962     } | 
 |   5963  | 
 |   5964     el.setAttribute(name, sanitizeValue(value)); | 
 |   5965   } | 
 |   5966  | 
 |   5967   function attributeBinding(el, name, conditional) { | 
 |   5968     return function(value) { | 
 |   5969       updateAttribute(el, name, conditional, value); | 
 |   5970     }; | 
 |   5971   } | 
 |   5972  | 
 |   5973   Element.prototype.bind = function(name, value, oneTime) { | 
 |   5974     var conditional = name[name.length - 1] == '?'; | 
 |   5975     if (conditional) { | 
 |   5976       this.removeAttribute(name); | 
 |   5977       name = name.slice(0, -1); | 
 |   5978     } | 
 |   5979  | 
 |   5980     if (oneTime) | 
 |   5981       return updateAttribute(this, name, conditional, value); | 
 |   5982  | 
 |   5983  | 
 |   5984     var observable = value; | 
 |   5985     updateAttribute(this, name, conditional, | 
 |   5986         observable.open(attributeBinding(this, name, conditional))); | 
 |   5987  | 
 |   5988     return maybeUpdateBindings(this, name, observable); | 
 |   5989   }; | 
 |   5990  | 
 |   5991   var checkboxEventType; | 
 |   5992   (function() { | 
 |   5993     // Attempt to feature-detect which event (change or click) is fired first | 
 |   5994     // for checkboxes. | 
 |   5995     var div = document.createElement('div'); | 
 |   5996     var checkbox = div.appendChild(document.createElement('input')); | 
 |   5997     checkbox.setAttribute('type', 'checkbox'); | 
 |   5998     var first; | 
 |   5999     var count = 0; | 
 |   6000     checkbox.addEventListener('click', function(e) { | 
 |   6001       count++; | 
 |   6002       first = first || 'click'; | 
 |   6003     }); | 
 |   6004     checkbox.addEventListener('change', function() { | 
 |   6005       count++; | 
 |   6006       first = first || 'change'; | 
 |   6007     }); | 
 |   6008  | 
 |   6009     var event = document.createEvent('MouseEvent'); | 
 |   6010     event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, | 
 |   6011         false, false, false, 0, null); | 
 |   6012     checkbox.dispatchEvent(event); | 
 |   6013     // WebKit/Blink don't fire the change event if the element is outside the | 
 |   6014     // document, so assume 'change' for that case. | 
 |   6015     checkboxEventType = count == 1 ? 'change' : first; | 
 |   6016   })(); | 
 |   6017  | 
 |   6018   function getEventForInputType(element) { | 
 |   6019     switch (element.type) { | 
 |   6020       case 'checkbox': | 
 |   6021         return checkboxEventType; | 
 |   6022       case 'radio': | 
 |   6023       case 'select-multiple': | 
 |   6024       case 'select-one': | 
 |   6025         return 'change'; | 
 |   6026       case 'range': | 
 |   6027         if (/Trident|MSIE/.test(navigator.userAgent)) | 
 |   6028           return 'change'; | 
 |   6029       default: | 
 |   6030         return 'input'; | 
 |   6031     } | 
 |   6032   } | 
 |   6033  | 
 |   6034   function updateInput(input, property, value, santizeFn) { | 
 |   6035     input[property] = (santizeFn || sanitizeValue)(value); | 
 |   6036   } | 
 |   6037  | 
 |   6038   function inputBinding(input, property, santizeFn) { | 
 |   6039     return function(value) { | 
 |   6040       return updateInput(input, property, value, santizeFn); | 
 |   6041     } | 
 |   6042   } | 
 |   6043  | 
 |   6044   function noop() {} | 
 |   6045  | 
 |   6046   function bindInputEvent(input, property, observable, postEventFn) { | 
 |   6047     var eventType = getEventForInputType(input); | 
 |   6048  | 
 |   6049     function eventHandler() { | 
 |   6050       observable.setValue(input[property]); | 
 |   6051       observable.discardChanges(); | 
 |   6052       (postEventFn || noop)(input); | 
 |   6053       Platform.performMicrotaskCheckpoint(); | 
 |   6054     } | 
 |   6055     input.addEventListener(eventType, eventHandler); | 
 |   6056  | 
 |   6057     return { | 
 |   6058       close: function() { | 
 |   6059         input.removeEventListener(eventType, eventHandler); | 
 |   6060         observable.close(); | 
 |   6061       }, | 
 |   6062  | 
 |   6063       observable_: observable | 
 |   6064     } | 
 |   6065   } | 
 |   6066  | 
 |   6067   function booleanSanitize(value) { | 
 |   6068     return Boolean(value); | 
 |   6069   } | 
 |   6070  | 
 |   6071   // |element| is assumed to be an HTMLInputElement with |type| == 'radio'. | 
 |   6072   // Returns an array containing all radio buttons other than |element| that | 
 |   6073   // have the same |name|, either in the form that |element| belongs to or, | 
 |   6074   // if no form, in the document tree to which |element| belongs. | 
 |   6075   // | 
 |   6076   // This implementation is based upon the HTML spec definition of a | 
 |   6077   // "radio button group": | 
 |   6078   //   http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state.
       html#radio-button-group | 
 |   6079   // | 
 |   6080   function getAssociatedRadioButtons(element) { | 
 |   6081     if (element.form) { | 
 |   6082       return filter(element.form.elements, function(el) { | 
 |   6083         return el != element && | 
 |   6084             el.tagName == 'INPUT' && | 
 |   6085             el.type == 'radio' && | 
 |   6086             el.name == element.name; | 
 |   6087       }); | 
 |   6088     } else { | 
 |   6089       var treeScope = getTreeScope(element); | 
 |   6090       if (!treeScope) | 
 |   6091         return []; | 
 |   6092       var radios = treeScope.querySelectorAll( | 
 |   6093           'input[type="radio"][name="' + element.name + '"]'); | 
 |   6094       return filter(radios, function(el) { | 
 |   6095         return el != element && !el.form; | 
 |   6096       }); | 
 |   6097     } | 
 |   6098   } | 
 |   6099  | 
 |   6100   function checkedPostEvent(input) { | 
 |   6101     // Only the radio button that is getting checked gets an event. We | 
 |   6102     // therefore find all the associated radio buttons and update their | 
 |   6103     // check binding manually. | 
 |   6104     if (input.tagName === 'INPUT' && | 
 |   6105         input.type === 'radio') { | 
 |   6106       getAssociatedRadioButtons(input).forEach(function(radio) { | 
 |   6107         var checkedBinding = radio.bindings_.checked; | 
 |   6108         if (checkedBinding) { | 
 |   6109           // Set the value directly to avoid an infinite call stack. | 
 |   6110           checkedBinding.observable_.setValue(false); | 
 |   6111         } | 
 |   6112       }); | 
 |   6113     } | 
 |   6114   } | 
 |   6115  | 
 |   6116   HTMLInputElement.prototype.bind = function(name, value, oneTime) { | 
 |   6117     if (name !== 'value' && name !== 'checked') | 
 |   6118       return HTMLElement.prototype.bind.call(this, name, value, oneTime); | 
 |   6119  | 
 |   6120     this.removeAttribute(name); | 
 |   6121     var sanitizeFn = name == 'checked' ? booleanSanitize : sanitizeValue; | 
 |   6122     var postEventFn = name == 'checked' ? checkedPostEvent : noop; | 
 |   6123  | 
 |   6124     if (oneTime) | 
 |   6125       return updateInput(this, name, value, sanitizeFn); | 
 |   6126  | 
 |   6127  | 
 |   6128     var observable = value; | 
 |   6129     var binding = bindInputEvent(this, name, observable, postEventFn); | 
 |   6130     updateInput(this, name, | 
 |   6131                 observable.open(inputBinding(this, name, sanitizeFn)), | 
 |   6132                 sanitizeFn); | 
 |   6133  | 
 |   6134     // Checkboxes may need to update bindings of other checkboxes. | 
 |   6135     return updateBindings(this, name, binding); | 
 |   6136   } | 
 |   6137  | 
 |   6138   HTMLTextAreaElement.prototype.bind = function(name, value, oneTime) { | 
 |   6139     if (name !== 'value') | 
 |   6140       return HTMLElement.prototype.bind.call(this, name, value, oneTime); | 
 |   6141  | 
 |   6142     this.removeAttribute('value'); | 
 |   6143  | 
 |   6144     if (oneTime) | 
 |   6145       return updateInput(this, 'value', value); | 
 |   6146  | 
 |   6147     var observable = value; | 
 |   6148     var binding = bindInputEvent(this, 'value', observable); | 
 |   6149     updateInput(this, 'value', | 
 |   6150                 observable.open(inputBinding(this, 'value', sanitizeValue))); | 
 |   6151     return maybeUpdateBindings(this, name, binding); | 
 |   6152   } | 
 |   6153  | 
 |   6154   function updateOption(option, value) { | 
 |   6155     var parentNode = option.parentNode;; | 
 |   6156     var select; | 
 |   6157     var selectBinding; | 
 |   6158     var oldValue; | 
 |   6159     if (parentNode instanceof HTMLSelectElement && | 
 |   6160         parentNode.bindings_ && | 
 |   6161         parentNode.bindings_.value) { | 
 |   6162       select = parentNode; | 
 |   6163       selectBinding = select.bindings_.value; | 
 |   6164       oldValue = select.value; | 
 |   6165     } | 
 |   6166  | 
 |   6167     option.value = sanitizeValue(value); | 
 |   6168  | 
 |   6169     if (select && select.value != oldValue) { | 
 |   6170       selectBinding.observable_.setValue(select.value); | 
 |   6171       selectBinding.observable_.discardChanges(); | 
 |   6172       Platform.performMicrotaskCheckpoint(); | 
 |   6173     } | 
 |   6174   } | 
 |   6175  | 
 |   6176   function optionBinding(option) { | 
 |   6177     return function(value) { | 
 |   6178       updateOption(option, value); | 
 |   6179     } | 
 |   6180   } | 
 |   6181  | 
 |   6182   HTMLOptionElement.prototype.bind = function(name, value, oneTime) { | 
 |   6183     if (name !== 'value') | 
 |   6184       return HTMLElement.prototype.bind.call(this, name, value, oneTime); | 
 |   6185  | 
 |   6186     this.removeAttribute('value'); | 
 |   6187  | 
 |   6188     if (oneTime) | 
 |   6189       return updateOption(this, value); | 
 |   6190  | 
 |   6191     var observable = value; | 
 |   6192     var binding = bindInputEvent(this, 'value', observable); | 
 |   6193     updateOption(this, observable.open(optionBinding(this))); | 
 |   6194     return maybeUpdateBindings(this, name, binding); | 
 |   6195   } | 
 |   6196  | 
 |   6197   HTMLSelectElement.prototype.bind = function(name, value, oneTime) { | 
 |   6198     if (name === 'selectedindex') | 
 |   6199       name = 'selectedIndex'; | 
 |   6200  | 
 |   6201     if (name !== 'selectedIndex' && name !== 'value') | 
 |   6202       return HTMLElement.prototype.bind.call(this, name, value, oneTime); | 
 |   6203  | 
 |   6204     this.removeAttribute(name); | 
 |   6205  | 
 |   6206     if (oneTime) | 
 |   6207       return updateInput(this, name, value); | 
 |   6208  | 
 |   6209     var observable = value; | 
 |   6210     var binding = bindInputEvent(this, name, observable); | 
 |   6211     updateInput(this, name, | 
 |   6212                 observable.open(inputBinding(this, name))); | 
 |   6213  | 
 |   6214     // Option update events may need to access select bindings. | 
 |   6215     return updateBindings(this, name, binding); | 
 |   6216   } | 
 |   6217 })(this); | 
 |   6218  | 
 |   6219 // Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | 
 |   6220 // This code may only be used under the BSD style license found at http://polyme
       r.github.io/LICENSE.txt | 
 |   6221 // The complete set of authors may be found at http://polymer.github.io/AUTHORS.
       txt | 
 |   6222 // The complete set of contributors may be found at http://polymer.github.io/CON
       TRIBUTORS.txt | 
 |   6223 // Code distributed by Google as part of the polymer project is also | 
 |   6224 // subject to an additional IP rights grant found at http://polymer.github.io/PA
       TENTS.txt | 
 |   6225  | 
 |   6226 (function(global) { | 
 |   6227   'use strict'; | 
 |   6228  | 
 |   6229   function assert(v) { | 
 |   6230     if (!v) | 
 |   6231       throw new Error('Assertion failed'); | 
 |   6232   } | 
 |   6233  | 
 |   6234   var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach); | 
 |   6235  | 
 |   6236   function getFragmentRoot(node) { | 
 |   6237     var p; | 
 |   6238     while (p = node.parentNode) { | 
 |   6239       node = p; | 
 |   6240     } | 
 |   6241  | 
 |   6242     return node; | 
 |   6243   } | 
 |   6244  | 
 |   6245   function searchRefId(node, id) { | 
 |   6246     if (!id) | 
 |   6247       return; | 
 |   6248  | 
 |   6249     var ref; | 
 |   6250     var selector = '#' + id; | 
 |   6251     while (!ref) { | 
 |   6252       node = getFragmentRoot(node); | 
 |   6253  | 
 |   6254       if (node.protoContent_) | 
 |   6255         ref = node.protoContent_.querySelector(selector); | 
 |   6256       else if (node.getElementById) | 
 |   6257         ref = node.getElementById(id); | 
 |   6258  | 
 |   6259       if (ref || !node.templateCreator_) | 
 |   6260         break | 
 |   6261  | 
 |   6262       node = node.templateCreator_; | 
 |   6263     } | 
 |   6264  | 
 |   6265     return ref; | 
 |   6266   } | 
 |   6267  | 
 |   6268   function getInstanceRoot(node) { | 
 |   6269     while (node.parentNode) { | 
 |   6270       node = node.parentNode; | 
 |   6271     } | 
 |   6272     return node.templateCreator_ ? node : null; | 
 |   6273   } | 
 |   6274  | 
 |   6275   var Map; | 
 |   6276   if (global.Map && typeof global.Map.prototype.forEach === 'function') { | 
 |   6277     Map = global.Map; | 
 |   6278   } else { | 
 |   6279     Map = function() { | 
 |   6280       this.keys = []; | 
 |   6281       this.values = []; | 
 |   6282     }; | 
 |   6283  | 
 |   6284     Map.prototype = { | 
 |   6285       set: function(key, value) { | 
 |   6286         var index = this.keys.indexOf(key); | 
 |   6287         if (index < 0) { | 
 |   6288           this.keys.push(key); | 
 |   6289           this.values.push(value); | 
 |   6290         } else { | 
 |   6291           this.values[index] = value; | 
 |   6292         } | 
 |   6293       }, | 
 |   6294  | 
 |   6295       get: function(key) { | 
 |   6296         var index = this.keys.indexOf(key); | 
 |   6297         if (index < 0) | 
 |   6298           return; | 
 |   6299  | 
 |   6300         return this.values[index]; | 
 |   6301       }, | 
 |   6302  | 
 |   6303       delete: function(key, value) { | 
 |   6304         var index = this.keys.indexOf(key); | 
 |   6305         if (index < 0) | 
 |   6306           return false; | 
 |   6307  | 
 |   6308         this.keys.splice(index, 1); | 
 |   6309         this.values.splice(index, 1); | 
 |   6310         return true; | 
 |   6311       }, | 
 |   6312  | 
 |   6313       forEach: function(f, opt_this) { | 
 |   6314         for (var i = 0; i < this.keys.length; i++) | 
 |   6315           f.call(opt_this || this, this.values[i], this.keys[i], this); | 
 |   6316       } | 
 |   6317     }; | 
 |   6318   } | 
 |   6319  | 
 |   6320   // JScript does not have __proto__. We wrap all object literals with | 
 |   6321   // createObject which uses Object.create, Object.defineProperty and | 
 |   6322   // Object.getOwnPropertyDescriptor to create a new object that does the exact | 
 |   6323   // same thing. The main downside to this solution is that we have to extract | 
 |   6324   // all those property descriptors for IE. | 
 |   6325   var createObject = ('__proto__' in {}) ? | 
 |   6326       function(obj) { return obj; } : | 
 |   6327       function(obj) { | 
 |   6328         var proto = obj.__proto__; | 
 |   6329         if (!proto) | 
 |   6330           return obj; | 
 |   6331         var newObject = Object.create(proto); | 
 |   6332         Object.getOwnPropertyNames(obj).forEach(function(name) { | 
 |   6333           Object.defineProperty(newObject, name, | 
 |   6334                                Object.getOwnPropertyDescriptor(obj, name)); | 
 |   6335         }); | 
 |   6336         return newObject; | 
 |   6337       }; | 
 |   6338  | 
 |   6339   // IE does not support have Document.prototype.contains. | 
 |   6340   if (typeof document.contains != 'function') { | 
 |   6341     Document.prototype.contains = function(node) { | 
 |   6342       if (node === this || node.parentNode === this) | 
 |   6343         return true; | 
 |   6344       return this.documentElement.contains(node); | 
 |   6345     } | 
 |   6346   } | 
 |   6347  | 
 |   6348   var BIND = 'bind'; | 
 |   6349   var REPEAT = 'repeat'; | 
 |   6350   var IF = 'if'; | 
 |   6351  | 
 |   6352   var templateAttributeDirectives = { | 
 |   6353     'template': true, | 
 |   6354     'repeat': true, | 
 |   6355     'bind': true, | 
 |   6356     'ref': true | 
 |   6357   }; | 
 |   6358  | 
 |   6359   var semanticTemplateElements = { | 
 |   6360     'THEAD': true, | 
 |   6361     'TBODY': true, | 
 |   6362     'TFOOT': true, | 
 |   6363     'TH': true, | 
 |   6364     'TR': true, | 
 |   6365     'TD': true, | 
 |   6366     'COLGROUP': true, | 
 |   6367     'COL': true, | 
 |   6368     'CAPTION': true, | 
 |   6369     'OPTION': true, | 
 |   6370     'OPTGROUP': true | 
 |   6371   }; | 
 |   6372  | 
 |   6373   var hasTemplateElement = typeof HTMLTemplateElement !== 'undefined'; | 
 |   6374   if (hasTemplateElement) { | 
 |   6375     // TODO(rafaelw): Remove when fix for | 
 |   6376     // https://codereview.chromium.org/164803002/ | 
 |   6377     // makes it to Chrome release. | 
 |   6378     (function() { | 
 |   6379       var t = document.createElement('template'); | 
 |   6380       var d = t.content.ownerDocument; | 
 |   6381       var html = d.appendChild(d.createElement('html')); | 
 |   6382       var head = html.appendChild(d.createElement('head')); | 
 |   6383       var base = d.createElement('base'); | 
 |   6384       base.href = document.baseURI; | 
 |   6385       head.appendChild(base); | 
 |   6386     })(); | 
 |   6387   } | 
 |   6388  | 
 |   6389   var allTemplatesSelectors = 'template, ' + | 
 |   6390       Object.keys(semanticTemplateElements).map(function(tagName) { | 
 |   6391         return tagName.toLowerCase() + '[template]'; | 
 |   6392       }).join(', '); | 
 |   6393  | 
 |   6394   function isSVGTemplate(el) { | 
 |   6395     return el.tagName == 'template' && | 
 |   6396            el.namespaceURI == 'http://www.w3.org/2000/svg'; | 
 |   6397   } | 
 |   6398  | 
 |   6399   function isHTMLTemplate(el) { | 
 |   6400     return el.tagName == 'TEMPLATE' && | 
 |   6401            el.namespaceURI == 'http://www.w3.org/1999/xhtml'; | 
 |   6402   } | 
 |   6403  | 
 |   6404   function isAttributeTemplate(el) { | 
 |   6405     return Boolean(semanticTemplateElements[el.tagName] && | 
 |   6406                    el.hasAttribute('template')); | 
 |   6407   } | 
 |   6408  | 
 |   6409   function isTemplate(el) { | 
 |   6410     if (el.isTemplate_ === undefined) | 
 |   6411       el.isTemplate_ = el.tagName == 'TEMPLATE' || isAttributeTemplate(el); | 
 |   6412  | 
 |   6413     return el.isTemplate_; | 
 |   6414   } | 
 |   6415  | 
 |   6416   // FIXME: Observe templates being added/removed from documents | 
 |   6417   // FIXME: Expose imperative API to decorate and observe templates in | 
 |   6418   // "disconnected tress" (e.g. ShadowRoot) | 
 |   6419   document.addEventListener('DOMContentLoaded', function(e) { | 
 |   6420     bootstrapTemplatesRecursivelyFrom(document); | 
 |   6421     // FIXME: Is this needed? Seems like it shouldn't be. | 
 |   6422     Platform.performMicrotaskCheckpoint(); | 
 |   6423   }, false); | 
 |   6424  | 
 |   6425   function forAllTemplatesFrom(node, fn) { | 
 |   6426     var subTemplates = node.querySelectorAll(allTemplatesSelectors); | 
 |   6427  | 
 |   6428     if (isTemplate(node)) | 
 |   6429       fn(node) | 
 |   6430     forEach(subTemplates, fn); | 
 |   6431   } | 
 |   6432  | 
 |   6433   function bootstrapTemplatesRecursivelyFrom(node) { | 
 |   6434     function bootstrap(template) { | 
 |   6435       if (!HTMLTemplateElement.decorate(template)) | 
 |   6436         bootstrapTemplatesRecursivelyFrom(template.content); | 
 |   6437     } | 
 |   6438  | 
 |   6439     forAllTemplatesFrom(node, bootstrap); | 
 |   6440   } | 
 |   6441  | 
 |   6442   if (!hasTemplateElement) { | 
 |   6443     /** | 
 |   6444      * This represents a <template> element. | 
 |   6445      * @constructor | 
 |   6446      * @extends {HTMLElement} | 
 |   6447      */ | 
 |   6448     global.HTMLTemplateElement = function() { | 
 |   6449       throw TypeError('Illegal constructor'); | 
 |   6450     }; | 
 |   6451   } | 
 |   6452  | 
 |   6453   var hasProto = '__proto__' in {}; | 
 |   6454  | 
 |   6455   function mixin(to, from) { | 
 |   6456     Object.getOwnPropertyNames(from).forEach(function(name) { | 
 |   6457       Object.defineProperty(to, name, | 
 |   6458                             Object.getOwnPropertyDescriptor(from, name)); | 
 |   6459     }); | 
 |   6460   } | 
 |   6461  | 
 |   6462   // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#
       dfn-template-contents-owner | 
 |   6463   function getOrCreateTemplateContentsOwner(template) { | 
 |   6464     var doc = template.ownerDocument | 
 |   6465     if (!doc.defaultView) | 
 |   6466       return doc; | 
 |   6467     var d = doc.templateContentsOwner_; | 
 |   6468     if (!d) { | 
 |   6469       // TODO(arv): This should either be a Document or HTMLDocument depending | 
 |   6470       // on doc. | 
 |   6471       d = doc.implementation.createHTMLDocument(''); | 
 |   6472       while (d.lastChild) { | 
 |   6473         d.removeChild(d.lastChild); | 
 |   6474       } | 
 |   6475       doc.templateContentsOwner_ = d; | 
 |   6476     } | 
 |   6477     return d; | 
 |   6478   } | 
 |   6479  | 
 |   6480   function getTemplateStagingDocument(template) { | 
 |   6481     if (!template.stagingDocument_) { | 
 |   6482       var owner = template.ownerDocument; | 
 |   6483       if (!owner.stagingDocument_) { | 
 |   6484         owner.stagingDocument_ = owner.implementation.createHTMLDocument(''); | 
 |   6485         owner.stagingDocument_.isStagingDocument = true; | 
 |   6486         // TODO(rafaelw): Remove when fix for | 
 |   6487         // https://codereview.chromium.org/164803002/ | 
 |   6488         // makes it to Chrome release. | 
 |   6489         var base = owner.stagingDocument_.createElement('base'); | 
 |   6490         base.href = document.baseURI; | 
 |   6491         owner.stagingDocument_.head.appendChild(base); | 
 |   6492  | 
 |   6493         owner.stagingDocument_.stagingDocument_ = owner.stagingDocument_; | 
 |   6494       } | 
 |   6495  | 
 |   6496       template.stagingDocument_ = owner.stagingDocument_; | 
 |   6497     } | 
 |   6498  | 
 |   6499     return template.stagingDocument_; | 
 |   6500   } | 
 |   6501  | 
 |   6502   // For non-template browsers, the parser will disallow <template> in certain | 
 |   6503   // locations, so we allow "attribute templates" which combine the template | 
 |   6504   // element with the top-level container node of the content, e.g. | 
 |   6505   // | 
 |   6506   //   <tr template repeat="{{ foo }}"" class="bar"><td>Bar</td></tr> | 
 |   6507   // | 
 |   6508   // becomes | 
 |   6509   // | 
 |   6510   //   <template repeat="{{ foo }}"> | 
 |   6511   //   + #document-fragment | 
 |   6512   //     + <tr class="bar"> | 
 |   6513   //       + <td>Bar</td> | 
 |   6514   // | 
 |   6515   function extractTemplateFromAttributeTemplate(el) { | 
 |   6516     var template = el.ownerDocument.createElement('template'); | 
 |   6517     el.parentNode.insertBefore(template, el); | 
 |   6518  | 
 |   6519     var attribs = el.attributes; | 
 |   6520     var count = attribs.length; | 
 |   6521     while (count-- > 0) { | 
 |   6522       var attrib = attribs[count]; | 
 |   6523       if (templateAttributeDirectives[attrib.name]) { | 
 |   6524         if (attrib.name !== 'template') | 
 |   6525           template.setAttribute(attrib.name, attrib.value); | 
 |   6526         el.removeAttribute(attrib.name); | 
 |   6527       } | 
 |   6528     } | 
 |   6529  | 
 |   6530     return template; | 
 |   6531   } | 
 |   6532  | 
 |   6533   function extractTemplateFromSVGTemplate(el) { | 
 |   6534     var template = el.ownerDocument.createElement('template'); | 
 |   6535     el.parentNode.insertBefore(template, el); | 
 |   6536  | 
 |   6537     var attribs = el.attributes; | 
 |   6538     var count = attribs.length; | 
 |   6539     while (count-- > 0) { | 
 |   6540       var attrib = attribs[count]; | 
 |   6541       template.setAttribute(attrib.name, attrib.value); | 
 |   6542       el.removeAttribute(attrib.name); | 
 |   6543     } | 
 |   6544  | 
 |   6545     el.parentNode.removeChild(el); | 
 |   6546     return template; | 
 |   6547   } | 
 |   6548  | 
 |   6549   function liftNonNativeTemplateChildrenIntoContent(template, el, useRoot) { | 
 |   6550     var content = template.content; | 
 |   6551     if (useRoot) { | 
 |   6552       content.appendChild(el); | 
 |   6553       return; | 
 |   6554     } | 
 |   6555  | 
 |   6556     var child; | 
 |   6557     while (child = el.firstChild) { | 
 |   6558       content.appendChild(child); | 
 |   6559     } | 
 |   6560   } | 
 |   6561  | 
 |   6562   var templateObserver; | 
 |   6563   if (typeof MutationObserver == 'function') { | 
 |   6564     templateObserver = new MutationObserver(function(records) { | 
 |   6565       for (var i = 0; i < records.length; i++) { | 
 |   6566         records[i].target.refChanged_(); | 
 |   6567       } | 
 |   6568     }); | 
 |   6569   } | 
 |   6570  | 
 |   6571   /** | 
 |   6572    * Ensures proper API and content model for template elements. | 
 |   6573    * @param {HTMLTemplateElement} opt_instanceRef The template element which | 
 |   6574    *     |el| template element will return as the value of its ref(), and whose | 
 |   6575    *     content will be used as source when createInstance() is invoked. | 
 |   6576    */ | 
 |   6577   HTMLTemplateElement.decorate = function(el, opt_instanceRef) { | 
 |   6578     if (el.templateIsDecorated_) | 
 |   6579       return false; | 
 |   6580  | 
 |   6581     var templateElement = el; | 
 |   6582     templateElement.templateIsDecorated_ = true; | 
 |   6583  | 
 |   6584     var isNativeHTMLTemplate = isHTMLTemplate(templateElement) && | 
 |   6585                                hasTemplateElement; | 
 |   6586     var bootstrapContents = isNativeHTMLTemplate; | 
 |   6587     var liftContents = !isNativeHTMLTemplate; | 
 |   6588     var liftRoot = false; | 
 |   6589  | 
 |   6590     if (!isNativeHTMLTemplate) { | 
 |   6591       if (isAttributeTemplate(templateElement)) { | 
 |   6592         assert(!opt_instanceRef); | 
 |   6593         templateElement = extractTemplateFromAttributeTemplate(el); | 
 |   6594         templateElement.templateIsDecorated_ = true; | 
 |   6595         isNativeHTMLTemplate = hasTemplateElement; | 
 |   6596         liftRoot = true; | 
 |   6597       } else if (isSVGTemplate(templateElement)) { | 
 |   6598         templateElement = extractTemplateFromSVGTemplate(el); | 
 |   6599         templateElement.templateIsDecorated_ = true; | 
 |   6600         isNativeHTMLTemplate = hasTemplateElement; | 
 |   6601       } | 
 |   6602     } | 
 |   6603  | 
 |   6604     if (!isNativeHTMLTemplate) { | 
 |   6605       fixTemplateElementPrototype(templateElement); | 
 |   6606       var doc = getOrCreateTemplateContentsOwner(templateElement); | 
 |   6607       templateElement.content_ = doc.createDocumentFragment(); | 
 |   6608     } | 
 |   6609  | 
 |   6610     if (opt_instanceRef) { | 
 |   6611       // template is contained within an instance, its direct content must be | 
 |   6612       // empty | 
 |   6613       templateElement.instanceRef_ = opt_instanceRef; | 
 |   6614     } else if (liftContents) { | 
 |   6615       liftNonNativeTemplateChildrenIntoContent(templateElement, | 
 |   6616                                                el, | 
 |   6617                                                liftRoot); | 
 |   6618     } else if (bootstrapContents) { | 
 |   6619       bootstrapTemplatesRecursivelyFrom(templateElement.content); | 
 |   6620     } | 
 |   6621  | 
 |   6622     return true; | 
 |   6623   }; | 
 |   6624  | 
 |   6625   // TODO(rafaelw): This used to decorate recursively all templates from a given | 
 |   6626   // node. This happens by default on 'DOMContentLoaded', but may be needed | 
 |   6627   // in subtrees not descendent from document (e.g. ShadowRoot). | 
 |   6628   // Review whether this is the right public API. | 
 |   6629   HTMLTemplateElement.bootstrap = bootstrapTemplatesRecursivelyFrom; | 
 |   6630  | 
 |   6631   var htmlElement = global.HTMLUnknownElement || HTMLElement; | 
 |   6632  | 
 |   6633   var contentDescriptor = { | 
 |   6634     get: function() { | 
 |   6635       return this.content_; | 
 |   6636     }, | 
 |   6637     enumerable: true, | 
 |   6638     configurable: true | 
 |   6639   }; | 
 |   6640  | 
 |   6641   if (!hasTemplateElement) { | 
 |   6642     // Gecko is more picky with the prototype than WebKit. Make sure to use the | 
 |   6643     // same prototype as created in the constructor. | 
 |   6644     HTMLTemplateElement.prototype = Object.create(htmlElement.prototype); | 
 |   6645  | 
 |   6646     Object.defineProperty(HTMLTemplateElement.prototype, 'content', | 
 |   6647                           contentDescriptor); | 
 |   6648   } | 
 |   6649  | 
 |   6650   function fixTemplateElementPrototype(el) { | 
 |   6651     if (hasProto) | 
 |   6652       el.__proto__ = HTMLTemplateElement.prototype; | 
 |   6653     else | 
 |   6654       mixin(el, HTMLTemplateElement.prototype); | 
 |   6655   } | 
 |   6656  | 
 |   6657   function ensureSetModelScheduled(template) { | 
 |   6658     if (!template.setModelFn_) { | 
 |   6659       template.setModelFn_ = function() { | 
 |   6660         template.setModelFnScheduled_ = false; | 
 |   6661         var map = getBindings(template, | 
 |   6662             template.delegate_ && template.delegate_.prepareBinding); | 
 |   6663         processBindings(template, map, template.model_); | 
 |   6664       }; | 
 |   6665     } | 
 |   6666  | 
 |   6667     if (!template.setModelFnScheduled_) { | 
 |   6668       template.setModelFnScheduled_ = true; | 
 |   6669       Observer.runEOM_(template.setModelFn_); | 
 |   6670     } | 
 |   6671   } | 
 |   6672  | 
 |   6673   mixin(HTMLTemplateElement.prototype, { | 
 |   6674     bind: function(name, value, oneTime) { | 
 |   6675       if (name != 'ref') | 
 |   6676         return Element.prototype.bind.call(this, name, value, oneTime); | 
 |   6677  | 
 |   6678       var self = this; | 
 |   6679       var ref = oneTime ? value : value.open(function(ref) { | 
 |   6680         self.setAttribute('ref', ref); | 
 |   6681         self.refChanged_(); | 
 |   6682       }); | 
 |   6683  | 
 |   6684       this.setAttribute('ref', ref); | 
 |   6685       this.refChanged_(); | 
 |   6686       if (oneTime) | 
 |   6687         return; | 
 |   6688  | 
 |   6689       if (!this.bindings_) { | 
 |   6690         this.bindings_ = { ref: value }; | 
 |   6691       } else { | 
 |   6692         this.bindings_.ref = value; | 
 |   6693       } | 
 |   6694  | 
 |   6695       return value; | 
 |   6696     }, | 
 |   6697  | 
 |   6698     processBindingDirectives_: function(directives) { | 
 |   6699       if (this.iterator_) | 
 |   6700         this.iterator_.closeDeps(); | 
 |   6701  | 
 |   6702       if (!directives.if && !directives.bind && !directives.repeat) { | 
 |   6703         if (this.iterator_) { | 
 |   6704           this.iterator_.close(); | 
 |   6705           this.iterator_ = undefined; | 
 |   6706         } | 
 |   6707  | 
 |   6708         return; | 
 |   6709       } | 
 |   6710  | 
 |   6711       if (!this.iterator_) { | 
 |   6712         this.iterator_ = new TemplateIterator(this); | 
 |   6713       } | 
 |   6714  | 
 |   6715       this.iterator_.updateDependencies(directives, this.model_); | 
 |   6716  | 
 |   6717       if (templateObserver) { | 
 |   6718         templateObserver.observe(this, { attributes: true, | 
 |   6719                                          attributeFilter: ['ref'] }); | 
 |   6720       } | 
 |   6721  | 
 |   6722       return this.iterator_; | 
 |   6723     }, | 
 |   6724  | 
 |   6725     createInstance: function(model, bindingDelegate, delegate_) { | 
 |   6726       if (bindingDelegate) | 
 |   6727         delegate_ = this.newDelegate_(bindingDelegate); | 
 |   6728       else if (!delegate_) | 
 |   6729         delegate_ = this.delegate_; | 
 |   6730  | 
 |   6731       if (!this.refContent_) | 
 |   6732         this.refContent_ = this.ref_.content; | 
 |   6733       var content = this.refContent_; | 
 |   6734       if (content.firstChild === null) | 
 |   6735         return emptyInstance; | 
 |   6736  | 
 |   6737       var map = getInstanceBindingMap(content, delegate_); | 
 |   6738       var stagingDocument = getTemplateStagingDocument(this); | 
 |   6739       var instance = stagingDocument.createDocumentFragment(); | 
 |   6740       instance.templateCreator_ = this; | 
 |   6741       instance.protoContent_ = content; | 
 |   6742       instance.bindings_ = []; | 
 |   6743       instance.terminator_ = null; | 
 |   6744       var instanceRecord = instance.templateInstance_ = { | 
 |   6745         firstNode: null, | 
 |   6746         lastNode: null, | 
 |   6747         model: model | 
 |   6748       }; | 
 |   6749  | 
 |   6750       var i = 0; | 
 |   6751       var collectTerminator = false; | 
 |   6752       for (var child = content.firstChild; child; child = child.nextSibling) { | 
 |   6753         // The terminator of the instance is the clone of the last child of the | 
 |   6754         // content. If the last child is an active template, it may produce | 
 |   6755         // instances as a result of production, so simply collecting the last | 
 |   6756         // child of the instance after it has finished producing may be wrong. | 
 |   6757         if (child.nextSibling === null) | 
 |   6758           collectTerminator = true; | 
 |   6759  | 
 |   6760         var clone = cloneAndBindInstance(child, instance, stagingDocument, | 
 |   6761                                          map.children[i++], | 
 |   6762                                          model, | 
 |   6763                                          delegate_, | 
 |   6764                                          instance.bindings_); | 
 |   6765         clone.templateInstance_ = instanceRecord; | 
 |   6766         if (collectTerminator) | 
 |   6767           instance.terminator_ = clone; | 
 |   6768       } | 
 |   6769  | 
 |   6770       instanceRecord.firstNode = instance.firstChild; | 
 |   6771       instanceRecord.lastNode = instance.lastChild; | 
 |   6772       instance.templateCreator_ = undefined; | 
 |   6773       instance.protoContent_ = undefined; | 
 |   6774       return instance; | 
 |   6775     }, | 
 |   6776  | 
 |   6777     get model() { | 
 |   6778       return this.model_; | 
 |   6779     }, | 
 |   6780  | 
 |   6781     set model(model) { | 
 |   6782       this.model_ = model; | 
 |   6783       ensureSetModelScheduled(this); | 
 |   6784     }, | 
 |   6785  | 
 |   6786     get bindingDelegate() { | 
 |   6787       return this.delegate_ && this.delegate_.raw; | 
 |   6788     }, | 
 |   6789  | 
 |   6790     refChanged_: function() { | 
 |   6791       if (!this.iterator_ || this.refContent_ === this.ref_.content) | 
 |   6792         return; | 
 |   6793  | 
 |   6794       this.refContent_ = undefined; | 
 |   6795       this.iterator_.valueChanged(); | 
 |   6796       this.iterator_.updateIteratedValue(this.iterator_.getUpdatedValue()); | 
 |   6797     }, | 
 |   6798  | 
 |   6799     clear: function() { | 
 |   6800       this.model_ = undefined; | 
 |   6801       this.delegate_ = undefined; | 
 |   6802       if (this.bindings_ && this.bindings_.ref) | 
 |   6803         this.bindings_.ref.close() | 
 |   6804       this.refContent_ = undefined; | 
 |   6805       if (!this.iterator_) | 
 |   6806         return; | 
 |   6807       this.iterator_.valueChanged(); | 
 |   6808       this.iterator_.close() | 
 |   6809       this.iterator_ = undefined; | 
 |   6810     }, | 
 |   6811  | 
 |   6812     setDelegate_: function(delegate) { | 
 |   6813       this.delegate_ = delegate; | 
 |   6814       this.bindingMap_ = undefined; | 
 |   6815       if (this.iterator_) { | 
 |   6816         this.iterator_.instancePositionChangedFn_ = undefined; | 
 |   6817         this.iterator_.instanceModelFn_ = undefined; | 
 |   6818       } | 
 |   6819     }, | 
 |   6820  | 
 |   6821     newDelegate_: function(bindingDelegate) { | 
 |   6822       if (!bindingDelegate) | 
 |   6823         return; | 
 |   6824  | 
 |   6825       function delegateFn(name) { | 
 |   6826         var fn = bindingDelegate && bindingDelegate[name]; | 
 |   6827         if (typeof fn != 'function') | 
 |   6828           return; | 
 |   6829  | 
 |   6830         return function() { | 
 |   6831           return fn.apply(bindingDelegate, arguments); | 
 |   6832         }; | 
 |   6833       } | 
 |   6834  | 
 |   6835       return { | 
 |   6836         bindingMaps: {}, | 
 |   6837         raw: bindingDelegate, | 
 |   6838         prepareBinding: delegateFn('prepareBinding'), | 
 |   6839         prepareInstanceModel: delegateFn('prepareInstanceModel'), | 
 |   6840         prepareInstancePositionChanged: | 
 |   6841             delegateFn('prepareInstancePositionChanged') | 
 |   6842       }; | 
 |   6843     }, | 
 |   6844  | 
 |   6845     set bindingDelegate(bindingDelegate) { | 
 |   6846       if (this.delegate_) { | 
 |   6847         throw Error('Template must be cleared before a new bindingDelegate ' + | 
 |   6848                     'can be assigned'); | 
 |   6849       } | 
 |   6850  | 
 |   6851       this.setDelegate_(this.newDelegate_(bindingDelegate)); | 
 |   6852     }, | 
 |   6853  | 
 |   6854     get ref_() { | 
 |   6855       var ref = searchRefId(this, this.getAttribute('ref')); | 
 |   6856       if (!ref) | 
 |   6857         ref = this.instanceRef_; | 
 |   6858  | 
 |   6859       if (!ref) | 
 |   6860         return this; | 
 |   6861  | 
 |   6862       var nextRef = ref.ref_; | 
 |   6863       return nextRef ? nextRef : ref; | 
 |   6864     } | 
 |   6865   }); | 
 |   6866  | 
 |   6867   // Returns | 
 |   6868   //   a) undefined if there are no mustaches. | 
 |   6869   //   b) [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+] if there is at least one
        mustache. | 
 |   6870   function parseMustaches(s, name, node, prepareBindingFn) { | 
 |   6871     if (!s || !s.length) | 
 |   6872       return; | 
 |   6873  | 
 |   6874     var tokens; | 
 |   6875     var length = s.length; | 
 |   6876     var startIndex = 0, lastIndex = 0, endIndex = 0; | 
 |   6877     var onlyOneTime = true; | 
 |   6878     while (lastIndex < length) { | 
 |   6879       var startIndex = s.indexOf('{{', lastIndex); | 
 |   6880       var oneTimeStart = s.indexOf('[[', lastIndex); | 
 |   6881       var oneTime = false; | 
 |   6882       var terminator = '}}'; | 
 |   6883  | 
 |   6884       if (oneTimeStart >= 0 && | 
 |   6885           (startIndex < 0 || oneTimeStart < startIndex)) { | 
 |   6886         startIndex = oneTimeStart; | 
 |   6887         oneTime = true; | 
 |   6888         terminator = ']]'; | 
 |   6889       } | 
 |   6890  | 
 |   6891       endIndex = startIndex < 0 ? -1 : s.indexOf(terminator, startIndex + 2); | 
 |   6892  | 
 |   6893       if (endIndex < 0) { | 
 |   6894         if (!tokens) | 
 |   6895           return; | 
 |   6896  | 
 |   6897         tokens.push(s.slice(lastIndex)); // TEXT | 
 |   6898         break; | 
 |   6899       } | 
 |   6900  | 
 |   6901       tokens = tokens || []; | 
 |   6902       tokens.push(s.slice(lastIndex, startIndex)); // TEXT | 
 |   6903       var pathString = s.slice(startIndex + 2, endIndex).trim(); | 
 |   6904       tokens.push(oneTime); // ONE_TIME? | 
 |   6905       onlyOneTime = onlyOneTime && oneTime; | 
 |   6906       var delegateFn = prepareBindingFn && | 
 |   6907                        prepareBindingFn(pathString, name, node); | 
 |   6908       // Don't try to parse the expression if there's a prepareBinding function | 
 |   6909       if (delegateFn == null) { | 
 |   6910         tokens.push(Path.get(pathString)); // PATH | 
 |   6911       } else { | 
 |   6912         tokens.push(null); | 
 |   6913       } | 
 |   6914       tokens.push(delegateFn); // DELEGATE_FN | 
 |   6915       lastIndex = endIndex + 2; | 
 |   6916     } | 
 |   6917  | 
 |   6918     if (lastIndex === length) | 
 |   6919       tokens.push(''); // TEXT | 
 |   6920  | 
 |   6921     tokens.hasOnePath = tokens.length === 5; | 
 |   6922     tokens.isSimplePath = tokens.hasOnePath && | 
 |   6923                           tokens[0] == '' && | 
 |   6924                           tokens[4] == ''; | 
 |   6925     tokens.onlyOneTime = onlyOneTime; | 
 |   6926  | 
 |   6927     tokens.combinator = function(values) { | 
 |   6928       var newValue = tokens[0]; | 
 |   6929  | 
 |   6930       for (var i = 1; i < tokens.length; i += 4) { | 
 |   6931         var value = tokens.hasOnePath ? values : values[(i - 1) / 4]; | 
 |   6932         if (value !== undefined) | 
 |   6933           newValue += value; | 
 |   6934         newValue += tokens[i + 3]; | 
 |   6935       } | 
 |   6936  | 
 |   6937       return newValue; | 
 |   6938     } | 
 |   6939  | 
 |   6940     return tokens; | 
 |   6941   }; | 
 |   6942  | 
 |   6943   function processOneTimeBinding(name, tokens, node, model) { | 
 |   6944     if (tokens.hasOnePath) { | 
 |   6945       var delegateFn = tokens[3]; | 
 |   6946       var value = delegateFn ? delegateFn(model, node, true) : | 
 |   6947                                tokens[2].getValueFrom(model); | 
 |   6948       return tokens.isSimplePath ? value : tokens.combinator(value); | 
 |   6949     } | 
 |   6950  | 
 |   6951     var values = []; | 
 |   6952     for (var i = 1; i < tokens.length; i += 4) { | 
 |   6953       var delegateFn = tokens[i + 2]; | 
 |   6954       values[(i - 1) / 4] = delegateFn ? delegateFn(model, node) : | 
 |   6955           tokens[i + 1].getValueFrom(model); | 
 |   6956     } | 
 |   6957  | 
 |   6958     return tokens.combinator(values); | 
 |   6959   } | 
 |   6960  | 
 |   6961   function processSinglePathBinding(name, tokens, node, model) { | 
 |   6962     var delegateFn = tokens[3]; | 
 |   6963     var observer = delegateFn ? delegateFn(model, node, false) : | 
 |   6964         new PathObserver(model, tokens[2]); | 
 |   6965  | 
 |   6966     return tokens.isSimplePath ? observer : | 
 |   6967         new ObserverTransform(observer, tokens.combinator); | 
 |   6968   } | 
 |   6969  | 
 |   6970   function processBinding(name, tokens, node, model) { | 
 |   6971     if (tokens.onlyOneTime) | 
 |   6972       return processOneTimeBinding(name, tokens, node, model); | 
 |   6973  | 
 |   6974     if (tokens.hasOnePath) | 
 |   6975       return processSinglePathBinding(name, tokens, node, model); | 
 |   6976  | 
 |   6977     var observer = new CompoundObserver(); | 
 |   6978  | 
 |   6979     for (var i = 1; i < tokens.length; i += 4) { | 
 |   6980       var oneTime = tokens[i]; | 
 |   6981       var delegateFn = tokens[i + 2]; | 
 |   6982  | 
 |   6983       if (delegateFn) { | 
 |   6984         var value = delegateFn(model, node, oneTime); | 
 |   6985         if (oneTime) | 
 |   6986           observer.addPath(value) | 
 |   6987         else | 
 |   6988           observer.addObserver(value); | 
 |   6989         continue; | 
 |   6990       } | 
 |   6991  | 
 |   6992       var path = tokens[i + 1]; | 
 |   6993       if (oneTime) | 
 |   6994         observer.addPath(path.getValueFrom(model)) | 
 |   6995       else | 
 |   6996         observer.addPath(model, path); | 
 |   6997     } | 
 |   6998  | 
 |   6999     return new ObserverTransform(observer, tokens.combinator); | 
 |   7000   } | 
 |   7001  | 
 |   7002   function processBindings(node, bindings, model, instanceBindings) { | 
 |   7003     for (var i = 0; i < bindings.length; i += 2) { | 
 |   7004       var name = bindings[i] | 
 |   7005       var tokens = bindings[i + 1]; | 
 |   7006       var value = processBinding(name, tokens, node, model); | 
 |   7007       var binding = node.bind(name, value, tokens.onlyOneTime); | 
 |   7008       if (binding && instanceBindings) | 
 |   7009         instanceBindings.push(binding); | 
 |   7010     } | 
 |   7011  | 
 |   7012     node.bindFinished(); | 
 |   7013     if (!bindings.isTemplate) | 
 |   7014       return; | 
 |   7015  | 
 |   7016     node.model_ = model; | 
 |   7017     var iter = node.processBindingDirectives_(bindings); | 
 |   7018     if (instanceBindings && iter) | 
 |   7019       instanceBindings.push(iter); | 
 |   7020   } | 
 |   7021  | 
 |   7022   function parseWithDefault(el, name, prepareBindingFn) { | 
 |   7023     var v = el.getAttribute(name); | 
 |   7024     return parseMustaches(v == '' ? '{{}}' : v, name, el, prepareBindingFn); | 
 |   7025   } | 
 |   7026  | 
 |   7027   function parseAttributeBindings(element, prepareBindingFn) { | 
 |   7028     assert(element); | 
 |   7029  | 
 |   7030     var bindings = []; | 
 |   7031     var ifFound = false; | 
 |   7032     var bindFound = false; | 
 |   7033  | 
 |   7034     for (var i = 0; i < element.attributes.length; i++) { | 
 |   7035       var attr = element.attributes[i]; | 
 |   7036       var name = attr.name; | 
 |   7037       var value = attr.value; | 
 |   7038  | 
 |   7039       // Allow bindings expressed in attributes to be prefixed with underbars. | 
 |   7040       // We do this to allow correct semantics for browsers that don't implement | 
 |   7041       // <template> where certain attributes might trigger side-effects -- and | 
 |   7042       // for IE which sanitizes certain attributes, disallowing mustache | 
 |   7043       // replacements in their text. | 
 |   7044       while (name[0] === '_') { | 
 |   7045         name = name.substring(1); | 
 |   7046       } | 
 |   7047  | 
 |   7048       if (isTemplate(element) && | 
 |   7049           (name === IF || name === BIND || name === REPEAT)) { | 
 |   7050         continue; | 
 |   7051       } | 
 |   7052  | 
 |   7053       var tokens = parseMustaches(value, name, element, | 
 |   7054                                   prepareBindingFn); | 
 |   7055       if (!tokens) | 
 |   7056         continue; | 
 |   7057  | 
 |   7058       bindings.push(name, tokens); | 
 |   7059     } | 
 |   7060  | 
 |   7061     if (isTemplate(element)) { | 
 |   7062       bindings.isTemplate = true; | 
 |   7063       bindings.if = parseWithDefault(element, IF, prepareBindingFn); | 
 |   7064       bindings.bind = parseWithDefault(element, BIND, prepareBindingFn); | 
 |   7065       bindings.repeat = parseWithDefault(element, REPEAT, prepareBindingFn); | 
 |   7066  | 
 |   7067       if (bindings.if && !bindings.bind && !bindings.repeat) | 
 |   7068         bindings.bind = parseMustaches('{{}}', BIND, element, prepareBindingFn); | 
 |   7069     } | 
 |   7070  | 
 |   7071     return bindings; | 
 |   7072   } | 
 |   7073  | 
 |   7074   function getBindings(node, prepareBindingFn) { | 
 |   7075     if (node.nodeType === Node.ELEMENT_NODE) | 
 |   7076       return parseAttributeBindings(node, prepareBindingFn); | 
 |   7077  | 
 |   7078     if (node.nodeType === Node.TEXT_NODE) { | 
 |   7079       var tokens = parseMustaches(node.data, 'textContent', node, | 
 |   7080                                   prepareBindingFn); | 
 |   7081       if (tokens) | 
 |   7082         return ['textContent', tokens]; | 
 |   7083     } | 
 |   7084  | 
 |   7085     return []; | 
 |   7086   } | 
 |   7087  | 
 |   7088   function cloneAndBindInstance(node, parent, stagingDocument, bindings, model, | 
 |   7089                                 delegate, | 
 |   7090                                 instanceBindings, | 
 |   7091                                 instanceRecord) { | 
 |   7092     var clone = parent.appendChild(stagingDocument.importNode(node, false)); | 
 |   7093  | 
 |   7094     var i = 0; | 
 |   7095     for (var child = node.firstChild; child; child = child.nextSibling) { | 
 |   7096       cloneAndBindInstance(child, clone, stagingDocument, | 
 |   7097                             bindings.children[i++], | 
 |   7098                             model, | 
 |   7099                             delegate, | 
 |   7100                             instanceBindings); | 
 |   7101     } | 
 |   7102  | 
 |   7103     if (bindings.isTemplate) { | 
 |   7104       HTMLTemplateElement.decorate(clone, node); | 
 |   7105       if (delegate) | 
 |   7106         clone.setDelegate_(delegate); | 
 |   7107     } | 
 |   7108  | 
 |   7109     processBindings(clone, bindings, model, instanceBindings); | 
 |   7110     return clone; | 
 |   7111   } | 
 |   7112  | 
 |   7113   function createInstanceBindingMap(node, prepareBindingFn) { | 
 |   7114     var map = getBindings(node, prepareBindingFn); | 
 |   7115     map.children = {}; | 
 |   7116     var index = 0; | 
 |   7117     for (var child = node.firstChild; child; child = child.nextSibling) { | 
 |   7118       map.children[index++] = createInstanceBindingMap(child, prepareBindingFn); | 
 |   7119     } | 
 |   7120  | 
 |   7121     return map; | 
 |   7122   } | 
 |   7123  | 
 |   7124   var contentUidCounter = 1; | 
 |   7125  | 
 |   7126   // TODO(rafaelw): Setup a MutationObserver on content which clears the id | 
 |   7127   // so that bindingMaps regenerate when the template.content changes. | 
 |   7128   function getContentUid(content) { | 
 |   7129     var id = content.id_; | 
 |   7130     if (!id) | 
 |   7131       id = content.id_ = contentUidCounter++; | 
 |   7132     return id; | 
 |   7133   } | 
 |   7134  | 
 |   7135   // Each delegate is associated with a set of bindingMaps, one for each | 
 |   7136   // content which may be used by a template. The intent is that each binding | 
 |   7137   // delegate gets the opportunity to prepare the instance (via the prepare* | 
 |   7138   // delegate calls) once across all uses. | 
 |   7139   // TODO(rafaelw): Separate out the parse map from the binding map. In the | 
 |   7140   // current implementation, if two delegates need a binding map for the same | 
 |   7141   // content, the second will have to reparse. | 
 |   7142   function getInstanceBindingMap(content, delegate_) { | 
 |   7143     var contentId = getContentUid(content); | 
 |   7144     if (delegate_) { | 
 |   7145       var map = delegate_.bindingMaps[contentId]; | 
 |   7146       if (!map) { | 
 |   7147         map = delegate_.bindingMaps[contentId] = | 
 |   7148             createInstanceBindingMap(content, delegate_.prepareBinding) || []; | 
 |   7149       } | 
 |   7150       return map; | 
 |   7151     } | 
 |   7152  | 
 |   7153     var map = content.bindingMap_; | 
 |   7154     if (!map) { | 
 |   7155       map = content.bindingMap_ = | 
 |   7156           createInstanceBindingMap(content, undefined) || []; | 
 |   7157     } | 
 |   7158     return map; | 
 |   7159   } | 
 |   7160  | 
 |   7161   Object.defineProperty(Node.prototype, 'templateInstance', { | 
 |   7162     get: function() { | 
 |   7163       var instance = this.templateInstance_; | 
 |   7164       return instance ? instance : | 
 |   7165           (this.parentNode ? this.parentNode.templateInstance : undefined); | 
 |   7166     } | 
 |   7167   }); | 
 |   7168  | 
 |   7169   var emptyInstance = document.createDocumentFragment(); | 
 |   7170   emptyInstance.bindings_ = []; | 
 |   7171   emptyInstance.terminator_ = null; | 
 |   7172  | 
 |   7173   function TemplateIterator(templateElement) { | 
 |   7174     this.closed = false; | 
 |   7175     this.templateElement_ = templateElement; | 
 |   7176     this.instances = []; | 
 |   7177     this.deps = undefined; | 
 |   7178     this.iteratedValue = []; | 
 |   7179     this.presentValue = undefined; | 
 |   7180     this.arrayObserver = undefined; | 
 |   7181   } | 
 |   7182  | 
 |   7183   TemplateIterator.prototype = { | 
 |   7184     closeDeps: function() { | 
 |   7185       var deps = this.deps; | 
 |   7186       if (deps) { | 
 |   7187         if (deps.ifOneTime === false) | 
 |   7188           deps.ifValue.close(); | 
 |   7189         if (deps.oneTime === false) | 
 |   7190           deps.value.close(); | 
 |   7191       } | 
 |   7192     }, | 
 |   7193  | 
 |   7194     updateDependencies: function(directives, model) { | 
 |   7195       this.closeDeps(); | 
 |   7196  | 
 |   7197       var deps = this.deps = {}; | 
 |   7198       var template = this.templateElement_; | 
 |   7199  | 
 |   7200       var ifValue = true; | 
 |   7201       if (directives.if) { | 
 |   7202         deps.hasIf = true; | 
 |   7203         deps.ifOneTime = directives.if.onlyOneTime; | 
 |   7204         deps.ifValue = processBinding(IF, directives.if, template, model); | 
 |   7205  | 
 |   7206         ifValue = deps.ifValue; | 
 |   7207  | 
 |   7208         // oneTime if & predicate is false. nothing else to do. | 
 |   7209         if (deps.ifOneTime && !ifValue) { | 
 |   7210           this.valueChanged(); | 
 |   7211           return; | 
 |   7212         } | 
 |   7213  | 
 |   7214         if (!deps.ifOneTime) | 
 |   7215           ifValue = ifValue.open(this.updateIfValue, this); | 
 |   7216       } | 
 |   7217  | 
 |   7218       if (directives.repeat) { | 
 |   7219         deps.repeat = true; | 
 |   7220         deps.oneTime = directives.repeat.onlyOneTime; | 
 |   7221         deps.value = processBinding(REPEAT, directives.repeat, template, model); | 
 |   7222       } else { | 
 |   7223         deps.repeat = false; | 
 |   7224         deps.oneTime = directives.bind.onlyOneTime; | 
 |   7225         deps.value = processBinding(BIND, directives.bind, template, model); | 
 |   7226       } | 
 |   7227  | 
 |   7228       var value = deps.value; | 
 |   7229       if (!deps.oneTime) | 
 |   7230         value = value.open(this.updateIteratedValue, this); | 
 |   7231  | 
 |   7232       if (!ifValue) { | 
 |   7233         this.valueChanged(); | 
 |   7234         return; | 
 |   7235       } | 
 |   7236  | 
 |   7237       this.updateValue(value); | 
 |   7238     }, | 
 |   7239  | 
 |   7240     /** | 
 |   7241      * Gets the updated value of the bind/repeat. This can potentially call | 
 |   7242      * user code (if a bindingDelegate is set up) so we try to avoid it if we | 
 |   7243      * already have the value in hand (from Observer.open). | 
 |   7244      */ | 
 |   7245     getUpdatedValue: function() { | 
 |   7246       var value = this.deps.value; | 
 |   7247       if (!this.deps.oneTime) | 
 |   7248         value = value.discardChanges(); | 
 |   7249       return value; | 
 |   7250     }, | 
 |   7251  | 
 |   7252     updateIfValue: function(ifValue) { | 
 |   7253       if (!ifValue) { | 
 |   7254         this.valueChanged(); | 
 |   7255         return; | 
 |   7256       } | 
 |   7257  | 
 |   7258       this.updateValue(this.getUpdatedValue()); | 
 |   7259     }, | 
 |   7260  | 
 |   7261     updateIteratedValue: function(value) { | 
 |   7262       if (this.deps.hasIf) { | 
 |   7263         var ifValue = this.deps.ifValue; | 
 |   7264         if (!this.deps.ifOneTime) | 
 |   7265           ifValue = ifValue.discardChanges(); | 
 |   7266         if (!ifValue) { | 
 |   7267           this.valueChanged(); | 
 |   7268           return; | 
 |   7269         } | 
 |   7270       } | 
 |   7271  | 
 |   7272       this.updateValue(value); | 
 |   7273     }, | 
 |   7274  | 
 |   7275     updateValue: function(value) { | 
 |   7276       if (!this.deps.repeat) | 
 |   7277         value = [value]; | 
 |   7278       var observe = this.deps.repeat && | 
 |   7279                     !this.deps.oneTime && | 
 |   7280                     Array.isArray(value); | 
 |   7281       this.valueChanged(value, observe); | 
 |   7282     }, | 
 |   7283  | 
 |   7284     valueChanged: function(value, observeValue) { | 
 |   7285       if (!Array.isArray(value)) | 
 |   7286         value = []; | 
 |   7287  | 
 |   7288       if (value === this.iteratedValue) | 
 |   7289         return; | 
 |   7290  | 
 |   7291       this.unobserve(); | 
 |   7292       this.presentValue = value; | 
 |   7293       if (observeValue) { | 
 |   7294         this.arrayObserver = new ArrayObserver(this.presentValue); | 
 |   7295         this.arrayObserver.open(this.handleSplices, this); | 
 |   7296       } | 
 |   7297  | 
 |   7298       this.handleSplices(ArrayObserver.calculateSplices(this.presentValue, | 
 |   7299                                                         this.iteratedValue)); | 
 |   7300     }, | 
 |   7301  | 
 |   7302     getLastInstanceNode: function(index) { | 
 |   7303       if (index == -1) | 
 |   7304         return this.templateElement_; | 
 |   7305       var instance = this.instances[index]; | 
 |   7306       var terminator = instance.terminator_; | 
 |   7307       if (!terminator) | 
 |   7308         return this.getLastInstanceNode(index - 1); | 
 |   7309  | 
 |   7310       if (terminator.nodeType !== Node.ELEMENT_NODE || | 
 |   7311           this.templateElement_ === terminator) { | 
 |   7312         return terminator; | 
 |   7313       } | 
 |   7314  | 
 |   7315       var subtemplateIterator = terminator.iterator_; | 
 |   7316       if (!subtemplateIterator) | 
 |   7317         return terminator; | 
 |   7318  | 
 |   7319       return subtemplateIterator.getLastTemplateNode(); | 
 |   7320     }, | 
 |   7321  | 
 |   7322     getLastTemplateNode: function() { | 
 |   7323       return this.getLastInstanceNode(this.instances.length - 1); | 
 |   7324     }, | 
 |   7325  | 
 |   7326     insertInstanceAt: function(index, fragment) { | 
 |   7327       var previousInstanceLast = this.getLastInstanceNode(index - 1); | 
 |   7328       var parent = this.templateElement_.parentNode; | 
 |   7329       this.instances.splice(index, 0, fragment); | 
 |   7330  | 
 |   7331       parent.insertBefore(fragment, previousInstanceLast.nextSibling); | 
 |   7332     }, | 
 |   7333  | 
 |   7334     extractInstanceAt: function(index) { | 
 |   7335       var previousInstanceLast = this.getLastInstanceNode(index - 1); | 
 |   7336       var lastNode = this.getLastInstanceNode(index); | 
 |   7337       var parent = this.templateElement_.parentNode; | 
 |   7338       var instance = this.instances.splice(index, 1)[0]; | 
 |   7339  | 
 |   7340       while (lastNode !== previousInstanceLast) { | 
 |   7341         var node = previousInstanceLast.nextSibling; | 
 |   7342         if (node == lastNode) | 
 |   7343           lastNode = previousInstanceLast; | 
 |   7344  | 
 |   7345         instance.appendChild(parent.removeChild(node)); | 
 |   7346       } | 
 |   7347  | 
 |   7348       return instance; | 
 |   7349     }, | 
 |   7350  | 
 |   7351     getDelegateFn: function(fn) { | 
 |   7352       fn = fn && fn(this.templateElement_); | 
 |   7353       return typeof fn === 'function' ? fn : null; | 
 |   7354     }, | 
 |   7355  | 
 |   7356     handleSplices: function(splices) { | 
 |   7357       if (this.closed || !splices.length) | 
 |   7358         return; | 
 |   7359  | 
 |   7360       var template = this.templateElement_; | 
 |   7361  | 
 |   7362       if (!template.parentNode) { | 
 |   7363         this.close(); | 
 |   7364         return; | 
 |   7365       } | 
 |   7366  | 
 |   7367       ArrayObserver.applySplices(this.iteratedValue, this.presentValue, | 
 |   7368                                  splices); | 
 |   7369  | 
 |   7370       var delegate = template.delegate_; | 
 |   7371       if (this.instanceModelFn_ === undefined) { | 
 |   7372         this.instanceModelFn_ = | 
 |   7373             this.getDelegateFn(delegate && delegate.prepareInstanceModel); | 
 |   7374       } | 
 |   7375  | 
 |   7376       if (this.instancePositionChangedFn_ === undefined) { | 
 |   7377         this.instancePositionChangedFn_ = | 
 |   7378             this.getDelegateFn(delegate && | 
 |   7379                                delegate.prepareInstancePositionChanged); | 
 |   7380       } | 
 |   7381  | 
 |   7382       // Instance Removals | 
 |   7383       var instanceCache = new Map; | 
 |   7384       var removeDelta = 0; | 
 |   7385       for (var i = 0; i < splices.length; i++) { | 
 |   7386         var splice = splices[i]; | 
 |   7387         var removed = splice.removed; | 
 |   7388         for (var j = 0; j < removed.length; j++) { | 
 |   7389           var model = removed[j]; | 
 |   7390           var instance = this.extractInstanceAt(splice.index + removeDelta); | 
 |   7391           if (instance !== emptyInstance) { | 
 |   7392             instanceCache.set(model, instance); | 
 |   7393           } | 
 |   7394         } | 
 |   7395  | 
 |   7396         removeDelta -= splice.addedCount; | 
 |   7397       } | 
 |   7398  | 
 |   7399       // Instance Insertions | 
 |   7400       for (var i = 0; i < splices.length; i++) { | 
 |   7401         var splice = splices[i]; | 
 |   7402         var addIndex = splice.index; | 
 |   7403         for (; addIndex < splice.index + splice.addedCount; addIndex++) { | 
 |   7404           var model = this.iteratedValue[addIndex]; | 
 |   7405           var instance = instanceCache.get(model); | 
 |   7406           if (instance) { | 
 |   7407             instanceCache.delete(model); | 
 |   7408           } else { | 
 |   7409             if (this.instanceModelFn_) { | 
 |   7410               model = this.instanceModelFn_(model); | 
 |   7411             } | 
 |   7412  | 
 |   7413             if (model === undefined) { | 
 |   7414               instance = emptyInstance; | 
 |   7415             } else { | 
 |   7416               instance = template.createInstance(model, undefined, delegate); | 
 |   7417             } | 
 |   7418           } | 
 |   7419  | 
 |   7420           this.insertInstanceAt(addIndex, instance); | 
 |   7421         } | 
 |   7422       } | 
 |   7423  | 
 |   7424       instanceCache.forEach(function(instance) { | 
 |   7425         this.closeInstanceBindings(instance); | 
 |   7426       }, this); | 
 |   7427  | 
 |   7428       if (this.instancePositionChangedFn_) | 
 |   7429         this.reportInstancesMoved(splices); | 
 |   7430     }, | 
 |   7431  | 
 |   7432     reportInstanceMoved: function(index) { | 
 |   7433       var instance = this.instances[index]; | 
 |   7434       if (instance === emptyInstance) | 
 |   7435         return; | 
 |   7436  | 
 |   7437       this.instancePositionChangedFn_(instance.templateInstance_, index); | 
 |   7438     }, | 
 |   7439  | 
 |   7440     reportInstancesMoved: function(splices) { | 
 |   7441       var index = 0; | 
 |   7442       var offset = 0; | 
 |   7443       for (var i = 0; i < splices.length; i++) { | 
 |   7444         var splice = splices[i]; | 
 |   7445         if (offset != 0) { | 
 |   7446           while (index < splice.index) { | 
 |   7447             this.reportInstanceMoved(index); | 
 |   7448             index++; | 
 |   7449           } | 
 |   7450         } else { | 
 |   7451           index = splice.index; | 
 |   7452         } | 
 |   7453  | 
 |   7454         while (index < splice.index + splice.addedCount) { | 
 |   7455           this.reportInstanceMoved(index); | 
 |   7456           index++; | 
 |   7457         } | 
 |   7458  | 
 |   7459         offset += splice.addedCount - splice.removed.length; | 
 |   7460       } | 
 |   7461  | 
 |   7462       if (offset == 0) | 
 |   7463         return; | 
 |   7464  | 
 |   7465       var length = this.instances.length; | 
 |   7466       while (index < length) { | 
 |   7467         this.reportInstanceMoved(index); | 
 |   7468         index++; | 
 |   7469       } | 
 |   7470     }, | 
 |   7471  | 
 |   7472     closeInstanceBindings: function(instance) { | 
 |   7473       var bindings = instance.bindings_; | 
 |   7474       for (var i = 0; i < bindings.length; i++) { | 
 |   7475         bindings[i].close(); | 
 |   7476       } | 
 |   7477     }, | 
 |   7478  | 
 |   7479     unobserve: function() { | 
 |   7480       if (!this.arrayObserver) | 
 |   7481         return; | 
 |   7482  | 
 |   7483       this.arrayObserver.close(); | 
 |   7484       this.arrayObserver = undefined; | 
 |   7485     }, | 
 |   7486  | 
 |   7487     close: function() { | 
 |   7488       if (this.closed) | 
 |   7489         return; | 
 |   7490       this.unobserve(); | 
 |   7491       for (var i = 0; i < this.instances.length; i++) { | 
 |   7492         this.closeInstanceBindings(this.instances[i]); | 
 |   7493       } | 
 |   7494  | 
 |   7495       this.instances.length = 0; | 
 |   7496       this.closeDeps(); | 
 |   7497       this.templateElement_.iterator_ = undefined; | 
 |   7498       this.closed = true; | 
 |   7499     } | 
 |   7500   }; | 
 |   7501  | 
 |   7502   // Polyfill-specific API. | 
 |   7503   HTMLTemplateElement.forAllTemplatesFrom_ = forAllTemplatesFrom; | 
 |   7504 })(this); | 
 |   7505  | 
 |   7506 (function(scope) { | 
 |   7507   'use strict'; | 
 |   7508  | 
 |   7509   // feature detect for URL constructor | 
 |   7510   var hasWorkingUrl = false; | 
 |   7511   if (!scope.forceJURL) { | 
 |   7512     try { | 
 |   7513       var u = new URL('b', 'http://a'); | 
 |   7514       hasWorkingUrl = u.href === 'http://a/b'; | 
 |   7515     } catch(e) {} | 
 |   7516   } | 
 |   7517  | 
 |   7518   if (hasWorkingUrl) | 
 |   7519     return; | 
 |   7520  | 
 |   7521   var relative = Object.create(null); | 
 |   7522   relative['ftp'] = 21; | 
 |   7523   relative['file'] = 0; | 
 |   7524   relative['gopher'] = 70; | 
 |   7525   relative['http'] = 80; | 
 |   7526   relative['https'] = 443; | 
 |   7527   relative['ws'] = 80; | 
 |   7528   relative['wss'] = 443; | 
 |   7529  | 
 |   7530   var relativePathDotMapping = Object.create(null); | 
 |   7531   relativePathDotMapping['%2e'] = '.'; | 
 |   7532   relativePathDotMapping['.%2e'] = '..'; | 
 |   7533   relativePathDotMapping['%2e.'] = '..'; | 
 |   7534   relativePathDotMapping['%2e%2e'] = '..'; | 
 |   7535  | 
 |   7536   function isRelativeScheme(scheme) { | 
 |   7537     return relative[scheme] !== undefined; | 
 |   7538   } | 
 |   7539  | 
 |   7540   function invalid() { | 
 |   7541     clear.call(this); | 
 |   7542     this._isInvalid = true; | 
 |   7543   } | 
 |   7544  | 
 |   7545   function IDNAToASCII(h) { | 
 |   7546     if ('' == h) { | 
 |   7547       invalid.call(this) | 
 |   7548     } | 
 |   7549     // XXX | 
 |   7550     return h.toLowerCase() | 
 |   7551   } | 
 |   7552  | 
 |   7553   function percentEscape(c) { | 
 |   7554     var unicode = c.charCodeAt(0); | 
 |   7555     if (unicode > 0x20 && | 
 |   7556        unicode < 0x7F && | 
 |   7557        // " # < > ? ` | 
 |   7558        [0x22, 0x23, 0x3C, 0x3E, 0x3F, 0x60].indexOf(unicode) == -1 | 
 |   7559       ) { | 
 |   7560       return c; | 
 |   7561     } | 
 |   7562     return encodeURIComponent(c); | 
 |   7563   } | 
 |   7564  | 
 |   7565   function percentEscapeQuery(c) { | 
 |   7566     // XXX This actually needs to encode c using encoding and then | 
 |   7567     // convert the bytes one-by-one. | 
 |   7568  | 
 |   7569     var unicode = c.charCodeAt(0); | 
 |   7570     if (unicode > 0x20 && | 
 |   7571        unicode < 0x7F && | 
 |   7572        // " # < > ` (do not escape '?') | 
 |   7573        [0x22, 0x23, 0x3C, 0x3E, 0x60].indexOf(unicode) == -1 | 
 |   7574       ) { | 
 |   7575       return c; | 
 |   7576     } | 
 |   7577     return encodeURIComponent(c); | 
 |   7578   } | 
 |   7579  | 
 |   7580   var EOF = undefined, | 
 |   7581       ALPHA = /[a-zA-Z]/, | 
 |   7582       ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/; | 
 |   7583  | 
 |   7584   function parse(input, stateOverride, base) { | 
 |   7585     function err(message) { | 
 |   7586       errors.push(message) | 
 |   7587     } | 
 |   7588  | 
 |   7589     var state = stateOverride || 'scheme start', | 
 |   7590         cursor = 0, | 
 |   7591         buffer = '', | 
 |   7592         seenAt = false, | 
 |   7593         seenBracket = false, | 
 |   7594         errors = []; | 
 |   7595  | 
 |   7596     loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) 
       { | 
 |   7597       var c = input[cursor]; | 
 |   7598       switch (state) { | 
 |   7599         case 'scheme start': | 
 |   7600           if (c && ALPHA.test(c)) { | 
 |   7601             buffer += c.toLowerCase(); // ASCII-safe | 
 |   7602             state = 'scheme'; | 
 |   7603           } else if (!stateOverride) { | 
 |   7604             buffer = ''; | 
 |   7605             state = 'no scheme'; | 
 |   7606             continue; | 
 |   7607           } else { | 
 |   7608             err('Invalid scheme.'); | 
 |   7609             break loop; | 
 |   7610           } | 
 |   7611           break; | 
 |   7612  | 
 |   7613         case 'scheme': | 
 |   7614           if (c && ALPHANUMERIC.test(c)) { | 
 |   7615             buffer += c.toLowerCase(); // ASCII-safe | 
 |   7616           } else if (':' == c) { | 
 |   7617             this._scheme = buffer; | 
 |   7618             buffer = ''; | 
 |   7619             if (stateOverride) { | 
 |   7620               break loop; | 
 |   7621             } | 
 |   7622             if (isRelativeScheme(this._scheme)) { | 
 |   7623               this._isRelative = true; | 
 |   7624             } | 
 |   7625             if ('file' == this._scheme) { | 
 |   7626               state = 'relative'; | 
 |   7627             } else if (this._isRelative && base && base._scheme == this._scheme)
        { | 
 |   7628               state = 'relative or authority'; | 
 |   7629             } else if (this._isRelative) { | 
 |   7630               state = 'authority first slash'; | 
 |   7631             } else { | 
 |   7632               state = 'scheme data'; | 
 |   7633             } | 
 |   7634           } else if (!stateOverride) { | 
 |   7635             buffer = ''; | 
 |   7636             cursor = 0; | 
 |   7637             state = 'no scheme'; | 
 |   7638             continue; | 
 |   7639           } else if (EOF == c) { | 
 |   7640             break loop; | 
 |   7641           } else { | 
 |   7642             err('Code point not allowed in scheme: ' + c) | 
 |   7643             break loop; | 
 |   7644           } | 
 |   7645           break; | 
 |   7646  | 
 |   7647         case 'scheme data': | 
 |   7648           if ('?' == c) { | 
 |   7649             query = '?'; | 
 |   7650             state = 'query'; | 
 |   7651           } else if ('#' == c) { | 
 |   7652             this._fragment = '#'; | 
 |   7653             state = 'fragment'; | 
 |   7654           } else { | 
 |   7655             // XXX error handling | 
 |   7656             if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { | 
 |   7657               this._schemeData += percentEscape(c); | 
 |   7658             } | 
 |   7659           } | 
 |   7660           break; | 
 |   7661  | 
 |   7662         case 'no scheme': | 
 |   7663           if (!base || !(isRelativeScheme(base._scheme))) { | 
 |   7664             err('Missing scheme.'); | 
 |   7665             invalid.call(this); | 
 |   7666           } else { | 
 |   7667             state = 'relative'; | 
 |   7668             continue; | 
 |   7669           } | 
 |   7670           break; | 
 |   7671  | 
 |   7672         case 'relative or authority': | 
 |   7673           if ('/' == c && '/' == input[cursor+1]) { | 
 |   7674             state = 'authority ignore slashes'; | 
 |   7675           } else { | 
 |   7676             err('Expected /, got: ' + c); | 
 |   7677             state = 'relative'; | 
 |   7678             continue | 
 |   7679           } | 
 |   7680           break; | 
 |   7681  | 
 |   7682         case 'relative': | 
 |   7683           this._isRelative = true; | 
 |   7684           if ('file' != this._scheme) | 
 |   7685             this._scheme = base._scheme; | 
 |   7686           if (EOF == c) { | 
 |   7687             this._host = base._host; | 
 |   7688             this._port = base._port; | 
 |   7689             this._path = base._path.slice(); | 
 |   7690             this._query = base._query; | 
 |   7691             break loop; | 
 |   7692           } else if ('/' == c || '\\' == c) { | 
 |   7693             if ('\\' == c) | 
 |   7694               err('\\ is an invalid code point.'); | 
 |   7695             state = 'relative slash'; | 
 |   7696           } else if ('?' == c) { | 
 |   7697             this._host = base._host; | 
 |   7698             this._port = base._port; | 
 |   7699             this._path = base._path.slice(); | 
 |   7700             this._query = '?'; | 
 |   7701             state = 'query'; | 
 |   7702           } else if ('#' == c) { | 
 |   7703             this._host = base._host; | 
 |   7704             this._port = base._port; | 
 |   7705             this._path = base._path.slice(); | 
 |   7706             this._query = base._query; | 
 |   7707             this._fragment = '#'; | 
 |   7708             state = 'fragment'; | 
 |   7709           } else { | 
 |   7710             var nextC = input[cursor+1] | 
 |   7711             var nextNextC = input[cursor+2] | 
 |   7712             if ( | 
 |   7713               'file' != this._scheme || !ALPHA.test(c) || | 
 |   7714               (nextC != ':' && nextC != '|') || | 
 |   7715               (EOF != nextNextC && '/' != nextNextC && '\\' != nextNextC && '?' 
       != nextNextC && '#' != nextNextC)) { | 
 |   7716               this._host = base._host; | 
 |   7717               this._port = base._port; | 
 |   7718               this._path = base._path.slice(); | 
 |   7719               this._path.pop(); | 
 |   7720             } | 
 |   7721             state = 'relative path'; | 
 |   7722             continue; | 
 |   7723           } | 
 |   7724           break; | 
 |   7725  | 
 |   7726         case 'relative slash': | 
 |   7727           if ('/' == c || '\\' == c) { | 
 |   7728             if ('\\' == c) { | 
 |   7729               err('\\ is an invalid code point.'); | 
 |   7730             } | 
 |   7731             if ('file' == this._scheme) { | 
 |   7732               state = 'file host'; | 
 |   7733             } else { | 
 |   7734               state = 'authority ignore slashes'; | 
 |   7735             } | 
 |   7736           } else { | 
 |   7737             if ('file' != this._scheme) { | 
 |   7738               this._host = base._host; | 
 |   7739               this._port = base._port; | 
 |   7740             } | 
 |   7741             state = 'relative path'; | 
 |   7742             continue; | 
 |   7743           } | 
 |   7744           break; | 
 |   7745  | 
 |   7746         case 'authority first slash': | 
 |   7747           if ('/' == c) { | 
 |   7748             state = 'authority second slash'; | 
 |   7749           } else { | 
 |   7750             err("Expected '/', got: " + c); | 
 |   7751             state = 'authority ignore slashes'; | 
 |   7752             continue; | 
 |   7753           } | 
 |   7754           break; | 
 |   7755  | 
 |   7756         case 'authority second slash': | 
 |   7757           state = 'authority ignore slashes'; | 
 |   7758           if ('/' != c) { | 
 |   7759             err("Expected '/', got: " + c); | 
 |   7760             continue; | 
 |   7761           } | 
 |   7762           break; | 
 |   7763  | 
 |   7764         case 'authority ignore slashes': | 
 |   7765           if ('/' != c && '\\' != c) { | 
 |   7766             state = 'authority'; | 
 |   7767             continue; | 
 |   7768           } else { | 
 |   7769             err('Expected authority, got: ' + c); | 
 |   7770           } | 
 |   7771           break; | 
 |   7772  | 
 |   7773         case 'authority': | 
 |   7774           if ('@' == c) { | 
 |   7775             if (seenAt) { | 
 |   7776               err('@ already seen.'); | 
 |   7777               buffer += '%40'; | 
 |   7778             } | 
 |   7779             seenAt = true; | 
 |   7780             for (var i = 0; i < buffer.length; i++) { | 
 |   7781               var cp = buffer[i]; | 
 |   7782               if ('\t' == cp || '\n' == cp || '\r' == cp) { | 
 |   7783                 err('Invalid whitespace in authority.'); | 
 |   7784                 continue; | 
 |   7785               } | 
 |   7786               // XXX check URL code points | 
 |   7787               if (':' == cp && null === this._password) { | 
 |   7788                 this._password = ''; | 
 |   7789                 continue; | 
 |   7790               } | 
 |   7791               var tempC = percentEscape(cp); | 
 |   7792               (null !== this._password) ? this._password += tempC : this._userna
       me += tempC; | 
 |   7793             } | 
 |   7794             buffer = ''; | 
 |   7795           } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) 
       { | 
 |   7796             cursor -= buffer.length; | 
 |   7797             buffer = ''; | 
 |   7798             state = 'host'; | 
 |   7799             continue; | 
 |   7800           } else { | 
 |   7801             buffer += c; | 
 |   7802           } | 
 |   7803           break; | 
 |   7804  | 
 |   7805         case 'file host': | 
 |   7806           if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) { | 
 |   7807             if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ':'
        || buffer[1] == '|')) { | 
 |   7808               state = 'relative path'; | 
 |   7809             } else if (buffer.length == 0) { | 
 |   7810               state = 'relative path start'; | 
 |   7811             } else { | 
 |   7812               this._host = IDNAToASCII.call(this, buffer); | 
 |   7813               buffer = ''; | 
 |   7814               state = 'relative path start'; | 
 |   7815             } | 
 |   7816             continue; | 
 |   7817           } else if ('\t' == c || '\n' == c || '\r' == c) { | 
 |   7818             err('Invalid whitespace in file host.'); | 
 |   7819           } else { | 
 |   7820             buffer += c; | 
 |   7821           } | 
 |   7822           break; | 
 |   7823  | 
 |   7824         case 'host': | 
 |   7825         case 'hostname': | 
 |   7826           if (':' == c && !seenBracket) { | 
 |   7827             // XXX host parsing | 
 |   7828             this._host = IDNAToASCII.call(this, buffer); | 
 |   7829             buffer = ''; | 
 |   7830             state = 'port'; | 
 |   7831             if ('hostname' == stateOverride) { | 
 |   7832               break loop; | 
 |   7833             } | 
 |   7834           } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) 
       { | 
 |   7835             this._host = IDNAToASCII.call(this, buffer); | 
 |   7836             buffer = ''; | 
 |   7837             state = 'relative path start'; | 
 |   7838             if (stateOverride) { | 
 |   7839               break loop; | 
 |   7840             } | 
 |   7841             continue; | 
 |   7842           } else if ('\t' != c && '\n' != c && '\r' != c) { | 
 |   7843             if ('[' == c) { | 
 |   7844               seenBracket = true; | 
 |   7845             } else if (']' == c) { | 
 |   7846               seenBracket = false; | 
 |   7847             } | 
 |   7848             buffer += c; | 
 |   7849           } else { | 
 |   7850             err('Invalid code point in host/hostname: ' + c); | 
 |   7851           } | 
 |   7852           break; | 
 |   7853  | 
 |   7854         case 'port': | 
 |   7855           if (/[0-9]/.test(c)) { | 
 |   7856             buffer += c; | 
 |   7857           } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c |
       | stateOverride) { | 
 |   7858             if ('' != buffer) { | 
 |   7859               var temp = parseInt(buffer, 10); | 
 |   7860               if (temp != relative[this._scheme]) { | 
 |   7861                 this._port = temp + ''; | 
 |   7862               } | 
 |   7863               buffer = ''; | 
 |   7864             } | 
 |   7865             if (stateOverride) { | 
 |   7866               break loop; | 
 |   7867             } | 
 |   7868             state = 'relative path start'; | 
 |   7869             continue; | 
 |   7870           } else if ('\t' == c || '\n' == c || '\r' == c) { | 
 |   7871             err('Invalid code point in port: ' + c); | 
 |   7872           } else { | 
 |   7873             invalid.call(this); | 
 |   7874           } | 
 |   7875           break; | 
 |   7876  | 
 |   7877         case 'relative path start': | 
 |   7878           if ('\\' == c) | 
 |   7879             err("'\\' not allowed in path."); | 
 |   7880           state = 'relative path'; | 
 |   7881           if ('/' != c && '\\' != c) { | 
 |   7882             continue; | 
 |   7883           } | 
 |   7884           break; | 
 |   7885  | 
 |   7886         case 'relative path': | 
 |   7887           if (EOF == c || '/' == c || '\\' == c || (!stateOverride && ('?' == c 
       || '#' == c))) { | 
 |   7888             if ('\\' == c) { | 
 |   7889               err('\\ not allowed in relative path.'); | 
 |   7890             } | 
 |   7891             var tmp; | 
 |   7892             if (tmp = relativePathDotMapping[buffer.toLowerCase()]) { | 
 |   7893               buffer = tmp; | 
 |   7894             } | 
 |   7895             if ('..' == buffer) { | 
 |   7896               this._path.pop(); | 
 |   7897               if ('/' != c && '\\' != c) { | 
 |   7898                 this._path.push(''); | 
 |   7899               } | 
 |   7900             } else if ('.' == buffer && '/' != c && '\\' != c) { | 
 |   7901               this._path.push(''); | 
 |   7902             } else if ('.' != buffer) { | 
 |   7903               if ('file' == this._scheme && this._path.length == 0 && buffer.len
       gth == 2 && ALPHA.test(buffer[0]) && buffer[1] == '|') { | 
 |   7904                 buffer = buffer[0] + ':'; | 
 |   7905               } | 
 |   7906               this._path.push(buffer); | 
 |   7907             } | 
 |   7908             buffer = ''; | 
 |   7909             if ('?' == c) { | 
 |   7910               this._query = '?'; | 
 |   7911               state = 'query'; | 
 |   7912             } else if ('#' == c) { | 
 |   7913               this._fragment = '#'; | 
 |   7914               state = 'fragment'; | 
 |   7915             } | 
 |   7916           } else if ('\t' != c && '\n' != c && '\r' != c) { | 
 |   7917             buffer += percentEscape(c); | 
 |   7918           } | 
 |   7919           break; | 
 |   7920  | 
 |   7921         case 'query': | 
 |   7922           if (!stateOverride && '#' == c) { | 
 |   7923             this._fragment = '#'; | 
 |   7924             state = 'fragment'; | 
 |   7925           } else if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { | 
 |   7926             this._query += percentEscapeQuery(c); | 
 |   7927           } | 
 |   7928           break; | 
 |   7929  | 
 |   7930         case 'fragment': | 
 |   7931           if (EOF != c && '\t' != c && '\n' != c && '\r' != c) { | 
 |   7932             this._fragment += c; | 
 |   7933           } | 
 |   7934           break; | 
 |   7935       } | 
 |   7936  | 
 |   7937       cursor++; | 
 |   7938     } | 
 |   7939   } | 
 |   7940  | 
 |   7941   function clear() { | 
 |   7942     this._scheme = ''; | 
 |   7943     this._schemeData = ''; | 
 |   7944     this._username = ''; | 
 |   7945     this._password = null; | 
 |   7946     this._host = ''; | 
 |   7947     this._port = ''; | 
 |   7948     this._path = []; | 
 |   7949     this._query = ''; | 
 |   7950     this._fragment = ''; | 
 |   7951     this._isInvalid = false; | 
 |   7952     this._isRelative = false; | 
 |   7953   } | 
 |   7954  | 
 |   7955   // Does not process domain names or IP addresses. | 
 |   7956   // Does not handle encoding for the query parameter. | 
 |   7957   function jURL(url, base /* , encoding */) { | 
 |   7958     if (base !== undefined && !(base instanceof jURL)) | 
 |   7959       base = new jURL(String(base)); | 
 |   7960  | 
 |   7961     this._url = url; | 
 |   7962     clear.call(this); | 
 |   7963  | 
 |   7964     var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, ''); | 
 |   7965     // encoding = encoding || 'utf-8' | 
 |   7966  | 
 |   7967     parse.call(this, input, null, base); | 
 |   7968   } | 
 |   7969  | 
 |   7970   jURL.prototype = { | 
 |   7971     get href() { | 
 |   7972       if (this._isInvalid) | 
 |   7973         return this._url; | 
 |   7974  | 
 |   7975       var authority = ''; | 
 |   7976       if ('' != this._username || null != this._password) { | 
 |   7977         authority = this._username + | 
 |   7978             (null != this._password ? ':' + this._password : '') + '@'; | 
 |   7979       } | 
 |   7980  | 
 |   7981       return this.protocol + | 
 |   7982           (this._isRelative ? '//' + authority + this.host : '') + | 
 |   7983           this.pathname + this._query + this._fragment; | 
 |   7984     }, | 
 |   7985     set href(href) { | 
 |   7986       clear.call(this); | 
 |   7987       parse.call(this, href); | 
 |   7988     }, | 
 |   7989  | 
 |   7990     get protocol() { | 
 |   7991       return this._scheme + ':'; | 
 |   7992     }, | 
 |   7993     set protocol(protocol) { | 
 |   7994       if (this._isInvalid) | 
 |   7995         return; | 
 |   7996       parse.call(this, protocol + ':', 'scheme start'); | 
 |   7997     }, | 
 |   7998  | 
 |   7999     get host() { | 
 |   8000       return this._isInvalid ? '' : this._port ? | 
 |   8001           this._host + ':' + this._port : this._host; | 
 |   8002     }, | 
 |   8003     set host(host) { | 
 |   8004       if (this._isInvalid || !this._isRelative) | 
 |   8005         return; | 
 |   8006       parse.call(this, host, 'host'); | 
 |   8007     }, | 
 |   8008  | 
 |   8009     get hostname() { | 
 |   8010       return this._host; | 
 |   8011     }, | 
 |   8012     set hostname(hostname) { | 
 |   8013       if (this._isInvalid || !this._isRelative) | 
 |   8014         return; | 
 |   8015       parse.call(this, hostname, 'hostname'); | 
 |   8016     }, | 
 |   8017  | 
 |   8018     get port() { | 
 |   8019       return this._port; | 
 |   8020     }, | 
 |   8021     set port(port) { | 
 |   8022       if (this._isInvalid || !this._isRelative) | 
 |   8023         return; | 
 |   8024       parse.call(this, port, 'port'); | 
 |   8025     }, | 
 |   8026  | 
 |   8027     get pathname() { | 
 |   8028       return this._isInvalid ? '' : this._isRelative ? | 
 |   8029           '/' + this._path.join('/') : this._schemeData; | 
 |   8030     }, | 
 |   8031     set pathname(pathname) { | 
 |   8032       if (this._isInvalid || !this._isRelative) | 
 |   8033         return; | 
 |   8034       this._path = []; | 
 |   8035       parse.call(this, pathname, 'relative path start'); | 
 |   8036     }, | 
 |   8037  | 
 |   8038     get search() { | 
 |   8039       return this._isInvalid || !this._query || '?' == this._query ? | 
 |   8040           '' : this._query; | 
 |   8041     }, | 
 |   8042     set search(search) { | 
 |   8043       if (this._isInvalid || !this._isRelative) | 
 |   8044         return; | 
 |   8045       this._query = '?'; | 
 |   8046       if ('?' == search[0]) | 
 |   8047         search = search.slice(1); | 
 |   8048       parse.call(this, search, 'query'); | 
 |   8049     }, | 
 |   8050  | 
 |   8051     get hash() { | 
 |   8052       return this._isInvalid || !this._fragment || '#' == this._fragment ? | 
 |   8053           '' : this._fragment; | 
 |   8054     }, | 
 |   8055     set hash(hash) { | 
 |   8056       if (this._isInvalid) | 
 |   8057         return; | 
 |   8058       this._fragment = '#'; | 
 |   8059       if ('#' == hash[0]) | 
 |   8060         hash = hash.slice(1); | 
 |   8061       parse.call(this, hash, 'fragment'); | 
 |   8062     }, | 
 |   8063  | 
 |   8064     get origin() { | 
 |   8065       var host; | 
 |   8066       if (this._isInvalid || !this._scheme) { | 
 |   8067         return ''; | 
 |   8068       } | 
 |   8069       // javascript: Gecko returns String(""), WebKit/Blink String("null") | 
 |   8070       // Gecko throws error for "data://" | 
 |   8071       // data: Gecko returns "", Blink returns "data://", WebKit returns "null" | 
 |   8072       // Gecko returns String("") for file: mailto: | 
 |   8073       // WebKit/Blink returns String("SCHEME://") for file: mailto: | 
 |   8074       switch (this._scheme) { | 
 |   8075         case 'data': | 
 |   8076         case 'file': | 
 |   8077         case 'javascript': | 
 |   8078         case 'mailto': | 
 |   8079           return 'null'; | 
 |   8080       } | 
 |   8081       host = this.host; | 
 |   8082       if (!host) { | 
 |   8083         return ''; | 
 |   8084       } | 
 |   8085       return this._scheme + '://' + host; | 
 |   8086     } | 
 |   8087   }; | 
 |   8088  | 
 |   8089   // Copy over the static methods | 
 |   8090   var OriginalURL = scope.URL; | 
 |   8091   if (OriginalURL) { | 
 |   8092     jURL.createObjectURL = function(blob) { | 
 |   8093       // IE extension allows a second optional options argument. | 
 |   8094       // http://msdn.microsoft.com/en-us/library/ie/hh772302(v=vs.85).aspx | 
 |   8095       return OriginalURL.createObjectURL.apply(OriginalURL, arguments); | 
 |   8096     }; | 
 |   8097     jURL.revokeObjectURL = function(url) { | 
 |   8098       OriginalURL.revokeObjectURL(url); | 
 |   8099     }; | 
 |   8100   } | 
 |   8101  | 
 |   8102   scope.URL = jURL; | 
 |   8103  | 
 |   8104 })(this); | 
 |   8105  | 
 |   8106 (function(scope) { | 
 |   8107  | 
 |   8108 var iterations = 0; | 
 |   8109 var callbacks = []; | 
 |   8110 var twiddle = document.createTextNode(''); | 
 |   8111  | 
 |   8112 function endOfMicrotask(callback) { | 
 |   8113   twiddle.textContent = iterations++; | 
 |   8114   callbacks.push(callback); | 
 |   8115 } | 
 |   8116  | 
 |   8117 function atEndOfMicrotask() { | 
 |   8118   while (callbacks.length) { | 
 |   8119     callbacks.shift()(); | 
 |   8120   } | 
 |   8121 } | 
 |   8122  | 
 |   8123 new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask) | 
 |   8124   .observe(twiddle, {characterData: true}) | 
 |   8125   ; | 
 |   8126  | 
 |   8127 // exports | 
 |   8128 scope.endOfMicrotask = endOfMicrotask; | 
 |   8129 // bc  | 
 |   8130 Platform.endOfMicrotask = endOfMicrotask; | 
 |   8131  | 
 |   8132 })(Polymer); | 
 |   8133  | 
 |   8134  | 
 |   8135 (function(scope) { | 
 |   8136  | 
 |   8137 /** | 
 |   8138  * @class Polymer | 
 |   8139  */ | 
 |   8140  | 
 |   8141 // imports | 
 |   8142 var endOfMicrotask = scope.endOfMicrotask; | 
 |   8143  | 
 |   8144 // logging | 
 |   8145 var log = window.WebComponents ? WebComponents.flags.log : {}; | 
 |   8146  | 
 |   8147 // inject style sheet | 
 |   8148 var style = document.createElement('style'); | 
 |   8149 style.textContent = 'template {display: none !important;} /* injected by platfor
       m.js */'; | 
 |   8150 var head = document.querySelector('head'); | 
 |   8151 head.insertBefore(style, head.firstChild); | 
 |   8152  | 
 |   8153  | 
 |   8154 /** | 
 |   8155  * Force any pending data changes to be observed before  | 
 |   8156  * the next task. Data changes are processed asynchronously but are guaranteed | 
 |   8157  * to be processed, for example, before painting. This method should rarely be  | 
 |   8158  * needed. It does nothing when Object.observe is available;  | 
 |   8159  * when Object.observe is not available, Polymer automatically flushes data  | 
 |   8160  * changes approximately every 1/10 second.  | 
 |   8161  * Therefore, `flush` should only be used when a data mutation should be  | 
 |   8162  * observed sooner than this. | 
 |   8163  *  | 
 |   8164  * @method flush | 
 |   8165  */ | 
 |   8166 // flush (with logging) | 
 |   8167 var flushing; | 
 |   8168 function flush() { | 
 |   8169   if (!flushing) { | 
 |   8170     flushing = true; | 
 |   8171     endOfMicrotask(function() { | 
 |   8172       flushing = false; | 
 |   8173       log.data && console.group('flush'); | 
 |   8174       Platform.performMicrotaskCheckpoint(); | 
 |   8175       log.data && console.groupEnd(); | 
 |   8176     }); | 
 |   8177   } | 
 |   8178 }; | 
 |   8179  | 
 |   8180 // polling dirty checker | 
 |   8181 // flush periodically if platform does not have object observe. | 
 |   8182 if (!Observer.hasObjectObserve) { | 
 |   8183   var FLUSH_POLL_INTERVAL = 125; | 
 |   8184   window.addEventListener('WebComponentsReady', function() { | 
 |   8185     flush(); | 
 |   8186     // watch document visiblity to toggle dirty-checking | 
 |   8187     var visibilityHandler = function() { | 
 |   8188       // only flush if the page is visibile | 
 |   8189       if (document.visibilityState === 'hidden') { | 
 |   8190         if (scope.flushPoll) { | 
 |   8191           clearInterval(scope.flushPoll); | 
 |   8192         } | 
 |   8193       } else { | 
 |   8194         scope.flushPoll = setInterval(flush, FLUSH_POLL_INTERVAL); | 
 |   8195       } | 
 |   8196     }; | 
 |   8197     if (typeof document.visibilityState === 'string') { | 
 |   8198       document.addEventListener('visibilitychange', visibilityHandler); | 
 |   8199     } | 
 |   8200     visibilityHandler(); | 
 |   8201   }); | 
 |   8202 } else { | 
 |   8203   // make flush a no-op when we have Object.observe | 
 |   8204   flush = function() {}; | 
 |   8205 } | 
 |   8206  | 
 |   8207 if (window.CustomElements && !CustomElements.useNative) { | 
 |   8208   var originalImportNode = Document.prototype.importNode; | 
 |   8209   Document.prototype.importNode = function(node, deep) { | 
 |   8210     var imported = originalImportNode.call(this, node, deep); | 
 |   8211     CustomElements.upgradeAll(imported); | 
 |   8212     return imported; | 
 |   8213   }; | 
 |   8214 } | 
 |   8215  | 
 |   8216 // exports | 
 |   8217 scope.flush = flush; | 
 |   8218 // bc | 
 |   8219 Platform.flush = flush; | 
 |   8220  | 
 |   8221 })(window.Polymer); | 
 |   8222  | 
 |   8223  | 
 |   8224 (function(scope) { | 
 |   8225  | 
 |   8226 var urlResolver = { | 
 |   8227   resolveDom: function(root, url) { | 
 |   8228     url = url || baseUrl(root); | 
 |   8229     this.resolveAttributes(root, url); | 
 |   8230     this.resolveStyles(root, url); | 
 |   8231     // handle template.content | 
 |   8232     var templates = root.querySelectorAll('template'); | 
 |   8233     if (templates) { | 
 |   8234       for (var i = 0, l = templates.length, t; (i < l) && (t = templates[i]); i+
       +) { | 
 |   8235         if (t.content) { | 
 |   8236           this.resolveDom(t.content, url); | 
 |   8237         } | 
 |   8238       } | 
 |   8239     } | 
 |   8240   }, | 
 |   8241   resolveTemplate: function(template) { | 
 |   8242     this.resolveDom(template.content, baseUrl(template)); | 
 |   8243   }, | 
 |   8244   resolveStyles: function(root, url) { | 
 |   8245     var styles = root.querySelectorAll('style'); | 
 |   8246     if (styles) { | 
 |   8247       for (var i = 0, l = styles.length, s; (i < l) && (s = styles[i]); i++) { | 
 |   8248         this.resolveStyle(s, url); | 
 |   8249       } | 
 |   8250     } | 
 |   8251   }, | 
 |   8252   resolveStyle: function(style, url) { | 
 |   8253     url = url || baseUrl(style); | 
 |   8254     style.textContent = this.resolveCssText(style.textContent, url); | 
 |   8255   }, | 
 |   8256   resolveCssText: function(cssText, baseUrl, keepAbsolute) { | 
 |   8257     cssText = replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_URL_REGEX
       P); | 
 |   8258     return replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_IMPORT_REGEX
       P); | 
 |   8259   }, | 
 |   8260   resolveAttributes: function(root, url) { | 
 |   8261     if (root.hasAttributes && root.hasAttributes()) { | 
 |   8262       this.resolveElementAttributes(root, url); | 
 |   8263     } | 
 |   8264     // search for attributes that host urls | 
 |   8265     var nodes = root && root.querySelectorAll(URL_ATTRS_SELECTOR); | 
 |   8266     if (nodes) { | 
 |   8267       for (var i = 0, l = nodes.length, n; (i < l) && (n = nodes[i]); i++) { | 
 |   8268         this.resolveElementAttributes(n, url); | 
 |   8269       } | 
 |   8270     } | 
 |   8271   }, | 
 |   8272   resolveElementAttributes: function(node, url) { | 
 |   8273     url = url || baseUrl(node); | 
 |   8274     URL_ATTRS.forEach(function(v) { | 
 |   8275       var attr = node.attributes[v]; | 
 |   8276       var value = attr && attr.value; | 
 |   8277       var replacement; | 
 |   8278       if (value && value.search(URL_TEMPLATE_SEARCH) < 0) { | 
 |   8279         if (v === 'style') { | 
 |   8280           replacement = replaceUrlsInCssText(value, url, false, CSS_URL_REGEXP); | 
 |   8281         } else { | 
 |   8282           replacement = resolveRelativeUrl(url, value); | 
 |   8283         } | 
 |   8284         attr.value = replacement; | 
 |   8285       } | 
 |   8286     }); | 
 |   8287   } | 
 |   8288 }; | 
 |   8289  | 
 |   8290 var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g; | 
 |   8291 var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g; | 
 |   8292 var URL_ATTRS = ['href', 'src', 'action', 'style', 'url']; | 
 |   8293 var URL_ATTRS_SELECTOR = '[' + URL_ATTRS.join('],[') + ']'; | 
 |   8294 var URL_TEMPLATE_SEARCH = '{{.*}}'; | 
 |   8295 var URL_HASH = '#'; | 
 |   8296  | 
 |   8297 function baseUrl(node) { | 
 |   8298   var u = new URL(node.ownerDocument.baseURI); | 
 |   8299   u.search = ''; | 
 |   8300   u.hash = ''; | 
 |   8301   return u; | 
 |   8302 } | 
 |   8303  | 
 |   8304 function replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, regexp) { | 
 |   8305   return cssText.replace(regexp, function(m, pre, url, post) { | 
 |   8306     var urlPath = url.replace(/["']/g, ''); | 
 |   8307     urlPath = resolveRelativeUrl(baseUrl, urlPath, keepAbsolute); | 
 |   8308     return pre + '\'' + urlPath + '\'' + post; | 
 |   8309   }); | 
 |   8310 } | 
 |   8311  | 
 |   8312 function resolveRelativeUrl(baseUrl, url, keepAbsolute) { | 
 |   8313   // do not resolve '/' absolute urls | 
 |   8314   if (url && url[0] === '/') { | 
 |   8315     return url; | 
 |   8316   } | 
 |   8317   var u = new URL(url, baseUrl); | 
 |   8318   return keepAbsolute ? u.href : makeDocumentRelPath(u.href); | 
 |   8319 } | 
 |   8320  | 
 |   8321 function makeDocumentRelPath(url) { | 
 |   8322   var root = baseUrl(document.documentElement); | 
 |   8323   var u = new URL(url, root); | 
 |   8324   if (u.host === root.host && u.port === root.port && | 
 |   8325       u.protocol === root.protocol) { | 
 |   8326     return makeRelPath(root, u); | 
 |   8327   } else { | 
 |   8328     return url; | 
 |   8329   } | 
 |   8330 } | 
 |   8331  | 
 |   8332 // make a relative path from source to target | 
 |   8333 function makeRelPath(sourceUrl, targetUrl) { | 
 |   8334   var source = sourceUrl.pathname; | 
 |   8335   var target = targetUrl.pathname; | 
 |   8336   var s = source.split('/'); | 
 |   8337   var t = target.split('/'); | 
 |   8338   while (s.length && s[0] === t[0]){ | 
 |   8339     s.shift(); | 
 |   8340     t.shift(); | 
 |   8341   } | 
 |   8342   for (var i = 0, l = s.length - 1; i < l; i++) { | 
 |   8343     t.unshift('..'); | 
 |   8344   } | 
 |   8345   // empty '#' is discarded but we need to preserve it. | 
 |   8346   var hash = (targetUrl.href.slice(-1) === URL_HASH) ? URL_HASH : targetUrl.hash
       ; | 
 |   8347   return t.join('/') + targetUrl.search + hash; | 
 |   8348 } | 
 |   8349  | 
 |   8350 // exports | 
 |   8351 scope.urlResolver = urlResolver; | 
 |   8352  | 
 |   8353 })(Polymer); | 
 |   8354  | 
 |   8355 (function(scope) { | 
 |   8356   var endOfMicrotask = Polymer.endOfMicrotask; | 
 |   8357  | 
 |   8358   // Generic url loader | 
 |   8359   function Loader(regex) { | 
 |   8360     this.cache = Object.create(null); | 
 |   8361     this.map = Object.create(null); | 
 |   8362     this.requests = 0; | 
 |   8363     this.regex = regex; | 
 |   8364   } | 
 |   8365   Loader.prototype = { | 
 |   8366  | 
 |   8367     // TODO(dfreedm): there may be a better factoring here | 
 |   8368     // extract absolute urls from the text (full of relative urls) | 
 |   8369     extractUrls: function(text, base) { | 
 |   8370       var matches = []; | 
 |   8371       var matched, u; | 
 |   8372       while ((matched = this.regex.exec(text))) { | 
 |   8373         u = new URL(matched[1], base); | 
 |   8374         matches.push({matched: matched[0], url: u.href}); | 
 |   8375       } | 
 |   8376       return matches; | 
 |   8377     }, | 
 |   8378     // take a text blob, a root url, and a callback and load all the urls found 
       within the text | 
 |   8379     // returns a map of absolute url to text | 
 |   8380     process: function(text, root, callback) { | 
 |   8381       var matches = this.extractUrls(text, root); | 
 |   8382  | 
 |   8383       // every call to process returns all the text this loader has ever receive
       d | 
 |   8384       var done = callback.bind(null, this.map); | 
 |   8385       this.fetch(matches, done); | 
 |   8386     }, | 
 |   8387     // build a mapping of url -> text from matches | 
 |   8388     fetch: function(matches, callback) { | 
 |   8389       var inflight = matches.length; | 
 |   8390  | 
 |   8391       // return early if there is no fetching to be done | 
 |   8392       if (!inflight) { | 
 |   8393         return callback(); | 
 |   8394       } | 
 |   8395  | 
 |   8396       // wait for all subrequests to return | 
 |   8397       var done = function() { | 
 |   8398         if (--inflight === 0) { | 
 |   8399           callback(); | 
 |   8400         } | 
 |   8401       }; | 
 |   8402  | 
 |   8403       // start fetching all subrequests | 
 |   8404       var m, req, url; | 
 |   8405       for (var i = 0; i < inflight; i++) { | 
 |   8406         m = matches[i]; | 
 |   8407         url = m.url; | 
 |   8408         req = this.cache[url]; | 
 |   8409         // if this url has already been requested, skip requesting it again | 
 |   8410         if (!req) { | 
 |   8411           req = this.xhr(url); | 
 |   8412           req.match = m; | 
 |   8413           this.cache[url] = req; | 
 |   8414         } | 
 |   8415         // wait for the request to process its subrequests | 
 |   8416         req.wait(done); | 
 |   8417       } | 
 |   8418     }, | 
 |   8419     handleXhr: function(request) { | 
 |   8420       var match = request.match; | 
 |   8421       var url = match.url; | 
 |   8422  | 
 |   8423       // handle errors with an empty string | 
 |   8424       var response = request.response || request.responseText || ''; | 
 |   8425       this.map[url] = response; | 
 |   8426       this.fetch(this.extractUrls(response, url), request.resolve); | 
 |   8427     }, | 
 |   8428     xhr: function(url) { | 
 |   8429       this.requests++; | 
 |   8430       var request = new XMLHttpRequest(); | 
 |   8431       request.open('GET', url, true); | 
 |   8432       request.send(); | 
 |   8433       request.onerror = request.onload = this.handleXhr.bind(this, request); | 
 |   8434  | 
 |   8435       // queue of tasks to run after XHR returns | 
 |   8436       request.pending = []; | 
 |   8437       request.resolve = function() { | 
 |   8438         var pending = request.pending; | 
 |   8439         for(var i = 0; i < pending.length; i++) { | 
 |   8440           pending[i](); | 
 |   8441         } | 
 |   8442         request.pending = null; | 
 |   8443       }; | 
 |   8444  | 
 |   8445       // if we have already resolved, pending is null, async call the callback | 
 |   8446       request.wait = function(fn) { | 
 |   8447         if (request.pending) { | 
 |   8448           request.pending.push(fn); | 
 |   8449         } else { | 
 |   8450           endOfMicrotask(fn); | 
 |   8451         } | 
 |   8452       }; | 
 |   8453  | 
 |   8454       return request; | 
 |   8455     } | 
 |   8456   }; | 
 |   8457  | 
 |   8458   scope.Loader = Loader; | 
 |   8459 })(Polymer); | 
 |   8460  | 
 |   8461 (function(scope) { | 
 |   8462  | 
 |   8463 var urlResolver = scope.urlResolver; | 
 |   8464 var Loader = scope.Loader; | 
 |   8465  | 
 |   8466 function StyleResolver() { | 
 |   8467   this.loader = new Loader(this.regex); | 
 |   8468 } | 
 |   8469 StyleResolver.prototype = { | 
 |   8470   regex: /@import\s+(?:url)?["'\(]*([^'"\)]*)['"\)]*;/g, | 
 |   8471   // Recursively replace @imports with the text at that url | 
 |   8472   resolve: function(text, url, callback) { | 
 |   8473     var done = function(map) { | 
 |   8474       callback(this.flatten(text, url, map)); | 
 |   8475     }.bind(this); | 
 |   8476     this.loader.process(text, url, done); | 
 |   8477   }, | 
 |   8478   // resolve the textContent of a style node | 
 |   8479   resolveNode: function(style, url, callback) { | 
 |   8480     var text = style.textContent; | 
 |   8481     var done = function(text) { | 
 |   8482       style.textContent = text; | 
 |   8483       callback(style); | 
 |   8484     }; | 
 |   8485     this.resolve(text, url, done); | 
 |   8486   }, | 
 |   8487   // flatten all the @imports to text | 
 |   8488   flatten: function(text, base, map) { | 
 |   8489     var matches = this.loader.extractUrls(text, base); | 
 |   8490     var match, url, intermediate; | 
 |   8491     for (var i = 0; i < matches.length; i++) { | 
 |   8492       match = matches[i]; | 
 |   8493       url = match.url; | 
 |   8494       // resolve any css text to be relative to the importer, keep absolute url | 
 |   8495       intermediate = urlResolver.resolveCssText(map[url], url, true); | 
 |   8496       // flatten intermediate @imports | 
 |   8497       intermediate = this.flatten(intermediate, base, map); | 
 |   8498       text = text.replace(match.matched, intermediate); | 
 |   8499     } | 
 |   8500     return text; | 
 |   8501   }, | 
 |   8502   loadStyles: function(styles, base, callback) { | 
 |   8503     var loaded=0, l = styles.length; | 
 |   8504     // called in the context of the style | 
 |   8505     function loadedStyle(style) { | 
 |   8506       loaded++; | 
 |   8507       if (loaded === l && callback) { | 
 |   8508         callback(); | 
 |   8509       } | 
 |   8510     } | 
 |   8511     for (var i=0, s; (i<l) && (s=styles[i]); i++) { | 
 |   8512       this.resolveNode(s, base, loadedStyle); | 
 |   8513     } | 
 |   8514   } | 
 |   8515 }; | 
 |   8516  | 
 |   8517 var styleResolver = new StyleResolver(); | 
 |   8518  | 
 |   8519 // exports | 
 |   8520 scope.styleResolver = styleResolver; | 
 |   8521  | 
 |   8522 })(Polymer); | 
 |   8523  | 
 |   8524 (function(scope) { | 
 |   8525  | 
 |   8526   // copy own properties from 'api' to 'prototype, with name hinting for 'super' | 
 |   8527   function extend(prototype, api) { | 
 |   8528     if (prototype && api) { | 
 |   8529       // use only own properties of 'api' | 
 |   8530       Object.getOwnPropertyNames(api).forEach(function(n) { | 
 |   8531         // acquire property descriptor | 
 |   8532         var pd = Object.getOwnPropertyDescriptor(api, n); | 
 |   8533         if (pd) { | 
 |   8534           // clone property via descriptor | 
 |   8535           Object.defineProperty(prototype, n, pd); | 
 |   8536           // cache name-of-method for 'super' engine | 
 |   8537           if (typeof pd.value == 'function') { | 
 |   8538             // hint the 'super' engine | 
 |   8539             pd.value.nom = n; | 
 |   8540           } | 
 |   8541         } | 
 |   8542       }); | 
 |   8543     } | 
 |   8544     return prototype; | 
 |   8545   } | 
 |   8546  | 
 |   8547  | 
 |   8548   // mixin | 
 |   8549  | 
 |   8550   // copy all properties from inProps (et al) to inObj | 
 |   8551   function mixin(inObj/*, inProps, inMoreProps, ...*/) { | 
 |   8552     var obj = inObj || {}; | 
 |   8553     for (var i = 1; i < arguments.length; i++) { | 
 |   8554       var p = arguments[i]; | 
 |   8555       try { | 
 |   8556         for (var n in p) { | 
 |   8557           copyProperty(n, p, obj); | 
 |   8558         } | 
 |   8559       } catch(x) { | 
 |   8560       } | 
 |   8561     } | 
 |   8562     return obj; | 
 |   8563   } | 
 |   8564  | 
 |   8565   // copy property inName from inSource object to inTarget object | 
 |   8566   function copyProperty(inName, inSource, inTarget) { | 
 |   8567     var pd = getPropertyDescriptor(inSource, inName); | 
 |   8568     Object.defineProperty(inTarget, inName, pd); | 
 |   8569   } | 
 |   8570  | 
 |   8571   // get property descriptor for inName on inObject, even if | 
 |   8572   // inName exists on some link in inObject's prototype chain | 
 |   8573   function getPropertyDescriptor(inObject, inName) { | 
 |   8574     if (inObject) { | 
 |   8575       var pd = Object.getOwnPropertyDescriptor(inObject, inName); | 
 |   8576       return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName
       ); | 
 |   8577     } | 
 |   8578   } | 
 |   8579  | 
 |   8580   // exports | 
 |   8581  | 
 |   8582   scope.extend = extend; | 
 |   8583   scope.mixin = mixin; | 
 |   8584  | 
 |   8585   // for bc | 
 |   8586   Platform.mixin = mixin; | 
 |   8587  | 
 |   8588 })(Polymer); | 
 |   8589  | 
 |   8590 (function(scope) { | 
 |   8591    | 
 |   8592   // usage | 
 |   8593    | 
 |   8594   // invoke cb.call(this) in 100ms, unless the job is re-registered, | 
 |   8595   // which resets the timer | 
 |   8596   //  | 
 |   8597   // this.myJob = this.job(this.myJob, cb, 100) | 
 |   8598   // | 
 |   8599   // returns a job handle which can be used to re-register a job | 
 |   8600  | 
 |   8601   var Job = function(inContext) { | 
 |   8602     this.context = inContext; | 
 |   8603     this.boundComplete = this.complete.bind(this) | 
 |   8604   }; | 
 |   8605   Job.prototype = { | 
 |   8606     go: function(callback, wait) { | 
 |   8607       this.callback = callback; | 
 |   8608       var h; | 
 |   8609       if (!wait) { | 
 |   8610         h = requestAnimationFrame(this.boundComplete); | 
 |   8611         this.handle = function() { | 
 |   8612           cancelAnimationFrame(h); | 
 |   8613         } | 
 |   8614       } else { | 
 |   8615         h = setTimeout(this.boundComplete, wait); | 
 |   8616         this.handle = function() { | 
 |   8617           clearTimeout(h); | 
 |   8618         } | 
 |   8619       } | 
 |   8620     }, | 
 |   8621     stop: function() { | 
 |   8622       if (this.handle) { | 
 |   8623         this.handle(); | 
 |   8624         this.handle = null; | 
 |   8625       } | 
 |   8626     }, | 
 |   8627     complete: function() { | 
 |   8628       if (this.handle) { | 
 |   8629         this.stop(); | 
 |   8630         this.callback.call(this.context); | 
 |   8631       } | 
 |   8632     } | 
 |   8633   }; | 
 |   8634    | 
 |   8635   function job(job, callback, wait) { | 
 |   8636     if (job) { | 
 |   8637       job.stop(); | 
 |   8638     } else { | 
 |   8639       job = new Job(this); | 
 |   8640     } | 
 |   8641     job.go(callback, wait); | 
 |   8642     return job; | 
 |   8643   } | 
 |   8644    | 
 |   8645   // exports  | 
 |   8646  | 
 |   8647   scope.job = job; | 
 |   8648    | 
 |   8649 })(Polymer); | 
 |   8650  | 
 |   8651 (function(scope) { | 
 |   8652  | 
 |   8653   // dom polyfill, additions, and utility methods | 
 |   8654  | 
 |   8655   var registry = {}; | 
 |   8656  | 
 |   8657   HTMLElement.register = function(tag, prototype) { | 
 |   8658     registry[tag] = prototype; | 
 |   8659   }; | 
 |   8660  | 
 |   8661   // get prototype mapped to node <tag> | 
 |   8662   HTMLElement.getPrototypeForTag = function(tag) { | 
 |   8663     var prototype = !tag ? HTMLElement.prototype : registry[tag]; | 
 |   8664     // TODO(sjmiles): creating <tag> is likely to have wasteful side-effects | 
 |   8665     return prototype || Object.getPrototypeOf(document.createElement(tag)); | 
 |   8666   }; | 
 |   8667  | 
 |   8668   // we have to flag propagation stoppage for the event dispatcher | 
 |   8669   var originalStopPropagation = Event.prototype.stopPropagation; | 
 |   8670   Event.prototype.stopPropagation = function() { | 
 |   8671     this.cancelBubble = true; | 
 |   8672     originalStopPropagation.apply(this, arguments); | 
 |   8673   }; | 
 |   8674    | 
 |   8675    | 
 |   8676   // polyfill DOMTokenList | 
 |   8677   // * add/remove: allow these methods to take multiple classNames | 
 |   8678   // * toggle: add a 2nd argument which forces the given state rather | 
 |   8679   //  than toggling. | 
 |   8680  | 
 |   8681   var add = DOMTokenList.prototype.add; | 
 |   8682   var remove = DOMTokenList.prototype.remove; | 
 |   8683   DOMTokenList.prototype.add = function() { | 
 |   8684     for (var i = 0; i < arguments.length; i++) { | 
 |   8685       add.call(this, arguments[i]); | 
 |   8686     } | 
 |   8687   }; | 
 |   8688   DOMTokenList.prototype.remove = function() { | 
 |   8689     for (var i = 0; i < arguments.length; i++) { | 
 |   8690       remove.call(this, arguments[i]); | 
 |   8691     } | 
 |   8692   }; | 
 |   8693   DOMTokenList.prototype.toggle = function(name, bool) { | 
 |   8694     if (arguments.length == 1) { | 
 |   8695       bool = !this.contains(name); | 
 |   8696     } | 
 |   8697     bool ? this.add(name) : this.remove(name); | 
 |   8698   }; | 
 |   8699   DOMTokenList.prototype.switch = function(oldName, newName) { | 
 |   8700     oldName && this.remove(oldName); | 
 |   8701     newName && this.add(newName); | 
 |   8702   }; | 
 |   8703  | 
 |   8704   // add array() to NodeList, NamedNodeMap, HTMLCollection | 
 |   8705  | 
 |   8706   var ArraySlice = function() { | 
 |   8707     return Array.prototype.slice.call(this); | 
 |   8708   }; | 
 |   8709  | 
 |   8710   var namedNodeMap = (window.NamedNodeMap || window.MozNamedAttrMap || {}); | 
 |   8711  | 
 |   8712   NodeList.prototype.array = ArraySlice; | 
 |   8713   namedNodeMap.prototype.array = ArraySlice; | 
 |   8714   HTMLCollection.prototype.array = ArraySlice; | 
 |   8715  | 
 |   8716   // utility | 
 |   8717  | 
 |   8718   function createDOM(inTagOrNode, inHTML, inAttrs) { | 
 |   8719     var dom = typeof inTagOrNode == 'string' ? | 
 |   8720         document.createElement(inTagOrNode) : inTagOrNode.cloneNode(true); | 
 |   8721     dom.innerHTML = inHTML; | 
 |   8722     if (inAttrs) { | 
 |   8723       for (var n in inAttrs) { | 
 |   8724         dom.setAttribute(n, inAttrs[n]); | 
 |   8725       } | 
 |   8726     } | 
 |   8727     return dom; | 
 |   8728   } | 
 |   8729  | 
 |   8730   // exports | 
 |   8731  | 
 |   8732   scope.createDOM = createDOM; | 
 |   8733  | 
 |   8734 })(Polymer); | 
 |   8735  | 
 |   8736 (function(scope) { | 
 |   8737     // super | 
 |   8738  | 
 |   8739     // `arrayOfArgs` is an optional array of args like one might pass | 
 |   8740     // to `Function.apply` | 
 |   8741  | 
 |   8742     // TODO(sjmiles): | 
 |   8743     //    $super must be installed on an instance or prototype chain | 
 |   8744     //    as `super`, and invoked via `this`, e.g. | 
 |   8745     //      `this.super();` | 
 |   8746  | 
 |   8747     //    will not work if function objects are not unique, for example, | 
 |   8748     //    when using mixins. | 
 |   8749     //    The memoization strategy assumes each function exists on only one  | 
 |   8750     //    prototype chain i.e. we use the function object for memoizing) | 
 |   8751     //    perhaps we can bookkeep on the prototype itself instead | 
 |   8752     function $super(arrayOfArgs) { | 
 |   8753       // since we are thunking a method call, performance is important here:  | 
 |   8754       // memoize all lookups, once memoized the fast path calls no other  | 
 |   8755       // functions | 
 |   8756       // | 
 |   8757       // find the caller (cannot be `strict` because of 'caller') | 
 |   8758       var caller = $super.caller; | 
 |   8759       // memoized 'name of method'  | 
 |   8760       var nom = caller.nom; | 
 |   8761       // memoized next implementation prototype | 
 |   8762       var _super = caller._super; | 
 |   8763       if (!_super) { | 
 |   8764         if (!nom) { | 
 |   8765           nom = caller.nom = nameInThis.call(this, caller); | 
 |   8766         } | 
 |   8767         if (!nom) { | 
 |   8768           console.warn('called super() on a method not installed declaratively (
       has no .nom property)'); | 
 |   8769         } | 
 |   8770         // super prototype is either cached or we have to find it | 
 |   8771         // by searching __proto__ (at the 'top') | 
 |   8772         // invariant: because we cache _super on fn below, we never reach  | 
 |   8773         // here from inside a series of calls to super(), so it's ok to  | 
 |   8774         // start searching from the prototype of 'this' (at the 'top') | 
 |   8775         // we must never memoize a null super for this reason | 
 |   8776         _super = memoizeSuper(caller, nom, getPrototypeOf(this)); | 
 |   8777       } | 
 |   8778       // our super function | 
 |   8779       var fn = _super[nom]; | 
 |   8780       if (fn) { | 
 |   8781         // memoize information so 'fn' can call 'super' | 
 |   8782         if (!fn._super) { | 
 |   8783           // must not memoize null, or we lose our invariant above | 
 |   8784           memoizeSuper(fn, nom, _super); | 
 |   8785         } | 
 |   8786         // invoke the inherited method | 
 |   8787         // if 'fn' is not function valued, this will throw | 
 |   8788         return fn.apply(this, arrayOfArgs || []); | 
 |   8789       } | 
 |   8790     } | 
 |   8791  | 
 |   8792     function nameInThis(value) { | 
 |   8793       var p = this.__proto__; | 
 |   8794       while (p && p !== HTMLElement.prototype) { | 
 |   8795         // TODO(sjmiles): getOwnPropertyNames is absurdly expensive | 
 |   8796         var n$ = Object.getOwnPropertyNames(p); | 
 |   8797         for (var i=0, l=n$.length, n; i<l && (n=n$[i]); i++) { | 
 |   8798           var d = Object.getOwnPropertyDescriptor(p, n); | 
 |   8799           if (typeof d.value === 'function' && d.value === value) { | 
 |   8800             return n; | 
 |   8801           } | 
 |   8802         } | 
 |   8803         p = p.__proto__; | 
 |   8804       } | 
 |   8805     } | 
 |   8806  | 
 |   8807     function memoizeSuper(method, name, proto) { | 
 |   8808       // find and cache next prototype containing `name` | 
 |   8809       // we need the prototype so we can do another lookup | 
 |   8810       // from here | 
 |   8811       var s = nextSuper(proto, name, method); | 
 |   8812       if (s[name]) { | 
 |   8813         // `s` is a prototype, the actual method is `s[name]` | 
 |   8814         // tag super method with it's name for quicker lookups | 
 |   8815         s[name].nom = name; | 
 |   8816       } | 
 |   8817       return method._super = s; | 
 |   8818     } | 
 |   8819  | 
 |   8820     function nextSuper(proto, name, caller) { | 
 |   8821       // look for an inherited prototype that implements name | 
 |   8822       while (proto) { | 
 |   8823         if ((proto[name] !== caller) && proto[name]) { | 
 |   8824           return proto; | 
 |   8825         } | 
 |   8826         proto = getPrototypeOf(proto); | 
 |   8827       } | 
 |   8828       // must not return null, or we lose our invariant above | 
 |   8829       // in this case, a super() call was invoked where no superclass | 
 |   8830       // method exists | 
 |   8831       // TODO(sjmiles): thow an exception? | 
 |   8832       return Object; | 
 |   8833     } | 
 |   8834  | 
 |   8835     // NOTE: In some platforms (IE10) the prototype chain is faked via  | 
 |   8836     // __proto__. Therefore, always get prototype via __proto__ instead of | 
 |   8837     // the more standard Object.getPrototypeOf. | 
 |   8838     function getPrototypeOf(prototype) { | 
 |   8839       return prototype.__proto__; | 
 |   8840     } | 
 |   8841  | 
 |   8842     // utility function to precompute name tags for functions | 
 |   8843     // in a (unchained) prototype | 
 |   8844     function hintSuper(prototype) { | 
 |   8845       // tag functions with their prototype name to optimize | 
 |   8846       // super call invocations | 
 |   8847       for (var n in prototype) { | 
 |   8848         var pd = Object.getOwnPropertyDescriptor(prototype, n); | 
 |   8849         if (pd && typeof pd.value === 'function') { | 
 |   8850           pd.value.nom = n; | 
 |   8851         } | 
 |   8852       } | 
 |   8853     } | 
 |   8854  | 
 |   8855     // exports | 
 |   8856  | 
 |   8857     scope.super = $super; | 
 |   8858  | 
 |   8859 })(Polymer); | 
 |   8860  | 
 |   8861 (function(scope) { | 
 |   8862  | 
 |   8863   function noopHandler(value) { | 
 |   8864     return value; | 
 |   8865   } | 
 |   8866  | 
 |   8867   // helper for deserializing properties of various types to strings | 
 |   8868   var typeHandlers = { | 
 |   8869     string: noopHandler, | 
 |   8870     'undefined': noopHandler, | 
 |   8871     date: function(value) { | 
 |   8872       return new Date(Date.parse(value) || Date.now()); | 
 |   8873     }, | 
 |   8874     boolean: function(value) { | 
 |   8875       if (value === '') { | 
 |   8876         return true; | 
 |   8877       } | 
 |   8878       return value === 'false' ? false : !!value; | 
 |   8879     }, | 
 |   8880     number: function(value) { | 
 |   8881       var n = parseFloat(value); | 
 |   8882       // hex values like "0xFFFF" parseFloat as 0 | 
 |   8883       if (n === 0) { | 
 |   8884         n = parseInt(value); | 
 |   8885       } | 
 |   8886       return isNaN(n) ? value : n; | 
 |   8887       // this code disabled because encoded values (like "0xFFFF") | 
 |   8888       // do not round trip to their original format | 
 |   8889       //return (String(floatVal) === value) ? floatVal : value; | 
 |   8890     }, | 
 |   8891     object: function(value, currentValue) { | 
 |   8892       if (currentValue === null) { | 
 |   8893         return value; | 
 |   8894       } | 
 |   8895       try { | 
 |   8896         // If the string is an object, we can parse is with the JSON library. | 
 |   8897         // include convenience replace for single-quotes. If the author omits | 
 |   8898         // quotes altogether, parse will fail. | 
 |   8899         return JSON.parse(value.replace(/'/g, '"')); | 
 |   8900       } catch(e) { | 
 |   8901         // The object isn't valid JSON, return the raw value | 
 |   8902         return value; | 
 |   8903       } | 
 |   8904     }, | 
 |   8905     // avoid deserialization of functions | 
 |   8906     'function': function(value, currentValue) { | 
 |   8907       return currentValue; | 
 |   8908     } | 
 |   8909   }; | 
 |   8910  | 
 |   8911   function deserializeValue(value, currentValue) { | 
 |   8912     // attempt to infer type from default value | 
 |   8913     var inferredType = typeof currentValue; | 
 |   8914     // invent 'date' type value for Date | 
 |   8915     if (currentValue instanceof Date) { | 
 |   8916       inferredType = 'date'; | 
 |   8917     } | 
 |   8918     // delegate deserialization via type string | 
 |   8919     return typeHandlers[inferredType](value, currentValue); | 
 |   8920   } | 
 |   8921  | 
 |   8922   // exports | 
 |   8923  | 
 |   8924   scope.deserializeValue = deserializeValue; | 
 |   8925  | 
 |   8926 })(Polymer); | 
 |   8927  | 
 |   8928 (function(scope) { | 
 |   8929  | 
 |   8930   // imports | 
 |   8931  | 
 |   8932   var extend = scope.extend; | 
 |   8933  | 
 |   8934   // module | 
 |   8935  | 
 |   8936   var api = {}; | 
 |   8937  | 
 |   8938   api.declaration = {}; | 
 |   8939   api.instance = {}; | 
 |   8940  | 
 |   8941   api.publish = function(apis, prototype) { | 
 |   8942     for (var n in apis) { | 
 |   8943       extend(prototype, apis[n]); | 
 |   8944     } | 
 |   8945   }; | 
 |   8946  | 
 |   8947   // exports | 
 |   8948  | 
 |   8949   scope.api = api; | 
 |   8950  | 
 |   8951 })(Polymer); | 
 |   8952  | 
 |   8953 (function(scope) { | 
 |   8954  | 
 |   8955   /** | 
 |   8956    * @class polymer-base | 
 |   8957    */ | 
 |   8958  | 
 |   8959   var utils = { | 
 |   8960  | 
 |   8961     /** | 
 |   8962       * Invokes a function asynchronously. The context of the callback | 
 |   8963       * function is bound to 'this' automatically. Returns a handle which may  | 
 |   8964       * be passed to <a href="#cancelAsync">cancelAsync</a> to cancel the  | 
 |   8965       * asynchronous call. | 
 |   8966       * | 
 |   8967       * @method async | 
 |   8968       * @param {Function|String} method | 
 |   8969       * @param {any|Array} args | 
 |   8970       * @param {number} timeout | 
 |   8971       */ | 
 |   8972     async: function(method, args, timeout) { | 
 |   8973       // when polyfilling Object.observe, ensure changes  | 
 |   8974       // propagate before executing the async method | 
 |   8975       Polymer.flush(); | 
 |   8976       // second argument to `apply` must be an array | 
 |   8977       args = (args && args.length) ? args : [args]; | 
 |   8978       // function to invoke | 
 |   8979       var fn = function() { | 
 |   8980         (this[method] || method).apply(this, args); | 
 |   8981       }.bind(this); | 
 |   8982       // execute `fn` sooner or later | 
 |   8983       var handle = timeout ? setTimeout(fn, timeout) : | 
 |   8984           requestAnimationFrame(fn); | 
 |   8985       // NOTE: switch on inverting handle to determine which time is used. | 
 |   8986       return timeout ? handle : ~handle; | 
 |   8987     }, | 
 |   8988  | 
 |   8989     /** | 
 |   8990       * Cancels a pending callback that was scheduled via  | 
 |   8991       * <a href="#async">async</a>.  | 
 |   8992       * | 
 |   8993       * @method cancelAsync | 
 |   8994       * @param {handle} handle Handle of the `async` to cancel. | 
 |   8995       */ | 
 |   8996     cancelAsync: function(handle) { | 
 |   8997       if (handle < 0) { | 
 |   8998         cancelAnimationFrame(~handle); | 
 |   8999       } else { | 
 |   9000         clearTimeout(handle); | 
 |   9001       } | 
 |   9002     }, | 
 |   9003  | 
 |   9004     /** | 
 |   9005       * Fire an event. | 
 |   9006       * | 
 |   9007       * @method fire | 
 |   9008       * @returns {Object} event | 
 |   9009       * @param {string} type An event name. | 
 |   9010       * @param {any} detail | 
 |   9011       * @param {Node} onNode Target node. | 
 |   9012       * @param {Boolean} bubbles Set false to prevent bubbling, defaults to true | 
 |   9013       * @param {Boolean} cancelable Set false to prevent cancellation, defaults 
       to true | 
 |   9014       */ | 
 |   9015     fire: function(type, detail, onNode, bubbles, cancelable) { | 
 |   9016       var node = onNode || this; | 
 |   9017       var detail = detail === null || detail === undefined ? {} : detail; | 
 |   9018       var event = new CustomEvent(type, { | 
 |   9019         bubbles: bubbles !== undefined ? bubbles : true, | 
 |   9020         cancelable: cancelable !== undefined ? cancelable : true, | 
 |   9021         detail: detail | 
 |   9022       }); | 
 |   9023       node.dispatchEvent(event); | 
 |   9024       return event; | 
 |   9025     }, | 
 |   9026  | 
 |   9027     /** | 
 |   9028       * Fire an event asynchronously. | 
 |   9029       * | 
 |   9030       * @method asyncFire | 
 |   9031       * @param {string} type An event name. | 
 |   9032       * @param detail | 
 |   9033       * @param {Node} toNode Target node. | 
 |   9034       */ | 
 |   9035     asyncFire: function(/*inType, inDetail*/) { | 
 |   9036       this.async("fire", arguments); | 
 |   9037     }, | 
 |   9038  | 
 |   9039     /** | 
 |   9040       * Remove class from old, add class to anew, if they exist. | 
 |   9041       * | 
 |   9042       * @param classFollows | 
 |   9043       * @param anew A node. | 
 |   9044       * @param old A node | 
 |   9045       * @param className | 
 |   9046       */ | 
 |   9047     classFollows: function(anew, old, className) { | 
 |   9048       if (old) { | 
 |   9049         old.classList.remove(className); | 
 |   9050       } | 
 |   9051       if (anew) { | 
 |   9052         anew.classList.add(className); | 
 |   9053       } | 
 |   9054     }, | 
 |   9055  | 
 |   9056     /** | 
 |   9057       * Inject HTML which contains markup bound to this element into | 
 |   9058       * a target element (replacing target element content). | 
 |   9059       * | 
 |   9060       * @param String html to inject | 
 |   9061       * @param Element target element | 
 |   9062       */ | 
 |   9063     injectBoundHTML: function(html, element) { | 
 |   9064       var template = document.createElement('template'); | 
 |   9065       template.innerHTML = html; | 
 |   9066       var fragment = this.instanceTemplate(template); | 
 |   9067       if (element) { | 
 |   9068         element.textContent = ''; | 
 |   9069         element.appendChild(fragment); | 
 |   9070       } | 
 |   9071       return fragment; | 
 |   9072     } | 
 |   9073   }; | 
 |   9074  | 
 |   9075   // no-operation function for handy stubs | 
 |   9076   var nop = function() {}; | 
 |   9077  | 
 |   9078   // null-object for handy stubs | 
 |   9079   var nob = {}; | 
 |   9080  | 
 |   9081   // deprecated | 
 |   9082  | 
 |   9083   utils.asyncMethod = utils.async; | 
 |   9084  | 
 |   9085   // exports | 
 |   9086  | 
 |   9087   scope.api.instance.utils = utils; | 
 |   9088   scope.nop = nop; | 
 |   9089   scope.nob = nob; | 
 |   9090  | 
 |   9091 })(Polymer); | 
 |   9092  | 
 |   9093 (function(scope) { | 
 |   9094  | 
 |   9095   // imports | 
 |   9096  | 
 |   9097   var log = window.WebComponents ? WebComponents.flags.log : {}; | 
 |   9098   var EVENT_PREFIX = 'on-'; | 
 |   9099  | 
 |   9100   // instance events api | 
 |   9101   var events = { | 
 |   9102     // read-only | 
 |   9103     EVENT_PREFIX: EVENT_PREFIX, | 
 |   9104     // event listeners on host | 
 |   9105     addHostListeners: function() { | 
 |   9106       var events = this.eventDelegates; | 
 |   9107       log.events && (Object.keys(events).length > 0) && console.log('[%s] addHos
       tListeners:', this.localName, events); | 
 |   9108       // NOTE: host events look like bindings but really are not; | 
 |   9109       // (1) we don't want the attribute to be set and (2) we want to support | 
 |   9110       // multiple event listeners ('host' and 'instance') and Node.bind | 
 |   9111       // by default supports 1 thing being bound. | 
 |   9112       for (var type in events) { | 
 |   9113         var methodName = events[type]; | 
 |   9114         PolymerGestures.addEventListener(this, type, this.element.getEventHandle
       r(this, this, methodName)); | 
 |   9115       } | 
 |   9116     }, | 
 |   9117     // call 'method' or function method on 'obj' with 'args', if the method exis
       ts | 
 |   9118     dispatchMethod: function(obj, method, args) { | 
 |   9119       if (obj) { | 
 |   9120         log.events && console.group('[%s] dispatch [%s]', obj.localName, method)
       ; | 
 |   9121         var fn = typeof method === 'function' ? method : obj[method]; | 
 |   9122         if (fn) { | 
 |   9123           fn[args ? 'apply' : 'call'](obj, args); | 
 |   9124         } | 
 |   9125         log.events && console.groupEnd(); | 
 |   9126         // NOTE: dirty check right after calling method to ensure  | 
 |   9127         // changes apply quickly; in a very complicated app using high  | 
 |   9128         // frequency events, this can be a perf concern; in this case, | 
 |   9129         // imperative handlers can be used to avoid flushing. | 
 |   9130         Polymer.flush(); | 
 |   9131       } | 
 |   9132     } | 
 |   9133   }; | 
 |   9134  | 
 |   9135   // exports | 
 |   9136  | 
 |   9137   scope.api.instance.events = events; | 
 |   9138  | 
 |   9139   /** | 
 |   9140    * @class Polymer | 
 |   9141    */ | 
 |   9142  | 
 |   9143   /** | 
 |   9144    * Add a gesture aware event handler to the given `node`. Can be used  | 
 |   9145    * in place of `element.addEventListener` and ensures gestures will function | 
 |   9146    * as expected on mobile platforms. Please note that Polymer's declarative | 
 |   9147    * event handlers include this functionality by default. | 
 |   9148    *  | 
 |   9149    * @method addEventListener | 
 |   9150    * @param {Node} node node on which to listen | 
 |   9151    * @param {String} eventType name of the event | 
 |   9152    * @param {Function} handlerFn event handler function | 
 |   9153    * @param {Boolean} capture set to true to invoke event capturing | 
 |   9154    * @type Function | 
 |   9155    */ | 
 |   9156   // alias PolymerGestures event listener logic | 
 |   9157   scope.addEventListener = function(node, eventType, handlerFn, capture) { | 
 |   9158     PolymerGestures.addEventListener(wrap(node), eventType, handlerFn, capture); | 
 |   9159   }; | 
 |   9160  | 
 |   9161   /** | 
 |   9162    * Remove a gesture aware event handler on the given `node`. To remove an | 
 |   9163    * event listener, the exact same arguments are required that were passed | 
 |   9164    * to `Polymer.addEventListener`. | 
 |   9165    *  | 
 |   9166    * @method removeEventListener | 
 |   9167    * @param {Node} node node on which to listen | 
 |   9168    * @param {String} eventType name of the event | 
 |   9169    * @param {Function} handlerFn event handler function | 
 |   9170    * @param {Boolean} capture set to true to invoke event capturing | 
 |   9171    * @type Function | 
 |   9172    */ | 
 |   9173   scope.removeEventListener = function(node, eventType, handlerFn, capture) { | 
 |   9174     PolymerGestures.removeEventListener(wrap(node), eventType, handlerFn, captur
       e); | 
 |   9175   }; | 
 |   9176  | 
 |   9177 })(Polymer); | 
 |   9178  | 
 |   9179 (function(scope) { | 
 |   9180  | 
 |   9181   // instance api for attributes | 
 |   9182  | 
 |   9183   var attributes = { | 
 |   9184     // copy attributes defined in the element declaration to the instance | 
 |   9185     // e.g. <polymer-element name="x-foo" tabIndex="0"> tabIndex is copied | 
 |   9186     // to the element instance here. | 
 |   9187     copyInstanceAttributes: function () { | 
 |   9188       var a$ = this._instanceAttributes; | 
 |   9189       for (var k in a$) { | 
 |   9190         if (!this.hasAttribute(k)) { | 
 |   9191           this.setAttribute(k, a$[k]); | 
 |   9192         } | 
 |   9193       } | 
 |   9194     }, | 
 |   9195     // for each attribute on this, deserialize value to property as needed | 
 |   9196     takeAttributes: function() { | 
 |   9197       // if we have no publish lookup table, we have no attributes to take | 
 |   9198       // TODO(sjmiles): ad hoc | 
 |   9199       if (this._publishLC) { | 
 |   9200         for (var i=0, a$=this.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++)
        { | 
 |   9201           this.attributeToProperty(a.name, a.value); | 
 |   9202         } | 
 |   9203       } | 
 |   9204     }, | 
 |   9205     // if attribute 'name' is mapped to a property, deserialize | 
 |   9206     // 'value' into that property | 
 |   9207     attributeToProperty: function(name, value) { | 
 |   9208       // try to match this attribute to a property (attributes are | 
 |   9209       // all lower-case, so this is case-insensitive search) | 
 |   9210       var name = this.propertyForAttribute(name); | 
 |   9211       if (name) { | 
 |   9212         // filter out 'mustached' values, these are to be | 
 |   9213         // replaced with bound-data and are not yet values | 
 |   9214         // themselves | 
 |   9215         if (value && value.search(scope.bindPattern) >= 0) { | 
 |   9216           return; | 
 |   9217         } | 
 |   9218         // get original value | 
 |   9219         var currentValue = this[name]; | 
 |   9220         // deserialize Boolean or Number values from attribute | 
 |   9221         var value = this.deserializeValue(value, currentValue); | 
 |   9222         // only act if the value has changed | 
 |   9223         if (value !== currentValue) { | 
 |   9224           // install new value (has side-effects) | 
 |   9225           this[name] = value; | 
 |   9226         } | 
 |   9227       } | 
 |   9228     }, | 
 |   9229     // return the published property matching name, or undefined | 
 |   9230     propertyForAttribute: function(name) { | 
 |   9231       var match = this._publishLC && this._publishLC[name]; | 
 |   9232       return match; | 
 |   9233     }, | 
 |   9234     // convert representation of `stringValue` based on type of `currentValue` | 
 |   9235     deserializeValue: function(stringValue, currentValue) { | 
 |   9236       return scope.deserializeValue(stringValue, currentValue); | 
 |   9237     }, | 
 |   9238     // convert to a string value based on the type of `inferredType` | 
 |   9239     serializeValue: function(value, inferredType) { | 
 |   9240       if (inferredType === 'boolean') { | 
 |   9241         return value ? '' : undefined; | 
 |   9242       } else if (inferredType !== 'object' && inferredType !== 'function' | 
 |   9243           && value !== undefined) { | 
 |   9244         return value; | 
 |   9245       } | 
 |   9246     }, | 
 |   9247     // serializes `name` property value and updates the corresponding attribute | 
 |   9248     // note that reflection is opt-in. | 
 |   9249     reflectPropertyToAttribute: function(name) { | 
 |   9250       var inferredType = typeof this[name]; | 
 |   9251       // try to intelligently serialize property value | 
 |   9252       var serializedValue = this.serializeValue(this[name], inferredType); | 
 |   9253       // boolean properties must reflect as boolean attributes | 
 |   9254       if (serializedValue !== undefined) { | 
 |   9255         this.setAttribute(name, serializedValue); | 
 |   9256         // TODO(sorvell): we should remove attr for all properties | 
 |   9257         // that have undefined serialization; however, we will need to | 
 |   9258         // refine the attr reflection system to achieve this; pica, for example, | 
 |   9259         // relies on having inferredType object properties not removed as | 
 |   9260         // attrs. | 
 |   9261       } else if (inferredType === 'boolean') { | 
 |   9262         this.removeAttribute(name); | 
 |   9263       } | 
 |   9264     } | 
 |   9265   }; | 
 |   9266  | 
 |   9267   // exports | 
 |   9268  | 
 |   9269   scope.api.instance.attributes = attributes; | 
 |   9270  | 
 |   9271 })(Polymer); | 
 |   9272  | 
 |   9273 (function(scope) { | 
 |   9274  | 
 |   9275   /** | 
 |   9276    * @class polymer-base | 
 |   9277    */ | 
 |   9278  | 
 |   9279   // imports | 
 |   9280  | 
 |   9281   var log = window.WebComponents ? WebComponents.flags.log : {}; | 
 |   9282  | 
 |   9283   // magic words | 
 |   9284  | 
 |   9285   var OBSERVE_SUFFIX = 'Changed'; | 
 |   9286  | 
 |   9287   // element api | 
 |   9288  | 
 |   9289   var empty = []; | 
 |   9290  | 
 |   9291   var updateRecord = { | 
 |   9292     object: undefined, | 
 |   9293     type: 'update', | 
 |   9294     name: undefined, | 
 |   9295     oldValue: undefined | 
 |   9296   }; | 
 |   9297  | 
 |   9298   var numberIsNaN = Number.isNaN || function(value) { | 
 |   9299     return typeof value === 'number' && isNaN(value); | 
 |   9300   }; | 
 |   9301  | 
 |   9302   function areSameValue(left, right) { | 
 |   9303     if (left === right) | 
 |   9304       return left !== 0 || 1 / left === 1 / right; | 
 |   9305     if (numberIsNaN(left) && numberIsNaN(right)) | 
 |   9306       return true; | 
 |   9307     return left !== left && right !== right; | 
 |   9308   } | 
 |   9309  | 
 |   9310   // capture A's value if B's value is null or undefined, | 
 |   9311   // otherwise use B's value | 
 |   9312   function resolveBindingValue(oldValue, value) { | 
 |   9313     if (value === undefined && oldValue === null) { | 
 |   9314       return value; | 
 |   9315     } | 
 |   9316     return (value === null || value === undefined) ? oldValue : value; | 
 |   9317   } | 
 |   9318  | 
 |   9319   var properties = { | 
 |   9320  | 
 |   9321     // creates a CompoundObserver to observe property changes | 
 |   9322     // NOTE, this is only done there are any properties in the `observe` object | 
 |   9323     createPropertyObserver: function() { | 
 |   9324       var n$ = this._observeNames; | 
 |   9325       if (n$ && n$.length) { | 
 |   9326         var o = this._propertyObserver = new CompoundObserver(true); | 
 |   9327         this.registerObserver(o); | 
 |   9328         // TODO(sorvell): may not be kosher to access the value here (this[n]); | 
 |   9329         // previously we looked at the descriptor on the prototype | 
 |   9330         // this doesn't work for inheritance and not for accessors without | 
 |   9331         // a value property | 
 |   9332         for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) { | 
 |   9333           o.addPath(this, n); | 
 |   9334           this.observeArrayValue(n, this[n], null); | 
 |   9335         } | 
 |   9336       } | 
 |   9337     }, | 
 |   9338  | 
 |   9339     // start observing property changes | 
 |   9340     openPropertyObserver: function() { | 
 |   9341       if (this._propertyObserver) { | 
 |   9342         this._propertyObserver.open(this.notifyPropertyChanges, this); | 
 |   9343       } | 
 |   9344     }, | 
 |   9345  | 
 |   9346     // handler for property changes; routes changes to observing methods | 
 |   9347     // note: array valued properties are observed for array splices | 
 |   9348     notifyPropertyChanges: function(newValues, oldValues, paths) { | 
 |   9349       var name, method, called = {}; | 
 |   9350       for (var i in oldValues) { | 
 |   9351         // note: paths is of form [object, path, object, path] | 
 |   9352         name = paths[2 * i + 1]; | 
 |   9353         method = this.observe[name]; | 
 |   9354         if (method) { | 
 |   9355           var ov = oldValues[i], nv = newValues[i]; | 
 |   9356           // observes the value if it is an array | 
 |   9357           this.observeArrayValue(name, nv, ov); | 
 |   9358           if (!called[method]) { | 
 |   9359             // only invoke change method if one of ov or nv is not (undefined | 
       null) | 
 |   9360             if ((ov !== undefined && ov !== null) || (nv !== undefined && nv !==
        null)) { | 
 |   9361               called[method] = true; | 
 |   9362               // TODO(sorvell): call method with the set of values it's expectin
       g; | 
 |   9363               // e.g. 'foo bar': 'invalidate' expects the new and old values for | 
 |   9364               // foo and bar. Currently we give only one of these and then | 
 |   9365               // deliver all the arguments. | 
 |   9366               this.invokeMethod(method, [ov, nv, arguments]); | 
 |   9367             } | 
 |   9368           } | 
 |   9369         } | 
 |   9370       } | 
 |   9371     }, | 
 |   9372  | 
 |   9373     // call method iff it exists. | 
 |   9374     invokeMethod: function(method, args) { | 
 |   9375       var fn = this[method] || method; | 
 |   9376       if (typeof fn === 'function') { | 
 |   9377         fn.apply(this, args); | 
 |   9378       } | 
 |   9379     }, | 
 |   9380  | 
 |   9381     /** | 
 |   9382      * Force any pending property changes to synchronously deliver to | 
 |   9383      * handlers specified in the `observe` object. | 
 |   9384      * Note, normally changes are processed at microtask time. | 
 |   9385      * | 
 |   9386      * @method deliverChanges | 
 |   9387      */ | 
 |   9388     deliverChanges: function() { | 
 |   9389       if (this._propertyObserver) { | 
 |   9390         this._propertyObserver.deliver(); | 
 |   9391       } | 
 |   9392     }, | 
 |   9393  | 
 |   9394     observeArrayValue: function(name, value, old) { | 
 |   9395       // we only care if there are registered side-effects | 
 |   9396       var callbackName = this.observe[name]; | 
 |   9397       if (callbackName) { | 
 |   9398         // if we are observing the previous value, stop | 
 |   9399         if (Array.isArray(old)) { | 
 |   9400           log.observe && console.log('[%s] observeArrayValue: unregister observe
       r [%s]', this.localName, name); | 
 |   9401           this.closeNamedObserver(name + '__array'); | 
 |   9402         } | 
 |   9403         // if the new value is an array, being observing it | 
 |   9404         if (Array.isArray(value)) { | 
 |   9405           log.observe && console.log('[%s] observeArrayValue: register observer 
       [%s]', this.localName, name, value); | 
 |   9406           var observer = new ArrayObserver(value); | 
 |   9407           observer.open(function(splices) { | 
 |   9408             this.invokeMethod(callbackName, [splices]); | 
 |   9409           }, this); | 
 |   9410           this.registerNamedObserver(name + '__array', observer); | 
 |   9411         } | 
 |   9412       } | 
 |   9413     }, | 
 |   9414  | 
 |   9415     emitPropertyChangeRecord: function(name, value, oldValue) { | 
 |   9416       var object = this; | 
 |   9417       if (areSameValue(value, oldValue)) { | 
 |   9418         return; | 
 |   9419       } | 
 |   9420       // invoke property change side effects | 
 |   9421       this._propertyChanged(name, value, oldValue); | 
 |   9422       // emit change record | 
 |   9423       if (!Observer.hasObjectObserve) { | 
 |   9424         return; | 
 |   9425       } | 
 |   9426       var notifier = this._objectNotifier; | 
 |   9427       if (!notifier) { | 
 |   9428         notifier = this._objectNotifier = Object.getNotifier(this); | 
 |   9429       } | 
 |   9430       updateRecord.object = this; | 
 |   9431       updateRecord.name = name; | 
 |   9432       updateRecord.oldValue = oldValue; | 
 |   9433       notifier.notify(updateRecord); | 
 |   9434     }, | 
 |   9435  | 
 |   9436     _propertyChanged: function(name, value, oldValue) { | 
 |   9437       if (this.reflect[name]) { | 
 |   9438         this.reflectPropertyToAttribute(name); | 
 |   9439       } | 
 |   9440     }, | 
 |   9441  | 
 |   9442     // creates a property binding (called via bind) to a published property. | 
 |   9443     bindProperty: function(property, observable, oneTime) { | 
 |   9444       if (oneTime) { | 
 |   9445         this[property] = observable; | 
 |   9446         return; | 
 |   9447       } | 
 |   9448       var computed = this.element.prototype.computed; | 
 |   9449       // Binding an "out-only" value to a computed property. Note that | 
 |   9450       // since this observer isn't opened, it doesn't need to be closed on | 
 |   9451       // cleanup. | 
 |   9452       if (computed && computed[property]) { | 
 |   9453         var privateComputedBoundValue = property + 'ComputedBoundObservable_'; | 
 |   9454         this[privateComputedBoundValue] = observable; | 
 |   9455         return; | 
 |   9456       } | 
 |   9457       return this.bindToAccessor(property, observable, resolveBindingValue); | 
 |   9458     }, | 
 |   9459  | 
 |   9460     // NOTE property `name` must be published. This makes it an accessor. | 
 |   9461     bindToAccessor: function(name, observable, resolveFn) { | 
 |   9462       var privateName = name + '_'; | 
 |   9463       var privateObservable  = name + 'Observable_'; | 
 |   9464       // Present for properties which are computed and published and have a | 
 |   9465       // bound value. | 
 |   9466       var privateComputedBoundValue = name + 'ComputedBoundObservable_'; | 
 |   9467       this[privateObservable] = observable; | 
 |   9468       var oldValue = this[privateName]; | 
 |   9469       // observable callback | 
 |   9470       var self = this; | 
 |   9471       function updateValue(value, oldValue) { | 
 |   9472         self[privateName] = value; | 
 |   9473         var setObserveable = self[privateComputedBoundValue]; | 
 |   9474         if (setObserveable && typeof setObserveable.setValue == 'function') { | 
 |   9475           setObserveable.setValue(value); | 
 |   9476         } | 
 |   9477         self.emitPropertyChangeRecord(name, value, oldValue); | 
 |   9478       } | 
 |   9479       // resolve initial value | 
 |   9480       var value = observable.open(updateValue); | 
 |   9481       if (resolveFn && !areSameValue(oldValue, value)) { | 
 |   9482         var resolvedValue = resolveFn(oldValue, value); | 
 |   9483         if (!areSameValue(value, resolvedValue)) { | 
 |   9484           value = resolvedValue; | 
 |   9485           if (observable.setValue) { | 
 |   9486             observable.setValue(value); | 
 |   9487           } | 
 |   9488         } | 
 |   9489       } | 
 |   9490       updateValue(value, oldValue); | 
 |   9491       // register and return observable | 
 |   9492       var observer = { | 
 |   9493         close: function() { | 
 |   9494           observable.close(); | 
 |   9495           self[privateObservable] = undefined; | 
 |   9496           self[privateComputedBoundValue] = undefined; | 
 |   9497         } | 
 |   9498       }; | 
 |   9499       this.registerObserver(observer); | 
 |   9500       return observer; | 
 |   9501     }, | 
 |   9502  | 
 |   9503     createComputedProperties: function() { | 
 |   9504       if (!this._computedNames) { | 
 |   9505         return; | 
 |   9506       } | 
 |   9507       for (var i = 0; i < this._computedNames.length; i++) { | 
 |   9508         var name = this._computedNames[i]; | 
 |   9509         var expressionText = this.computed[name]; | 
 |   9510         try { | 
 |   9511           var expression = PolymerExpressions.getExpression(expressionText); | 
 |   9512           var observable = expression.getBinding(this, this.element.syntax); | 
 |   9513           this.bindToAccessor(name, observable); | 
 |   9514         } catch (ex) { | 
 |   9515           console.error('Failed to create computed property', ex); | 
 |   9516         } | 
 |   9517       } | 
 |   9518     }, | 
 |   9519  | 
 |   9520     // property bookkeeping | 
 |   9521     registerObserver: function(observer) { | 
 |   9522       if (!this._observers) { | 
 |   9523         this._observers = [observer]; | 
 |   9524         return; | 
 |   9525       } | 
 |   9526       this._observers.push(observer); | 
 |   9527     }, | 
 |   9528  | 
 |   9529     closeObservers: function() { | 
 |   9530       if (!this._observers) { | 
 |   9531         return; | 
 |   9532       } | 
 |   9533       // observer array items are arrays of observers. | 
 |   9534       var observers = this._observers; | 
 |   9535       for (var i = 0; i < observers.length; i++) { | 
 |   9536         var observer = observers[i]; | 
 |   9537         if (observer && typeof observer.close == 'function') { | 
 |   9538           observer.close(); | 
 |   9539         } | 
 |   9540       } | 
 |   9541       this._observers = []; | 
 |   9542     }, | 
 |   9543  | 
 |   9544     // bookkeeping observers for memory management | 
 |   9545     registerNamedObserver: function(name, observer) { | 
 |   9546       var o$ = this._namedObservers || (this._namedObservers = {}); | 
 |   9547       o$[name] = observer; | 
 |   9548     }, | 
 |   9549  | 
 |   9550     closeNamedObserver: function(name) { | 
 |   9551       var o$ = this._namedObservers; | 
 |   9552       if (o$ && o$[name]) { | 
 |   9553         o$[name].close(); | 
 |   9554         o$[name] = null; | 
 |   9555         return true; | 
 |   9556       } | 
 |   9557     }, | 
 |   9558  | 
 |   9559     closeNamedObservers: function() { | 
 |   9560       if (this._namedObservers) { | 
 |   9561         for (var i in this._namedObservers) { | 
 |   9562           this.closeNamedObserver(i); | 
 |   9563         } | 
 |   9564         this._namedObservers = {}; | 
 |   9565       } | 
 |   9566     } | 
 |   9567  | 
 |   9568   }; | 
 |   9569  | 
 |   9570   // logging | 
 |   9571   var LOG_OBSERVE = '[%s] watching [%s]'; | 
 |   9572   var LOG_OBSERVED = '[%s#%s] watch: [%s] now [%s] was [%s]'; | 
 |   9573   var LOG_CHANGED = '[%s#%s] propertyChanged: [%s] now [%s] was [%s]'; | 
 |   9574  | 
 |   9575   // exports | 
 |   9576  | 
 |   9577   scope.api.instance.properties = properties; | 
 |   9578  | 
 |   9579 })(Polymer); | 
 |   9580  | 
 |   9581 (function(scope) { | 
 |   9582  | 
 |   9583   /** | 
 |   9584    * @class polymer-base | 
 |   9585    */ | 
 |   9586  | 
 |   9587   // imports | 
 |   9588  | 
 |   9589   var log = window.WebComponents ? WebComponents.flags.log : {}; | 
 |   9590  | 
 |   9591   // element api supporting mdv | 
 |   9592   var mdv = { | 
 |   9593  | 
 |   9594     /** | 
 |   9595      * Creates dom cloned from the given template, instantiating bindings | 
 |   9596      * with this element as the template model and `PolymerExpressions` as the | 
 |   9597      * binding delegate. | 
 |   9598      * | 
 |   9599      * @method instanceTemplate | 
 |   9600      * @param {Template} template source template from which to create dom. | 
 |   9601      */ | 
 |   9602     instanceTemplate: function(template) { | 
 |   9603       // ensure template is decorated (lets' things like <tr template ...> work) | 
 |   9604       HTMLTemplateElement.decorate(template); | 
 |   9605       // ensure a default bindingDelegate | 
 |   9606       var syntax = this.syntax || (!template.bindingDelegate && | 
 |   9607           this.element.syntax); | 
 |   9608       var dom = template.createInstance(this, syntax); | 
 |   9609       var observers = dom.bindings_; | 
 |   9610       for (var i = 0; i < observers.length; i++) { | 
 |   9611         this.registerObserver(observers[i]); | 
 |   9612       } | 
 |   9613       return dom; | 
 |   9614     }, | 
 |   9615  | 
 |   9616     // Called by TemplateBinding/NodeBind to setup a binding to the given | 
 |   9617     // property. It's overridden here to support property bindings | 
 |   9618     // in addition to attribute bindings that are supported by default. | 
 |   9619     bind: function(name, observable, oneTime) { | 
 |   9620       var property = this.propertyForAttribute(name); | 
 |   9621       if (!property) { | 
 |   9622         // TODO(sjmiles): this mixin method must use the special form | 
 |   9623         // of `super` installed by `mixinMethod` in declaration/prototype.js | 
 |   9624         return this.mixinSuper(arguments); | 
 |   9625       } else { | 
 |   9626         // use n-way Polymer binding | 
 |   9627         var observer = this.bindProperty(property, observable, oneTime); | 
 |   9628         // NOTE: reflecting binding information is typically required only for | 
 |   9629         // tooling. It has a performance cost so it's opt-in in Node.bind. | 
 |   9630         if (Platform.enableBindingsReflection && observer) { | 
 |   9631           observer.path = observable.path_; | 
 |   9632           this._recordBinding(property, observer); | 
 |   9633         } | 
 |   9634         if (this.reflect[property]) { | 
 |   9635           this.reflectPropertyToAttribute(property); | 
 |   9636         } | 
 |   9637         return observer; | 
 |   9638       } | 
 |   9639     }, | 
 |   9640  | 
 |   9641     _recordBinding: function(name, observer) { | 
 |   9642       this.bindings_ = this.bindings_ || {}; | 
 |   9643       this.bindings_[name] = observer; | 
 |   9644     }, | 
 |   9645  | 
 |   9646     // Called by TemplateBinding when all bindings on an element have been  | 
 |   9647     // executed. This signals that all element inputs have been gathered | 
 |   9648     // and it's safe to ready the element, create shadow-root and start | 
 |   9649     // data-observation. | 
 |   9650     bindFinished: function() { | 
 |   9651       this.makeElementReady(); | 
 |   9652     }, | 
 |   9653  | 
 |   9654     // called at detached time to signal that an element's bindings should be | 
 |   9655     // cleaned up. This is done asynchronously so that users have the chance | 
 |   9656     // to call `cancelUnbindAll` to prevent unbinding. | 
 |   9657     asyncUnbindAll: function() { | 
 |   9658       if (!this._unbound) { | 
 |   9659         log.unbind && console.log('[%s] asyncUnbindAll', this.localName); | 
 |   9660         this._unbindAllJob = this.job(this._unbindAllJob, this.unbindAll, 0); | 
 |   9661       } | 
 |   9662     }, | 
 |   9663      | 
 |   9664     /** | 
 |   9665      * This method should rarely be used and only if  | 
 |   9666      * <a href="#cancelUnbindAll">`cancelUnbindAll`</a> has been called to  | 
 |   9667      * prevent element unbinding. In this case, the element's bindings will  | 
 |   9668      * not be automatically cleaned up and it cannot be garbage collected  | 
 |   9669      * by the system. If memory pressure is a concern or a  | 
 |   9670      * large amount of elements need to be managed in this way, `unbindAll` | 
 |   9671      * can be called to deactivate the element's bindings and allow its  | 
 |   9672      * memory to be reclaimed. | 
 |   9673      * | 
 |   9674      * @method unbindAll | 
 |   9675      */ | 
 |   9676     unbindAll: function() { | 
 |   9677       if (!this._unbound) { | 
 |   9678         this.closeObservers(); | 
 |   9679         this.closeNamedObservers(); | 
 |   9680         this._unbound = true; | 
 |   9681       } | 
 |   9682     }, | 
 |   9683  | 
 |   9684     /** | 
 |   9685      * Call in `detached` to prevent the element from unbinding when it is  | 
 |   9686      * detached from the dom. The element is unbound as a cleanup step that  | 
 |   9687      * allows its memory to be reclaimed.  | 
 |   9688      * If `cancelUnbindAll` is used, consider calling  | 
 |   9689      * <a href="#unbindAll">`unbindAll`</a> when the element is no longer | 
 |   9690      * needed. This will allow its memory to be reclaimed. | 
 |   9691      *  | 
 |   9692      * @method cancelUnbindAll | 
 |   9693      */ | 
 |   9694     cancelUnbindAll: function() { | 
 |   9695       if (this._unbound) { | 
 |   9696         log.unbind && console.warn('[%s] already unbound, cannot cancel unbindAl
       l', this.localName); | 
 |   9697         return; | 
 |   9698       } | 
 |   9699       log.unbind && console.log('[%s] cancelUnbindAll', this.localName); | 
 |   9700       if (this._unbindAllJob) { | 
 |   9701         this._unbindAllJob = this._unbindAllJob.stop(); | 
 |   9702       } | 
 |   9703     } | 
 |   9704  | 
 |   9705   }; | 
 |   9706  | 
 |   9707   function unbindNodeTree(node) { | 
 |   9708     forNodeTree(node, _nodeUnbindAll); | 
 |   9709   } | 
 |   9710  | 
 |   9711   function _nodeUnbindAll(node) { | 
 |   9712     node.unbindAll(); | 
 |   9713   } | 
 |   9714  | 
 |   9715   function forNodeTree(node, callback) { | 
 |   9716     if (node) { | 
 |   9717       callback(node); | 
 |   9718       for (var child = node.firstChild; child; child = child.nextSibling) { | 
 |   9719         forNodeTree(child, callback); | 
 |   9720       } | 
 |   9721     } | 
 |   9722   } | 
 |   9723  | 
 |   9724   var mustachePattern = /\{\{([^{}]*)}}/; | 
 |   9725  | 
 |   9726   // exports | 
 |   9727  | 
 |   9728   scope.bindPattern = mustachePattern; | 
 |   9729   scope.api.instance.mdv = mdv; | 
 |   9730  | 
 |   9731 })(Polymer); | 
 |   9732  | 
 |   9733 (function(scope) { | 
 |   9734  | 
 |   9735   /** | 
 |   9736    * Common prototype for all Polymer Elements. | 
 |   9737    *  | 
 |   9738    * @class polymer-base | 
 |   9739    * @homepage polymer.github.io | 
 |   9740    */ | 
 |   9741   var base = { | 
 |   9742     /** | 
 |   9743      * Tags this object as the canonical Base prototype. | 
 |   9744      * | 
 |   9745      * @property PolymerBase | 
 |   9746      * @type boolean | 
 |   9747      * @default true | 
 |   9748      */ | 
 |   9749     PolymerBase: true, | 
 |   9750  | 
 |   9751     /** | 
 |   9752      * Debounce signals.  | 
 |   9753      *  | 
 |   9754      * Call `job` to defer a named signal, and all subsequent matching signals,  | 
 |   9755      * until a wait time has elapsed with no new signal. | 
 |   9756      *  | 
 |   9757      *     debouncedClickAction: function(e) { | 
 |   9758      *       // processClick only when it's been 100ms since the last click | 
 |   9759      *       this.job('click', function() { | 
 |   9760      *        this.processClick; | 
 |   9761      *       }, 100); | 
 |   9762      *     } | 
 |   9763      * | 
 |   9764      * @method job | 
 |   9765      * @param String {String} job A string identifier for the job to debounce. | 
 |   9766      * @param Function {Function} callback A function that is called (with `this
       ` context) when the wait time elapses. | 
 |   9767      * @param Number {Number} wait Time in milliseconds (ms) after the last sign
       al that must elapse before invoking `callback` | 
 |   9768      * @type Handle | 
 |   9769      */ | 
 |   9770     job: function(job, callback, wait) { | 
 |   9771       if (typeof job === 'string') { | 
 |   9772         var n = '___' + job; | 
 |   9773         this[n] = Polymer.job.call(this, this[n], callback, wait); | 
 |   9774       } else { | 
 |   9775         // TODO(sjmiles): suggest we deprecate this call signature | 
 |   9776         return Polymer.job.call(this, job, callback, wait); | 
 |   9777       } | 
 |   9778     }, | 
 |   9779  | 
 |   9780     /** | 
 |   9781      * Invoke a superclass method.  | 
 |   9782      *  | 
 |   9783      * Use `super()` to invoke the most recently overridden call to the  | 
 |   9784      * currently executing function.  | 
 |   9785      *  | 
 |   9786      * To pass arguments through, use the literal `arguments` as the parameter  | 
 |   9787      * to `super()`. | 
 |   9788      * | 
 |   9789      *     nextPageAction: function(e) { | 
 |   9790      *       // invoke the superclass version of `nextPageAction` | 
 |   9791      *       this.super(arguments);  | 
 |   9792      *     } | 
 |   9793      * | 
 |   9794      * To pass custom arguments, arrange them in an array. | 
 |   9795      * | 
 |   9796      *     appendSerialNo: function(value, serial) { | 
 |   9797      *       // prefix the superclass serial number with our lot # before | 
 |   9798      *       // invoking the superlcass | 
 |   9799      *       return this.super([value, this.lotNo + serial]) | 
 |   9800      *     } | 
 |   9801      * | 
 |   9802      * @method super | 
 |   9803      * @type Any | 
 |   9804      * @param {args) An array of arguments to use when calling the superclass me
       thod, or null. | 
 |   9805      */ | 
 |   9806     super: Polymer.super, | 
 |   9807  | 
 |   9808     /** | 
 |   9809      * Lifecycle method called when the element is instantiated. | 
 |   9810      *  | 
 |   9811      * Override `created` to perform custom create-time tasks. No need to call  | 
 |   9812      * super-class `created` unless you are extending another Polymer element. | 
 |   9813      * Created is called before the element creates `shadowRoot` or prepares | 
 |   9814      * data-observation. | 
 |   9815      *  | 
 |   9816      * @method created | 
 |   9817      * @type void | 
 |   9818      */ | 
 |   9819     created: function() { | 
 |   9820     }, | 
 |   9821  | 
 |   9822     /** | 
 |   9823      * Lifecycle method called when the element has populated it's `shadowRoot`, | 
 |   9824      * prepared data-observation, and made itself ready for API interaction. | 
 |   9825      *  | 
 |   9826      * @method ready | 
 |   9827      * @type void | 
 |   9828      */ | 
 |   9829     ready: function() { | 
 |   9830     }, | 
 |   9831  | 
 |   9832     /** | 
 |   9833      * Low-level lifecycle method called as part of standard Custom Elements | 
 |   9834      * operation. Polymer implements this method to provide basic default  | 
 |   9835      * functionality. For custom create-time tasks, implement `created`  | 
 |   9836      * instead, which is called immediately after `createdCallback`.  | 
 |   9837      *  | 
 |   9838      * @method createdCallback | 
 |   9839      */ | 
 |   9840     createdCallback: function() { | 
 |   9841       if (this.templateInstance && this.templateInstance.model) { | 
 |   9842         console.warn('Attributes on ' + this.localName + ' were data bound ' + | 
 |   9843             'prior to Polymer upgrading the element. This may result in ' + | 
 |   9844             'incorrect binding types.'); | 
 |   9845       } | 
 |   9846       this.created(); | 
 |   9847       this.prepareElement(); | 
 |   9848       if (!this.ownerDocument.isStagingDocument) { | 
 |   9849         this.makeElementReady(); | 
 |   9850       } | 
 |   9851     }, | 
 |   9852  | 
 |   9853     // system entry point, do not override | 
 |   9854     prepareElement: function() { | 
 |   9855       if (this._elementPrepared) { | 
 |   9856         console.warn('Element already prepared', this.localName); | 
 |   9857         return; | 
 |   9858       } | 
 |   9859       this._elementPrepared = true; | 
 |   9860       // storage for shadowRoots info | 
 |   9861       this.shadowRoots = {}; | 
 |   9862       // install property observers | 
 |   9863       this.createPropertyObserver(); | 
 |   9864       this.openPropertyObserver(); | 
 |   9865       // install boilerplate attributes | 
 |   9866       this.copyInstanceAttributes(); | 
 |   9867       // process input attributes | 
 |   9868       this.takeAttributes(); | 
 |   9869       // add event listeners | 
 |   9870       this.addHostListeners(); | 
 |   9871     }, | 
 |   9872  | 
 |   9873     // system entry point, do not override | 
 |   9874     makeElementReady: function() { | 
 |   9875       if (this._readied) { | 
 |   9876         return; | 
 |   9877       } | 
 |   9878       this._readied = true; | 
 |   9879       this.createComputedProperties(); | 
 |   9880       this.parseDeclarations(this.__proto__); | 
 |   9881       // NOTE: Support use of the `unresolved` attribute to help polyfill | 
 |   9882       // custom elements' `:unresolved` feature. | 
 |   9883       this.removeAttribute('unresolved'); | 
 |   9884       // user entry point | 
 |   9885       this.ready(); | 
 |   9886     }, | 
 |   9887  | 
 |   9888     /** | 
 |   9889      * Low-level lifecycle method called as part of standard Custom Elements | 
 |   9890      * operation. Polymer implements this method to provide basic default  | 
 |   9891      * functionality. For custom tasks in your element, implement `attributeChan
       ged`  | 
 |   9892      * instead, which is called immediately after `attributeChangedCallback`.  | 
 |   9893      *  | 
 |   9894      * @method attributeChangedCallback | 
 |   9895      */ | 
 |   9896     attributeChangedCallback: function(name, oldValue) { | 
 |   9897       // TODO(sjmiles): adhoc filter | 
 |   9898       if (name !== 'class' && name !== 'style') { | 
 |   9899         this.attributeToProperty(name, this.getAttribute(name)); | 
 |   9900       } | 
 |   9901       if (this.attributeChanged) { | 
 |   9902         this.attributeChanged.apply(this, arguments); | 
 |   9903       } | 
 |   9904     }, | 
 |   9905  | 
 |   9906     /** | 
 |   9907      * Low-level lifecycle method called as part of standard Custom Elements | 
 |   9908      * operation. Polymer implements this method to provide basic default  | 
 |   9909      * functionality. For custom create-time tasks, implement `attached`  | 
 |   9910      * instead, which is called immediately after `attachedCallback`.  | 
 |   9911      *  | 
 |   9912      * @method attachedCallback | 
 |   9913      */ | 
 |   9914      attachedCallback: function() { | 
 |   9915       // when the element is attached, prevent it from unbinding. | 
 |   9916       this.cancelUnbindAll(); | 
 |   9917       // invoke user action | 
 |   9918       if (this.attached) { | 
 |   9919         this.attached(); | 
 |   9920       } | 
 |   9921       if (!this.hasBeenAttached) { | 
 |   9922         this.hasBeenAttached = true; | 
 |   9923         if (this.domReady) { | 
 |   9924           this.async('domReady'); | 
 |   9925         } | 
 |   9926       } | 
 |   9927     }, | 
 |   9928  | 
 |   9929      /** | 
 |   9930      * Implement to access custom elements in dom descendants, ancestors,  | 
 |   9931      * or siblings. Because custom elements upgrade in document order,  | 
 |   9932      * elements accessed in `ready` or `attached` may not be upgraded. When | 
 |   9933      * `domReady` is called, all registered custom elements are guaranteed | 
 |   9934      * to have been upgraded. | 
 |   9935      *  | 
 |   9936      * @method domReady | 
 |   9937      */ | 
 |   9938  | 
 |   9939     /** | 
 |   9940      * Low-level lifecycle method called as part of standard Custom Elements | 
 |   9941      * operation. Polymer implements this method to provide basic default  | 
 |   9942      * functionality. For custom create-time tasks, implement `detached`  | 
 |   9943      * instead, which is called immediately after `detachedCallback`.  | 
 |   9944      *  | 
 |   9945      * @method detachedCallback | 
 |   9946      */ | 
 |   9947     detachedCallback: function() { | 
 |   9948       if (!this.preventDispose) { | 
 |   9949         this.asyncUnbindAll(); | 
 |   9950       } | 
 |   9951       // invoke user action | 
 |   9952       if (this.detached) { | 
 |   9953         this.detached(); | 
 |   9954       } | 
 |   9955       // TODO(sorvell): bc | 
 |   9956       if (this.leftView) { | 
 |   9957         this.leftView(); | 
 |   9958       } | 
 |   9959     }, | 
 |   9960  | 
 |   9961     /** | 
 |   9962      * Walks the prototype-chain of this element and allows specific | 
 |   9963      * classes a chance to process static declarations. | 
 |   9964      *  | 
 |   9965      * In particular, each polymer-element has it's own `template`. | 
 |   9966      * `parseDeclarations` is used to accumulate all element `template`s | 
 |   9967      * from an inheritance chain. | 
 |   9968      * | 
 |   9969      * `parseDeclaration` static methods implemented in the chain are called | 
 |   9970      * recursively, oldest first, with the `<polymer-element>` associated | 
 |   9971      * with the current prototype passed as an argument. | 
 |   9972      *  | 
 |   9973      * An element may override this method to customize shadow-root generation.  | 
 |   9974      *  | 
 |   9975      * @method parseDeclarations | 
 |   9976      */ | 
 |   9977     parseDeclarations: function(p) { | 
 |   9978       if (p && p.element) { | 
 |   9979         this.parseDeclarations(p.__proto__); | 
 |   9980         p.parseDeclaration.call(this, p.element); | 
 |   9981       } | 
 |   9982     }, | 
 |   9983  | 
 |   9984     /** | 
 |   9985      * Perform init-time actions based on static information in the | 
 |   9986      * `<polymer-element>` instance argument. | 
 |   9987      * | 
 |   9988      * For example, the standard implementation locates the template associated | 
 |   9989      * with the given `<polymer-element>` and stamps it into a shadow-root to | 
 |   9990      * implement shadow inheritance. | 
 |   9991      *   | 
 |   9992      * An element may override this method for custom behavior.  | 
 |   9993      *  | 
 |   9994      * @method parseDeclaration | 
 |   9995      */ | 
 |   9996     parseDeclaration: function(elementElement) { | 
 |   9997       var template = this.fetchTemplate(elementElement); | 
 |   9998       if (template) { | 
 |   9999         var root = this.shadowFromTemplate(template); | 
 |  10000         this.shadowRoots[elementElement.name] = root; | 
 |  10001       } | 
 |  10002     }, | 
 |  10003  | 
 |  10004     /** | 
 |  10005      * Given a `<polymer-element>`, find an associated template (if any) to be | 
 |  10006      * used for shadow-root generation. | 
 |  10007      * | 
 |  10008      * An element may override this method for custom behavior.  | 
 |  10009      *  | 
 |  10010      * @method fetchTemplate | 
 |  10011      */ | 
 |  10012     fetchTemplate: function(elementElement) { | 
 |  10013       return elementElement.querySelector('template'); | 
 |  10014     }, | 
 |  10015  | 
 |  10016     /** | 
 |  10017      * Create a shadow-root in this host and stamp `template` as it's  | 
 |  10018      * content.  | 
 |  10019      * | 
 |  10020      * An element may override this method for custom behavior.  | 
 |  10021      *  | 
 |  10022      * @method shadowFromTemplate | 
 |  10023      */ | 
 |  10024     shadowFromTemplate: function(template) { | 
 |  10025       if (template) { | 
 |  10026         // make a shadow root | 
 |  10027         var root = this.createShadowRoot(); | 
 |  10028         // stamp template | 
 |  10029         // which includes parsing and applying MDV bindings before being | 
 |  10030         // inserted (to avoid {{}} in attribute values) | 
 |  10031         // e.g. to prevent <img src="images/{{icon}}"> from generating a 404. | 
 |  10032         var dom = this.instanceTemplate(template); | 
 |  10033         // append to shadow dom | 
 |  10034         root.appendChild(dom); | 
 |  10035         // perform post-construction initialization tasks on shadow root | 
 |  10036         this.shadowRootReady(root, template); | 
 |  10037         // return the created shadow root | 
 |  10038         return root; | 
 |  10039       } | 
 |  10040     }, | 
 |  10041  | 
 |  10042     // utility function that stamps a <template> into light-dom | 
 |  10043     lightFromTemplate: function(template, refNode) { | 
 |  10044       if (template) { | 
 |  10045         // TODO(sorvell): mark this element as an eventController so that | 
 |  10046         // event listeners on bound nodes inside it will be called on it. | 
 |  10047         // Note, the expectation here is that events on all descendants | 
 |  10048         // should be handled by this element. | 
 |  10049         this.eventController = this; | 
 |  10050         // stamp template | 
 |  10051         // which includes parsing and applying MDV bindings before being | 
 |  10052         // inserted (to avoid {{}} in attribute values) | 
 |  10053         // e.g. to prevent <img src="images/{{icon}}"> from generating a 404. | 
 |  10054         var dom = this.instanceTemplate(template); | 
 |  10055         // append to shadow dom | 
 |  10056         if (refNode) { | 
 |  10057           this.insertBefore(dom, refNode); | 
 |  10058         } else { | 
 |  10059           this.appendChild(dom); | 
 |  10060         } | 
 |  10061         // perform post-construction initialization tasks on ahem, light root | 
 |  10062         this.shadowRootReady(this); | 
 |  10063         // return the created shadow root | 
 |  10064         return dom; | 
 |  10065       } | 
 |  10066     }, | 
 |  10067  | 
 |  10068     shadowRootReady: function(root) { | 
 |  10069       // locate nodes with id and store references to them in this.$ hash | 
 |  10070       this.marshalNodeReferences(root); | 
 |  10071     }, | 
 |  10072  | 
 |  10073     // locate nodes with id and store references to them in this.$ hash | 
 |  10074     marshalNodeReferences: function(root) { | 
 |  10075       // establish $ instance variable | 
 |  10076       var $ = this.$ = this.$ || {}; | 
 |  10077       // populate $ from nodes with ID from the LOCAL tree | 
 |  10078       if (root) { | 
 |  10079         var n$ = root.querySelectorAll("[id]"); | 
 |  10080         for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) { | 
 |  10081           $[n.id] = n; | 
 |  10082         }; | 
 |  10083       } | 
 |  10084     }, | 
 |  10085  | 
 |  10086     /** | 
 |  10087      * Register a one-time callback when a child-list or sub-tree mutation | 
 |  10088      * occurs on node.  | 
 |  10089      * | 
 |  10090      * For persistent callbacks, call onMutation from your listener.  | 
 |  10091      *  | 
 |  10092      * @method onMutation | 
 |  10093      * @param Node {Node} node Node to watch for mutations. | 
 |  10094      * @param Function {Function} listener Function to call on mutation. The fun
       ction is invoked as `listener.call(this, observer, mutations);` where `observer`
        is the MutationObserver that triggered the notification, and `mutations` is the
        native mutation list. | 
 |  10095      */ | 
 |  10096     onMutation: function(node, listener) { | 
 |  10097       var observer = new MutationObserver(function(mutations) { | 
 |  10098         listener.call(this, observer, mutations); | 
 |  10099         observer.disconnect(); | 
 |  10100       }.bind(this)); | 
 |  10101       observer.observe(node, {childList: true, subtree: true}); | 
 |  10102     } | 
 |  10103   }; | 
 |  10104  | 
 |  10105   /** | 
 |  10106    * @class Polymer | 
 |  10107    */ | 
 |  10108    | 
 |  10109   /** | 
 |  10110    * Returns true if the object includes <a href="#polymer-base">polymer-base</a
       > in it's prototype chain. | 
 |  10111    *  | 
 |  10112    * @method isBase | 
 |  10113    * @param Object {Object} object Object to test. | 
 |  10114    * @type Boolean | 
 |  10115    */ | 
 |  10116   function isBase(object) { | 
 |  10117     return object.hasOwnProperty('PolymerBase') | 
 |  10118   } | 
 |  10119  | 
 |  10120   // name a base constructor for dev tools | 
 |  10121  | 
 |  10122   /** | 
 |  10123    * The Polymer base-class constructor. | 
 |  10124    *  | 
 |  10125    * @property Base | 
 |  10126    * @type Function | 
 |  10127    */ | 
 |  10128   function PolymerBase() {}; | 
 |  10129   PolymerBase.prototype = base; | 
 |  10130   base.constructor = PolymerBase; | 
 |  10131  | 
 |  10132   // exports | 
 |  10133  | 
 |  10134   scope.Base = PolymerBase; | 
 |  10135   scope.isBase = isBase; | 
 |  10136   scope.api.instance.base = base; | 
 |  10137  | 
 |  10138 })(Polymer); | 
 |  10139  | 
 |  10140 (function(scope) { | 
 |  10141  | 
 |  10142   // imports | 
 |  10143  | 
 |  10144   var log = window.WebComponents ? WebComponents.flags.log : {}; | 
 |  10145   var hasShadowDOMPolyfill = window.ShadowDOMPolyfill; | 
 |  10146  | 
 |  10147   // magic words | 
 |  10148    | 
 |  10149   var STYLE_SCOPE_ATTRIBUTE = 'element'; | 
 |  10150   var STYLE_CONTROLLER_SCOPE = 'controller'; | 
 |  10151    | 
 |  10152   var styles = { | 
 |  10153     STYLE_SCOPE_ATTRIBUTE: STYLE_SCOPE_ATTRIBUTE, | 
 |  10154     /** | 
 |  10155      * Installs external stylesheets and <style> elements with the attribute  | 
 |  10156      * polymer-scope='controller' into the scope of element. This is intended | 
 |  10157      * to be a called during custom element construction. | 
 |  10158     */ | 
 |  10159     installControllerStyles: function() { | 
 |  10160       // apply controller styles, but only if they are not yet applied | 
 |  10161       var scope = this.findStyleScope(); | 
 |  10162       if (scope && !this.scopeHasNamedStyle(scope, this.localName)) { | 
 |  10163         // allow inherited controller styles | 
 |  10164         var proto = getPrototypeOf(this), cssText = ''; | 
 |  10165         while (proto && proto.element) { | 
 |  10166           cssText += proto.element.cssTextForScope(STYLE_CONTROLLER_SCOPE); | 
 |  10167           proto = getPrototypeOf(proto); | 
 |  10168         } | 
 |  10169         if (cssText) { | 
 |  10170           this.installScopeCssText(cssText, scope); | 
 |  10171         } | 
 |  10172       } | 
 |  10173     }, | 
 |  10174     installScopeStyle: function(style, name, scope) { | 
 |  10175       var scope = scope || this.findStyleScope(), name = name || ''; | 
 |  10176       if (scope && !this.scopeHasNamedStyle(scope, this.localName + name)) { | 
 |  10177         var cssText = ''; | 
 |  10178         if (style instanceof Array) { | 
 |  10179           for (var i=0, l=style.length, s; (i<l) && (s=style[i]); i++) { | 
 |  10180             cssText += s.textContent + '\n\n'; | 
 |  10181           } | 
 |  10182         } else { | 
 |  10183           cssText = style.textContent; | 
 |  10184         } | 
 |  10185         this.installScopeCssText(cssText, scope, name); | 
 |  10186       } | 
 |  10187     }, | 
 |  10188     installScopeCssText: function(cssText, scope, name) { | 
 |  10189       scope = scope || this.findStyleScope(); | 
 |  10190       name = name || ''; | 
 |  10191       if (!scope) { | 
 |  10192         return; | 
 |  10193       } | 
 |  10194       if (hasShadowDOMPolyfill) { | 
 |  10195         cssText = shimCssText(cssText, scope.host); | 
 |  10196       } | 
 |  10197       var style = this.element.cssTextToScopeStyle(cssText, | 
 |  10198           STYLE_CONTROLLER_SCOPE); | 
 |  10199       Polymer.applyStyleToScope(style, scope); | 
 |  10200       // cache that this style has been applied | 
 |  10201       this.styleCacheForScope(scope)[this.localName + name] = true; | 
 |  10202     }, | 
 |  10203     findStyleScope: function(node) { | 
 |  10204       // find the shadow root that contains this element | 
 |  10205       var n = node || this; | 
 |  10206       while (n.parentNode) { | 
 |  10207         n = n.parentNode; | 
 |  10208       } | 
 |  10209       return n; | 
 |  10210     }, | 
 |  10211     scopeHasNamedStyle: function(scope, name) { | 
 |  10212       var cache = this.styleCacheForScope(scope); | 
 |  10213       return cache[name]; | 
 |  10214     }, | 
 |  10215     styleCacheForScope: function(scope) { | 
 |  10216       if (hasShadowDOMPolyfill) { | 
 |  10217         var scopeName = scope.host ? scope.host.localName : scope.localName; | 
 |  10218         return polyfillScopeStyleCache[scopeName] || (polyfillScopeStyleCache[sc
       opeName] = {}); | 
 |  10219       } else { | 
 |  10220         return scope._scopeStyles = (scope._scopeStyles || {}); | 
 |  10221       } | 
 |  10222     } | 
 |  10223   }; | 
 |  10224  | 
 |  10225   var polyfillScopeStyleCache = {}; | 
 |  10226    | 
 |  10227   // NOTE: use raw prototype traversal so that we ensure correct traversal | 
 |  10228   // on platforms where the protoype chain is simulated via __proto__ (IE10) | 
 |  10229   function getPrototypeOf(prototype) { | 
 |  10230     return prototype.__proto__; | 
 |  10231   } | 
 |  10232  | 
 |  10233   function shimCssText(cssText, host) { | 
 |  10234     var name = '', is = false; | 
 |  10235     if (host) { | 
 |  10236       name = host.localName; | 
 |  10237       is = host.hasAttribute('is'); | 
 |  10238     } | 
 |  10239     var selector = WebComponents.ShadowCSS.makeScopeSelector(name, is); | 
 |  10240     return WebComponents.ShadowCSS.shimCssText(cssText, selector); | 
 |  10241   } | 
 |  10242  | 
 |  10243   // exports | 
 |  10244  | 
 |  10245   scope.api.instance.styles = styles; | 
 |  10246    | 
 |  10247 })(Polymer); | 
 |  10248  | 
 |  10249 (function(scope) { | 
 |  10250  | 
 |  10251   // imports | 
 |  10252  | 
 |  10253   var extend = scope.extend; | 
 |  10254   var api = scope.api; | 
 |  10255  | 
 |  10256   // imperative implementation: Polymer() | 
 |  10257  | 
 |  10258   // specify an 'own' prototype for tag `name` | 
 |  10259   function element(name, prototype) { | 
 |  10260     if (typeof name !== 'string') { | 
 |  10261       var script = prototype || document._currentScript; | 
 |  10262       prototype = name; | 
 |  10263       name = script && script.parentNode && script.parentNode.getAttribute ? | 
 |  10264           script.parentNode.getAttribute('name') : ''; | 
 |  10265       if (!name) { | 
 |  10266         throw 'Element name could not be inferred.'; | 
 |  10267       } | 
 |  10268     } | 
 |  10269     if (getRegisteredPrototype(name)) { | 
 |  10270       throw 'Already registered (Polymer) prototype for element ' + name; | 
 |  10271     } | 
 |  10272     // cache the prototype | 
 |  10273     registerPrototype(name, prototype); | 
 |  10274     // notify the registrar waiting for 'name', if any | 
 |  10275     notifyPrototype(name); | 
 |  10276   } | 
 |  10277  | 
 |  10278   // async prototype source | 
 |  10279  | 
 |  10280   function waitingForPrototype(name, client) { | 
 |  10281     waitPrototype[name] = client; | 
 |  10282   } | 
 |  10283  | 
 |  10284   var waitPrototype = {}; | 
 |  10285  | 
 |  10286   function notifyPrototype(name) { | 
 |  10287     if (waitPrototype[name]) { | 
 |  10288       waitPrototype[name].registerWhenReady(); | 
 |  10289       delete waitPrototype[name]; | 
 |  10290     } | 
 |  10291   } | 
 |  10292  | 
 |  10293   // utility and bookkeeping | 
 |  10294  | 
 |  10295   // maps tag names to prototypes, as registered with | 
 |  10296   // Polymer. Prototypes associated with a tag name | 
 |  10297   // using document.registerElement are available from | 
 |  10298   // HTMLElement.getPrototypeForTag(). | 
 |  10299   // If an element was fully registered by Polymer, then | 
 |  10300   // Polymer.getRegisteredPrototype(name) ===  | 
 |  10301   //   HTMLElement.getPrototypeForTag(name) | 
 |  10302  | 
 |  10303   var prototypesByName = {}; | 
 |  10304  | 
 |  10305   function registerPrototype(name, prototype) { | 
 |  10306     return prototypesByName[name] = prototype || {}; | 
 |  10307   } | 
 |  10308  | 
 |  10309   function getRegisteredPrototype(name) { | 
 |  10310     return prototypesByName[name]; | 
 |  10311   } | 
 |  10312  | 
 |  10313   function instanceOfType(element, type) { | 
 |  10314     if (typeof type !== 'string') { | 
 |  10315       return false; | 
 |  10316     } | 
 |  10317     var proto = HTMLElement.getPrototypeForTag(type); | 
 |  10318     var ctor = proto && proto.constructor; | 
 |  10319     if (!ctor) { | 
 |  10320       return false; | 
 |  10321     } | 
 |  10322     if (CustomElements.instanceof) { | 
 |  10323       return CustomElements.instanceof(element, ctor); | 
 |  10324     } | 
 |  10325     return element instanceof ctor; | 
 |  10326   } | 
 |  10327  | 
 |  10328   // exports | 
 |  10329  | 
 |  10330   scope.getRegisteredPrototype = getRegisteredPrototype; | 
 |  10331   scope.waitingForPrototype = waitingForPrototype; | 
 |  10332   scope.instanceOfType = instanceOfType; | 
 |  10333  | 
 |  10334   // namespace shenanigans so we can expose our scope on the registration  | 
 |  10335   // function | 
 |  10336  | 
 |  10337   // make window.Polymer reference `element()` | 
 |  10338  | 
 |  10339   window.Polymer = element; | 
 |  10340  | 
 |  10341   // TODO(sjmiles): find a way to do this that is less terrible | 
 |  10342   // copy window.Polymer properties onto `element()` | 
 |  10343  | 
 |  10344   extend(Polymer, scope); | 
 |  10345  | 
 |  10346   // Under the HTMLImports polyfill, scripts in the main document | 
 |  10347   // do not block on imports; we want to allow calls to Polymer in the main | 
 |  10348   // document. WebComponents collects those calls until we can process them, whi
       ch | 
 |  10349   // we do here. | 
 |  10350  | 
 |  10351   if (WebComponents.consumeDeclarations) { | 
 |  10352     WebComponents.consumeDeclarations(function(declarations) { | 
 |  10353       if (declarations) { | 
 |  10354         for (var i=0, l=declarations.length, d; (i<l) && (d=declarations[i]); i+
       +) { | 
 |  10355           element.apply(null, d); | 
 |  10356         } | 
 |  10357       } | 
 |  10358     }); | 
 |  10359   } | 
 |  10360  | 
 |  10361 })(Polymer); | 
 |  10362  | 
 |  10363 (function(scope) { | 
 |  10364  | 
 |  10365 /** | 
 |  10366  * @class polymer-base | 
 |  10367  */ | 
 |  10368  | 
 |  10369  /** | 
 |  10370   * Resolve a url path to be relative to a `base` url. If unspecified, `base` | 
 |  10371   * defaults to the element's ownerDocument url. Can be used to resolve | 
 |  10372   * paths from element's in templates loaded in HTMLImports to be relative | 
 |  10373   * to the document containing the element. Polymer automatically does this for | 
 |  10374   * url attributes in element templates; however, if a url, for | 
 |  10375   * example, contains a binding, then `resolvePath` can be used to ensure it is  | 
 |  10376   * relative to the element document. For example, in an element's template, | 
 |  10377   * | 
 |  10378   *     <a href="{{resolvePath(path)}}">Resolved</a> | 
 |  10379   *  | 
 |  10380   * @method resolvePath | 
 |  10381   * @param {String} url Url path to resolve. | 
 |  10382   * @param {String} base Optional base url against which to resolve, defaults | 
 |  10383   * to the element's ownerDocument url. | 
 |  10384   * returns {String} resolved url. | 
 |  10385   */ | 
 |  10386  | 
 |  10387 var path = { | 
 |  10388   resolveElementPaths: function(node) { | 
 |  10389     Polymer.urlResolver.resolveDom(node); | 
 |  10390   }, | 
 |  10391   addResolvePathApi: function() { | 
 |  10392     // let assetpath attribute modify the resolve path | 
 |  10393     var assetPath = this.getAttribute('assetpath') || ''; | 
 |  10394     var root = new URL(assetPath, this.ownerDocument.baseURI); | 
 |  10395     this.prototype.resolvePath = function(urlPath, base) { | 
 |  10396       var u = new URL(urlPath, base || root); | 
 |  10397       return u.href; | 
 |  10398     }; | 
 |  10399   } | 
 |  10400 }; | 
 |  10401  | 
 |  10402 // exports | 
 |  10403 scope.api.declaration.path = path; | 
 |  10404  | 
 |  10405 })(Polymer); | 
 |  10406  | 
 |  10407 (function(scope) { | 
 |  10408  | 
 |  10409   // imports | 
 |  10410  | 
 |  10411   var log = window.WebComponents ? WebComponents.flags.log : {}; | 
 |  10412   var api = scope.api.instance.styles; | 
 |  10413   var STYLE_SCOPE_ATTRIBUTE = api.STYLE_SCOPE_ATTRIBUTE; | 
 |  10414  | 
 |  10415   var hasShadowDOMPolyfill = window.ShadowDOMPolyfill; | 
 |  10416  | 
 |  10417   // magic words | 
 |  10418  | 
 |  10419   var STYLE_SELECTOR = 'style'; | 
 |  10420   var STYLE_LOADABLE_MATCH = '@import'; | 
 |  10421   var SHEET_SELECTOR = 'link[rel=stylesheet]'; | 
 |  10422   var STYLE_GLOBAL_SCOPE = 'global'; | 
 |  10423   var SCOPE_ATTR = 'polymer-scope'; | 
 |  10424  | 
 |  10425   var styles = { | 
 |  10426     // returns true if resources are loading | 
 |  10427     loadStyles: function(callback) { | 
 |  10428       var template = this.fetchTemplate(); | 
 |  10429       var content = template && this.templateContent(); | 
 |  10430       if (content) { | 
 |  10431         this.convertSheetsToStyles(content); | 
 |  10432         var styles = this.findLoadableStyles(content); | 
 |  10433         if (styles.length) { | 
 |  10434           var templateUrl = template.ownerDocument.baseURI; | 
 |  10435           return Polymer.styleResolver.loadStyles(styles, templateUrl, callback)
       ; | 
 |  10436         } | 
 |  10437       } | 
 |  10438       if (callback) { | 
 |  10439         callback(); | 
 |  10440       } | 
 |  10441     }, | 
 |  10442     convertSheetsToStyles: function(root) { | 
 |  10443       var s$ = root.querySelectorAll(SHEET_SELECTOR); | 
 |  10444       for (var i=0, l=s$.length, s, c; (i<l) && (s=s$[i]); i++) { | 
 |  10445         c = createStyleElement(importRuleForSheet(s, this.ownerDocument.baseURI)
       , | 
 |  10446             this.ownerDocument); | 
 |  10447         this.copySheetAttributes(c, s); | 
 |  10448         s.parentNode.replaceChild(c, s); | 
 |  10449       } | 
 |  10450     }, | 
 |  10451     copySheetAttributes: function(style, link) { | 
 |  10452       for (var i=0, a$=link.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++) { | 
 |  10453         if (a.name !== 'rel' && a.name !== 'href') { | 
 |  10454           style.setAttribute(a.name, a.value); | 
 |  10455         } | 
 |  10456       } | 
 |  10457     }, | 
 |  10458     findLoadableStyles: function(root) { | 
 |  10459       var loadables = []; | 
 |  10460       if (root) { | 
 |  10461         var s$ = root.querySelectorAll(STYLE_SELECTOR); | 
 |  10462         for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) { | 
 |  10463           if (s.textContent.match(STYLE_LOADABLE_MATCH)) { | 
 |  10464             loadables.push(s); | 
 |  10465           } | 
 |  10466         } | 
 |  10467       } | 
 |  10468       return loadables; | 
 |  10469     }, | 
 |  10470     /** | 
 |  10471      * Install external stylesheets loaded in <polymer-element> elements into th
       e  | 
 |  10472      * element's template. | 
 |  10473      * @param elementElement The <element> element to style. | 
 |  10474      */ | 
 |  10475     installSheets: function() { | 
 |  10476       this.cacheSheets(); | 
 |  10477       this.cacheStyles(); | 
 |  10478       this.installLocalSheets(); | 
 |  10479       this.installGlobalStyles(); | 
 |  10480     }, | 
 |  10481     /** | 
 |  10482      * Remove all sheets from element and store for later use. | 
 |  10483      */ | 
 |  10484     cacheSheets: function() { | 
 |  10485       this.sheets = this.findNodes(SHEET_SELECTOR); | 
 |  10486       this.sheets.forEach(function(s) { | 
 |  10487         if (s.parentNode) { | 
 |  10488           s.parentNode.removeChild(s); | 
 |  10489         } | 
 |  10490       }); | 
 |  10491     }, | 
 |  10492     cacheStyles: function() { | 
 |  10493       this.styles = this.findNodes(STYLE_SELECTOR + '[' + SCOPE_ATTR + ']'); | 
 |  10494       this.styles.forEach(function(s) { | 
 |  10495         if (s.parentNode) { | 
 |  10496           s.parentNode.removeChild(s); | 
 |  10497         } | 
 |  10498       }); | 
 |  10499     }, | 
 |  10500     /** | 
 |  10501      * Takes external stylesheets loaded in an <element> element and moves | 
 |  10502      * their content into a <style> element inside the <element>'s template. | 
 |  10503      * The sheet is then removed from the <element>. This is done only so  | 
 |  10504      * that if the element is loaded in the main document, the sheet does | 
 |  10505      * not become active. | 
 |  10506      * Note, ignores sheets with the attribute 'polymer-scope'. | 
 |  10507      * @param elementElement The <element> element to style. | 
 |  10508      */ | 
 |  10509     installLocalSheets: function () { | 
 |  10510       var sheets = this.sheets.filter(function(s) { | 
 |  10511         return !s.hasAttribute(SCOPE_ATTR); | 
 |  10512       }); | 
 |  10513       var content = this.templateContent(); | 
 |  10514       if (content) { | 
 |  10515         var cssText = ''; | 
 |  10516         sheets.forEach(function(sheet) { | 
 |  10517           cssText += cssTextFromSheet(sheet) + '\n'; | 
 |  10518         }); | 
 |  10519         if (cssText) { | 
 |  10520           var style = createStyleElement(cssText, this.ownerDocument); | 
 |  10521           content.insertBefore(style, content.firstChild); | 
 |  10522         } | 
 |  10523       } | 
 |  10524     }, | 
 |  10525     findNodes: function(selector, matcher) { | 
 |  10526       var nodes = this.querySelectorAll(selector).array(); | 
 |  10527       var content = this.templateContent(); | 
 |  10528       if (content) { | 
 |  10529         var templateNodes = content.querySelectorAll(selector).array(); | 
 |  10530         nodes = nodes.concat(templateNodes); | 
 |  10531       } | 
 |  10532       return matcher ? nodes.filter(matcher) : nodes; | 
 |  10533     }, | 
 |  10534     /** | 
 |  10535      * Promotes external stylesheets and <style> elements with the attribute  | 
 |  10536      * polymer-scope='global' into global scope. | 
 |  10537      * This is particularly useful for defining @keyframe rules which  | 
 |  10538      * currently do not function in scoped or shadow style elements. | 
 |  10539      * (See wkb.ug/72462) | 
 |  10540      * @param elementElement The <element> element to style. | 
 |  10541     */ | 
 |  10542     // TODO(sorvell): remove when wkb.ug/72462 is addressed. | 
 |  10543     installGlobalStyles: function() { | 
 |  10544       var style = this.styleForScope(STYLE_GLOBAL_SCOPE); | 
 |  10545       applyStyleToScope(style, document.head); | 
 |  10546     }, | 
 |  10547     cssTextForScope: function(scopeDescriptor) { | 
 |  10548       var cssText = ''; | 
 |  10549       // handle stylesheets | 
 |  10550       var selector = '[' + SCOPE_ATTR + '=' + scopeDescriptor + ']'; | 
 |  10551       var matcher = function(s) { | 
 |  10552         return matchesSelector(s, selector); | 
 |  10553       }; | 
 |  10554       var sheets = this.sheets.filter(matcher); | 
 |  10555       sheets.forEach(function(sheet) { | 
 |  10556         cssText += cssTextFromSheet(sheet) + '\n\n'; | 
 |  10557       }); | 
 |  10558       // handle cached style elements | 
 |  10559       var styles = this.styles.filter(matcher); | 
 |  10560       styles.forEach(function(style) { | 
 |  10561         cssText += style.textContent + '\n\n'; | 
 |  10562       }); | 
 |  10563       return cssText; | 
 |  10564     }, | 
 |  10565     styleForScope: function(scopeDescriptor) { | 
 |  10566       var cssText = this.cssTextForScope(scopeDescriptor); | 
 |  10567       return this.cssTextToScopeStyle(cssText, scopeDescriptor); | 
 |  10568     }, | 
 |  10569     cssTextToScopeStyle: function(cssText, scopeDescriptor) { | 
 |  10570       if (cssText) { | 
 |  10571         var style = createStyleElement(cssText); | 
 |  10572         style.setAttribute(STYLE_SCOPE_ATTRIBUTE, this.getAttribute('name') + | 
 |  10573             '-' + scopeDescriptor); | 
 |  10574         return style; | 
 |  10575       } | 
 |  10576     } | 
 |  10577   }; | 
 |  10578  | 
 |  10579   function importRuleForSheet(sheet, baseUrl) { | 
 |  10580     var href = new URL(sheet.getAttribute('href'), baseUrl).href; | 
 |  10581     return '@import \'' + href + '\';'; | 
 |  10582   } | 
 |  10583  | 
 |  10584   function applyStyleToScope(style, scope) { | 
 |  10585     if (style) { | 
 |  10586       if (scope === document) { | 
 |  10587         scope = document.head; | 
 |  10588       } | 
 |  10589       if (hasShadowDOMPolyfill) { | 
 |  10590         scope = document.head; | 
 |  10591       } | 
 |  10592       // TODO(sorvell): necessary for IE | 
 |  10593       // see https://connect.microsoft.com/IE/feedback/details/790212/ | 
 |  10594       // cloning-a-style-element-and-adding-to-document-produces | 
 |  10595       // -unexpected-result#details | 
 |  10596       // var clone = style.cloneNode(true); | 
 |  10597       var clone = createStyleElement(style.textContent); | 
 |  10598       var attr = style.getAttribute(STYLE_SCOPE_ATTRIBUTE); | 
 |  10599       if (attr) { | 
 |  10600         clone.setAttribute(STYLE_SCOPE_ATTRIBUTE, attr); | 
 |  10601       } | 
 |  10602       // TODO(sorvell): probably too brittle; try to figure out  | 
 |  10603       // where to put the element. | 
 |  10604       var refNode = scope.firstElementChild; | 
 |  10605       if (scope === document.head) { | 
 |  10606         var selector = 'style[' + STYLE_SCOPE_ATTRIBUTE + ']'; | 
 |  10607         var s$ = document.head.querySelectorAll(selector); | 
 |  10608         if (s$.length) { | 
 |  10609           refNode = s$[s$.length-1].nextElementSibling; | 
 |  10610         } | 
 |  10611       } | 
 |  10612       scope.insertBefore(clone, refNode); | 
 |  10613     } | 
 |  10614   } | 
 |  10615  | 
 |  10616   function createStyleElement(cssText, scope) { | 
 |  10617     scope = scope || document; | 
 |  10618     scope = scope.createElement ? scope : scope.ownerDocument; | 
 |  10619     var style = scope.createElement('style'); | 
 |  10620     style.textContent = cssText; | 
 |  10621     return style; | 
 |  10622   } | 
 |  10623  | 
 |  10624   function cssTextFromSheet(sheet) { | 
 |  10625     return (sheet && sheet.__resource) || ''; | 
 |  10626   } | 
 |  10627  | 
 |  10628   function matchesSelector(node, inSelector) { | 
 |  10629     if (matches) { | 
 |  10630       return matches.call(node, inSelector); | 
 |  10631     } | 
 |  10632   } | 
 |  10633   var p = HTMLElement.prototype; | 
 |  10634   var matches = p.matches || p.matchesSelector || p.webkitMatchesSelector  | 
 |  10635       || p.mozMatchesSelector; | 
 |  10636    | 
 |  10637   // exports | 
 |  10638  | 
 |  10639   scope.api.declaration.styles = styles; | 
 |  10640   scope.applyStyleToScope = applyStyleToScope; | 
 |  10641    | 
 |  10642 })(Polymer); | 
 |  10643  | 
 |  10644 (function(scope) { | 
 |  10645  | 
 |  10646   // imports | 
 |  10647  | 
 |  10648   var log = window.WebComponents ? WebComponents.flags.log : {}; | 
 |  10649   var api = scope.api.instance.events; | 
 |  10650   var EVENT_PREFIX = api.EVENT_PREFIX; | 
 |  10651  | 
 |  10652   var mixedCaseEventTypes = {}; | 
 |  10653   [ | 
 |  10654     'webkitAnimationStart', | 
 |  10655     'webkitAnimationEnd', | 
 |  10656     'webkitTransitionEnd', | 
 |  10657     'DOMFocusOut', | 
 |  10658     'DOMFocusIn', | 
 |  10659     'DOMMouseScroll' | 
 |  10660   ].forEach(function(e) { | 
 |  10661     mixedCaseEventTypes[e.toLowerCase()] = e; | 
 |  10662   }); | 
 |  10663  | 
 |  10664   // polymer-element declarative api: events feature | 
 |  10665   var events = { | 
 |  10666     parseHostEvents: function() { | 
 |  10667       // our delegates map | 
 |  10668       var delegates = this.prototype.eventDelegates; | 
 |  10669       // extract data from attributes into delegates | 
 |  10670       this.addAttributeDelegates(delegates); | 
 |  10671     }, | 
 |  10672     addAttributeDelegates: function(delegates) { | 
 |  10673       // for each attribute | 
 |  10674       for (var i=0, a; a=this.attributes[i]; i++) { | 
 |  10675         // does it have magic marker identifying it as an event delegate? | 
 |  10676         if (this.hasEventPrefix(a.name)) { | 
 |  10677           // if so, add the info to delegates | 
 |  10678           delegates[this.removeEventPrefix(a.name)] = a.value.replace('{{', '') | 
 |  10679               .replace('}}', '').trim(); | 
 |  10680         } | 
 |  10681       } | 
 |  10682     }, | 
 |  10683     // starts with 'on-' | 
 |  10684     hasEventPrefix: function (n) { | 
 |  10685       return n && (n[0] === 'o') && (n[1] === 'n') && (n[2] === '-'); | 
 |  10686     }, | 
 |  10687     removeEventPrefix: function(n) { | 
 |  10688       return n.slice(prefixLength); | 
 |  10689     }, | 
 |  10690     findController: function(node) { | 
 |  10691       while (node.parentNode) { | 
 |  10692         if (node.eventController) { | 
 |  10693           return node.eventController; | 
 |  10694         } | 
 |  10695         node = node.parentNode; | 
 |  10696       } | 
 |  10697       return node.host; | 
 |  10698     }, | 
 |  10699     getEventHandler: function(controller, target, method) { | 
 |  10700       var events = this; | 
 |  10701       return function(e) { | 
 |  10702         if (!controller || !controller.PolymerBase) { | 
 |  10703           controller = events.findController(target); | 
 |  10704         } | 
 |  10705  | 
 |  10706         var args = [e, e.detail, e.currentTarget]; | 
 |  10707         controller.dispatchMethod(controller, method, args); | 
 |  10708       }; | 
 |  10709     }, | 
 |  10710     prepareEventBinding: function(pathString, name, node) { | 
 |  10711       if (!this.hasEventPrefix(name)) | 
 |  10712         return; | 
 |  10713  | 
 |  10714       var eventType = this.removeEventPrefix(name); | 
 |  10715       eventType = mixedCaseEventTypes[eventType] || eventType; | 
 |  10716  | 
 |  10717       var events = this; | 
 |  10718  | 
 |  10719       return function(model, node, oneTime) { | 
 |  10720         var handler = events.getEventHandler(undefined, node, pathString); | 
 |  10721         PolymerGestures.addEventListener(node, eventType, handler); | 
 |  10722  | 
 |  10723         if (oneTime) | 
 |  10724           return; | 
 |  10725  | 
 |  10726         // TODO(rafaelw): This is really pointless work. Aside from the cost | 
 |  10727         // of these allocations, NodeBind is going to setAttribute back to its | 
 |  10728         // current value. Fixing this would mean changing the TemplateBinding | 
 |  10729         // binding delegate API. | 
 |  10730         function bindingValue() { | 
 |  10731           return '{{ ' + pathString + ' }}'; | 
 |  10732         } | 
 |  10733  | 
 |  10734         return { | 
 |  10735           open: bindingValue, | 
 |  10736           discardChanges: bindingValue, | 
 |  10737           close: function() { | 
 |  10738             PolymerGestures.removeEventListener(node, eventType, handler); | 
 |  10739           } | 
 |  10740         }; | 
 |  10741       }; | 
 |  10742     } | 
 |  10743   }; | 
 |  10744  | 
 |  10745   var prefixLength = EVENT_PREFIX.length; | 
 |  10746  | 
 |  10747   // exports | 
 |  10748   scope.api.declaration.events = events; | 
 |  10749  | 
 |  10750 })(Polymer); | 
 |  10751  | 
 |  10752 (function(scope) { | 
 |  10753  | 
 |  10754   // element api | 
 |  10755  | 
 |  10756   var observationBlacklist = ['attribute']; | 
 |  10757  | 
 |  10758   var properties = { | 
 |  10759     inferObservers: function(prototype) { | 
 |  10760       // called before prototype.observe is chained to inherited object | 
 |  10761       var observe = prototype.observe, property; | 
 |  10762       for (var n in prototype) { | 
 |  10763         if (n.slice(-7) === 'Changed') { | 
 |  10764           property = n.slice(0, -7); | 
 |  10765           if (this.canObserveProperty(property)) { | 
 |  10766             if (!observe) { | 
 |  10767               observe  = (prototype.observe = {}); | 
 |  10768             } | 
 |  10769             observe[property] = observe[property] || n; | 
 |  10770           } | 
 |  10771         } | 
 |  10772       } | 
 |  10773     }, | 
 |  10774     canObserveProperty: function(property) { | 
 |  10775       return (observationBlacklist.indexOf(property) < 0); | 
 |  10776     }, | 
 |  10777     explodeObservers: function(prototype) { | 
 |  10778       // called before prototype.observe is chained to inherited object | 
 |  10779       var o = prototype.observe; | 
 |  10780       if (o) { | 
 |  10781         var exploded = {}; | 
 |  10782         for (var n in o) { | 
 |  10783           var names = n.split(' '); | 
 |  10784           for (var i=0, ni; ni=names[i]; i++) { | 
 |  10785             exploded[ni] = o[n]; | 
 |  10786           } | 
 |  10787         } | 
 |  10788         prototype.observe = exploded; | 
 |  10789       } | 
 |  10790     }, | 
 |  10791     optimizePropertyMaps: function(prototype) { | 
 |  10792       if (prototype.observe) { | 
 |  10793         // construct name list | 
 |  10794         var a = prototype._observeNames = []; | 
 |  10795         for (var n in prototype.observe) { | 
 |  10796           var names = n.split(' '); | 
 |  10797           for (var i=0, ni; ni=names[i]; i++) { | 
 |  10798             a.push(ni); | 
 |  10799           } | 
 |  10800         } | 
 |  10801       } | 
 |  10802       if (prototype.publish) { | 
 |  10803         // construct name list | 
 |  10804         var a = prototype._publishNames = []; | 
 |  10805         for (var n in prototype.publish) { | 
 |  10806           a.push(n); | 
 |  10807         } | 
 |  10808       } | 
 |  10809       if (prototype.computed) { | 
 |  10810         // construct name list | 
 |  10811         var a = prototype._computedNames = []; | 
 |  10812         for (var n in prototype.computed) { | 
 |  10813           a.push(n); | 
 |  10814         } | 
 |  10815       } | 
 |  10816     }, | 
 |  10817     publishProperties: function(prototype, base) { | 
 |  10818       // if we have any properties to publish | 
 |  10819       var publish = prototype.publish; | 
 |  10820       if (publish) { | 
 |  10821         // transcribe `publish` entries onto own prototype | 
 |  10822         this.requireProperties(publish, prototype, base); | 
 |  10823         // warn and remove accessor names that are broken on some browsers | 
 |  10824         this.filterInvalidAccessorNames(publish); | 
 |  10825         // construct map of lower-cased property names | 
 |  10826         prototype._publishLC = this.lowerCaseMap(publish); | 
 |  10827       } | 
 |  10828       var computed = prototype.computed; | 
 |  10829       if (computed) { | 
 |  10830         // warn and remove accessor names that are broken on some browsers | 
 |  10831         this.filterInvalidAccessorNames(computed); | 
 |  10832       } | 
 |  10833     }, | 
 |  10834     // Publishing/computing a property where the name might conflict with a | 
 |  10835     // browser property is not currently supported to help users of Polymer | 
 |  10836     // avoid browser bugs: | 
 |  10837     // | 
 |  10838     // https://code.google.com/p/chromium/issues/detail?id=43394 | 
 |  10839     // https://bugs.webkit.org/show_bug.cgi?id=49739 | 
 |  10840     // | 
 |  10841     // We can lift this restriction when those bugs are fixed. | 
 |  10842     filterInvalidAccessorNames: function(propertyNames) { | 
 |  10843       for (var name in propertyNames) { | 
 |  10844         // Check if the name is in our blacklist. | 
 |  10845         if (this.propertyNameBlacklist[name]) { | 
 |  10846           console.warn('Cannot define property "' + name + '" for element "' + | 
 |  10847             this.name + '" because it has the same name as an HTMLElement ' + | 
 |  10848             'property, and not all browsers support overriding that. ' + | 
 |  10849             'Consider giving it a different name.'); | 
 |  10850           // Remove the invalid accessor from the list. | 
 |  10851           delete propertyNames[name]; | 
 |  10852         } | 
 |  10853       } | 
 |  10854     }, | 
 |  10855     // | 
 |  10856     // `name: value` entries in the `publish` object may need to generate  | 
 |  10857     // matching properties on the prototype. | 
 |  10858     // | 
 |  10859     // Values that are objects may have a `reflect` property, which | 
 |  10860     // signals that the value describes property control metadata. | 
 |  10861     // In metadata objects, the prototype default value (if any) | 
 |  10862     // is encoded in the `value` property. | 
 |  10863     // | 
 |  10864     // publish: { | 
 |  10865     //   foo: 5,  | 
 |  10866     //   bar: {value: true, reflect: true}, | 
 |  10867     //   zot: {} | 
 |  10868     // } | 
 |  10869     // | 
 |  10870     // `reflect` metadata property controls whether changes to the property | 
 |  10871     // are reflected back to the attribute (default false).  | 
 |  10872     // | 
 |  10873     // A value is stored on the prototype unless it's === `undefined`, | 
 |  10874     // in which case the base chain is checked for a value. | 
 |  10875     // If the basal value is also undefined, `null` is stored on the prototype. | 
 |  10876     // | 
 |  10877     // The reflection data is stored on another prototype object, `reflect` | 
 |  10878     // which also can be specified directly. | 
 |  10879     // | 
 |  10880     // reflect: { | 
 |  10881     //   foo: true | 
 |  10882     // } | 
 |  10883     // | 
 |  10884     requireProperties: function(propertyInfos, prototype, base) { | 
 |  10885       // per-prototype storage for reflected properties | 
 |  10886       prototype.reflect = prototype.reflect || {}; | 
 |  10887       // ensure a prototype value for each property | 
 |  10888       // and update the property's reflect to attribute status | 
 |  10889       for (var n in propertyInfos) { | 
 |  10890         var value = propertyInfos[n]; | 
 |  10891         // value has metadata if it has a `reflect` property | 
 |  10892         if (value && value.reflect !== undefined) { | 
 |  10893           prototype.reflect[n] = Boolean(value.reflect); | 
 |  10894           value = value.value; | 
 |  10895         } | 
 |  10896         // only set a value if one is specified | 
 |  10897         if (value !== undefined) { | 
 |  10898           prototype[n] = value; | 
 |  10899         } | 
 |  10900       } | 
 |  10901     }, | 
 |  10902     lowerCaseMap: function(properties) { | 
 |  10903       var map = {}; | 
 |  10904       for (var n in properties) { | 
 |  10905         map[n.toLowerCase()] = n; | 
 |  10906       } | 
 |  10907       return map; | 
 |  10908     }, | 
 |  10909     createPropertyAccessor: function(name, ignoreWrites) { | 
 |  10910       var proto = this.prototype; | 
 |  10911  | 
 |  10912       var privateName = name + '_'; | 
 |  10913       var privateObservable  = name + 'Observable_'; | 
 |  10914       proto[privateName] = proto[name]; | 
 |  10915  | 
 |  10916       Object.defineProperty(proto, name, { | 
 |  10917         get: function() { | 
 |  10918           var observable = this[privateObservable]; | 
 |  10919           if (observable) | 
 |  10920             observable.deliver(); | 
 |  10921  | 
 |  10922           return this[privateName]; | 
 |  10923         }, | 
 |  10924         set: function(value) { | 
 |  10925           if (ignoreWrites) { | 
 |  10926             return this[privateName]; | 
 |  10927           } | 
 |  10928  | 
 |  10929           var observable = this[privateObservable]; | 
 |  10930           if (observable) { | 
 |  10931             observable.setValue(value); | 
 |  10932             return; | 
 |  10933           } | 
 |  10934  | 
 |  10935           var oldValue = this[privateName]; | 
 |  10936           this[privateName] = value; | 
 |  10937           this.emitPropertyChangeRecord(name, value, oldValue); | 
 |  10938  | 
 |  10939           return value; | 
 |  10940         }, | 
 |  10941         configurable: true | 
 |  10942       }); | 
 |  10943     }, | 
 |  10944     createPropertyAccessors: function(prototype) { | 
 |  10945       var n$ = prototype._computedNames; | 
 |  10946       if (n$ && n$.length) { | 
 |  10947         for (var i=0, l=n$.length, n, fn; (i<l) && (n=n$[i]); i++) { | 
 |  10948           this.createPropertyAccessor(n, true); | 
 |  10949         } | 
 |  10950       } | 
 |  10951       var n$ = prototype._publishNames; | 
 |  10952       if (n$ && n$.length) { | 
 |  10953         for (var i=0, l=n$.length, n, fn; (i<l) && (n=n$[i]); i++) { | 
 |  10954           // If the property is computed and published, the accessor is created | 
 |  10955           // above. | 
 |  10956           if (!prototype.computed || !prototype.computed[n]) { | 
 |  10957             this.createPropertyAccessor(n); | 
 |  10958           } | 
 |  10959         } | 
 |  10960       } | 
 |  10961     }, | 
 |  10962     // This list contains some property names that people commonly want to use, | 
 |  10963     // but won't work because of Chrome/Safari bugs. It isn't an exhaustive | 
 |  10964     // list. In particular it doesn't contain any property names found on | 
 |  10965     // subtypes of HTMLElement (e.g. name, value). Rather it attempts to catch | 
 |  10966     // some common cases. | 
 |  10967     propertyNameBlacklist: { | 
 |  10968       children: 1, | 
 |  10969       'class': 1, | 
 |  10970       id: 1, | 
 |  10971       hidden: 1, | 
 |  10972       style: 1, | 
 |  10973       title: 1, | 
 |  10974     } | 
 |  10975   }; | 
 |  10976  | 
 |  10977   // exports | 
 |  10978  | 
 |  10979   scope.api.declaration.properties = properties; | 
 |  10980  | 
 |  10981 })(Polymer); | 
 |  10982  | 
 |  10983 (function(scope) { | 
 |  10984  | 
 |  10985   // magic words | 
 |  10986  | 
 |  10987   var ATTRIBUTES_ATTRIBUTE = 'attributes'; | 
 |  10988   var ATTRIBUTES_REGEX = /\s|,/; | 
 |  10989  | 
 |  10990   // attributes api | 
 |  10991  | 
 |  10992   var attributes = { | 
 |  10993      | 
 |  10994     inheritAttributesObjects: function(prototype) { | 
 |  10995       // chain our lower-cased publish map to the inherited version | 
 |  10996       this.inheritObject(prototype, 'publishLC'); | 
 |  10997       // chain our instance attributes map to the inherited version | 
 |  10998       this.inheritObject(prototype, '_instanceAttributes'); | 
 |  10999     }, | 
 |  11000  | 
 |  11001     publishAttributes: function(prototype, base) { | 
 |  11002       // merge names from 'attributes' attribute into the 'publish' object | 
 |  11003       var attributes = this.getAttribute(ATTRIBUTES_ATTRIBUTE); | 
 |  11004       if (attributes) { | 
 |  11005         // create a `publish` object if needed. | 
 |  11006         // the `publish` object is only relevant to this prototype, the  | 
 |  11007         // publishing logic in `declaration/properties.js` is responsible for | 
 |  11008         // managing property values on the prototype chain. | 
 |  11009         // TODO(sjmiles): the `publish` object is later chained to it's  | 
 |  11010         //                ancestor object, presumably this is only for  | 
 |  11011         //                reflection or other non-library uses.  | 
 |  11012         var publish = prototype.publish || (prototype.publish = {});  | 
 |  11013         // names='a b c' or names='a,b,c' | 
 |  11014         var names = attributes.split(ATTRIBUTES_REGEX); | 
 |  11015         // record each name for publishing | 
 |  11016         for (var i=0, l=names.length, n; i<l; i++) { | 
 |  11017           // remove excess ws | 
 |  11018           n = names[i].trim(); | 
 |  11019           // looks weird, but causes n to exist on `publish` if it does not; | 
 |  11020           // a more careful test would need expensive `in` operator | 
 |  11021           if (n && publish[n] === undefined) { | 
 |  11022             publish[n] = undefined; | 
 |  11023           } | 
 |  11024         } | 
 |  11025       } | 
 |  11026     }, | 
 |  11027  | 
 |  11028     // record clonable attributes from <element> | 
 |  11029     accumulateInstanceAttributes: function() { | 
 |  11030       // inherit instance attributes | 
 |  11031       var clonable = this.prototype._instanceAttributes; | 
 |  11032       // merge attributes from element | 
 |  11033       var a$ = this.attributes; | 
 |  11034       for (var i=0, l=a$.length, a; (i<l) && (a=a$[i]); i++) {   | 
 |  11035         if (this.isInstanceAttribute(a.name)) { | 
 |  11036           clonable[a.name] = a.value; | 
 |  11037         } | 
 |  11038       } | 
 |  11039     }, | 
 |  11040  | 
 |  11041     isInstanceAttribute: function(name) { | 
 |  11042       return !this.blackList[name] && name.slice(0,3) !== 'on-'; | 
 |  11043     }, | 
 |  11044  | 
 |  11045     // do not clone these attributes onto instances | 
 |  11046     blackList: { | 
 |  11047       name: 1, | 
 |  11048       'extends': 1, | 
 |  11049       constructor: 1, | 
 |  11050       noscript: 1, | 
 |  11051       assetpath: 1, | 
 |  11052       'cache-csstext': 1 | 
 |  11053     } | 
 |  11054      | 
 |  11055   }; | 
 |  11056  | 
 |  11057   // add ATTRIBUTES_ATTRIBUTE to the blacklist | 
 |  11058   attributes.blackList[ATTRIBUTES_ATTRIBUTE] = 1; | 
 |  11059  | 
 |  11060   // exports | 
 |  11061  | 
 |  11062   scope.api.declaration.attributes = attributes; | 
 |  11063  | 
 |  11064 })(Polymer); | 
 |  11065  | 
 |  11066 (function(scope) { | 
 |  11067  | 
 |  11068   // imports | 
 |  11069   var events = scope.api.declaration.events; | 
 |  11070  | 
 |  11071   var syntax = new PolymerExpressions(); | 
 |  11072   var prepareBinding = syntax.prepareBinding; | 
 |  11073  | 
 |  11074   // Polymer takes a first crack at the binding to see if it's a declarative | 
 |  11075   // event handler. | 
 |  11076   syntax.prepareBinding = function(pathString, name, node) { | 
 |  11077     return events.prepareEventBinding(pathString, name, node) || | 
 |  11078            prepareBinding.call(syntax, pathString, name, node); | 
 |  11079   }; | 
 |  11080  | 
 |  11081   // declaration api supporting mdv | 
 |  11082   var mdv = { | 
 |  11083     syntax: syntax, | 
 |  11084     fetchTemplate: function() { | 
 |  11085       return this.querySelector('template'); | 
 |  11086     }, | 
 |  11087     templateContent: function() { | 
 |  11088       var template = this.fetchTemplate(); | 
 |  11089       return template && template.content; | 
 |  11090     }, | 
 |  11091     installBindingDelegate: function(template) { | 
 |  11092       if (template) { | 
 |  11093         template.bindingDelegate = this.syntax; | 
 |  11094       } | 
 |  11095     } | 
 |  11096   }; | 
 |  11097  | 
 |  11098   // exports | 
 |  11099   scope.api.declaration.mdv = mdv; | 
 |  11100  | 
 |  11101 })(Polymer); | 
 |  11102  | 
 |  11103 (function(scope) { | 
 |  11104  | 
 |  11105   // imports | 
 |  11106    | 
 |  11107   var api = scope.api; | 
 |  11108   var isBase = scope.isBase; | 
 |  11109   var extend = scope.extend; | 
 |  11110  | 
 |  11111   var hasShadowDOMPolyfill = window.ShadowDOMPolyfill; | 
 |  11112  | 
 |  11113   // prototype api | 
 |  11114  | 
 |  11115   var prototype = { | 
 |  11116  | 
 |  11117     register: function(name, extendeeName) { | 
 |  11118       // build prototype combining extendee, Polymer base, and named api | 
 |  11119       this.buildPrototype(name, extendeeName); | 
 |  11120       // register our custom element with the platform | 
 |  11121       this.registerPrototype(name, extendeeName); | 
 |  11122       // reference constructor in a global named by 'constructor' attribute | 
 |  11123       this.publishConstructor(); | 
 |  11124     }, | 
 |  11125  | 
 |  11126     buildPrototype: function(name, extendeeName) { | 
 |  11127       // get our custom prototype (before chaining) | 
 |  11128       var extension = scope.getRegisteredPrototype(name); | 
 |  11129       // get basal prototype | 
 |  11130       var base = this.generateBasePrototype(extendeeName); | 
 |  11131       // implement declarative features | 
 |  11132       this.desugarBeforeChaining(extension, base); | 
 |  11133       // join prototypes | 
 |  11134       this.prototype = this.chainPrototypes(extension, base); | 
 |  11135       // more declarative features | 
 |  11136       this.desugarAfterChaining(name, extendeeName); | 
 |  11137     }, | 
 |  11138  | 
 |  11139     desugarBeforeChaining: function(prototype, base) { | 
 |  11140       // back reference declaration element | 
 |  11141       // TODO(sjmiles): replace `element` with `elementElement` or `declaration` | 
 |  11142       prototype.element = this; | 
 |  11143       // transcribe `attributes` declarations onto own prototype's `publish` | 
 |  11144       this.publishAttributes(prototype, base); | 
 |  11145       // `publish` properties to the prototype and to attribute watch | 
 |  11146       this.publishProperties(prototype, base); | 
 |  11147       // infer observers for `observe` list based on method names | 
 |  11148       this.inferObservers(prototype); | 
 |  11149       // desugar compound observer syntax, e.g. 'a b c'  | 
 |  11150       this.explodeObservers(prototype); | 
 |  11151     }, | 
 |  11152  | 
 |  11153     chainPrototypes: function(prototype, base) { | 
 |  11154       // chain various meta-data objects to inherited versions | 
 |  11155       this.inheritMetaData(prototype, base); | 
 |  11156       // chain custom api to inherited | 
 |  11157       var chained = this.chainObject(prototype, base); | 
 |  11158       // x-platform fixup | 
 |  11159       ensurePrototypeTraversal(chained); | 
 |  11160       return chained; | 
 |  11161     }, | 
 |  11162  | 
 |  11163     inheritMetaData: function(prototype, base) { | 
 |  11164       // chain observe object to inherited | 
 |  11165       this.inheritObject('observe', prototype, base); | 
 |  11166       // chain publish object to inherited | 
 |  11167       this.inheritObject('publish', prototype, base); | 
 |  11168       // chain reflect object to inherited | 
 |  11169       this.inheritObject('reflect', prototype, base); | 
 |  11170       // chain our lower-cased publish map to the inherited version | 
 |  11171       this.inheritObject('_publishLC', prototype, base); | 
 |  11172       // chain our instance attributes map to the inherited version | 
 |  11173       this.inheritObject('_instanceAttributes', prototype, base); | 
 |  11174       // chain our event delegates map to the inherited version | 
 |  11175       this.inheritObject('eventDelegates', prototype, base); | 
 |  11176     }, | 
 |  11177  | 
 |  11178     // implement various declarative features | 
 |  11179     desugarAfterChaining: function(name, extendee) { | 
 |  11180       // build side-chained lists to optimize iterations | 
 |  11181       this.optimizePropertyMaps(this.prototype); | 
 |  11182       this.createPropertyAccessors(this.prototype); | 
 |  11183       // install mdv delegate on template | 
 |  11184       this.installBindingDelegate(this.fetchTemplate()); | 
 |  11185       // install external stylesheets as if they are inline | 
 |  11186       this.installSheets(); | 
 |  11187       // adjust any paths in dom from imports | 
 |  11188       this.resolveElementPaths(this); | 
 |  11189       // compile list of attributes to copy to instances | 
 |  11190       this.accumulateInstanceAttributes(); | 
 |  11191       // parse on-* delegates declared on `this` element | 
 |  11192       this.parseHostEvents(); | 
 |  11193       // | 
 |  11194       // install a helper method this.resolvePath to aid in  | 
 |  11195       // setting resource urls. e.g. | 
 |  11196       // this.$.image.src = this.resolvePath('images/foo.png') | 
 |  11197       this.addResolvePathApi(); | 
 |  11198       // under ShadowDOMPolyfill, transforms to approximate missing CSS features | 
 |  11199       if (hasShadowDOMPolyfill) { | 
 |  11200         WebComponents.ShadowCSS.shimStyling(this.templateContent(), name, | 
 |  11201           extendee); | 
 |  11202       } | 
 |  11203       // allow custom element access to the declarative context | 
 |  11204       if (this.prototype.registerCallback) { | 
 |  11205         this.prototype.registerCallback(this); | 
 |  11206       } | 
 |  11207     }, | 
 |  11208  | 
 |  11209     // if a named constructor is requested in element, map a reference | 
 |  11210     // to the constructor to the given symbol | 
 |  11211     publishConstructor: function() { | 
 |  11212       var symbol = this.getAttribute('constructor'); | 
 |  11213       if (symbol) { | 
 |  11214         window[symbol] = this.ctor; | 
 |  11215       } | 
 |  11216     }, | 
 |  11217  | 
 |  11218     // build prototype combining extendee, Polymer base, and named api | 
 |  11219     generateBasePrototype: function(extnds) { | 
 |  11220       var prototype = this.findBasePrototype(extnds); | 
 |  11221       if (!prototype) { | 
 |  11222         // create a prototype based on tag-name extension | 
 |  11223         var prototype = HTMLElement.getPrototypeForTag(extnds); | 
 |  11224         // insert base api in inheritance chain (if needed) | 
 |  11225         prototype = this.ensureBaseApi(prototype); | 
 |  11226         // memoize this base | 
 |  11227         memoizedBases[extnds] = prototype; | 
 |  11228       } | 
 |  11229       return prototype; | 
 |  11230     }, | 
 |  11231  | 
 |  11232     findBasePrototype: function(name) { | 
 |  11233       return memoizedBases[name]; | 
 |  11234     }, | 
 |  11235  | 
 |  11236     // install Polymer instance api into prototype chain, as needed  | 
 |  11237     ensureBaseApi: function(prototype) { | 
 |  11238       if (prototype.PolymerBase) { | 
 |  11239         return prototype; | 
 |  11240       } | 
 |  11241       var extended = Object.create(prototype); | 
 |  11242       // we need a unique copy of base api for each base prototype | 
 |  11243       // therefore we 'extend' here instead of simply chaining | 
 |  11244       api.publish(api.instance, extended); | 
 |  11245       // TODO(sjmiles): sharing methods across prototype chains is | 
 |  11246       // not supported by 'super' implementation which optimizes | 
 |  11247       // by memoizing prototype relationships. | 
 |  11248       // Probably we should have a version of 'extend' that is  | 
 |  11249       // share-aware: it could study the text of each function, | 
 |  11250       // look for usage of 'super', and wrap those functions in | 
 |  11251       // closures. | 
 |  11252       // As of now, there is only one problematic method, so  | 
 |  11253       // we just patch it manually. | 
 |  11254       // To avoid re-entrancy problems, the special super method | 
 |  11255       // installed is called `mixinSuper` and the mixin method | 
 |  11256       // must use this method instead of the default `super`. | 
 |  11257       this.mixinMethod(extended, prototype, api.instance.mdv, 'bind'); | 
 |  11258       // return buffed-up prototype | 
 |  11259       return extended; | 
 |  11260     }, | 
 |  11261  | 
 |  11262     mixinMethod: function(extended, prototype, api, name) { | 
 |  11263       var $super = function(args) { | 
 |  11264         return prototype[name].apply(this, args); | 
 |  11265       }; | 
 |  11266       extended[name] = function() { | 
 |  11267         this.mixinSuper = $super; | 
 |  11268         return api[name].apply(this, arguments); | 
 |  11269       } | 
 |  11270     }, | 
 |  11271  | 
 |  11272     // ensure prototype[name] inherits from a prototype.prototype[name] | 
 |  11273     inheritObject: function(name, prototype, base) { | 
 |  11274       // require an object | 
 |  11275       var source = prototype[name] || {}; | 
 |  11276       // chain inherited properties onto a new object | 
 |  11277       prototype[name] = this.chainObject(source, base[name]); | 
 |  11278     }, | 
 |  11279  | 
 |  11280     // register 'prototype' to custom element 'name', store constructor  | 
 |  11281     registerPrototype: function(name, extendee) {  | 
 |  11282       var info = { | 
 |  11283         prototype: this.prototype | 
 |  11284       } | 
 |  11285       // native element must be specified in extends | 
 |  11286       var typeExtension = this.findTypeExtension(extendee); | 
 |  11287       if (typeExtension) { | 
 |  11288         info.extends = typeExtension; | 
 |  11289       } | 
 |  11290       // register the prototype with HTMLElement for name lookup | 
 |  11291       HTMLElement.register(name, this.prototype); | 
 |  11292       // register the custom type | 
 |  11293       this.ctor = document.registerElement(name, info); | 
 |  11294     }, | 
 |  11295  | 
 |  11296     findTypeExtension: function(name) { | 
 |  11297       if (name && name.indexOf('-') < 0) { | 
 |  11298         return name; | 
 |  11299       } else { | 
 |  11300         var p = this.findBasePrototype(name); | 
 |  11301         if (p.element) { | 
 |  11302           return this.findTypeExtension(p.element.extends); | 
 |  11303         } | 
 |  11304       } | 
 |  11305     } | 
 |  11306  | 
 |  11307   }; | 
 |  11308  | 
 |  11309   // memoize base prototypes | 
 |  11310   var memoizedBases = {}; | 
 |  11311  | 
 |  11312   // implementation of 'chainObject' depends on support for __proto__ | 
 |  11313   if (Object.__proto__) { | 
 |  11314     prototype.chainObject = function(object, inherited) { | 
 |  11315       if (object && inherited && object !== inherited) { | 
 |  11316         object.__proto__ = inherited; | 
 |  11317       } | 
 |  11318       return object; | 
 |  11319     } | 
 |  11320   } else { | 
 |  11321     prototype.chainObject = function(object, inherited) { | 
 |  11322       if (object && inherited && object !== inherited) { | 
 |  11323         var chained = Object.create(inherited); | 
 |  11324         object = extend(chained, object); | 
 |  11325       } | 
 |  11326       return object; | 
 |  11327     } | 
 |  11328   } | 
 |  11329  | 
 |  11330   // On platforms that do not support __proto__ (versions of IE), the prototype | 
 |  11331   // chain of a custom element is simulated via installation of __proto__. | 
 |  11332   // Although custom elements manages this, we install it here so it's | 
 |  11333   // available during desugaring. | 
 |  11334   function ensurePrototypeTraversal(prototype) { | 
 |  11335     if (!Object.__proto__) { | 
 |  11336       var ancestor = Object.getPrototypeOf(prototype); | 
 |  11337       prototype.__proto__ = ancestor; | 
 |  11338       if (isBase(ancestor)) { | 
 |  11339         ancestor.__proto__ = Object.getPrototypeOf(ancestor); | 
 |  11340       } | 
 |  11341     } | 
 |  11342   } | 
 |  11343  | 
 |  11344   // exports | 
 |  11345  | 
 |  11346   api.declaration.prototype = prototype; | 
 |  11347  | 
 |  11348 })(Polymer); | 
 |  11349  | 
 |  11350 (function(scope) { | 
 |  11351  | 
 |  11352   /* | 
 |  11353  | 
 |  11354     Elements are added to a registration queue so that they register in  | 
 |  11355     the proper order at the appropriate time. We do this for a few reasons: | 
 |  11356  | 
 |  11357     * to enable elements to load resources (like stylesheets)  | 
 |  11358     asynchronously. We need to do this until the platform provides an efficient | 
 |  11359     alternative. One issue is that remote @import stylesheets are  | 
 |  11360     re-fetched whenever stamped into a shadowRoot. | 
 |  11361  | 
 |  11362     * to ensure elements loaded 'at the same time' (e.g. via some set of | 
 |  11363     imports) are registered as a batch. This allows elements to be enured from | 
 |  11364     upgrade ordering as long as they query the dom tree 1 task after | 
 |  11365     upgrade (aka domReady). This is a performance tradeoff. On the one hand, | 
 |  11366     elements that could register while imports are loading are prevented from  | 
 |  11367     doing so. On the other, grouping upgrades into a single task means less | 
 |  11368     incremental work (for example style recalcs),  Also, we can ensure the  | 
 |  11369     document is in a known state at the single quantum of time when  | 
 |  11370     elements upgrade. | 
 |  11371  | 
 |  11372   */ | 
 |  11373   var queue = { | 
 |  11374  | 
 |  11375     // tell the queue to wait for an element to be ready | 
 |  11376     wait: function(element) { | 
 |  11377       if (!element.__queue) { | 
 |  11378         element.__queue = {}; | 
 |  11379         elements.push(element); | 
 |  11380       } | 
 |  11381     }, | 
 |  11382  | 
 |  11383     // enqueue an element to the next spot in the queue. | 
 |  11384     enqueue: function(element, check, go) { | 
 |  11385       var shouldAdd = element.__queue && !element.__queue.check; | 
 |  11386       if (shouldAdd) { | 
 |  11387         queueForElement(element).push(element); | 
 |  11388         element.__queue.check = check; | 
 |  11389         element.__queue.go = go; | 
 |  11390       } | 
 |  11391       return (this.indexOf(element) !== 0); | 
 |  11392     }, | 
 |  11393  | 
 |  11394     indexOf: function(element) { | 
 |  11395       var i = queueForElement(element).indexOf(element); | 
 |  11396       if (i >= 0 && document.contains(element)) { | 
 |  11397         i += (HTMLImports.useNative || HTMLImports.ready) ?  | 
 |  11398           importQueue.length : 1e9; | 
 |  11399       } | 
 |  11400       return i;   | 
 |  11401     }, | 
 |  11402  | 
 |  11403     // tell the queue an element is ready to be registered | 
 |  11404     go: function(element) { | 
 |  11405       var readied = this.remove(element); | 
 |  11406       if (readied) { | 
 |  11407         element.__queue.flushable = true; | 
 |  11408         this.addToFlushQueue(readied); | 
 |  11409         this.check(); | 
 |  11410       } | 
 |  11411     }, | 
 |  11412  | 
 |  11413     remove: function(element) { | 
 |  11414       var i = this.indexOf(element); | 
 |  11415       if (i !== 0) { | 
 |  11416         //console.warn('queue order wrong', i); | 
 |  11417         return; | 
 |  11418       } | 
 |  11419       return queueForElement(element).shift(); | 
 |  11420     }, | 
 |  11421  | 
 |  11422     check: function() { | 
 |  11423       // next | 
 |  11424       var element = this.nextElement(); | 
 |  11425       if (element) { | 
 |  11426         element.__queue.check.call(element); | 
 |  11427       } | 
 |  11428       if (this.canReady()) { | 
 |  11429         this.ready(); | 
 |  11430         return true; | 
 |  11431       } | 
 |  11432     }, | 
 |  11433  | 
 |  11434     nextElement: function() { | 
 |  11435       return nextQueued(); | 
 |  11436     }, | 
 |  11437  | 
 |  11438     canReady: function() { | 
 |  11439       return !this.waitToReady && this.isEmpty(); | 
 |  11440     }, | 
 |  11441  | 
 |  11442     isEmpty: function() { | 
 |  11443       for (var i=0, l=elements.length, e; (i<l) &&  | 
 |  11444           (e=elements[i]); i++) { | 
 |  11445         if (e.__queue && !e.__queue.flushable) { | 
 |  11446           return; | 
 |  11447         } | 
 |  11448       } | 
 |  11449       return true; | 
 |  11450     }, | 
 |  11451  | 
 |  11452     addToFlushQueue: function(element) { | 
 |  11453       flushQueue.push(element);   | 
 |  11454     }, | 
 |  11455  | 
 |  11456     flush: function() { | 
 |  11457       // prevent re-entrance | 
 |  11458       if (this.flushing) { | 
 |  11459         return; | 
 |  11460       } | 
 |  11461       this.flushing = true; | 
 |  11462       var element; | 
 |  11463       while (flushQueue.length) { | 
 |  11464         element = flushQueue.shift(); | 
 |  11465         element.__queue.go.call(element); | 
 |  11466         element.__queue = null; | 
 |  11467       } | 
 |  11468       this.flushing = false; | 
 |  11469     }, | 
 |  11470  | 
 |  11471     ready: function() { | 
 |  11472       // TODO(sorvell): As an optimization, turn off CE polyfill upgrading | 
 |  11473       // while registering. This way we avoid having to upgrade each document | 
 |  11474       // piecemeal per registration and can instead register all elements | 
 |  11475       // and upgrade once in a batch. Without this optimization, upgrade time | 
 |  11476       // degrades significantly when SD polyfill is used. This is mainly because | 
 |  11477       // querying the document tree for elements is slow under the SD polyfill. | 
 |  11478       var polyfillWasReady = CustomElements.ready; | 
 |  11479       CustomElements.ready = false; | 
 |  11480       this.flush(); | 
 |  11481       if (!CustomElements.useNative) { | 
 |  11482         CustomElements.upgradeDocumentTree(document); | 
 |  11483       } | 
 |  11484       CustomElements.ready = polyfillWasReady; | 
 |  11485       Polymer.flush(); | 
 |  11486       requestAnimationFrame(this.flushReadyCallbacks); | 
 |  11487     }, | 
 |  11488  | 
 |  11489     addReadyCallback: function(callback) { | 
 |  11490       if (callback) { | 
 |  11491         readyCallbacks.push(callback); | 
 |  11492       } | 
 |  11493     }, | 
 |  11494  | 
 |  11495     flushReadyCallbacks: function() { | 
 |  11496       if (readyCallbacks) { | 
 |  11497         var fn; | 
 |  11498         while (readyCallbacks.length) { | 
 |  11499           fn = readyCallbacks.shift(); | 
 |  11500           fn(); | 
 |  11501         } | 
 |  11502       } | 
 |  11503     }, | 
 |  11504    | 
 |  11505     /** | 
 |  11506     Returns a list of elements that have had polymer-elements created but  | 
 |  11507     are not yet ready to register. The list is an array of element definitions. | 
 |  11508     */ | 
 |  11509     waitingFor: function() { | 
 |  11510       var e$ = []; | 
 |  11511       for (var i=0, l=elements.length, e; (i<l) &&  | 
 |  11512           (e=elements[i]); i++) { | 
 |  11513         if (e.__queue && !e.__queue.flushable) { | 
 |  11514           e$.push(e); | 
 |  11515         } | 
 |  11516       } | 
 |  11517       return e$; | 
 |  11518     }, | 
 |  11519  | 
 |  11520     waitToReady: true | 
 |  11521  | 
 |  11522   }; | 
 |  11523  | 
 |  11524   var elements = []; | 
 |  11525   var flushQueue = []; | 
 |  11526   var importQueue = []; | 
 |  11527   var mainQueue = []; | 
 |  11528   var readyCallbacks = []; | 
 |  11529  | 
 |  11530   function queueForElement(element) { | 
 |  11531     return document.contains(element) ? mainQueue : importQueue; | 
 |  11532   } | 
 |  11533  | 
 |  11534   function nextQueued() { | 
 |  11535     return importQueue.length ? importQueue[0] : mainQueue[0]; | 
 |  11536   } | 
 |  11537  | 
 |  11538   function whenReady(callback) { | 
 |  11539     queue.waitToReady = true; | 
 |  11540     Polymer.endOfMicrotask(function() { | 
 |  11541       HTMLImports.whenReady(function() { | 
 |  11542         queue.addReadyCallback(callback); | 
 |  11543         queue.waitToReady = false; | 
 |  11544         queue.check(); | 
 |  11545     }); | 
 |  11546     }); | 
 |  11547   } | 
 |  11548  | 
 |  11549   /** | 
 |  11550     Forces polymer to register any pending elements. Can be used to abort | 
 |  11551     waiting for elements that are partially defined. | 
 |  11552     @param timeout {Integer} Optional timeout in milliseconds | 
 |  11553   */ | 
 |  11554   function forceReady(timeout) { | 
 |  11555     if (timeout === undefined) { | 
 |  11556       queue.ready(); | 
 |  11557       return; | 
 |  11558     } | 
 |  11559     var handle = setTimeout(function() { | 
 |  11560       queue.ready(); | 
 |  11561     }, timeout); | 
 |  11562     Polymer.whenReady(function() { | 
 |  11563       clearTimeout(handle); | 
 |  11564     }); | 
 |  11565   } | 
 |  11566  | 
 |  11567   // exports | 
 |  11568   scope.elements = elements; | 
 |  11569   scope.waitingFor = queue.waitingFor.bind(queue); | 
 |  11570   scope.forceReady = forceReady; | 
 |  11571   scope.queue = queue; | 
 |  11572   scope.whenReady = scope.whenPolymerReady = whenReady; | 
 |  11573 })(Polymer); | 
 |  11574  | 
 |  11575 (function(scope) { | 
 |  11576  | 
 |  11577   // imports | 
 |  11578  | 
 |  11579   var extend = scope.extend; | 
 |  11580   var api = scope.api; | 
 |  11581   var queue = scope.queue; | 
 |  11582   var whenReady = scope.whenReady; | 
 |  11583   var getRegisteredPrototype = scope.getRegisteredPrototype; | 
 |  11584   var waitingForPrototype = scope.waitingForPrototype; | 
 |  11585  | 
 |  11586   // declarative implementation: <polymer-element> | 
 |  11587  | 
 |  11588   var prototype = extend(Object.create(HTMLElement.prototype), { | 
 |  11589  | 
 |  11590     createdCallback: function() { | 
 |  11591       if (this.getAttribute('name')) { | 
 |  11592         this.init(); | 
 |  11593       } | 
 |  11594     }, | 
 |  11595  | 
 |  11596     init: function() { | 
 |  11597       // fetch declared values | 
 |  11598       this.name = this.getAttribute('name'); | 
 |  11599       this.extends = this.getAttribute('extends'); | 
 |  11600       queue.wait(this); | 
 |  11601       // initiate any async resource fetches | 
 |  11602       this.loadResources(); | 
 |  11603       // register when all constraints are met | 
 |  11604       this.registerWhenReady(); | 
 |  11605     }, | 
 |  11606  | 
 |  11607     // TODO(sorvell): we currently queue in the order the prototypes are  | 
 |  11608     // registered, but we should queue in the order that polymer-elements | 
 |  11609     // are registered. We are currently blocked from doing this based on  | 
 |  11610     // crbug.com/395686. | 
 |  11611     registerWhenReady: function() { | 
 |  11612      if (this.registered | 
 |  11613        || this.waitingForPrototype(this.name) | 
 |  11614        || this.waitingForQueue() | 
 |  11615        || this.waitingForResources()) { | 
 |  11616           return; | 
 |  11617       } | 
 |  11618       queue.go(this); | 
 |  11619     }, | 
 |  11620  | 
 |  11621     _register: function() { | 
 |  11622       //console.log('registering', this.name); | 
 |  11623       // warn if extending from a custom element not registered via Polymer | 
 |  11624       if (isCustomTag(this.extends) && !isRegistered(this.extends)) { | 
 |  11625         console.warn('%s is attempting to extend %s, an unregistered element ' + | 
 |  11626             'or one that was not registered with Polymer.', this.name, | 
 |  11627             this.extends); | 
 |  11628       } | 
 |  11629       this.register(this.name, this.extends); | 
 |  11630       this.registered = true; | 
 |  11631     }, | 
 |  11632  | 
 |  11633     waitingForPrototype: function(name) { | 
 |  11634       if (!getRegisteredPrototype(name)) { | 
 |  11635         // then wait for a prototype | 
 |  11636         waitingForPrototype(name, this); | 
 |  11637         // emulate script if user is not supplying one | 
 |  11638         this.handleNoScript(name); | 
 |  11639         // prototype not ready yet | 
 |  11640         return true; | 
 |  11641       } | 
 |  11642     }, | 
 |  11643  | 
 |  11644     handleNoScript: function(name) { | 
 |  11645       // if explicitly marked as 'noscript' | 
 |  11646       if (this.hasAttribute('noscript') && !this.noscript) { | 
 |  11647         this.noscript = true; | 
 |  11648         // imperative element registration | 
 |  11649         Polymer(name); | 
 |  11650       } | 
 |  11651     }, | 
 |  11652  | 
 |  11653     waitingForResources: function() { | 
 |  11654       return this._needsResources; | 
 |  11655     }, | 
 |  11656  | 
 |  11657     // NOTE: Elements must be queued in proper order for inheritance/composition | 
 |  11658     // dependency resolution. Previously this was enforced for inheritance, | 
 |  11659     // and by rule for composition. It's now entirely by rule. | 
 |  11660     waitingForQueue: function() { | 
 |  11661       return queue.enqueue(this, this.registerWhenReady, this._register); | 
 |  11662     }, | 
 |  11663  | 
 |  11664     loadResources: function() { | 
 |  11665       this._needsResources = true; | 
 |  11666       this.loadStyles(function() { | 
 |  11667         this._needsResources = false; | 
 |  11668         this.registerWhenReady(); | 
 |  11669       }.bind(this)); | 
 |  11670     } | 
 |  11671  | 
 |  11672   }); | 
 |  11673  | 
 |  11674   // semi-pluggable APIs  | 
 |  11675  | 
 |  11676   // TODO(sjmiles): should be fully pluggable (aka decoupled, currently | 
 |  11677   // the various plugins are allowed to depend on each other directly) | 
 |  11678   api.publish(api.declaration, prototype); | 
 |  11679  | 
 |  11680   // utility and bookkeeping | 
 |  11681  | 
 |  11682   function isRegistered(name) { | 
 |  11683     return Boolean(HTMLElement.getPrototypeForTag(name)); | 
 |  11684   } | 
 |  11685  | 
 |  11686   function isCustomTag(name) { | 
 |  11687     return (name && name.indexOf('-') >= 0); | 
 |  11688   } | 
 |  11689  | 
 |  11690   // boot tasks | 
 |  11691  | 
 |  11692   whenReady(function() { | 
 |  11693     document.body.removeAttribute('unresolved'); | 
 |  11694     document.dispatchEvent( | 
 |  11695       new CustomEvent('polymer-ready', {bubbles: true}) | 
 |  11696     ); | 
 |  11697   }); | 
 |  11698  | 
 |  11699   // register polymer-element with document | 
 |  11700  | 
 |  11701   document.registerElement('polymer-element', {prototype: prototype}); | 
 |  11702  | 
 |  11703 })(Polymer); | 
 |  11704  | 
 |  11705 (function(scope) { | 
 |  11706  | 
 |  11707 /** | 
 |  11708  * @class Polymer | 
 |  11709  */ | 
 |  11710  | 
 |  11711 var whenReady = scope.whenReady; | 
 |  11712  | 
 |  11713 /** | 
 |  11714  * Loads the set of HTMLImports contained in `node`. Notifies when all | 
 |  11715  * the imports have loaded by calling the `callback` function argument. | 
 |  11716  * This method can be used to lazily load imports. For example, given a  | 
 |  11717  * template: | 
 |  11718  *      | 
 |  11719  *     <template> | 
 |  11720  *       <link rel="import" href="my-import1.html"> | 
 |  11721  *       <link rel="import" href="my-import2.html"> | 
 |  11722  *     </template> | 
 |  11723  * | 
 |  11724  *     Polymer.importElements(template.content, function() { | 
 |  11725  *       console.log('imports lazily loaded');  | 
 |  11726  *     }); | 
 |  11727  *  | 
 |  11728  * @method importElements | 
 |  11729  * @param {Node} node Node containing the HTMLImports to load. | 
 |  11730  * @param {Function} callback Callback called when all imports have loaded. | 
 |  11731  */ | 
 |  11732 function importElements(node, callback) { | 
 |  11733   if (node) { | 
 |  11734     document.head.appendChild(node); | 
 |  11735     whenReady(callback); | 
 |  11736   } else if (callback) { | 
 |  11737     callback(); | 
 |  11738   } | 
 |  11739 } | 
 |  11740  | 
 |  11741 /** | 
 |  11742  * Loads an HTMLImport for each url specified in the `urls` array. | 
 |  11743  * Notifies when all the imports have loaded by calling the `callback`  | 
 |  11744  * function argument. This method can be used to lazily load imports.  | 
 |  11745  * For example, | 
 |  11746  * | 
 |  11747  *     Polymer.import(['my-import1.html', 'my-import2.html'], function() { | 
 |  11748  *       console.log('imports lazily loaded');  | 
 |  11749  *     }); | 
 |  11750  *  | 
 |  11751  * @method import | 
 |  11752  * @param {Array} urls Array of urls to load as HTMLImports. | 
 |  11753  * @param {Function} callback Callback called when all imports have loaded. | 
 |  11754  */ | 
 |  11755 function _import(urls, callback) { | 
 |  11756   if (urls && urls.length) { | 
 |  11757       var frag = document.createDocumentFragment(); | 
 |  11758       for (var i=0, l=urls.length, url, link; (i<l) && (url=urls[i]); i++) { | 
 |  11759         link = document.createElement('link'); | 
 |  11760         link.rel = 'import'; | 
 |  11761         link.href = url; | 
 |  11762         frag.appendChild(link); | 
 |  11763       } | 
 |  11764       importElements(frag, callback); | 
 |  11765   } else if (callback) { | 
 |  11766     callback(); | 
 |  11767   } | 
 |  11768 } | 
 |  11769  | 
 |  11770 // exports | 
 |  11771 scope.import = _import; | 
 |  11772 scope.importElements = importElements; | 
 |  11773  | 
 |  11774 })(Polymer); | 
 |  11775  | 
 |  11776 /** | 
 |  11777  * The `auto-binding` element extends the template element. It provides a quick  | 
 |  11778  * and easy way to do data binding without the need to setup a model.  | 
 |  11779  * The `auto-binding` element itself serves as the model and controller for the  | 
 |  11780  * elements it contains. Both data and event handlers can be bound.  | 
 |  11781  * | 
 |  11782  * The `auto-binding` element acts just like a template that is bound to  | 
 |  11783  * a model. It stamps its content in the dom adjacent to itself. When the  | 
 |  11784  * content is stamped, the `template-bound` event is fired. | 
 |  11785  * | 
 |  11786  * Example: | 
 |  11787  * | 
 |  11788  *     <template is="auto-binding"> | 
 |  11789  *       <div>Say something: <input value="{{value}}"></div> | 
 |  11790  *       <div>You said: {{value}}</div> | 
 |  11791  *       <button on-tap="{{buttonTap}}">Tap me!</button> | 
 |  11792  *     </template> | 
 |  11793  *     <script> | 
 |  11794  *       var template = document.querySelector('template'); | 
 |  11795  *       template.value = 'something'; | 
 |  11796  *       template.buttonTap = function() { | 
 |  11797  *         console.log('tap!'); | 
 |  11798  *       }; | 
 |  11799  *     </script> | 
 |  11800  * | 
 |  11801  * @module Polymer | 
 |  11802  * @status stable | 
 |  11803 */ | 
 |  11804  | 
 |  11805 (function() { | 
 |  11806  | 
 |  11807   var element = document.createElement('polymer-element'); | 
 |  11808   element.setAttribute('name', 'auto-binding'); | 
 |  11809   element.setAttribute('extends', 'template'); | 
 |  11810   element.init(); | 
 |  11811  | 
 |  11812   Polymer('auto-binding', { | 
 |  11813  | 
 |  11814     createdCallback: function() { | 
 |  11815       this.syntax = this.bindingDelegate = this.makeSyntax(); | 
 |  11816       // delay stamping until polymer-ready so that auto-binding is not | 
 |  11817       // required to load last. | 
 |  11818       Polymer.whenPolymerReady(function() { | 
 |  11819         this.model = this; | 
 |  11820         this.setAttribute('bind', ''); | 
 |  11821         // we don't bother with an explicit signal here, we could ust a MO | 
 |  11822         // if necessary | 
 |  11823         this.async(function() { | 
 |  11824           // note: this will marshall *all* the elements in the parentNode | 
 |  11825           // rather than just stamped ones. We'd need to use createInstance | 
 |  11826           // to fix this or something else fancier. | 
 |  11827           this.marshalNodeReferences(this.parentNode); | 
 |  11828           // template stamping is asynchronous so stamping isn't complete | 
 |  11829           // by polymer-ready; fire an event so users can use stamped elements | 
 |  11830           this.fire('template-bound'); | 
 |  11831         }); | 
 |  11832       }.bind(this)); | 
 |  11833     }, | 
 |  11834  | 
 |  11835     makeSyntax: function() { | 
 |  11836       var events = Object.create(Polymer.api.declaration.events); | 
 |  11837       var self = this; | 
 |  11838       events.findController = function() { return self.model; }; | 
 |  11839  | 
 |  11840       var syntax = new PolymerExpressions(); | 
 |  11841       var prepareBinding = syntax.prepareBinding;   | 
 |  11842       syntax.prepareBinding = function(pathString, name, node) { | 
 |  11843         return events.prepareEventBinding(pathString, name, node) || | 
 |  11844                prepareBinding.call(syntax, pathString, name, node); | 
 |  11845       }; | 
 |  11846       return syntax; | 
 |  11847     } | 
 |  11848  | 
 |  11849   }); | 
 |  11850  | 
 |  11851 })(); | 
| OLD | NEW |