UNPKG

gmaps.core

Version:

gmaps.js core that contains the base for create simple maps using Google Maps API

403 lines (326 loc) 12.2 kB
'use strict'; var _forEach = require('lodash-compat/collection/forEach'), _map = require('lodash-compat/collection/map'), _forIn = require('lodash-compat/object/forIn'), _extend = require('lodash-compat/object/extend'), _omit = require('lodash-compat/object/omit'), isGoogleMapsRegistered = (typeof google === 'object' && google.maps), nativeMethods = [ 'setCenter', 'streetView_changed', 'getDiv', 'panBy', 'panTo', 'panToBounds', 'fitBounds', 'getBounds', 'getStreetView', 'setStreetView', 'getCenter', 'getZoom', 'setZoom', 'getMapTypeId', 'setMapTypeId', 'getProjection', 'getHeading', 'setHeading', 'getTilt', 'setTilt', 'get', 'set', 'notify', 'setValues', 'setOptions', 'changed', 'bindTo', 'unbind', 'unbindAll', 'addListener' ]; function querySelector(selector, context) { var element; if (!context) { context = document; } if (context.querySelector) { element = context.querySelector(selector); } else if (selector[0] === '#') { element = document.getElementById(selector.replace(/^#/, '')); } else if (selector[0] === '.') { element = context.getElementsByClassName(selector.replace(/^\./, ''))[0]; } return element; } function coordsToLatLngs(coords, useGeoJSON) { var firstCoordinate = coords[0], secondCoordinate = coords[1]; if (useGeoJSON) { firstCoordinate = coords[1]; secondCoordinate = coords[0]; } return new google.maps.LatLng(firstCoordinate, secondCoordinate); } function arrayToLatLng(coords, useGeoJSON) { var coordinates = _map(coords, function(coordinate) { var isGoogleLatLng = (coordinate instanceof google.maps.LatLng), looksLikeMultiDimensionalArray = (coordinate.length > 0 && typeof coordinate[0] === 'object'); if (!isGoogleLatLng) { if (looksLikeMultiDimensionalArray) { coordinate = arrayToLatLng(coordinate, useGeoJSON); } else { coordinate = coordsToLatLngs(coordinate, useGeoJSON); } } return coordinate; }); return coordinates; } function findAbsolutePosition(element) { var left = 0, top = 0, absolutePosition = {}; if (element.offsetParent) { do { left += element.offsetLeft; top += element.offsetTop; } while ((element = element.offsetParent)); } absolutePosition.left = left; absolutePosition.top = top; return absolutePosition; } function buildContextMenuContent(gmaps, control, rightClickEvent) { var html = '', controlOptions = gmaps.contextMenu[control], contextMenuElement = querySelector('#gmaps_context_menu_' + gmaps._id), contextMenuItems; if (!contextMenuElement) { return; } _forIn(controlOptions, function(option, key) { if (controlOptions.hasOwnProperty(key)) { html += '<li><a id="' + control + '_' + key + '" href="#">' + option.title + '</a></li>'; } }); contextMenuElement.innerHTML = html; contextMenuItems = contextMenuElement.getElementsByTagName('a'); function assignMenuItemAction(clickEvent) { clickEvent.preventDefault(); var controlKey = this.id.replace(control + '_', ''); controlOptions[controlKey].action.call(gmaps, rightClickEvent); gmaps.hideContextMenu(); } _forEach(contextMenuItems, function(contextMenuItem) { google.maps.event.clearListeners(contextMenuItem, 'click'); google.maps.event.addDomListenerOnce(contextMenuItem, 'click', assignMenuItemAction, false); }); var position = findAbsolutePosition(gmaps.element), left = position.left + rightClickEvent.pixel.x - 15, top = position.top + rightClickEvent.pixel.y - 15; contextMenuElement.style.left = left + 'px'; contextMenuElement.style.top = top + 'px'; contextMenuElement.style.display = 'block'; } function setupListener(gmaps, object, eventName, listener) { google.maps.event.addListener(object, eventName, function(mapEvent) { if (!mapEvent) { mapEvent = gmaps; } listener.call(gmaps, mapEvent); gmaps.hideContextMenu(); }); } function GMaps(options) { if (!isGoogleMapsRegistered) { throw 'Google Maps API is required. Please register the following JavaScript library in your site: http://maps.google.com/maps/api/js?sensor=true'; } if (!this) { return new GMaps(options); } var self = this, optionsToDelete = [ 'el', 'div', 'lat', 'lng', 'mapType', 'width', 'height', 'markerClusterer', 'enableNewStyle' ], hidingContextMenuEvents = [ 'bounds_changed', 'center_changed', 'click', 'dblclick', 'drag', 'dragend', 'dragstart', 'idle', 'maptypeid_changed', 'projection_changed', 'resize', 'tilesloaded', 'zoom_changed' ], notHidingContextMenuEvents = ['mousemove', 'mouseout', 'mouseover'], containerIdentifier = options.el || options.div, zoom = options.zoom || 15, mapType = google.maps.MapTypeId[(options.mapType || 'roadmap').toUpperCase()], mapCenter = new google.maps.LatLng(options.lat, options.lng), markerClustererFunction = options.markerClusterer, zoomControl = options.zoomControl || true, zoomControlOptions = options.zoomControlOptions || { style: 'default', position: 'top_left' }, panControl = options.panControl || true, mapTypeControl = options.mapTypeControl || true, scaleControl = options.scaleControl || true, streetViewControl = options.streetViewControl || true, overviewMapControl = options.overviewMapControl || true, mapBaseOptions = { zoom: zoom, mapTypeId: mapType, center: mapCenter }, mapControlsOptions = { panControl: panControl, zoomControl: zoomControl, zoomControlOptions: { style: google.maps.ZoomControlStyle[(zoomControlOptions.style || 'default').toUpperCase()], position: google.maps.ControlPosition[(zoomControlOptions.position || 'top_left').toUpperCase()] }, mapTypeControl: mapTypeControl, scaleControl: scaleControl, streetViewControl: streetViewControl, overviewMapControl: overviewMapControl }, mapOptions; if (typeof options.el === 'string' || typeof options.div === 'string') { this.element = querySelector(containerIdentifier, options.context); } else { this.element = containerIdentifier; } if (!this.element) { throw 'No element defined.'; } this.contextMenu = {}; this.controls = []; this.overlays = []; this.layers = []; // array with kml/georss and fusiontables layers, can be as many this.singleLayers = {}; // object with the other layers, only one per layer this.markers = []; this.polylines = []; this.routes = []; this.polygons = []; this.infoWindow = null; this.overlayElement = this.overlayEl = null; this.zoom = zoom; this.registeredEvents = {}; this.element.style.width = options.width || this.element.scrollWidth || this.element.offsetWidth; this.element.style.height = options.height || this.element.scrollHeight || this.element.offsetHeight; this._id = Date.now() + '_' + Math.ceil(Math.random() * Date.now()); google.maps.visualRefresh = options.enableNewStyle; if (options.disableDefaultUI !== true) { mapBaseOptions = _extend(mapBaseOptions, mapControlsOptions); } mapOptions = _extend(mapBaseOptions, options); mapOptions = _omit(mapOptions, optionsToDelete); mapOptions = _omit(mapOptions, hidingContextMenuEvents); mapOptions = _omit(mapOptions, notHidingContextMenuEvents); this.map = new google.maps.Map(this.element, mapOptions); _forEach(hidingContextMenuEvents, function(eventName) { if (eventName in options) { setupListener(self, self.map, eventName, options[eventName]); } }); _forEach(notHidingContextMenuEvents, function(eventName) { if (eventName in options) { setupListener(self, self.map, eventName, options[eventName]); } }); google.maps.event.addListener(this.map, 'zoom_changed', this.hideContextMenu); google.maps.event.addListener(this.map, 'rightclick', function(rightClickEvent) { if (typeof options.rightclick === 'function') { options.rightclick.call(this, rightClickEvent); } if (self.contextMenu.map) { self.buildContextMenu('map', rightClickEvent); } }); if (typeof markerClustererFunction === 'function') { this.markerClusterer = markerClustererFunction.apply(this, [this.map]); } } for (var counter = 0; counter < nativeMethods.length; counter++) { (function(gmapsPrototype, methodName) { gmapsPrototype[methodName] = function() { return this.map[methodName].apply(this.map, arguments); }; })(GMaps.prototype, nativeMethods[counter]); } GMaps.prototype.refresh = function() { google.maps.event.trigger(this.map, 'resize'); }; GMaps.prototype.fitZoom = function() { var latLngs = []; _forEach(this.markers, function(marker) { if (marker.visible === true) { latLngs.push(marker.getPosition()); } }); if (latLngs.length > 0) { this.fitLatLngBounds(latLngs); } }; GMaps.prototype.fitLatLngBounds = function(latLngs) { var bounds = new google.maps.LatLngBounds(); _forEach(latLngs, function(latLng) { bounds.extend(latLng); }); if (!bounds.isEmpty()) { this.map.fitBounds(bounds); } }; GMaps.prototype.setCenter = function(lat, lng, callback) { this.map.panTo(new google.maps.LatLng(lat, lng)); if (typeof callback === 'function') { callback.call(this, lat, lng); } }; GMaps.prototype.getElement = function() { return this.element; }; GMaps.prototype.zoomIn = function(value) { value = value || 1; this.zoom = this.map.getZoom() + value; this.map.setZoom(this.zoom); }; GMaps.prototype.zoomOut = function(value) { value = value || 1; this.zoom = this.map.getZoom() - value; this.map.setZoom(this.zoom); }; GMaps.prototype.buildContextMenu = function(control, rightClickEvent) { var self = this; if (control === 'marker') { var overlay = new google.maps.OverlayView(); overlay.setMap(self.map); overlay.draw = function() { var projection = overlay.getProjection(), position = rightClickEvent.marker.getPosition(); rightClickEvent.pixel = projection.fromLatLngToContainerPixel(position); buildContextMenuContent(self, control, rightClickEvent); }; } else { buildContextMenuContent(self, control, rightClickEvent); } }; GMaps.prototype.getContextMenu = function() { return querySelector('#gmaps_context_menu_' + this._id); }; GMaps.prototype.setContextMenu = function(options) { var contextMenuElement = document.createElement('ul'), contextMenuControlType = options.control, contextMenuOptions = options.options, contextMenu = {}; _forIn(contextMenuOptions, function(option, key) { if (contextMenuOptions.hasOwnProperty(key)) { contextMenu[option.name] = { title: option.title, action: option.action }; } }); this.contextMenu[contextMenuControlType] = contextMenu; contextMenuElement.id = 'gmaps_context_menu_' + this._id; contextMenuElement.style.display = 'none'; contextMenuElement.style.position = 'absolute'; contextMenuElement.style.minWidth = '100px'; contextMenuElement.style.background = 'white'; contextMenuElement.style.listStyle = 'none'; contextMenuElement.style.padding = '8px'; contextMenuElement.style.boxShadow = '2px 2px 6px #ccc'; document.body.appendChild(contextMenuElement); google.maps.event.addDomListener(contextMenuElement, 'mouseout', function(mouseOutEvent) { if (!mouseOutEvent.relatedTarget || !this.contains(mouseOutEvent.relatedTarget)) { setTimeout(function() { contextMenuElement.style.display = 'none'; }, 400); } }, false); }; GMaps.prototype.hideContextMenu = function() { var contextMenuElement = querySelector('#gmaps_context_menu_' + this._id); if (contextMenuElement) { contextMenuElement.style.display = 'none'; } }; GMaps.arrayToLatLng = arrayToLatLng; GMaps.coordsToLatLngs = coordsToLatLngs; global.GMaps = GMaps; module.exports = GMaps;