UNPKG

maptoolkit

Version:

Utilidades para trabajar con el mapa de google web.

1,690 lines (1,434 loc) 368 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define("MapTk", [], factory); else if(typeof exports === 'object') exports["MapTk"] = factory(); else root["MapTk"] = factory(); })(this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 1); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return MtEventSource; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_ts_eventemitter__ = __webpack_require__(4); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_ts_eventemitter___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_ts_eventemitter__); var MtEventSource = (function () { function MtEventSource() { this.eventDispatcher = __WEBPACK_IMPORTED_MODULE_0_ts_eventemitter___default.a.create(); } MtEventSource.prototype.event = function (name) { return this.eventDispatcher.event(name); }; MtEventSource.prototype.removeAllListeners = function () { this.eventDispatcher.removeAllListeners(); }; MtEventSource.prototype.setMaxListeners = function (n) { this.eventDispatcher.setMaxListeners(n); }; return MtEventSource; }()); MtEventSource.EVT_MAP_IDLE = "onMapIdle"; MtEventSource.EVT_MAP_CLICK = "onMapClick"; MtEventSource.EVT_MAP_ZOOM_CHANGED = "onZoomChanged"; MtEventSource.EVT_MAP_MARKER_CLICK = "onMarkerClick"; /***/ }), /* 1 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__maptoolkit__ = __webpack_require__(2); /* harmony namespace reexport (by provided) */ __webpack_require__.d(__webpack_exports__, "MapToolkit", function() { return __WEBPACK_IMPORTED_MODULE_0__maptoolkit__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__mtmarker__ = __webpack_require__(8); /* harmony namespace reexport (by provided) */ __webpack_require__.d(__webpack_exports__, "MtMarker", function() { return __WEBPACK_IMPORTED_MODULE_1__mtmarker__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__mteventsource__ = __webpack_require__(0); /* harmony namespace reexport (by provided) */ __webpack_require__.d(__webpack_exports__, "MtEventSource", function() { return __WEBPACK_IMPORTED_MODULE_2__mteventsource__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__math__ = __webpack_require__(9); /* harmony namespace reexport (by provided) */ __webpack_require__.d(__webpack_exports__, "MapMath", function() { return __WEBPACK_IMPORTED_MODULE_3__math__["a"]; }); /***/ }), /* 2 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return MapToolkit; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_js_info_bubble__ = __webpack_require__(3); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_js_info_bubble___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_js_info_bubble__); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__mteventsource__ = __webpack_require__(0); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__mkcollection__ = __webpack_require__(6); var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var _noop = function () { }; var _defRouteOptions = { travelMode: 'TRANSIT', strokeColor: 'pink', strokeOpacity: 1, strokeWeight: 6 }; var _defBubbleInfoOptions = { content: '', position: null, shadowStyle: 1, padding: 0, backgroundColor: "#fff", borderRadius: 2, arrowSize: 0, borderWidth: 1, borderColor: "#f3f3f3", disableAutoPan: false, hideCloseButton: false, arrowPosition: 50, backgroundClassName: '', minWidth: 300, minHeight: 50, arrowStyle: 0, }; var MapToolkit = (function (_super) { __extends(MapToolkit, _super); function MapToolkit(nativeMap, _options) { var _this = _super.call(this) || this; _this.nativeMap = nativeMap; _this._bubbleMarkup = "<div>{{name}}</div>"; var self = _this; _this._options = _options || {}; google.maps.event.addListener(self.nativeMap, 'idle', function () { self.event(MapToolkit.EVT_MAP_IDLE).emit({ source: self, evt: {} }); }); google.maps.event.addListener(self.nativeMap, 'click', function (event) { self.event(MapToolkit.EVT_MAP_CLICK).emit({ source: self, evt: event }); }); google.maps.event.addListener(self.nativeMap, "zoom_changed", function () { self.event(MapToolkit.EVT_MAP_ZOOM_CHANGED).emit({ source: self, evt: {} }); if (self._markerSelected) { self._ubicarInfoBubble(self.nativeMap.getZoom(), self._markerSelected); } }); _this._markerGroupCollection = new __WEBPACK_IMPORTED_MODULE_2__mkcollection__["a" /* MtMarkerGroupCollection */](); return _this; } MapToolkit.prototype.option = function (name, defValue) { return this._options[name] != void 0 ? this._options[name] : defValue; }; MapToolkit.prototype.getSelectedMarker = function () { return this._markerSelected; }; MapToolkit.prototype.addMarker = function (marker, group) { if (marker) { this.addAllMarkers([marker], group); } return this; }; MapToolkit.prototype.addAllMarkers = function (markers, group) { var _this = this; if (markers && markers.length) { this._markerGroupCollection.addAllMarkers(markers, group).forEach(function (mk) { mk.event(MapToolkit.EVT_MAP_MARKER_CLICK).addListener(function (args) { _this._markerSelected = args.source; _this.event(MapToolkit.EVT_MAP_MARKER_CLICK).emit({ source: _this, marker: args.source, evt: args.evt }); }); if (_this.option("markerIconSelector")) { mk.setIcon(_this.option("markerIconSelector")(mk.model)); } mk.show(_this.nativeMap); }); } return this; }; MapToolkit.prototype.setMarker = function (marker, group) { this.setAllMarkers([marker], group); }; /** * Elimina los markers que no estan mostrandose y adiciona los nuevos. * @param markers * @param group */ MapToolkit.prototype.setAllMarkers = function (markers, group) { if (markers && markers.length) { var toRemove = [], toUpdate = [], toAdd = []; var currMarkers = this._markerGroupCollection.getGroup(group); var setMkIds = {}; //busco los markers que no estan en el mapa y los pongo para adicionar //ademas hago un map de los ids para luego verificar los q no estan en currMarkers y eliminarlos markers.forEach(function (m) { setMkIds[m.getId()] = true; if (!currMarkers.containsKey(m.getId())) { toAdd.push(m); } else { toUpdate.push(m); } }); currMarkers.forEach(function (id, mk) { if (!setMkIds[mk.getId()]) { toRemove.push(mk); } }); //actualizo los markers toUpdate.forEach(function (m) { var actualMk = currMarkers.getValue(m.getId()); actualMk.setPosition(m.getPosition()); actualMk.setModel(m.model); }); //elimino los que no estan en markers this.removeAllMarkers(toRemove, group); //adiciono los que no estan en currMarkers this.addAllMarkers(toAdd, group); } return this; }; MapToolkit.prototype.removeMarker = function (id, group) { var mk = this._markerGroupCollection.removeMarker(id, group); if (mk) { mk.remove(); return mk; } }; MapToolkit.prototype.removeAllMarkers = function (markers, group) { if (markers && markers.length) { this._markerGroupCollection.removeMarkers(markers, group).forEach(function (m) { m.remove(); }); } }; MapToolkit.prototype.removeGroup = function (group) { var markers = this._markerGroupCollection.removeGroup(group); if (markers) { markers.forEach(function (mk) { mk.remove(); }); } }; MapToolkit.prototype.centerMapToMarkers = function () { var markers = this._markerGroupCollection.toArray(); if (markers.length > 0) { var latMin = markers[0].getPosition().lat(), latMax = markers[0].getPosition().lat(), lngMin = markers[0].getPosition().lng(), lngMax = markers[0].getPosition().lng(); var i; for (i = 1; i < markers.length; i++) { latMin = markers[i].getPosition().lat() < latMin ? markers[i].getPosition().lat() : latMin; latMax = markers[i].getPosition().lat() > latMax ? markers[i].getPosition().lat() : latMax; lngMin = markers[i].getPosition().lng() < lngMin ? markers[i].getPosition().lng() : lngMin; lngMax = markers[i].getPosition().lng() > lngMax ? markers[i].getPosition().lng() : lngMax; } var mediaLat = (latMin + latMax) / 2, mediaLng = (lngMin + lngMax) / 2; //busco el rig mas cercano a la media var markerMiddle = markers[0]; var difCentro = Math.abs(Math.abs(markerMiddle.getPosition().lat()) - Math.abs(mediaLat)) + Math.abs(Math.abs(markerMiddle.getPosition().lng() - Math.abs(mediaLng))); for (i = 1; i < markers.length; i++) { var difRig = Math.abs(Math.abs(markers[i].getPosition().lat()) - Math.abs(mediaLat)) + Math.abs(Math.abs(markers[i].getPosition().lng()) - Math.abs(mediaLng)); if (difRig < difCentro) { markerMiddle = markers[i]; difCentro = difRig; } } this.centerMap(markerMiddle.getPosition(), 5); } }; MapToolkit.prototype.centerMap = function (pos, zoom) { if (typeof (pos) == "string") { var vals = pos.split(","); pos = new google.maps.LatLng(parseFloat(vals[0]), parseFloat(vals[1])); } this.nativeMap.setCenter(pos); this.nativeMap.setZoom(zoom); }; MapToolkit.prototype.centerAndZoomToMarkers = function (positions) { positions = positions || this._markerGroupCollection.toArray().map(function (mk) { return mk.getPosition(); }); var latlngbounds = new google.maps.LatLngBounds(); for (var i = 0; i < positions.length; i++) { latlngbounds.extend(new google.maps.LatLng(positions[i].lat(), positions[i].lng())); } var zprev = this.nativeMap.getZoom(); this.nativeMap.fitBounds(latlngbounds); this.nativeMap.setZoom(zprev); }; MapToolkit.prototype.getZoom = function () { return this.nativeMap.getZoom(); }; MapToolkit.prototype.getCenter = function () { return this.nativeMap.getCenter(); }; MapToolkit.prototype.getBounds = function () { return this.nativeMap.getBounds(); }; MapToolkit.prototype.paintCircle = function (radio, center, circleOpts, clickListener) { var self = this; if (self._radioSearchPaint) self._radioSearchPaint.setMap(null); // Add the circle for this city to the map. self._radioSearchPaint = new google.maps.Circle(circleOpts); if (clickListener) google.maps.event.addListener(self._radioSearchPaint, 'click', function (event) { clickListener(event); }); return self._radioSearchPaint; }; MapToolkit.prototype.showInfoBubble = function (options) { options = Object.assign({}, _defBubbleInfoOptions, options, { map: this.nativeMap, position: this._getInfoBubblePositionForMarkerPosition(this.nativeMap.getZoom(), options.position) }); this.closeInfoBubble(); var infoBubble = new __WEBPACK_IMPORTED_MODULE_0_js_info_bubble__["InfoBubble"](options); infoBubble.open(); this._infoBubbleOpened = infoBubble; }; MapToolkit.prototype.closeInfoBubble = function () { this._infoBubbleOpened && this._infoBubbleOpened.close(); }; MapToolkit.prototype.showRoute = function (start, end, options) { var self = this; options = Object.assign({}, _defRouteOptions, options); return new Promise(function (resolve, reject) { var request = { origin: start, destination: end, travelMode: options.travelMode }; var directionsService = new google.maps.DirectionsService(); var directionsDisplay = new google.maps.DirectionsRenderer({ polylineOptions: { strokeColor: options.strokeColor }, suppressMarkers: true }); directionsService.route(request, function (response, status) { if (status == google.maps.DirectionsStatus.OK) { directionsDisplay.setDirections(response); directionsDisplay.setMap(self.nativeMap); self._routeDrawed = directionsDisplay; resolve(self._routeDrawed); } else { reject({ response: response, status: status }); } }); }); }; MapToolkit.prototype.clearRoute = function () { if (this._routeDrawed) { this._routeDrawed.setMap(null); this._routeDrawed = null; } }; MapToolkit.prototype._getInfoBubblePositionForMarkerPosition = function (zoom, pos) { var newLat = pos.lat() + (0.00004 * Math.pow(2, (19 - zoom))); return new google.maps.LatLng(newLat, pos.lng()); }; MapToolkit.prototype._ubicarInfoBubble = function (zoom, marker) { if (this._infoBubbleOpened) { //position infoBubble for each zoom level change, based on arithmetic logic (notice the power of 2) this._infoBubbleOpened.setPosition(this._getInfoBubblePositionForMarkerPosition(zoom, marker.getPosition())); } }; return MapToolkit; }(__WEBPACK_IMPORTED_MODULE_1__mteventsource__["a" /* MtEventSource */])); /***/ }), /* 3 */ /***/ (function(module, exports) { // ==ClosureCompiler== // @compilation_level ADVANCED_OPTIMIZATIONS // @externs_url https://raw.githubusercontent.com/google/closure-compiler/master/contrib/externs/maps/google_maps_api_v3_16.js // ==/ClosureCompiler== /** * @name CSS3 InfoBubble with tabs for Google Maps API V3 * @version 0.8 * @author Luke Mahe * @fileoverview * This library is a CSS Infobubble with tabs. It uses css3 rounded corners and * drop shadows and animations. It also allows tabs */ /* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * A CSS3 InfoBubble v0.8 * @param {Object.<string, *>=} opt_options Optional properties to set. * @extends {google.maps.OverlayView} * @constructor */ function InfoBubble(opt_options) { this.extend(InfoBubble, google.maps.OverlayView); this.tabs_ = []; this.activeTab_ = null; this.baseZIndex_ = 100; this.isOpen_ = false; var options = opt_options || {}; if (options['backgroundColor'] == undefined) { options['backgroundColor'] = this.BACKGROUND_COLOR_; } if (options['borderColor'] == undefined) { options['borderColor'] = this.BORDER_COLOR_; } if (options['borderRadius'] == undefined) { options['borderRadius'] = this.BORDER_RADIUS_; } if (options['borderWidth'] == undefined) { options['borderWidth'] = this.BORDER_WIDTH_; } if (options['padding'] == undefined) { options['padding'] = this.PADDING_; } if (options['arrowPosition'] == undefined) { options['arrowPosition'] = this.ARROW_POSITION_; } if (options['disableAutoPan'] == undefined) { options['disableAutoPan'] = false; } if (options['disableAnimation'] == undefined) { options['disableAnimation'] = false; } if (options['minWidth'] == undefined) { options['minWidth'] = this.MIN_WIDTH_; } if (options['shadowStyle'] == undefined) { options['shadowStyle'] = this.SHADOW_STYLE_; } if (options['arrowSize'] == undefined) { options['arrowSize'] = this.ARROW_SIZE_; } if (options['arrowStyle'] == undefined) { options['arrowStyle'] = this.ARROW_STYLE_; } if (options['closeSrc'] == undefined) { options['closeSrc'] = this.CLOSE_SRC_; } this.buildDom_(); this.setValues(options); } window['InfoBubble'] = InfoBubble; /** * Default arrow size * @const * @private */ InfoBubble.prototype.ARROW_SIZE_ = 15; /** * Default arrow style * @const * @private */ InfoBubble.prototype.ARROW_STYLE_ = 0; /** * Default shadow style * @const * @private */ InfoBubble.prototype.SHADOW_STYLE_ = 1; /** * Default min width * @const * @private */ InfoBubble.prototype.MIN_WIDTH_ = 50; /** * Default arrow position * @const * @private */ InfoBubble.prototype.ARROW_POSITION_ = 50; /** * Default padding * @const * @private */ InfoBubble.prototype.PADDING_ = 10; /** * Default border width * @const * @private */ InfoBubble.prototype.BORDER_WIDTH_ = 1; /** * Default border color * @const * @private */ InfoBubble.prototype.BORDER_COLOR_ = '#ccc'; /** * Default border radius * @const * @private */ InfoBubble.prototype.BORDER_RADIUS_ = 10; /** * Default background color * @const * @private */ InfoBubble.prototype.BACKGROUND_COLOR_ = '#fff'; /** * Default close image source * @const * @private */ InfoBubble.prototype.CLOSE_SRC_ = 'https://maps.gstatic.com/intl/en_us/mapfiles/iw_close.gif'; /** * Extends a objects prototype by anothers. * * @param {Object} obj1 The object to be extended. * @param {Object} obj2 The object to extend with. * @return {Object} The new extended object. * @ignore */ InfoBubble.prototype.extend = function(obj1, obj2) { return (function(object) { for (var property in object.prototype) { this.prototype[property] = object.prototype[property]; } return this; }).apply(obj1, [obj2]); }; /** * Builds the InfoBubble dom * @private */ InfoBubble.prototype.buildDom_ = function() { var bubble = this.bubble_ = document.createElement('DIV'); bubble.style['position'] = 'absolute'; bubble.style['zIndex'] = this.baseZIndex_; var tabsContainer = this.tabsContainer_ = document.createElement('DIV'); tabsContainer.style['position'] = 'relative'; // Close button var close = this.close_ = document.createElement('IMG'); close.style['position'] = 'absolute'; close.style['border'] = 0; close.style['zIndex'] = this.baseZIndex_ + 1; close.style['cursor'] = 'pointer'; close.src = this.get('closeSrc'); var that = this; google.maps.event.addDomListener(close, 'click', function() { that.close(); google.maps.event.trigger(that, 'closeclick'); }); // Content area var contentContainer = this.contentContainer_ = document.createElement('DIV'); contentContainer.style['overflowX'] = 'auto'; contentContainer.style['overflowY'] = 'auto'; contentContainer.style['cursor'] = 'default'; contentContainer.style['clear'] = 'both'; contentContainer.style['position'] = 'relative'; var content = this.content_ = document.createElement('DIV'); contentContainer.appendChild(content); // Arrow var arrow = this.arrow_ = document.createElement('DIV'); arrow.style['position'] = 'relative'; var arrowOuter = this.arrowOuter_ = document.createElement('DIV'); var arrowInner = this.arrowInner_ = document.createElement('DIV'); var arrowSize = this.getArrowSize_(); arrowOuter.style['position'] = arrowInner.style['position'] = 'absolute'; arrowOuter.style['left'] = arrowInner.style['left'] = '50%'; arrowOuter.style['height'] = arrowInner.style['height'] = '0'; arrowOuter.style['width'] = arrowInner.style['width'] = '0'; arrowOuter.style['marginLeft'] = this.px(-arrowSize); arrowOuter.style['borderWidth'] = this.px(arrowSize); arrowOuter.style['borderBottomWidth'] = 0; // Shadow var bubbleShadow = this.bubbleShadow_ = document.createElement('DIV'); bubbleShadow.style['position'] = 'absolute'; // Hide the InfoBubble by default bubble.style['display'] = bubbleShadow.style['display'] = 'none'; bubble.appendChild(this.tabsContainer_); bubble.appendChild(close); bubble.appendChild(contentContainer); arrow.appendChild(arrowOuter); arrow.appendChild(arrowInner); bubble.appendChild(arrow); var stylesheet = document.createElement('style'); stylesheet.setAttribute('type', 'text/css'); /** * The animation for the infobubble * @type {string} */ this.animationName_ = '_ibani_' + Math.round(Math.random() * 10000); var css = '.' + this.animationName_ + '{-webkit-animation-name:' + this.animationName_ + ';-webkit-animation-duration:0.5s;' + '-webkit-animation-iteration-count:1;}' + '@-webkit-keyframes ' + this.animationName_ + ' {from {' + '-webkit-transform: scale(0)}50% {-webkit-transform: scale(1.2)}90% ' + '{-webkit-transform: scale(0.95)}to {-webkit-transform: scale(1)}}'; stylesheet.textContent = css; document.getElementsByTagName('head')[0].appendChild(stylesheet); }; /** * Sets the background class name * * @param {string} className The class name to set. */ InfoBubble.prototype.setBackgroundClassName = function(className) { this.set('backgroundClassName', className); }; InfoBubble.prototype['setBackgroundClassName'] = InfoBubble.prototype.setBackgroundClassName; /** * changed MVC callback */ InfoBubble.prototype.backgroundClassName_changed = function() { this.content_.className = this.get('backgroundClassName'); }; InfoBubble.prototype['backgroundClassName_changed'] = InfoBubble.prototype.backgroundClassName_changed; /** * Sets the class of the tab * * @param {string} className the class name to set. */ InfoBubble.prototype.setTabClassName = function(className) { this.set('tabClassName', className); }; InfoBubble.prototype['setTabClassName'] = InfoBubble.prototype.setTabClassName; /** * tabClassName changed MVC callback */ InfoBubble.prototype.tabClassName_changed = function() { this.updateTabStyles_(); }; InfoBubble.prototype['tabClassName_changed'] = InfoBubble.prototype.tabClassName_changed; /** * Gets the style of the arrow * * @private * @return {number} The style of the arrow. */ InfoBubble.prototype.getArrowStyle_ = function() { return parseInt(this.get('arrowStyle'), 10) || 0; }; /** * Sets the style of the arrow * * @param {number} style The style of the arrow. */ InfoBubble.prototype.setArrowStyle = function(style) { this.set('arrowStyle', style); }; InfoBubble.prototype['setArrowStyle'] = InfoBubble.prototype.setArrowStyle; /** * Arrow style changed MVC callback */ InfoBubble.prototype.arrowStyle_changed = function() { this.arrowSize_changed(); }; InfoBubble.prototype['arrowStyle_changed'] = InfoBubble.prototype.arrowStyle_changed; /** * Gets the size of the arrow * * @private * @return {number} The size of the arrow. */ InfoBubble.prototype.getArrowSize_ = function() { return parseInt(this.get('arrowSize'), 10) || 0; }; /** * Sets the size of the arrow * * @param {number} size The size of the arrow. */ InfoBubble.prototype.setArrowSize = function(size) { this.set('arrowSize', size); }; InfoBubble.prototype['setArrowSize'] = InfoBubble.prototype.setArrowSize; /** * Arrow size changed MVC callback */ InfoBubble.prototype.arrowSize_changed = function() { this.borderWidth_changed(); }; InfoBubble.prototype['arrowSize_changed'] = InfoBubble.prototype.arrowSize_changed; /** * Set the position of the InfoBubble arrow * * @param {number} pos The position to set. */ InfoBubble.prototype.setArrowPosition = function(pos) { this.set('arrowPosition', pos); }; InfoBubble.prototype['setArrowPosition'] = InfoBubble.prototype.setArrowPosition; /** * Get the position of the InfoBubble arrow * * @private * @return {number} The position.. */ InfoBubble.prototype.getArrowPosition_ = function() { return parseInt(this.get('arrowPosition'), 10) || 0; }; /** * arrowPosition changed MVC callback */ InfoBubble.prototype.arrowPosition_changed = function() { var pos = this.getArrowPosition_(); this.arrowOuter_.style['left'] = this.arrowInner_.style['left'] = pos + '%'; this.redraw_(); }; InfoBubble.prototype['arrowPosition_changed'] = InfoBubble.prototype.arrowPosition_changed; /** * Set the zIndex of the InfoBubble * * @param {number} zIndex The zIndex to set. */ InfoBubble.prototype.setZIndex = function(zIndex) { this.set('zIndex', zIndex); }; InfoBubble.prototype['setZIndex'] = InfoBubble.prototype.setZIndex; /** * Get the zIndex of the InfoBubble * * @return {number} The zIndex to set. */ InfoBubble.prototype.getZIndex = function() { return parseInt(this.get('zIndex'), 10) || this.baseZIndex_; }; /** * zIndex changed MVC callback */ InfoBubble.prototype.zIndex_changed = function() { var zIndex = this.getZIndex(); this.bubble_.style['zIndex'] = this.baseZIndex_ = zIndex; this.close_.style['zIndex'] = zIndex + 1; }; InfoBubble.prototype['zIndex_changed'] = InfoBubble.prototype.zIndex_changed; /** * Set the style of the shadow * * @param {number} shadowStyle The style of the shadow. */ InfoBubble.prototype.setShadowStyle = function(shadowStyle) { this.set('shadowStyle', shadowStyle); }; InfoBubble.prototype['setShadowStyle'] = InfoBubble.prototype.setShadowStyle; /** * Get the style of the shadow * * @private * @return {number} The style of the shadow. */ InfoBubble.prototype.getShadowStyle_ = function() { return parseInt(this.get('shadowStyle'), 10) || 0; }; /** * shadowStyle changed MVC callback */ InfoBubble.prototype.shadowStyle_changed = function() { var shadowStyle = this.getShadowStyle_(); var display = ''; var shadow = ''; var backgroundColor = ''; switch (shadowStyle) { case 0: display = 'none'; break; case 1: shadow = '40px 15px 10px rgba(33,33,33,0.3)'; backgroundColor = 'transparent'; break; case 2: shadow = '0 0 2px rgba(33,33,33,0.3)'; backgroundColor = 'rgba(33,33,33,0.35)'; break; } this.bubbleShadow_.style['boxShadow'] = this.bubbleShadow_.style['webkitBoxShadow'] = this.bubbleShadow_.style['MozBoxShadow'] = shadow; this.bubbleShadow_.style['backgroundColor'] = backgroundColor; if (this.isOpen_) { this.bubbleShadow_.style['display'] = display; this.draw(); } }; InfoBubble.prototype['shadowStyle_changed'] = InfoBubble.prototype.shadowStyle_changed; /** * Show the close button */ InfoBubble.prototype.showCloseButton = function() { this.set('hideCloseButton', false); }; InfoBubble.prototype['showCloseButton'] = InfoBubble.prototype.showCloseButton; /** * Hide the close button */ InfoBubble.prototype.hideCloseButton = function() { this.set('hideCloseButton', true); }; InfoBubble.prototype['hideCloseButton'] = InfoBubble.prototype.hideCloseButton; /** * hideCloseButton changed MVC callback */ InfoBubble.prototype.hideCloseButton_changed = function() { this.close_.style['display'] = this.get('hideCloseButton') ? 'none' : ''; }; InfoBubble.prototype['hideCloseButton_changed'] = InfoBubble.prototype.hideCloseButton_changed; /** * Set the background color * * @param {string} color The color to set. */ InfoBubble.prototype.setBackgroundColor = function(color) { if (color) { this.set('backgroundColor', color); } }; InfoBubble.prototype['setBackgroundColor'] = InfoBubble.prototype.setBackgroundColor; /** * backgroundColor changed MVC callback */ InfoBubble.prototype.backgroundColor_changed = function() { var backgroundColor = this.get('backgroundColor'); this.contentContainer_.style['backgroundColor'] = backgroundColor; this.arrowInner_.style['borderColor'] = backgroundColor + ' transparent transparent'; this.updateTabStyles_(); }; InfoBubble.prototype['backgroundColor_changed'] = InfoBubble.prototype.backgroundColor_changed; /** * Set the border color * * @param {string} color The border color. */ InfoBubble.prototype.setBorderColor = function(color) { if (color) { this.set('borderColor', color); } }; InfoBubble.prototype['setBorderColor'] = InfoBubble.prototype.setBorderColor; /** * borderColor changed MVC callback */ InfoBubble.prototype.borderColor_changed = function() { var borderColor = this.get('borderColor'); var contentContainer = this.contentContainer_; var arrowOuter = this.arrowOuter_; contentContainer.style['borderColor'] = borderColor; arrowOuter.style['borderColor'] = borderColor + ' transparent transparent'; contentContainer.style['borderStyle'] = arrowOuter.style['borderStyle'] = this.arrowInner_.style['borderStyle'] = 'solid'; this.updateTabStyles_(); }; InfoBubble.prototype['borderColor_changed'] = InfoBubble.prototype.borderColor_changed; /** * Set the radius of the border * * @param {number} radius The radius of the border. */ InfoBubble.prototype.setBorderRadius = function(radius) { this.set('borderRadius', radius); }; InfoBubble.prototype['setBorderRadius'] = InfoBubble.prototype.setBorderRadius; /** * Get the radius of the border * * @private * @return {number} The radius of the border. */ InfoBubble.prototype.getBorderRadius_ = function() { return parseInt(this.get('borderRadius'), 10) || 0; }; /** * borderRadius changed MVC callback */ InfoBubble.prototype.borderRadius_changed = function() { var borderRadius = this.getBorderRadius_(); var borderWidth = this.getBorderWidth_(); this.contentContainer_.style['borderRadius'] = this.contentContainer_.style['MozBorderRadius'] = this.contentContainer_.style['webkitBorderRadius'] = this.bubbleShadow_.style['borderRadius'] = this.bubbleShadow_.style['MozBorderRadius'] = this.bubbleShadow_.style['webkitBorderRadius'] = this.px(borderRadius); this.tabsContainer_.style['paddingLeft'] = this.tabsContainer_.style['paddingRight'] = this.px(borderRadius + borderWidth); this.redraw_(); }; InfoBubble.prototype['borderRadius_changed'] = InfoBubble.prototype.borderRadius_changed; /** * Get the width of the border * * @private * @return {number} width The width of the border. */ InfoBubble.prototype.getBorderWidth_ = function() { return parseInt(this.get('borderWidth'), 10) || 0; }; /** * Set the width of the border * * @param {number} width The width of the border. */ InfoBubble.prototype.setBorderWidth = function(width) { this.set('borderWidth', width); }; InfoBubble.prototype['setBorderWidth'] = InfoBubble.prototype.setBorderWidth; /** * borderWidth change MVC callback */ InfoBubble.prototype.borderWidth_changed = function() { var borderWidth = this.getBorderWidth_(); this.contentContainer_.style['borderWidth'] = this.px(borderWidth); this.tabsContainer_.style['top'] = this.px(borderWidth); this.updateArrowStyle_(); this.updateTabStyles_(); this.borderRadius_changed(); this.redraw_(); }; InfoBubble.prototype['borderWidth_changed'] = InfoBubble.prototype.borderWidth_changed; /** * Update the arrow style * @private */ InfoBubble.prototype.updateArrowStyle_ = function() { var borderWidth = this.getBorderWidth_(); var arrowSize = this.getArrowSize_(); var arrowStyle = this.getArrowStyle_(); var arrowOuterSizePx = this.px(arrowSize); var arrowInnerSizePx = this.px(Math.max(0, arrowSize - borderWidth)); var outer = this.arrowOuter_; var inner = this.arrowInner_; this.arrow_.style['marginTop'] = this.px(-borderWidth); outer.style['borderTopWidth'] = arrowOuterSizePx; inner.style['borderTopWidth'] = arrowInnerSizePx; // Full arrow or arrow pointing to the left if (arrowStyle == 0 || arrowStyle == 1) { outer.style['borderLeftWidth'] = arrowOuterSizePx; inner.style['borderLeftWidth'] = arrowInnerSizePx; } else { outer.style['borderLeftWidth'] = inner.style['borderLeftWidth'] = 0; } // Full arrow or arrow pointing to the right if (arrowStyle == 0 || arrowStyle == 2) { outer.style['borderRightWidth'] = arrowOuterSizePx; inner.style['borderRightWidth'] = arrowInnerSizePx; } else { outer.style['borderRightWidth'] = inner.style['borderRightWidth'] = 0; } if (arrowStyle < 2) { outer.style['marginLeft'] = this.px(-(arrowSize)); inner.style['marginLeft'] = this.px(-(arrowSize - borderWidth)); } else { outer.style['marginLeft'] = inner.style['marginLeft'] = 0; } // If there is no border then don't show thw outer arrow if (borderWidth == 0) { outer.style['display'] = 'none'; } else { outer.style['display'] = ''; } }; /** * Set the padding of the InfoBubble * * @param {number} padding The padding to apply. */ InfoBubble.prototype.setPadding = function(padding) { this.set('padding', padding); }; InfoBubble.prototype['setPadding'] = InfoBubble.prototype.setPadding; /** * Set the close image url * * @param {string} src The url of the image used as a close icon */ InfoBubble.prototype.setCloseSrc = function(src) { if (src && this.close_) { this.close_.src = src; } }; InfoBubble.prototype['setCloseSrc'] = InfoBubble.prototype.setCloseSrc; /** * Set the padding of the InfoBubble * * @private * @return {number} padding The padding to apply. */ InfoBubble.prototype.getPadding_ = function() { return parseInt(this.get('padding'), 10) || 0; }; /** * padding changed MVC callback */ InfoBubble.prototype.padding_changed = function() { var padding = this.getPadding_(); this.contentContainer_.style['padding'] = this.px(padding); this.updateTabStyles_(); this.redraw_(); }; InfoBubble.prototype['padding_changed'] = InfoBubble.prototype.padding_changed; /** * Add px extention to the number * * @param {number} num The number to wrap. * @return {string|number} A wrapped number. */ InfoBubble.prototype.px = function(num) { if (num) { // 0 doesn't need to be wrapped return num + 'px'; } return num; }; /** * Add events to stop propagation * @private */ InfoBubble.prototype.addEvents_ = function() { // We want to cancel all the events so they do not go to the map var events = ['mousedown', 'mousemove', 'mouseover', 'mouseout', 'mouseup', 'mousewheel', 'DOMMouseScroll', 'touchstart', 'touchend', 'touchmove', 'dblclick', 'contextmenu', 'click']; var bubble = this.bubble_; this.listeners_ = []; for (var i = 0, event; event = events[i]; i++) { this.listeners_.push( google.maps.event.addDomListener(bubble, event, function(e) { e.cancelBubble = true; if (e.stopPropagation) { e.stopPropagation(); } }) ); } }; /** * On Adding the InfoBubble to a map * Implementing the OverlayView interface */ InfoBubble.prototype.onAdd = function() { if (!this.bubble_) { this.buildDom_(); } this.addEvents_(); var panes = this.getPanes(); if (panes) { panes.floatPane.appendChild(this.bubble_); panes.floatShadow.appendChild(this.bubbleShadow_); } /* once the infoBubble has been added to the DOM, fire 'domready' event */ google.maps.event.trigger(this, 'domready'); }; InfoBubble.prototype['onAdd'] = InfoBubble.prototype.onAdd; /** * Draw the InfoBubble * Implementing the OverlayView interface */ InfoBubble.prototype.draw = function() { var projection = this.getProjection(); if (!projection) { // The map projection is not ready yet so do nothing return; } var latLng = /** @type {google.maps.LatLng} */ (this.get('position')); if (!latLng) { this.close(); return; } var tabHeight = 0; if (this.activeTab_) { tabHeight = this.activeTab_.offsetHeight; } var anchorHeight = this.getAnchorHeight_(); var arrowSize = this.getArrowSize_(); var arrowPosition = this.getArrowPosition_(); arrowPosition = arrowPosition / 100; var pos = projection.fromLatLngToDivPixel(latLng); var width = this.contentContainer_.offsetWidth; var height = this.bubble_.offsetHeight; if (!width) { return; } // Adjust for the height of the info bubble var top = pos.y - (height + arrowSize); if (anchorHeight) { // If there is an anchor then include the height top -= anchorHeight; } var left = pos.x - (width * arrowPosition); this.bubble_.style['top'] = this.px(top); this.bubble_.style['left'] = this.px(left); var shadowStyle = parseInt(this.get('shadowStyle'), 10); switch (shadowStyle) { case 1: // Shadow is behind this.bubbleShadow_.style['top'] = this.px(top + tabHeight - 1); this.bubbleShadow_.style['left'] = this.px(left); this.bubbleShadow_.style['width'] = this.px(width); this.bubbleShadow_.style['height'] = this.px(this.contentContainer_.offsetHeight - arrowSize); break; case 2: // Shadow is below width = width * 0.8; if (anchorHeight) { this.bubbleShadow_.style['top'] = this.px(pos.y); } else { this.bubbleShadow_.style['top'] = this.px(pos.y + arrowSize); } this.bubbleShadow_.style['left'] = this.px(pos.x - width * arrowPosition); this.bubbleShadow_.style['width'] = this.px(width); this.bubbleShadow_.style['height'] = this.px(2); break; } }; InfoBubble.prototype['draw'] = InfoBubble.prototype.draw; /** * Removing the InfoBubble from a map */ InfoBubble.prototype.onRemove = function() { if (this.bubble_ && this.bubble_.parentNode) { this.bubble_.parentNode.removeChild(this.bubble_); } if (this.bubbleShadow_ && this.bubbleShadow_.parentNode) { this.bubbleShadow_.parentNode.removeChild(this.bubbleShadow_); } for (var i = 0, listener; listener = this.listeners_[i]; i++) { google.maps.event.removeListener(listener); } }; InfoBubble.prototype['onRemove'] = InfoBubble.prototype.onRemove; /** * Is the InfoBubble open * * @return {boolean} If the InfoBubble is open. */ InfoBubble.prototype.isOpen = function() { return this.isOpen_; }; InfoBubble.prototype['isOpen'] = InfoBubble.prototype.isOpen; /** * Close the InfoBubble */ InfoBubble.prototype.close = function() { if (this.bubble_) { this.bubble_.style['display'] = 'none'; // Remove the animation so we next time it opens it will animate again this.bubble_.className = this.bubble_.className.replace(this.animationName_, ''); } if (this.bubbleShadow_) { this.bubbleShadow_.style['display'] = 'none'; this.bubbleShadow_.className = this.bubbleShadow_.className.replace(this.animationName_, ''); } this.isOpen_ = false; }; InfoBubble.prototype['close'] = InfoBubble.prototype.close; /** * Open the InfoBubble (asynchronous). * * @param {google.maps.Map=} opt_map Optional map to open on. * @param {google.maps.MVCObject=} opt_anchor Optional anchor to position at. */ InfoBubble.prototype.open = function(opt_map, opt_anchor) { var that = this; window.setTimeout(function() { that.open_(opt_map, opt_anchor); }, 0); }; /** * Open the InfoBubble * @private * @param {google.maps.Map=} opt_map Optional map to open on. * @param {google.maps.MVCObject=} opt_anchor Optional anchor to position at. */ InfoBubble.prototype.open_ = function(opt_map, opt_anchor) { this.updateContent_(); if (opt_map) { this.setMap(opt_map); } if (opt_anchor) { this.set('anchor', opt_anchor); this.bindTo('anchorPoint', opt_anchor); this.bindTo('position', opt_anchor); } // Show the bubble and the show this.bubble_.style['display'] = this.bubbleShadow_.style['display'] = ''; var animation = !this.get('disableAnimation'); if (animation) { // Add the animation this.bubble_.className += ' ' + this.animationName_; this.bubbleShadow_.className += ' ' + this.animationName_; } this.redraw_(); this.isOpen_ = true; var pan = !this.get('disableAutoPan'); if (pan) { var that = this; window.setTimeout(function() { // Pan into view, done in a time out to make it feel nicer :) that.panToView(); }, 200); } }; InfoBubble.prototype['open'] = InfoBubble.prototype.open; /** * Set the position of the InfoBubble * * @param {google.maps.LatLng} position The position to set. */ InfoBubble.prototype.setPosition = function(position) { if (position) { this.set('position', position); } }; InfoBubble.prototype['setPosition'] = InfoBubble.prototype.setPosition; /** * Returns the position of the InfoBubble * * @return {google.maps.LatLng} the position. */ InfoBubble.prototype.getPosition = function() { return /** @type {google.maps.LatLng} */ (this.get('position')); }; InfoBubble.prototype['getPosition'] = InfoBubble.prototype.getPosition; /** * position changed MVC callback */ InfoBubble.prototype.position_changed = function() { this.draw(); }; InfoBubble.prototype['position_changed'] = InfoBubble.prototype.position_changed; /** * Pan the InfoBubble into view */ InfoBubble.prototype.panToView = function() { var projection = this.getProjection(); if (!projection) { // The map projection is not ready yet so do nothing return; } if (!this.bubble_) { // No Bubble yet so do nothing return; } var anchorHeight = this.getAnchorHeight_(); var height = this.bubble_.offsetHeight + anchorHeight; var map = this.get('map'); var mapDiv = map.getDiv(); var mapHeight = mapDiv.offsetHeight; var latLng = this.getPosition(); var centerPos = projection.fromLatLngToContainerPixel(map.getCenter()); var pos = projection.fromLatLngToContainerPixel(latLng); // Find out how much space at the top is free var spaceTop = centerPos.y - height; // Fine out how much space at the bottom is free var spaceBottom = mapHeight - centerPos.y; var needsTop = spaceTop < 0; var deltaY = 0; if (needsTop) { spaceTop *= -1; deltaY = (spaceTop + spaceBottom) / 2; } pos.y -= deltaY; latLng = projection.fromContainerPixelToLatLng(pos); if (map.getCenter() != latLng) { map.panTo(latLng); } }; InfoBubble.prototype['panToView'] = InfoBubble.prototype.panToView; /** * Converts a HTML string to a document fragment. * * @param {string} htmlString The HTML string to convert. * @return {Node} A HTML document fragment. * @private */ InfoBubble.prototype.htmlToDocumentFragment_ = function(htmlString) { htmlString = htmlString.replace(/^\s*([\S\s]*)\b\s*$/, '$1'); var tempDiv = document.createElement('DIV'); tempDiv.innerHTML = htmlString; if (tempDiv.childNodes.length == 1) { return /** @type {!Node} */ (tempDiv.removeChild(tempDiv.firstChild)); } else { var fragment = document.createDocumentFragment(); while (tempDiv.firstChild) { fragment.appendChild(tempDiv.firstChild); } return fragment; } }; /** * Removes all children from the node. * * @param {Node} node The node to remove all children from. * @private */ InfoBubble.prototype.removeChildren_ = function(node) { if (!node) { return; } var child; while (child = node.firstChild) { node.removeChild(child); } }; /** * Sets the content of the infobubble. * * @param {string|Node} content The content to set. */ InfoBubble.prototype.setContent = function(content) { this.set('content', content); }; InfoBubble.prototype['setContent'] = InfoBubble.prototype.setContent; /** * Get the content of the infobubble. * * @return {string|Node} The marker content. */ InfoBubble.prototype.getContent = function() { return /** @type {Node|string} */ (this.get('content')); }; InfoBubble.prototype['getContent'] = InfoBubble.prototype.getContent; /** * Sets the marker content and adds loading events to images */ InfoBubble.prototype.updateContent_ = function() { if (!this.content_) { // The Content area doesnt exist. return; } this.removeChildren_(this.content_); var content = this.getContent(); if (content) { if (typeof content == 'string') { content = this.htmlToDocumentFragment_(content); } this.content_.appendChild(content); var that = this; var images = this.content_.getElementsByTagName('IMG'); for (var i = 0, image; image = images[i]; i++) { // Because we don't know the size of an image till it loads, add a // listener to the image load so the marker can resize and reposition // itself to be the correct height. google.maps.event.addDomListener(image, 'load', function() { that.imageLoaded_(); }); } } this.redraw_(); }; /** * Image loaded * @private */ InfoBubble.prototype.imageLoaded_ = function() { var pan = !this.get('disableAutoPan'); this.redraw_(); if (pan && (this.tabs_.length == 0 || this.activeTab_.index == 0)) { this.panToView(); } }; /** * Updates the styles of the tabs * @private */ InfoBubble.prototype.updateTabStyles_ = function() { if (this.tabs_ && this.tabs_.length) { for (var i = 0, tab; tab = this.tabs_[i]; i++) { this.setTabStyle_(tab.tab); } this.activeTab_.style['zIndex'] = this.baseZIndex_; var borderWidth = this.getBorderWidth_(); var padding = this.getPadding_() / 2; this.activeTab_.style['borderBottomWidth'] = 0; this.activeTab_.style['paddingBottom'] = this.px(padding + borderWidth); } }; /** * Sets the style of a tab * @private * @param {Element} tab The tab to style. */ InfoBubble.prototype.setTabStyle_ = function(tab) { var backgroundColor = this.get('backgroundColor'); var borderColor = this.get('borderColor'); var borderRadius = this.getBorderRadius_(); var borderWidth = this.getBorderWidth_(); var padd