| Index: lib/src/google-map/google-map-poly.html
 | 
| diff --git a/lib/src/google-map/google-map-poly.html b/lib/src/google-map/google-map-poly.html
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..122b6711af2026af4cb56eed4138c1835c3f3866
 | 
| --- /dev/null
 | 
| +++ b/lib/src/google-map/google-map-poly.html
 | 
| @@ -0,0 +1,558 @@
 | 
| +<!-- Copyright (c) 2015 Google Inc. All rights reserved. -->
 | 
| +
 | 
| +<link rel="import" href="../polymer/polymer.html">
 | 
| +<link rel="import" href="../google-apis/google-maps-api.html">
 | 
| +<link rel="import" href="google-map-point.html">
 | 
| +
 | 
| +<!--
 | 
| +The `google-map-poly` element represents a series of connected line segments (aka a polyline) which
 | 
| +may also be closed to form a polygon (provided there are at least three points). It is used as a
 | 
| +child of `google-map` and will contain at least two `google-map-point` child elements.
 | 
| +
 | 
| +<b>Example</b>—a simple line:
 | 
| +
 | 
| +    <google-map latitude="37.77493" longitude="-122.41942">
 | 
| +      <google-map-poly>
 | 
| +        <google-map-point latitude="37.77493" longitude="-122.41942"></google-map-point>
 | 
| +        <google-map-point latitude="38.77493" longitude="-123.41942"></google-map-point>
 | 
| +      </google-map-poly>
 | 
| +    </google-map>
 | 
| +
 | 
| +<b>Example</b>—a semi-translucent blue triangle:
 | 
| +
 | 
| +    <google-map latitude="37.77493" longitude="-122.41942">
 | 
| +      <google-map-poly closed fill-color="blue" fill-opacity=".5">
 | 
| +        <google-map-point latitude="36.77493" longitude="-121.41942"></google-map-point>
 | 
| +        <google-map-point latitude="38.77493" longitude="-122.41942"></google-map-point>
 | 
| +        <google-map-point latitude="36.77493" longitude="-123.41942"></google-map-point>
 | 
| +      </google-map-poly>
 | 
| +    </google-map>
 | 
| +-->
 | 
| +
 | 
| +<dom-module id="google-map-poly">
 | 
| +  <style>
 | 
| +    :host {
 | 
| +      display: none;
 | 
| +    }
 | 
| +  </style>
 | 
| +  <template>
 | 
| +    <content id="points" select="google-map-point"></content>
 | 
| +  </template>
 | 
| +</polymer-element>
 | 
| +
 | 
| +<script>
 | 
| +  Polymer({
 | 
| +    is: 'google-map-poly',
 | 
| +
 | 
| +    /**
 | 
| +     * Fired when the `path` property is built based on child `google-map-point` elements, either
 | 
| +     * initially or when they are changed.
 | 
| +     * @event google-map-poly-path-built
 | 
| +     * @param {MVCArray.<LatLng>} path The poly path.
 | 
| +     */
 | 
| +    /**
 | 
| +     * Fired when the user finishes adding vertices to the poly. The host component can use the
 | 
| +     * provided path to rebuild its list of points.
 | 
| +     * @event google-map-poly-path-updated
 | 
| +     * @param {MVCArray.<LatLng>} path The poly path.
 | 
| +     */
 | 
| +    /**
 | 
| +     * Fired when the DOM `click` event is fired on the poly. Requires the clickEvents attribute to
 | 
| +     * be true.
 | 
| +     * @event google-map-poly-click
 | 
| +     * @param {google.maps.PolyMouseEvent} event The poly event.
 | 
| +     */
 | 
| +    /**
 | 
| +     * Fired when the DOM `dblclick` event is fired on the poly. Requires the clickEvents attribute
 | 
| +     * to be true.
 | 
| +     * @event google-map-poly-dblclick
 | 
| +     * @param {google.maps.PolyMouseEvent} event The poly event.
 | 
| +     */
 | 
| +    /**
 | 
| +     * Fired repeatedly while the user drags the poly. Requires the dragEvents attribute to be true.
 | 
| +     * @event google-map-poly-drag
 | 
| +     * @param {google.maps.MouseEvent} event The mouse event.
 | 
| +     */
 | 
| +    /**
 | 
| +     * Fired when the user stops dragging the poly. Requires the dragEvents attribute to be true.
 | 
| +     * @event google-map-poly-dragend
 | 
| +     * @param {google.maps.MouseEvent} event The mouse event.
 | 
| +     */
 | 
| +    /**
 | 
| +     * Fired when the user starts dragging the poly. Requires the dragEvents attribute to be true.
 | 
| +     * @event google-map-poly-dragstart
 | 
| +     * @param {google.maps.MouseEvent} event The mouse event.
 | 
| +     */
 | 
| +    /**
 | 
| +     * Fired when the DOM `mousedown` event is fired on the poly. Requires the mouseEvents attribute
 | 
| +     * to be true.
 | 
| +     * @event google-map-poly-mousedown
 | 
| +     * @param {google.maps.PolyMouseEvent} event The poly event.
 | 
| +     */
 | 
| +    /**
 | 
| +     * Fired when the DOM `mousemove` event is fired on the poly. Requires the mouseEvents attribute
 | 
| +     * to be true.
 | 
| +     * @event google-map-poly-mousemove
 | 
| +     * @param {google.maps.PolyMouseEvent} event The poly event.
 | 
| +     */
 | 
| +    /**
 | 
| +     * Fired on poly mouseout. Requires the mouseEvents attribute to be true.
 | 
| +     * @event google-map-poly-mouseout
 | 
| +     * @param {google.maps.PolyMouseEvent} event The poly event.
 | 
| +     */
 | 
| +    /**
 | 
| +     * Fired on poly mouseover. Requires the mouseEvents attribute to be true.
 | 
| +     * @event google-map-poly-mouseover
 | 
| +     * @param {google.maps.PolyMouseEvent} event The poly event.
 | 
| +     */
 | 
| +    /**
 | 
| +     * Fired when the DOM `mouseup` event is fired on the poly. Requires the mouseEvents attribute
 | 
| +     * to be true.
 | 
| +     * @event google-map-poly-mouseup
 | 
| +     * @param {google.maps.PolyMouseEvent} event The poly event.
 | 
| +     */
 | 
| +    /**
 | 
| +     * Fired when the poly is right-clicked on. Requires the clickEvents attribute to be true.
 | 
| +     * @event google-map-poly-rightclick
 | 
| +     * @param {google.maps.PolyMouseEvent} event The poly event.
 | 
| +     */
 | 
| +    properties: {
 | 
| +      /**
 | 
| +       * A Google Maps polyline or polygon object (depending on value of "closed" attribute).
 | 
| +       * @type google.maps.Polyline|google.maps.Polygon
 | 
| +       */
 | 
| +      poly: {
 | 
| +        type: Object,
 | 
| +        readOnly: true
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * An array of the Google Maps LatLng objects that define the poly shape.
 | 
| +       * @type MVCArray.<LatLng>
 | 
| +       */
 | 
| +      path: {
 | 
| +        type: Object,
 | 
| +        readOnly: true
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * The Google map object.
 | 
| +       * @type google.maps.Map
 | 
| +       */
 | 
| +      map: {
 | 
| +        type: Object,
 | 
| +        observer: '_mapChanged'
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * When true, the poly will generate mouse events.
 | 
| +       */
 | 
| +      clickable: {
 | 
| +        type: Boolean,
 | 
| +        value: false,
 | 
| +        observer: '_clickableChanged'
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * When true, the google-map-poly-*click events will be automatically registered.
 | 
| +       */
 | 
| +       clickEvents: {
 | 
| +         type: Boolean,
 | 
| +         value: false,
 | 
| +         observer: '_clickEventsChanged'
 | 
| +       },
 | 
| +
 | 
| +      /**
 | 
| +       * When true, the path will be closed by connecting the last point to the first one and
 | 
| +       * treating the poly as a polygon.
 | 
| +       */
 | 
| +      closed: {
 | 
| +        type: Boolean,
 | 
| +        value: false,
 | 
| +        observer: '_closedChanged'
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * When true, the poly may be dragged to a new position.
 | 
| +       */
 | 
| +      draggable: {
 | 
| +        type: Boolean,
 | 
| +        value: false,
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * When true, the google-map-poly-drag* events will be automatically registered.
 | 
| +       */
 | 
| +      dragEvents: {
 | 
| +        type: Boolean,
 | 
| +        value: false,
 | 
| +        observer: '_dragEventsChanged'
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * When true, the poly's vertices may be individually moved or new ones added.
 | 
| +       */
 | 
| +      editable: {
 | 
| +        type: Boolean,
 | 
| +        value: false,
 | 
| +        observer: '_editableChanged'
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * When true, indicates that the user has begun editing the poly path (adding vertices).
 | 
| +       */
 | 
| +      editing: {
 | 
| +        type: Boolean,
 | 
| +        value: false,
 | 
| +        notify: true,
 | 
| +        readOnly: true
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * If the path is closed, the polygon fill color. All CSS3 colors are supported except for
 | 
| +       * extended named colors.
 | 
| +       */
 | 
| +      fillColor: {
 | 
| +        type: String,
 | 
| +        value: '',
 | 
| +        observer: '_fillColorChanged'
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * If the path is closed, the polygon fill opacity (between 0.0 and 1.0).
 | 
| +       */
 | 
| +      fillOpacity: {
 | 
| +        type: Number,
 | 
| +        value: 0,
 | 
| +        observer: '_fillOpacityChanged'
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * When true, the poly's edges are interpreted as geodesic and will follow the curvature of
 | 
| +       * the Earth. When not set, the poly's edges are rendered as straight lines in screen space.
 | 
| +       * Note that the poly of a geodesic poly may appear to change when dragged, as the dimensions
 | 
| +       * are maintained relative to the surface of the earth.
 | 
| +       */
 | 
| +      geodesic: {
 | 
| +        type: Boolean,
 | 
| +        value: false,
 | 
| +        observer: '_geodesicChanged'
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * If the path is not closed, the icons to be rendered along the polyline.
 | 
| +       */
 | 
| +      icons: {
 | 
| +        type: Array,
 | 
| +        value: null,
 | 
| +        observer: '_iconsChanged'
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * When true, the google-map-poly-mouse* events will be automatically registered.
 | 
| +       */
 | 
| +       mouseEvents: {
 | 
| +         type: Boolean,
 | 
| +         value: false,
 | 
| +         observer: '_mouseEventsChanged'
 | 
| +       },
 | 
| +
 | 
| +      /**
 | 
| +       * The color to draw the poly's stroke with. All CSS3 colors are supported except for extended
 | 
| +       * named colors.
 | 
| +       */
 | 
| +      strokeColor: {
 | 
| +        type: String,
 | 
| +        value: 'black',
 | 
| +        observer: '_strokeColorChanged'
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * The stroke opacity (between 0.0 and 1.0).
 | 
| +       */
 | 
| +      strokeOpacity: {
 | 
| +        type: Number,
 | 
| +        value: 1,
 | 
| +        observer: '_strokeOpacityChanged'
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * The stroke position (center, inside, or outside).
 | 
| +       */
 | 
| +      strokePosition: {
 | 
| +        type: String,
 | 
| +        value: 'center',
 | 
| +        observer: '_strokePositionChanged'
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * The stroke width in pixels.
 | 
| +       */
 | 
| +      strokeWeight: {
 | 
| +        type: Number,
 | 
| +        value: 3,
 | 
| +        observer: '_strokeWeightChanged'
 | 
| +      },
 | 
| +
 | 
| +      /**
 | 
| +       * The Z-index relative to other objects on the map.
 | 
| +       */
 | 
| +      zIndex: {
 | 
| +        type: Number,
 | 
| +        value: 0,
 | 
| +        observer: '_zIndexChanged'
 | 
| +      }
 | 
| +    },
 | 
| +
 | 
| +    // Lifecycle event handlers.
 | 
| +
 | 
| +    detached: function() {
 | 
| +      this.poly.setMap(null);
 | 
| +      if (this._pointsObserver) {
 | 
| +        this._pointsObserver.disconnect();
 | 
| +        this._pointsObserver = null;
 | 
| +      }
 | 
| +      for (var name in this._listeners) {
 | 
| +        this._clearListener(name);
 | 
| +      }
 | 
| +    },
 | 
| +
 | 
| +    attached: function() {
 | 
| +      // If element is added back to DOM, put it back on the map.
 | 
| +      this.poly && this.poly.setMap(this.map);
 | 
| +    },
 | 
| +
 | 
| +    // Attribute/property change watchers.
 | 
| +
 | 
| +    attributeChanged: function(attrName, oldVal, newVal) {
 | 
| +      if (!this.poly) {
 | 
| +        return;
 | 
| +      }
 | 
| +
 | 
| +      // Cannot use *Changed watchers for native properties.
 | 
| +      switch (attrName) {
 | 
| +        case 'hidden':
 | 
| +          this.poly.setVisible(!this.hidden);
 | 
| +          break;
 | 
| +        case 'draggable':
 | 
| +          this.poly.setDraggable(this.draggable);
 | 
| +          break;
 | 
| +      }
 | 
| +    },
 | 
| +
 | 
| +    _clickableChanged: function() {
 | 
| +      this.poly && this.poly.set('clickable', this.clickable);
 | 
| +    },
 | 
| +
 | 
| +    _clickEventsChanged: function() {
 | 
| +      if (this.poly) {
 | 
| +        if (this.clickEvents) {
 | 
| +          this._forwardEvent('click');
 | 
| +          this._forwardEvent('dblclick');
 | 
| +          this._forwardEvent('rightclick');
 | 
| +        } else {
 | 
| +          this._clearListener('click');
 | 
| +          this._clearListener('dblclick');
 | 
| +          this._clearListener('rightclick');
 | 
| +        }
 | 
| +      }
 | 
| +    },
 | 
| +
 | 
| +    _closedChanged: function() {
 | 
| +      this._mapChanged();
 | 
| +    },
 | 
| +
 | 
| +    _dragEventsChanged: function() {
 | 
| +      if (this.poly) {
 | 
| +        if (this.clickEvents) {
 | 
| +          this._forwardEvent('drag');
 | 
| +          this._forwardEvent('dragend');
 | 
| +          this._forwardEvent('dragstart');
 | 
| +        } else {
 | 
| +          this._clearListener('drag');
 | 
| +          this._clearListener('dragend');
 | 
| +          this._clearListener('dragstart');
 | 
| +        }
 | 
| +      }
 | 
| +    },
 | 
| +
 | 
| +    _editableChanged: function() {
 | 
| +      this.poly && this.poly.setEditable(this.editable);
 | 
| +    },
 | 
| +
 | 
| +    _fillColorChanged: function() {
 | 
| +      this.poly && this.poly.set('fillColor', this.fillColor);
 | 
| +    },
 | 
| +
 | 
| +    _fillOpacityChanged: function() {
 | 
| +      this.poly && this.poly.set('fillOpacity', this.fillOpacity);
 | 
| +    },
 | 
| +
 | 
| +    _geodesicChanged: function() {
 | 
| +      this.poly && this.poly.set('geodesic', this.geodesic);
 | 
| +    },
 | 
| +
 | 
| +    _iconsChanged: function() {
 | 
| +      this.poly && this.poly.set('icons', this.icons);
 | 
| +    },
 | 
| +
 | 
| +    _mapChanged: function() {
 | 
| +      // Poly will be rebuilt, so disconnect existing one from old map and listeners.
 | 
| +      if (this.poly) {
 | 
| +        this.poly.setMap(null);
 | 
| +        google.maps.event.clearInstanceListeners(this.poly);
 | 
| +      }
 | 
| +
 | 
| +      if (this.map && this.map instanceof google.maps.Map) {
 | 
| +        this._createPoly();
 | 
| +      }
 | 
| +    },
 | 
| +
 | 
| +    _mouseEventsChanged: function() {
 | 
| +      if (this.poly) {
 | 
| +        if (this.mouseEvents) {
 | 
| +          this._forwardEvent('mousedown');
 | 
| +          this._forwardEvent('mousemove');
 | 
| +          this._forwardEvent('mouseout');
 | 
| +          this._forwardEvent('mouseover');
 | 
| +          this._forwardEvent('mouseup');
 | 
| +        } else {
 | 
| +          this._clearListener('mousedown');
 | 
| +          this._clearListener('mousemove');
 | 
| +          this._clearListener('mouseout');
 | 
| +          this._clearListener('mouseover');
 | 
| +          this._clearListener('mouseup');
 | 
| +        }
 | 
| +      }
 | 
| +    },
 | 
| +
 | 
| +    _strokeColorChanged: function() {
 | 
| +      this.poly && this.poly.set('strokeColor', this.strokeColor);
 | 
| +    },
 | 
| +
 | 
| +    _strokeOpacityChanged: function() {
 | 
| +      this.poly && this.poly.set('strokeOpacity', this.strokeOpacity);
 | 
| +    },
 | 
| +
 | 
| +    _strokePositionChanged: function() {
 | 
| +      this.poly && this.poly.set('strokePosition', this._convertStrokePosition());
 | 
| +    },
 | 
| +
 | 
| +    _strokeWeightChanged: function() {
 | 
| +      this.poly && this.poly.set('strokeWeight', this.strokeWeight);
 | 
| +    },
 | 
| +
 | 
| +    _zIndexChanged: function() {
 | 
| +      this.poly && this.poly.set('zIndex', this.zIndex);
 | 
| +    },
 | 
| +
 | 
| +    // Helper logic.
 | 
| +
 | 
| +    _buildPathFromPoints: function() {
 | 
| +      this._points = Array.prototype.slice.call(Polymer.dom(this.$.points).getDistributedNodes());
 | 
| +
 | 
| +      // Build path from current points (ignoring vertex insertions while doing so).
 | 
| +      this._building = true;
 | 
| +      this.path.clear();
 | 
| +      for (var i = 0, point; point = this._points[i]; ++i) {
 | 
| +        this.path.push(point.getPosition());
 | 
| +      }
 | 
| +      this._building = false;
 | 
| +
 | 
| +      this.fire('google-map-poly-path-built', this.path);
 | 
| +
 | 
| +      // Watch for future updates.
 | 
| +      if (this._pointsObserver) {
 | 
| +        return;
 | 
| +      }
 | 
| +      this._pointsObserver = new MutationObserver(this._buildPathFromPoints.bind(this));
 | 
| +      this._pointsObserver.observe(this, {
 | 
| +        childList: true
 | 
| +      });
 | 
| +    },
 | 
| +
 | 
| +    _clearListener: function(name) {
 | 
| +      if (this._listeners[name]) {
 | 
| +        google.maps.event.removeListener(this._listeners[name]);
 | 
| +        this._listeners[name] = null;
 | 
| +      }
 | 
| +    },
 | 
| +
 | 
| +    _convertStrokePosition: function() {
 | 
| +      return google.maps.StrokePosition && this.strokePosition ?
 | 
| +          google.maps.StrokePosition[this.strokePosition.toUpperCase()] : 0;
 | 
| +    },
 | 
| +
 | 
| +    _createPoly: function() {
 | 
| +      // Build poly's path and register mutation listeners on first creation.
 | 
| +      if (!this.path) {
 | 
| +        this._setPath(new google.maps.MVCArray());
 | 
| +        google.maps.event.addListener(this.path, 'insert_at', this._startEditing.bind(this));
 | 
| +        google.maps.event.addListener(this.path, 'set_at', this._updatePoint.bind(this));
 | 
| +        this._buildPathFromPoints();
 | 
| +      }
 | 
| +
 | 
| +      var options = {
 | 
| +        clickable: this.clickable || this.draggable,  // draggable must be clickable to work.
 | 
| +        draggable: this.draggable,
 | 
| +        editable: this.editable,
 | 
| +        geodesic: this.geodesic,
 | 
| +        map: this.map,
 | 
| +        path: this.path,
 | 
| +        strokeColor: this.strokeColor,
 | 
| +        strokeOpacity: this.strokeOpacity,
 | 
| +        strokePosition: this._convertStrokePosition(),
 | 
| +        strokeWeight: this.strokeWeight,
 | 
| +        visible: !this.hidden,
 | 
| +        zIndex: this.zIndex
 | 
| +      };
 | 
| +
 | 
| +      if (this.closed) {
 | 
| +        options.fillColor = this.fillColor;
 | 
| +        options.fillOpacity = this.fillOpacity;
 | 
| +        this._setPoly(new google.maps.Polygon(options));
 | 
| +      } else {
 | 
| +        options.icons = this.icons;
 | 
| +        this._setPoly(new google.maps.Polyline(options));
 | 
| +      }
 | 
| +
 | 
| +      this._listeners = {};
 | 
| +    },
 | 
| +
 | 
| +    _forwardEvent: function(name) {
 | 
| +      this._listeners[name] = google.maps.event.addListener(this.poly, name, function(event) {
 | 
| +        this.fire('google-map-poly-' + name, event);
 | 
| +      }.bind(this));
 | 
| +    },
 | 
| +
 | 
| +    _startEditing: function(index) {
 | 
| +      if (this._building) {
 | 
| +        // Ignore changes while building path.
 | 
| +        return;
 | 
| +      }
 | 
| +
 | 
| +      // Signal start of editing when first vertex inserted, end when map clicked.
 | 
| +      if (!this.editing) {
 | 
| +        this._setEditing(true);
 | 
| +        // The poly path and google-map-point elements lose sync once the user starts adding points,
 | 
| +        // so invalidate the _points array.
 | 
| +        this._points = null;
 | 
| +        google.maps.event.addListenerOnce(this.map, 'click', function() {
 | 
| +          this._setEditing(false);
 | 
| +          this.fire('google-map-poly-path-updated', this.path);
 | 
| +        }.bind(this));
 | 
| +      }
 | 
| +    },
 | 
| +
 | 
| +    _updatePoint: function(index, vertex) {
 | 
| +      // Ignore changes if path is out of sync with google-map-point elements.
 | 
| +      if (!this._points) {
 | 
| +        return;
 | 
| +      }
 | 
| +
 | 
| +      // Update existing point so bound properties are updated. too.
 | 
| +      this._points[index].latitude = vertex.lat();
 | 
| +      this._points[index].longitude = vertex.lng();
 | 
| +    }
 | 
| +  });
 | 
| +</script>
 | 
| 
 |