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> |