UNPKG

devextreme

Version:

JavaScript/TypeScript Component Suite for Responsive Web Development

409 lines (408 loc) • 15.4 kB
/** * DevExtreme (esm/__internal/ui/map/provider.dynamic.bing.js) * Version: 25.2.7 * Build date: Tue May 05 2026 * * Copyright (c) 2012 - 2026 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ import Color from "../../../color"; import ajax from "../../../core/utils/ajax"; import { noop } from "../../../core/utils/common"; import { extend } from "../../../core/utils/extend"; import { getHeight, getWidth } from "../../../core/utils/size"; import { isDefined } from "../../../core/utils/type"; import { getWindow } from "../../../core/utils/window"; import errors from "../../../ui/widget/ui.errors"; import DynamicProvider from "./provider.dynamic"; const window = getWindow(); const BING_MAP_READY = "_bingScriptReady"; let BING_URL_V8 = `https://www.bing.com/api/maps/mapcontrol?callback=${BING_MAP_READY}`; const INFOBOX_V_OFFSET_V8 = 13; const MIN_LOCATION_RECT_LENGTH = 1e-16; const msMapsLoaded = () => { var _window$Microsoft; return Boolean(null === (_window$Microsoft = window.Microsoft) || void 0 === _window$Microsoft ? void 0 : _window$Microsoft.Maps) }; let msMapsLoader; class BingProvider extends DynamicProvider { _mapType(type) { const mapTypes = { roadmap: Microsoft.Maps.MapTypeId.road, hybrid: Microsoft.Maps.MapTypeId.aerial, satellite: Microsoft.Maps.MapTypeId.aerial }; if (!type) { return mapTypes.roadmap } return mapTypes[type] ?? mapTypes.roadmap } _movementMode() { let type = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : ""; if (!type) { return Microsoft.Maps.Directions.RouteMode.driving } return Microsoft.Maps.Directions.RouteMode[type] } _resolveLocation(location) { return new Promise(resolve => { const latLng = this._getLatLng(location); if (latLng) { resolve(new Microsoft.Maps.Location(latLng.lat, latLng.lng)) } else { this._geocodeLocation(location).then(geocodedLocation => { resolve(geocodedLocation) }) } }) } _geocodeLocationImpl(location) { return new Promise(resolve => { if (!isDefined(location)) { resolve(new Microsoft.Maps.Location(0, 0)); return } const searchManager = new Microsoft.Maps.Search.SearchManager(this._map); const searchRequest = { where: location, count: 1, callback(searchResponse) { const result = searchResponse.results[0]; if (result) { const boundsBox = searchResponse.results[0].location; resolve(new Microsoft.Maps.Location(boundsBox.latitude, boundsBox.longitude)) } else { resolve(new Microsoft.Maps.Location(0, 0)) } } }; searchManager.geocode(searchRequest) }) } _normalizeLocation(location) { return { lat: location.latitude, lng: location.longitude } } _normalizeLocationRect(locationRect) { const northWest = this._normalizeLocation(locationRect.getNorthwest()); const southEast = this._normalizeLocation(locationRect.getSoutheast()); return { northEast: { lat: northWest.lat, lng: southEast.lng }, southWest: { lat: southEast.lat, lng: northWest.lng } } } _loadImpl() { return new Promise(resolve => { if (msMapsLoaded()) { resolve() } else { if (!msMapsLoader) { msMapsLoader = this._loadMapScript() } msMapsLoader.then(() => { if (msMapsLoaded()) { resolve(); return } this._loadMapScript().then(resolve) }) } }).then(() => Promise.all([new Promise(resolve => { Microsoft.Maps.loadModule("Microsoft.Maps.Search", { callback: resolve }) }), new Promise(resolve => { Microsoft.Maps.loadModule("Microsoft.Maps.Directions", { callback: resolve }) })])).then(() => {}) } _loadMapScript() { return new Promise(resolve => { window[BING_MAP_READY] = resolve; ajax.sendRequest({ url: BING_URL_V8, dataType: "script" }) }).then(() => { try { delete window[BING_MAP_READY] } catch (e) { window[BING_MAP_READY] = void 0 } }) } _init() { this._createMap(); return Promise.resolve() } _createMap() { const controls = this._option("controls"); this._map = new Microsoft.Maps.Map(this._$container[0], { credentials: this._keyOption("bing"), zoom: this._option("zoom"), showDashboard: controls, showMapTypeSelector: controls, showScalebar: controls }) } _attachHandlers() { this._providerViewChangeHandler = Microsoft.Maps.Events.addHandler(this._map, "viewchange", this._viewChangeHandler.bind(this)); this._providerClickHandler = Microsoft.Maps.Events.addHandler(this._map, "click", this._clickActionHandler.bind(this)) } _viewChangeHandler() { const bounds = this._map.getBounds(); this._option("bounds", this._normalizeLocationRect(bounds)); const center = this._map.getCenter(); this._option("center", this._normalizeLocation(center)); if (!this._preventZoomChangeEvent) { this._option("zoom", this._map.getZoom()) } } _clickActionHandler(e) { if ("map" === e.targetType) { this._fireClickAction({ location: this._normalizeLocation(e.location) }) } } updateDimensions() { const $container = this._$container; this._map.setOptions({ width: getWidth($container), height: getHeight($container) }); return Promise.resolve() } updateMapType() { const type = this._option("type"); const labelOverlay = Microsoft.Maps.LabelOverlay; this._map.setView({ animate: false, mapTypeId: this._mapType(type), labelOverlay: "satellite" === type ? labelOverlay.hidden : labelOverlay.visible }); return Promise.resolve() } updateBounds() { const boundsOption = this._option("bounds"); return Promise.all([this._resolveLocation(null === boundsOption || void 0 === boundsOption ? void 0 : boundsOption.northEast), this._resolveLocation(null === boundsOption || void 0 === boundsOption ? void 0 : boundsOption.southWest)]).then(result => { const bounds = new Microsoft.Maps.LocationRect.fromLocations(result[0], result[1]); this._map.setView({ animate: false, bounds: bounds }) }) } updateCenter() { return this._resolveLocation(this._option("center")).then(center => { this._map.setView({ animate: false, center: center }) }) } updateZoom() { this._map.setView({ animate: false, zoom: this._option("zoom") }); return Promise.resolve() } updateControls(markers, routes) { this.clean(); return this.render(markers, routes) } _renderMarker(options) { const { location: markerLocation } = options; return this._resolveLocation(markerLocation).then(location => { const pushpinOptions = { icon: options.iconSrc || this._option("markerIconSrc") }; if (options.html) { extend(pushpinOptions, { htmlContent: options.html, width: null, height: null }); const { htmlOffset: htmlOffset } = options; if (htmlOffset) { pushpinOptions.anchor = new Microsoft.Maps.Point(-htmlOffset.left, -htmlOffset.top) } } const pushpin = new Microsoft.Maps.Pushpin(location, pushpinOptions); this._map.entities.push(pushpin); const infobox = this._renderTooltip(location, options.tooltip); let handler; if (options.onClick || options.tooltip) { const markerClickAction = this._mapWidget._createAction(options.onClick ?? noop); const markerNormalizedLocation = this._normalizeLocation(location); handler = Microsoft.Maps.Events.addHandler(pushpin, "click", () => { markerClickAction({ location: markerNormalizedLocation }); if (infobox) { infobox.setOptions({ visible: true }) } }) } return { location: location, marker: pushpin, infobox: infobox, handler: handler } }) } _renderTooltip(location, options) { if (!options) { return } const parsedOptions = this._parseTooltipOptions(options); const infobox = new Microsoft.Maps.Infobox(location, { description: parsedOptions.text, offset: new Microsoft.Maps.Point(0, 13), visible: parsedOptions.visible }); infobox.setMap(this._map); return infobox } _destroyMarker(marker) { this._map.entities.remove(marker.marker); if (marker.infobox) { marker.infobox.setMap(null) } if (marker.handler) { Microsoft.Maps.Events.removeHandler(marker.handler) } } _renderRoute(options) { const routeLocations = options.locations ?? []; return Promise.all(routeLocations.map(point => this._resolveLocation(point))).then(locations => new Promise(resolve => { const direction = new Microsoft.Maps.Directions.DirectionsManager(this._map); const color = new Color(options.color || this._defaultRouteColor()).toHex(); const routeColor = new Microsoft.Maps.Color.fromHex(color); routeColor.a = 255 * (options.opacity || this._defaultRouteOpacity()); direction.setRenderOptions({ autoUpdateMapView: false, displayRouteSelector: false, waypointPushpinOptions: { visible: false }, drivingPolylineOptions: { strokeColor: routeColor, strokeThickness: options.weight || this._defaultRouteWeight() }, walkingPolylineOptions: { strokeColor: routeColor, strokeThickness: options.weight || this._defaultRouteWeight() } }); direction.setRequestOptions({ routeMode: this._movementMode(options.mode), routeDraggable: false }); locations.forEach(location => { const waypoint = new Microsoft.Maps.Directions.Waypoint({ location: location }); direction.addWaypoint(waypoint) }); const directionHandlers = []; directionHandlers.push(Microsoft.Maps.Events.addHandler(direction, "directionsUpdated", args => { while (directionHandlers.length) { Microsoft.Maps.Events.removeHandler(directionHandlers.pop()) } const routeSummary = args.routeSummary[0]; resolve({ instance: direction, northEast: routeSummary.northEast, southWest: routeSummary.southWest }) })); directionHandlers.push(Microsoft.Maps.Events.addHandler(direction, "directionsError", args => { while (directionHandlers.length) { Microsoft.Maps.Events.removeHandler(directionHandlers.pop()) } const status = `RouteResponseCode: ${args.responseCode} - ${args.message}`; errors.log("W1006", status); resolve({ instance: direction }) })); direction.calculateDirections() })) } _destroyRoute(routeObject) { routeObject.instance.dispose() } _fitBounds() { this._updateBounds(); if (this._bounds && this._option("autoAdjust")) { const zoomBeforeFitting = this._map.getZoom(); this._preventZoomChangeEvent = true; const bounds = this._bounds.clone(); bounds.height *= 1.1; bounds.width *= 1.1; this._map.setView({ animate: false, bounds: bounds, zoom: zoomBeforeFitting }); const zoomAfterFitting = this._map.getZoom(); if (zoomBeforeFitting < zoomAfterFitting) { this._map.setView({ animate: false, zoom: zoomBeforeFitting }) } else { this._option("zoom", zoomAfterFitting) } delete this._preventZoomChangeEvent } return Promise.resolve() } _extendBounds(location) { if (this._bounds) { this._bounds = new Microsoft.Maps.LocationRect.fromLocations(this._bounds.getNorthwest(), this._bounds.getSoutheast(), location) } else { this._bounds = new Microsoft.Maps.LocationRect(location, 1e-16, 1e-16) } } clean() { if (this._map) { Microsoft.Maps.Events.removeHandler(this._providerViewChangeHandler); Microsoft.Maps.Events.removeHandler(this._providerClickHandler); this._clearMarkers(); this._clearRoutes(); this._map.dispose() } return Promise.resolve() } } export default BingProvider;