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