532 lines
18 KiB
JavaScript
532 lines
18 KiB
JavaScript
"use strict";
|
|
/**
|
|
* This js file defines an abstract map API.
|
|
* This abstract map API is used by location related features, to replace previous direct usage of Google Maps API.
|
|
* The API design is almost as same as Google Maps API and some 3rd party Google Maps Plugins API (RichMarker, InfoBubble, MarkerClusterer).
|
|
* Note that this abstract map API is NOT a full set of API replacement to Google Maps API, it only contains corresponding APIs used by our code.
|
|
*/
|
|
(function () {
|
|
var bmcMaps = {
|
|
/**
|
|
* Identifiers for common MapTypes.
|
|
*/
|
|
MapTypeId: {
|
|
/**
|
|
* This map type displays a normal street map.
|
|
*/
|
|
ROADMAP: 'roadmap',
|
|
/**
|
|
* This map type displays satellite images.
|
|
*/
|
|
SATELLITE: 'satellite',
|
|
/**
|
|
* This map type displays a transparent layer of major streets on satellite images.
|
|
*/
|
|
HYBRID: 'hybrid'
|
|
},
|
|
/**
|
|
* Identifiers used to specify the placement of controls on the map.
|
|
*/
|
|
ControlPosition: {
|
|
/**
|
|
* Elements are positioned on the left, above bottom-left elements, and flow upwards.
|
|
*/
|
|
LEFT_BOTTOM: 6
|
|
},
|
|
/**
|
|
* Identifiers for the zoom control.
|
|
*/
|
|
ZoomControlStyle: {
|
|
/**
|
|
* The larger control, with the zoom slider in addition to +/- buttons.
|
|
*/
|
|
LARGE: 2
|
|
}
|
|
};
|
|
/**
|
|
* Namespace for adapters static methods
|
|
*/
|
|
bmcMaps.adapters = {};
|
|
bmcMaps._adapters = {};
|
|
// Load multiple scripts
|
|
bmcMaps.adapters.loadScripts = function (scripts) {
|
|
var promises = [];
|
|
for (var i = 0, l = scripts.length; i < l; i++) {
|
|
promises.push($.getScript(scripts[i]));
|
|
}
|
|
return $.when.apply($, promises);
|
|
};
|
|
bmcMaps.adapters.saveMapsAvailability = function (availability) {
|
|
angular.mapsAvailable = availability;
|
|
};
|
|
bmcMaps.adapters.getMapAdapterLink = function (adapterId) {
|
|
return 'scripts/app/location/map-api-adapters/map-adapter-' + adapterId + '.js';
|
|
};
|
|
bmcMaps.adapters.loadAdapter = function (adapterId, callback) {
|
|
var mapAdapterLink = this.getMapAdapterLink(adapterId);
|
|
$.getScript(mapAdapterLink, callback);
|
|
};
|
|
bmcMaps.adapters.init = function (data) {
|
|
var adapterId = data.MAP_API || 'google';
|
|
var deferred = $.Deferred();
|
|
this.loadAdapter(adapterId, function () {
|
|
var adapter = bmcMaps.adapters.activate(adapterId);
|
|
// init specific adapter
|
|
adapter.init(data, deferred);
|
|
});
|
|
return deferred.promise();
|
|
};
|
|
/**
|
|
* Register a map adapter
|
|
* @param {string} id Identifier of the map adapter to be registered
|
|
* @param {Object} adapter Map adapter object
|
|
*/
|
|
bmcMaps.adapters.register = function (id, adapter) {
|
|
bmcMaps._adapters[id] = adapter;
|
|
};
|
|
/**
|
|
* Set active map adapter
|
|
* @param {string} id Identifier of the map adapter to be active
|
|
*/
|
|
bmcMaps.adapters.activate = function (id) {
|
|
bmcMaps.adapter = bmcMaps._adapters[id];
|
|
return bmcMaps.adapter;
|
|
};
|
|
bmcMaps.mapInit = function (map) {
|
|
if (_.isFunction(bmcMaps.adapter.mapInit)) {
|
|
bmcMaps.adapter.mapInit(map.adapter);
|
|
}
|
|
};
|
|
/**
|
|
* Namespace for event static methods
|
|
*/
|
|
bmcMaps.event = {};
|
|
/**
|
|
* Add the given listener function to the given event name for the given object instance.
|
|
* @param {bmcMaps.MVCObject} abstraction Abstract map object instance
|
|
* @param {string} eventType Event name to listen on
|
|
* @param {Function} listener Listener function callback
|
|
*/
|
|
bmcMaps.event.addListener = function (abstraction, eventType, listener) {
|
|
bmcMaps.adapter.event.addListener(abstraction.adapter, eventType, listener);
|
|
};
|
|
/**
|
|
* Trigger the given event on given object instance.
|
|
* @param {bmcMaps.MVCObject} abstraction Abstract map object instance
|
|
* @param {string} eventType Event name to listen on
|
|
*/
|
|
bmcMaps.event.trigger = function (abstraction, eventType) {
|
|
bmcMaps.adapter.event.trigger(abstraction.adapter || { impl: abstraction.impl || abstraction }, eventType);
|
|
};
|
|
/**
|
|
* Cross browser event handler registration.
|
|
* @param {Node} element HTML node element
|
|
* @param {string} eventType Event name to listen on
|
|
* @param {Function} listener Listener function callback
|
|
*/
|
|
bmcMaps.event.addDomListener = function (element, eventType, listener) {
|
|
bmcMaps.adapter.event.addDomListener(element, eventType, listener);
|
|
};
|
|
/**
|
|
* Like addListener, but the handler removes itself after handling the first event.
|
|
* @param {bmcMaps.MVCObject} abstraction Abstract map object instance
|
|
* @param {string} eventType Event name to listen on
|
|
* @param {Function} listener Listener function callback
|
|
*/
|
|
bmcMaps.event.addListenerOnce = function (abstraction, eventType, listener) {
|
|
bmcMaps.adapter.event.addListenerOnce(abstraction.adapter, eventType, listener);
|
|
};
|
|
/**
|
|
* Removes all event listeners from element
|
|
* @param {bmcMaps.MVCObject} abstraction Abstract map object instance
|
|
*/
|
|
bmcMaps.event.clearInstanceListeners = function (abstraction) {
|
|
bmcMaps.adapter.event.clearInstanceListeners(abstraction.adapter);
|
|
};
|
|
/**
|
|
* Base class wrapping key-value pairs object.
|
|
*/
|
|
bmcMaps.MVCObject = function () {
|
|
};
|
|
/**
|
|
* Set a collection of key-value pairs.
|
|
* @param {Object} options Object with key-value pairs
|
|
*/
|
|
bmcMaps.MVCObject.prototype.setValues = function (options) {
|
|
for (var key in options) {
|
|
if (this[key] === undefined) {
|
|
this[key] = options[key];
|
|
}
|
|
}
|
|
};
|
|
bmcMaps.geocoder = {};
|
|
bmcMaps.geocoder.geocode = function (address, successCb, failCB) {
|
|
bmcMaps.adapter.geocoder.geocode(address, successCb, failCB);
|
|
};
|
|
bmcMaps.getDirectionUrl = function (address) {
|
|
return bmcMaps.adapter.getDirectionUrl(address);
|
|
};
|
|
/**
|
|
* Map class. This class extends MVCObject.
|
|
* @param {Node} element HTML node element as map container
|
|
* @param {Object} options Map options with key-value pairs
|
|
* @constructor
|
|
*/
|
|
bmcMaps.Map = function (element, options) {
|
|
this.setValues(options);
|
|
this.adapter = bmcMaps.adapter.map(element, options);
|
|
this.mapTypes = new bmcMaps.MapTypeRegistry(this);
|
|
};
|
|
bmcMaps.Map.prototype = new bmcMaps.MVCObject();
|
|
/**
|
|
* Map type registry class.
|
|
* @param {bmcMaps.Map} map Atract map object
|
|
* @constructor
|
|
*/
|
|
bmcMaps.MapTypeRegistry = function (map) {
|
|
this.adapter = map.adapter.getMapTypeRegistry();
|
|
};
|
|
/**
|
|
* Set a custom map type.
|
|
* @param {string} id Identifier of map type
|
|
* @param {Object} mapType Object to specify a custom map type
|
|
*/
|
|
bmcMaps.MapTypeRegistry.prototype.set = function (id, mapType) {
|
|
this[id] = mapType;
|
|
this.adapter.set(id, mapType);
|
|
};
|
|
/**
|
|
* Add Marker to the map based on location.
|
|
* @param {location} location of the marker
|
|
*/
|
|
bmcMaps.Map.prototype.addMarker = function (location, icon) {
|
|
this.adapter.addMarker(location, icon);
|
|
};
|
|
/**
|
|
* Set the position displayed at the center of the map.
|
|
* @param {bmcMaps.LatLng} latlng Position to be the center of the map
|
|
*/
|
|
bmcMaps.Map.prototype.setCenter = function (latlng) {
|
|
this.adapter.setCenter(latlng);
|
|
};
|
|
/**
|
|
* Set the map type to be displayed
|
|
* @param {bmcMaps.MapTypeId|String} mapTypeId Identifier of map type to be displayed
|
|
*/
|
|
bmcMaps.Map.prototype.setMapTypeId = function (mapTypeId) {
|
|
this.mapTypeId = mapTypeId;
|
|
this.adapter.setMapTypeId(mapTypeId);
|
|
};
|
|
/**
|
|
* Return the position displayed at the center of the map.
|
|
* @returns {bmcMaps.LatLng} Position of the map center
|
|
*/
|
|
bmcMaps.Map.prototype.getCenter = function () {
|
|
return this.adapter.getCenter();
|
|
};
|
|
/**
|
|
* Set the viewport to contain the given bounds.
|
|
* @param {bmcMaps.LatLngBounds} bounds
|
|
*/
|
|
bmcMaps.Map.prototype.fitBounds = function (bounds) {
|
|
this.adapter.fitBounds(bounds);
|
|
};
|
|
/**
|
|
* Set the zoom level of the map.
|
|
* @param {number} zoom
|
|
*/
|
|
bmcMaps.Map.prototype.setZoom = function (zoom) {
|
|
this.adapter.setZoom(zoom);
|
|
};
|
|
/**
|
|
* Get the zoom level of the map.
|
|
* @return {number}
|
|
*/
|
|
bmcMaps.Map.prototype.getZoom = function () {
|
|
return this.adapter.getZoom();
|
|
};
|
|
/**
|
|
* Returns the lat/lng bounds of the current viewport.
|
|
* @return {bmcMaps.LatLngBounds}
|
|
*/
|
|
bmcMaps.Map.prototype.getBounds = function () {
|
|
return this.adapter.getBounds();
|
|
};
|
|
/**
|
|
* Returns the current Projection.
|
|
* @return {bmcMaps.Projection}
|
|
*/
|
|
bmcMaps.Map.prototype.getProjection = function () {
|
|
return new bmcMaps.Projection(this);
|
|
};
|
|
/**
|
|
* Map projection class.
|
|
* @param {bmcMaps.Map} map Atract map object
|
|
* @constructor
|
|
*/
|
|
bmcMaps.Projection = function (map) {
|
|
this.adapter = map.adapter.getProjection();
|
|
};
|
|
/**
|
|
* Translates from world coordinates on a map projection to LatLng values.
|
|
* @param {bmcMaps.Point} point World coordinates point
|
|
* @return {bmcMaps.LatLng}
|
|
*/
|
|
bmcMaps.Projection.prototype.fromPointToLatLng = function (point) {
|
|
return this.adapter.fromPointToLatLng(point);
|
|
};
|
|
/**
|
|
* Pans the map by the minimum amount necessary to contain the given LatLngBounds.
|
|
* @param {bmcMaps.LatLngBounds} bounds
|
|
*/
|
|
bmcMaps.Map.prototype.panToBounds = function (bounds) {
|
|
this.adapter.panToBounds(bounds);
|
|
};
|
|
/**
|
|
* Changes the center of the map to the given LatLng.
|
|
* @param {bmcMaps.LatLng} latlng
|
|
*/
|
|
bmcMaps.Map.prototype.panTo = function (latlng) {
|
|
this.adapter.panTo(latlng);
|
|
};
|
|
/**
|
|
* A point on a two-dimensional plane.
|
|
* @param {number} x x-axis value
|
|
* @param {number} y y-axis value
|
|
*/
|
|
bmcMaps.Point = function (x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
};
|
|
/**
|
|
* Two-dimensonal size, where width is the distance on the x-axis, and height is the distance on the y-axis.
|
|
* @param {number} width
|
|
* @param {number} height
|
|
*/
|
|
bmcMaps.Size = function (width, height) {
|
|
this.width = width;
|
|
this.height = height;
|
|
};
|
|
/**
|
|
* A LatLng is a point in geographical coordinates: latitude and longitude.
|
|
* Latitude is specified in degrees within the range [-90, 90]. Longitude is specified in degrees within the range [-180, 180]
|
|
* @param {number} lat latitude
|
|
* @param {number} lng longitude
|
|
*/
|
|
bmcMaps.LatLng = function (lat, lng) {
|
|
this._lat = lat;
|
|
this._lng = lng;
|
|
};
|
|
/**
|
|
* Returns the latitude in degrees.
|
|
* @return {number}
|
|
*/
|
|
bmcMaps.LatLng.prototype.lat = function () {
|
|
return this._lat;
|
|
};
|
|
/**
|
|
* Returns the longitude in degrees.
|
|
* @return {number}
|
|
*/
|
|
bmcMaps.LatLng.prototype.lng = function () {
|
|
return this._lng;
|
|
};
|
|
/**
|
|
* A LatLngBounds instance represents a rectangle in geographical coordinates.
|
|
* Constructs a rectangle from the points at its south-west and north-east corners.
|
|
* @param {bmcMaps.LatLng} sw South-west corner
|
|
* @param {bmcMaps.LatLng} ne North-east corner
|
|
*/
|
|
bmcMaps.LatLngBounds = function (sw, ne) {
|
|
if (sw) {
|
|
this.sw = new bmcMaps.LatLng(sw._lat, sw._lng);
|
|
if (!ne) {
|
|
this.ne = new bmcMaps.LatLng(sw._lat, sw._lng);
|
|
}
|
|
}
|
|
if (ne) {
|
|
this.ne = new bmcMaps.LatLng(ne._lat, ne._lng);
|
|
if (!sw) {
|
|
this.sw = new bmcMaps.LatLng(ne._lat, ne._lng);
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* Returns true if the given lat/lng is in this bounds.
|
|
* @param {bmcMaps.LatLng} latlng position to be tested
|
|
* @return {boolean}
|
|
*/
|
|
bmcMaps.LatLngBounds.prototype.contains = function (latlng) {
|
|
return latlng._lat >= this.sw._lat && latlng._lat <= this.ne._lat
|
|
&& (this.sw._lng <= this.ne._lng && latlng._lng >= this.sw._lng && latlng._lng <= this.ne._lng
|
|
|| this.sw._lng > this.ne._lng && (latlng._lng >= this.sw._lng || latlng._lng <= this.ne._lng));
|
|
};
|
|
/**
|
|
* Extends this bounds to contain the given point.
|
|
* @param {bmcMaps.LatLng} latlng position to be contained
|
|
*/
|
|
bmcMaps.LatLngBounds.prototype.extend = function (latlng) {
|
|
if (!this.sw && !this.ne) {
|
|
this.sw = new bmcMaps.LatLng(latlng._lat, latlng._lng);
|
|
this.ne = new bmcMaps.LatLng(latlng._lat, latlng._lng);
|
|
return;
|
|
}
|
|
if (this.sw._lat > latlng._lat) {
|
|
this.sw._lat = latlng._lat;
|
|
}
|
|
if (this.sw._lng > latlng._lng) {
|
|
this.sw._lng = latlng._lng;
|
|
}
|
|
if (this.ne._lat < latlng._lat) {
|
|
this.ne._lat = latlng._lat;
|
|
}
|
|
if (this.ne._lng < latlng._lng) {
|
|
this.ne._lng = latlng._lng;
|
|
}
|
|
};
|
|
/**
|
|
* Computes the center of this LatLngBounds
|
|
* @return {bmcMaps.LatLng}
|
|
*/
|
|
bmcMaps.LatLngBounds.prototype.getCenter = function () {
|
|
if (!this.sw && !this.ne) {
|
|
return undefined;
|
|
}
|
|
return new bmcMaps.LatLng((this.sw._lat + this.ne._lat) / 2, (this.sw._lng + this.ne._lng) / 2);
|
|
};
|
|
/**
|
|
* An overlay that looks like a bubble and is often connected to a marker. This class extends MVCObject.
|
|
* @param {Object} options Optional properties to set
|
|
* @constructor
|
|
*/
|
|
bmcMaps.InfoWindow = function (options) {
|
|
this.setValues(options);
|
|
if (options.map) {
|
|
options.map = options.map.adapter;
|
|
}
|
|
this.adapter = bmcMaps.adapter.infoWindow(options);
|
|
};
|
|
bmcMaps.InfoWindow.prototype = new bmcMaps.MVCObject();
|
|
/**
|
|
* Opens this InfoWindow on the given map.
|
|
*/
|
|
bmcMaps.InfoWindow.prototype.open = function () {
|
|
this.adapter.open(arguments);
|
|
};
|
|
/**
|
|
* A RichMarker that allows any HTML/DOM to be added to a map and be draggable. This class extends MVCObject.
|
|
*
|
|
* @param {Object} options Optional properties to set
|
|
* @constructor
|
|
*/
|
|
bmcMaps.RichMarker = function (options) {
|
|
this.setValues(options);
|
|
if (options.map) {
|
|
options.map = options.map.adapter;
|
|
}
|
|
this.adapter = bmcMaps.adapter.richMarker(options);
|
|
this.content = this.adapter.content;
|
|
};
|
|
bmcMaps.RichMarker.prototype = new bmcMaps.MVCObject();
|
|
/**
|
|
* Set position to display the marker
|
|
* @param {bmcMaps.LatLng} latlng position The position to set
|
|
*/
|
|
bmcMaps.RichMarker.prototype.setPosition = function (latlng) {
|
|
this.position = latlng;
|
|
this.adapter.setPosition(latlng);
|
|
};
|
|
/**
|
|
* Get position of the marker
|
|
* @return {bmcMaps.LatLng} The position of the marker
|
|
*/
|
|
bmcMaps.RichMarker.prototype.getPosition = function () {
|
|
return this.position;
|
|
};
|
|
/**
|
|
* Sets the visiblility state of the marker.
|
|
*
|
|
* @param {boolean} visible The visiblilty of the marker
|
|
*/
|
|
bmcMaps.RichMarker.prototype.setVisible = function (visible) {
|
|
this.adapter.setVisible(visible);
|
|
};
|
|
/**
|
|
* Destroy the marker instance
|
|
*/
|
|
bmcMaps.RichMarker.prototype.destroy = function () {
|
|
this.adapter.destroy();
|
|
};
|
|
/**
|
|
* A CSS3 InfoBubble. This class extends MVCObject.
|
|
* @param {Object} options Optional properties to set
|
|
* @constructor
|
|
*/
|
|
bmcMaps.InfoBubble = function (options) {
|
|
this.setValues(options);
|
|
if (options.map) {
|
|
options.map = options.map.adapter;
|
|
}
|
|
this.adapter = bmcMaps.adapter.infoBubble(options);
|
|
};
|
|
bmcMaps.InfoBubble.prototype = new bmcMaps.MVCObject();
|
|
/**
|
|
* Open the InfoBubble.
|
|
*
|
|
* @param {bmcMaps.Map} map Map to open on.
|
|
* @param {bmcMaps.MVCObject} anchor Optional anchor to position at.
|
|
*/
|
|
bmcMaps.InfoBubble.prototype.open = function (map, anchor) {
|
|
this.adapter.open(map.adapter, anchor ? anchor.adapter : undefined);
|
|
};
|
|
/**
|
|
* Close the InfoBubble
|
|
*/
|
|
bmcMaps.InfoBubble.prototype.close = function () {
|
|
this.adapter.close();
|
|
};
|
|
/**
|
|
* Get the content of the infobubble.
|
|
*
|
|
* @return {string|Node} The marker content.
|
|
*/
|
|
bmcMaps.InfoBubble.prototype.getContent = function () {
|
|
return this.adapter.getContent();
|
|
};
|
|
/**
|
|
* Sets the content of the infobubble.
|
|
*
|
|
* @param {string|Node} content The content to set.
|
|
*/
|
|
bmcMaps.InfoBubble.prototype.setContent = function (content) {
|
|
this.adapter.setContent(content);
|
|
};
|
|
/**
|
|
* Creates a MarkerClusterer object with the specified options. This class extends MVCObject.
|
|
* A MarkerClusterer object manages specified markers and displays groups of markers as a single marker with a number when necessary.
|
|
* @param {bmcMaps.Map} map Abstract map to attach to.
|
|
* @param {Array.<bmcMaps.RichMarker>} markers The markers to be added to the cluster.
|
|
* @param {Object} options The optional parameters.
|
|
* @constructor
|
|
*/
|
|
bmcMaps.MarkerClusterer = function (map, markers, options) {
|
|
this.setValues(options);
|
|
var markersAdapter = [];
|
|
for (var i = 0; i < markers.length; i++) {
|
|
markersAdapter.push(markers[i].adapter);
|
|
}
|
|
this.adapter = bmcMaps.adapter.markerClusterer(map.adapter, markersAdapter, options);
|
|
};
|
|
bmcMaps.MarkerClusterer.prototype = new bmcMaps.MVCObject();
|
|
/**
|
|
* Removes all clusters and markers from the map and also removes all markers managed by the clusterer.
|
|
*/
|
|
bmcMaps.MarkerClusterer.prototype.clearMarkers = function () {
|
|
this.adapter.clearMarkers();
|
|
};
|
|
/**
|
|
* Declar 'bmcMaps' in window scope so that it can be used same way as 'google.maps' namespace
|
|
*/
|
|
window.bmcMaps = bmcMaps;
|
|
})();
|