devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
462 lines (382 loc) • 15.4 kB
JavaScript
"use strict";
/* global google */
var $ = require("../../core/renderer"),
window = require("../../core/utils/window").getWindow(),
noop = require("../../core/utils/common").noop,
devices = require("../../core/devices"),
Promise = require("../../core/polyfills/promise"),
extend = require("../../core/utils/extend").extend,
map = require("../../core/utils/iterator").map,
DynamicProvider = require("./provider.dynamic"),
errors = require("../widget/ui.errors"),
Color = require("../../color"),
ajax = require("../../core/utils/ajax"),
isDefined = require("../../core/utils/type").isDefined;
var GOOGLE_MAP_READY = "_googleScriptReady",
GOOGLE_URL = "https://maps.googleapis.com/maps/api/js?callback=" + GOOGLE_MAP_READY;
var CustomMarker;
var initCustomMarkerClass = function initCustomMarkerClass() {
CustomMarker = function CustomMarker(options) {
this._position = options.position;
this._offset = options.offset;
this._$overlayContainer = $("<div>").css({
position: "absolute",
display: "none",
cursor: "pointer"
}).append(options.html);
this.setMap(options.map);
};
CustomMarker.prototype = new google.maps.OverlayView();
CustomMarker.prototype.onAdd = function () {
var $pane = $(this.getPanes().overlayMouseTarget);
$pane.append(this._$overlayContainer);
this._clickListener = google.maps.event.addDomListener(this._$overlayContainer.get(0), 'click', function (e) {
google.maps.event.trigger(this, 'click');
e.preventDefault();
}.bind(this));
this.draw();
};
CustomMarker.prototype.onRemove = function () {
google.maps.event.removeListener(this._clickListener);
this._$overlayContainer.remove();
};
CustomMarker.prototype.draw = function () {
var position = this.getProjection().fromLatLngToDivPixel(this._position);
this._$overlayContainer.css({
left: position.x + this._offset.left,
top: position.y + this._offset.top,
display: 'block'
});
};
};
var googleMapsLoaded = function googleMapsLoaded() {
return window.google && window.google.maps;
};
var googleMapsLoader;
var GoogleProvider = DynamicProvider.inherit({
_mapType: function _mapType(type) {
var mapTypes = {
hybrid: google.maps.MapTypeId.HYBRID,
roadmap: google.maps.MapTypeId.ROADMAP,
satellite: google.maps.MapTypeId.SATELLITE
};
return mapTypes[type] || mapTypes.hybrid;
},
_movementMode: function _movementMode(type) {
var movementTypes = {
driving: google.maps.TravelMode.DRIVING,
walking: google.maps.TravelMode.WALKING
};
return movementTypes[type] || movementTypes.driving;
},
_resolveLocation: function _resolveLocation(location) {
return new Promise(function (resolve) {
var latLng = this._getLatLng(location);
if (latLng) {
resolve(new google.maps.LatLng(latLng.lat, latLng.lng));
} else {
this._geocodeLocation(location).then(function (geocodedLocation) {
resolve(geocodedLocation);
});
}
}.bind(this));
},
_geocodedLocations: {},
_geocodeLocationImpl: function _geocodeLocationImpl(location) {
return new Promise(function (resolve) {
if (!isDefined(location)) {
resolve(new google.maps.LatLng(0, 0));
return;
}
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'address': location }, function (results, status) {
if (status === google.maps.GeocoderStatus.OK) {
resolve(results[0].geometry.location);
} else {
errors.log("W1006", status);
resolve(new google.maps.LatLng(0, 0));
}
});
});
},
_normalizeLocation: function _normalizeLocation(location) {
return {
lat: location.lat(),
lng: location.lng()
};
},
_normalizeLocationRect: function _normalizeLocationRect(locationRect) {
return {
northEast: this._normalizeLocation(locationRect.getNorthEast()),
southWest: this._normalizeLocation(locationRect.getSouthWest())
};
},
_loadImpl: function _loadImpl() {
return new Promise(function (resolve) {
if (googleMapsLoaded()) {
resolve();
} else {
if (!googleMapsLoader) {
googleMapsLoader = this._loadMapScript();
}
googleMapsLoader.then(function () {
if (googleMapsLoaded()) {
resolve();
return;
}
this._loadMapScript().then(resolve);
}.bind(this));
}
}.bind(this)).then(function () {
initCustomMarkerClass();
});
},
_loadMapScript: function _loadMapScript() {
return new Promise(function (resolve) {
var key = this._keyOption("google");
window[GOOGLE_MAP_READY] = resolve;
ajax.sendRequest({
url: GOOGLE_URL + (key ? "&key=" + key : ""),
dataType: "script"
});
}.bind(this)).then(function () {
try {
delete window[GOOGLE_MAP_READY];
} catch (e) {
window[GOOGLE_MAP_READY] = undefined;
}
});
},
_init: function _init() {
return new Promise(function (resolve) {
var controls = this._option("controls");
this._map = new google.maps.Map(this._$container[0], {
zoom: this._option("zoom"),
center: this._option("center"),
panControl: controls,
zoomControl: controls,
mapTypeControl: controls,
streetViewControl: controls
});
var listener = google.maps.event.addListener(this._map, 'idle', function () {
resolve(listener);
});
}.bind(this)).then(function (listener) {
google.maps.event.removeListener(listener);
});
},
_attachHandlers: function _attachHandlers() {
this._boundsChangeListener = google.maps.event.addListener(this._map, 'bounds_changed', this._boundsChangeHandler.bind(this));
this._clickListener = google.maps.event.addListener(this._map, 'click', this._clickActionHandler.bind(this));
},
_boundsChangeHandler: function _boundsChangeHandler() {
var bounds = this._map.getBounds();
this._option("bounds", this._normalizeLocationRect(bounds));
var center = this._map.getCenter();
this._option("center", this._normalizeLocation(center));
if (!this._preventZoomChangeEvent) {
this._option("zoom", this._map.getZoom());
}
},
_clickActionHandler: function _clickActionHandler(e) {
this._fireClickAction({ location: this._normalizeLocation(e.latLng) });
},
updateDimensions: function updateDimensions() {
var center = this._option("center");
google.maps.event.trigger(this._map, 'resize');
this._option("center", center);
return this.updateCenter();
},
updateMapType: function updateMapType() {
this._map.setMapTypeId(this._mapType(this._option("type")));
return Promise.resolve();
},
updateBounds: function updateBounds() {
return Promise.all([this._resolveLocation(this._option("bounds.northEast")), this._resolveLocation(this._option("bounds.southWest"))]).then(function (result) {
var bounds = new google.maps.LatLngBounds();
bounds.extend(result[0]);
bounds.extend(result[1]);
this._map.fitBounds(bounds);
}.bind(this));
},
updateCenter: function updateCenter() {
return this._resolveLocation(this._option("center")).then(function (center) {
this._map.setCenter(center);
this._option("center", this._normalizeLocation(center));
}.bind(this));
},
updateZoom: function updateZoom() {
this._map.setZoom(this._option("zoom"));
return Promise.resolve();
},
updateControls: function updateControls() {
var controls = this._option("controls");
this._map.setOptions({
panControl: controls,
zoomControl: controls,
mapTypeControl: controls,
streetViewControl: controls
});
return Promise.resolve();
},
isEventsCanceled: function isEventsCanceled() {
var gestureHandling = this._map && this._map.get("gestureHandling");
if (devices.real().deviceType !== "desktop" && gestureHandling === "cooperative") {
return false;
}
return this.callBase();
},
_renderMarker: function _renderMarker(options) {
return this._resolveLocation(options.location).then(function (location) {
var marker;
if (options.html) {
marker = new CustomMarker({
map: this._map,
position: location,
html: options.html,
offset: extend({
top: 0,
left: 0
}, options.htmlOffset)
});
} else {
marker = new google.maps.Marker({
position: location,
map: this._map,
icon: options.iconSrc || this._option("markerIconSrc")
});
}
var infoWindow = this._renderTooltip(marker, options.tooltip);
var listener;
if (options.onClick || options.tooltip) {
var markerClickAction = this._mapWidget._createAction(options.onClick || noop),
markerNormalizedLocation = this._normalizeLocation(location);
listener = google.maps.event.addListener(marker, "click", function () {
markerClickAction({
location: markerNormalizedLocation
});
if (infoWindow) {
infoWindow.open(this._map, marker);
}
}.bind(this));
}
return {
location: location,
marker: marker,
listener: listener
};
}.bind(this));
},
_renderTooltip: function _renderTooltip(marker, options) {
if (!options) {
return;
}
options = this._parseTooltipOptions(options);
var infoWindow = new google.maps.InfoWindow({
content: options.text
});
if (options.visible) {
infoWindow.open(this._map, marker);
}
return infoWindow;
},
_destroyMarker: function _destroyMarker(marker) {
marker.marker.setMap(null);
if (marker.listener) {
google.maps.event.removeListener(marker.listener);
}
},
_renderRoute: function _renderRoute(options) {
return Promise.all(map(options.locations, function (point) {
return this._resolveLocation(point);
}.bind(this))).then(function (locations) {
return new Promise(function (resolve) {
var origin = locations.shift(),
destination = locations.pop(),
waypoints = map(locations, function (location) {
return { location: location, stopover: true };
});
var request = {
origin: origin,
destination: destination,
waypoints: waypoints,
optimizeWaypoints: true,
travelMode: this._movementMode(options.mode)
};
new google.maps.DirectionsService().route(request, function (response, status) {
if (status === google.maps.DirectionsStatus.OK) {
var color = new Color(options.color || this._defaultRouteColor()).toHex(),
directionOptions = {
directions: response,
map: this._map,
suppressMarkers: true,
preserveViewport: true,
polylineOptions: {
strokeWeight: options.weight || this._defaultRouteWeight(),
strokeOpacity: options.opacity || this._defaultRouteOpacity(),
strokeColor: color
}
};
var route = new google.maps.DirectionsRenderer(directionOptions),
bounds = response.routes[0].bounds;
resolve({
instance: route,
northEast: bounds.getNorthEast(),
southWest: bounds.getSouthWest()
});
} else {
errors.log("W1006", status);
resolve({
instance: new google.maps.DirectionsRenderer({})
});
}
}.bind(this));
}.bind(this));
}.bind(this));
},
_destroyRoute: function _destroyRoute(routeObject) {
routeObject.instance.setMap(null);
},
_fitBounds: function _fitBounds() {
this._updateBounds();
if (this._bounds && this._option("autoAdjust")) {
var zoomBeforeFitting = this._map.getZoom();
this._preventZoomChangeEvent = true;
this._map.fitBounds(this._bounds);
this._boundsChangeHandler();
var zoomAfterFitting = this._map.getZoom();
if (zoomBeforeFitting < zoomAfterFitting) {
this._map.setZoom(zoomBeforeFitting);
} else {
this._option("zoom", zoomAfterFitting);
}
delete this._preventZoomChangeEvent;
}
return Promise.resolve();
},
_extendBounds: function _extendBounds(location) {
if (this._bounds) {
this._bounds.extend(location);
} else {
this._bounds = new google.maps.LatLngBounds();
this._bounds.extend(location);
}
},
clean: function clean() {
if (this._map) {
google.maps.event.removeListener(this._boundsChangeListener);
google.maps.event.removeListener(this._clickListener);
this._clearMarkers();
this._clearRoutes();
delete this._map;
this._$container.empty();
}
return Promise.resolve();
}
});
///#DEBUG
GoogleProvider.remapConstant = function (newValue) {
GOOGLE_URL = newValue;
};
///#ENDDEBUG
module.exports = GoogleProvider;