map.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /**
  2. * DevExtreme (ui/map.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var $ = require("../core/renderer");
  11. var eventsEngine = require("../events/core/events_engine");
  12. var Promise = require("../core/polyfills/promise");
  13. var fromPromise = require("../core/utils/deferred").fromPromise;
  14. var registerComponent = require("../core/component_registrator");
  15. var errors = require("./widget/ui.errors");
  16. var devices = require("../core/devices");
  17. var Widget = require("./widget/ui.widget");
  18. var inflector = require("../core/utils/inflector");
  19. var each = require("../core/utils/iterator").each;
  20. var extend = require("../core/utils/extend").extend;
  21. var inArray = require("../core/utils/array").inArray;
  22. var isNumeric = require("../core/utils/type").isNumeric;
  23. var eventUtils = require("../events/utils");
  24. var pointerEvents = require("../events/pointer");
  25. var wrapToArray = require("../core/utils/array").wrapToArray;
  26. var PROVIDERS = {
  27. googleStatic: require("./map/provider.google_static"),
  28. google: require("./map/provider.dynamic.google"),
  29. bing: require("./map/provider.dynamic.bing")
  30. };
  31. var MAP_CLASS = "dx-map";
  32. var MAP_CONTAINER_CLASS = "dx-map-container";
  33. var MAP_SHIELD_CLASS = "dx-map-shield";
  34. var NATIVE_CLICK_CLASS = "dx-native-click";
  35. var Map = Widget.inherit({
  36. _getDefaultOptions: function() {
  37. return extend(this.callBase(), {
  38. bounds: {
  39. northEast: null,
  40. southWest: null
  41. },
  42. center: {
  43. lat: 0,
  44. lng: 0
  45. },
  46. zoom: 1,
  47. width: 300,
  48. height: 300,
  49. type: "roadmap",
  50. provider: "google",
  51. autoAdjust: true,
  52. markers: [],
  53. markerIconSrc: null,
  54. onMarkerAdded: null,
  55. onMarkerRemoved: null,
  56. routes: [],
  57. onRouteAdded: null,
  58. onRouteRemoved: null,
  59. key: {
  60. bing: "",
  61. google: "",
  62. googleStatic: ""
  63. },
  64. controls: false,
  65. onReady: null,
  66. onUpdated: null,
  67. onClick: null
  68. })
  69. },
  70. _defaultOptionsRules: function() {
  71. return this.callBase().concat([{
  72. device: function() {
  73. return "desktop" === devices.real().deviceType && !devices.isSimulator()
  74. },
  75. options: {
  76. focusStateEnabled: true
  77. }
  78. }])
  79. },
  80. _init: function() {
  81. this.callBase();
  82. this.$element().addClass(MAP_CLASS).addClass(NATIVE_CLICK_CLASS);
  83. this._lastAsyncAction = Promise.resolve();
  84. this._checkOption("provider");
  85. this._checkOption("markers");
  86. this._checkOption("routes");
  87. this._initContainer();
  88. this._grabEvents();
  89. this._rendered = {}
  90. },
  91. _checkOption: function(option) {
  92. var value = this.option(option);
  93. if ("markers" === option && !Array.isArray(value)) {
  94. throw errors.Error("E1022")
  95. }
  96. if ("routes" === option && !Array.isArray(value)) {
  97. throw errors.Error("E1023")
  98. }
  99. },
  100. _initContainer: function() {
  101. this._$container = $("<div>").addClass(MAP_CONTAINER_CLASS);
  102. this.$element().append(this._$container)
  103. },
  104. _grabEvents: function() {
  105. var eventName = eventUtils.addNamespace(pointerEvents.down, this.NAME);
  106. eventsEngine.on(this.$element(), eventName, this._cancelEvent.bind(this))
  107. },
  108. _cancelEvent: function(e) {
  109. var cancelByProvider = this._provider && this._provider.isEventsCanceled(e) && !this.option("disabled");
  110. if (cancelByProvider) {
  111. e.stopPropagation()
  112. }
  113. },
  114. _saveRendered: function(option) {
  115. var value = this.option(option);
  116. this._rendered[option] = value.slice()
  117. },
  118. _render: function() {
  119. this.callBase();
  120. this._renderShield();
  121. this._saveRendered("markers");
  122. this._saveRendered("routes");
  123. this._provider = new(PROVIDERS[this.option("provider")])(this, this._$container);
  124. this._queueAsyncAction("render", this._rendered.markers, this._rendered.routes)
  125. },
  126. _renderShield: function() {
  127. var $shield;
  128. if (this.option("disabled")) {
  129. $shield = $("<div>").addClass(MAP_SHIELD_CLASS);
  130. this.$element().append($shield)
  131. } else {
  132. $shield = this.$element().find("." + MAP_SHIELD_CLASS);
  133. $shield.remove()
  134. }
  135. },
  136. _clean: function() {
  137. this._cleanFocusState();
  138. if (this._provider) {
  139. this._provider.clean()
  140. }
  141. this._provider = null;
  142. this._lastAsyncAction = Promise.resolve();
  143. this.setOptionSilent("bounds", {
  144. northEast: null,
  145. southWest: null
  146. });
  147. delete this._suppressAsyncAction
  148. },
  149. _optionChanged: function(args) {
  150. var name = args.name;
  151. var changeBag = this._optionChangeBag;
  152. this._optionChangeBag = null;
  153. switch (name) {
  154. case "disabled":
  155. this._renderShield();
  156. this.callBase(args);
  157. break;
  158. case "width":
  159. case "height":
  160. this.callBase(args);
  161. this._dimensionChanged();
  162. break;
  163. case "provider":
  164. this._suppressAsyncAction = true;
  165. this._invalidate();
  166. break;
  167. case "key":
  168. errors.log("W1001");
  169. break;
  170. case "bounds":
  171. this._queueAsyncAction("updateBounds");
  172. break;
  173. case "center":
  174. this._queueAsyncAction("updateCenter");
  175. break;
  176. case "zoom":
  177. this._queueAsyncAction("updateZoom");
  178. break;
  179. case "type":
  180. this._queueAsyncAction("updateMapType");
  181. break;
  182. case "controls":
  183. this._queueAsyncAction("updateControls", this._rendered.markers, this._rendered.routes);
  184. break;
  185. case "autoAdjust":
  186. this._queueAsyncAction("adjustViewport");
  187. break;
  188. case "markers":
  189. case "routes":
  190. this._checkOption(name);
  191. var prevValue = this._rendered[name];
  192. this._saveRendered(name);
  193. this._queueAsyncAction("update" + inflector.titleize(name), changeBag ? changeBag.removed : prevValue, changeBag ? changeBag.added : this._rendered[name]).then(function(result) {
  194. if (changeBag) {
  195. changeBag.resolve(result)
  196. }
  197. });
  198. break;
  199. case "markerIconSrc":
  200. this._queueAsyncAction("updateMarkers", this._rendered.markers, this._rendered.markers);
  201. break;
  202. case "onReady":
  203. case "onUpdated":
  204. case "onMarkerAdded":
  205. case "onMarkerRemoved":
  206. case "onRouteAdded":
  207. case "onRouteRemoved":
  208. case "onClick":
  209. break;
  210. default:
  211. this.callBase.apply(this, arguments)
  212. }
  213. },
  214. _visibilityChanged: function(visible) {
  215. if (visible) {
  216. this._dimensionChanged()
  217. }
  218. },
  219. _dimensionChanged: function() {
  220. this._queueAsyncAction("updateDimensions")
  221. },
  222. _queueAsyncAction: function(name) {
  223. var options = [].slice.call(arguments).slice(1);
  224. var isActionSuppressed = this._suppressAsyncAction;
  225. this._lastAsyncAction = this._lastAsyncAction.then(function() {
  226. if (!this._provider || isActionSuppressed) {
  227. return Promise.resolve()
  228. }
  229. return this._provider[name].apply(this._provider, options).then(function(result) {
  230. result = wrapToArray(result);
  231. var mapRefreshed = result[0];
  232. if (mapRefreshed) {
  233. this._triggerReadyAction()
  234. }
  235. return result[1]
  236. }.bind(this))
  237. }.bind(this));
  238. return this._lastAsyncAction
  239. },
  240. _triggerReadyAction: function() {
  241. this._createActionByOption("onReady")({
  242. originalMap: this._provider.map()
  243. })
  244. },
  245. _triggerUpdateAction: function() {
  246. this._createActionByOption("onUpdated")()
  247. },
  248. setOptionSilent: function(name, value) {
  249. this._setOptionSilent(name, value)
  250. },
  251. addMarker: function(marker) {
  252. return this._addFunction("markers", marker)
  253. },
  254. removeMarker: function(marker) {
  255. return this._removeFunction("markers", marker)
  256. },
  257. addRoute: function(route) {
  258. return this._addFunction("routes", route)
  259. },
  260. removeRoute: function(route) {
  261. return this._removeFunction("routes", route)
  262. },
  263. _addFunction: function(optionName, addingValue) {
  264. var optionValue = this.option(optionName);
  265. var addingValues = wrapToArray(addingValue);
  266. optionValue.push.apply(optionValue, addingValues);
  267. return this._partialArrayOptionChange(optionName, optionValue, addingValues, [])
  268. },
  269. _removeFunction: function(optionName, removingValue) {
  270. var optionValue = this.option(optionName);
  271. var removingValues = wrapToArray(removingValue);
  272. each(removingValues, function(removingIndex, removingValue) {
  273. var index = isNumeric(removingValue) ? removingValue : inArray(removingValue, optionValue);
  274. if (index !== -1) {
  275. var removing = optionValue.splice(index, 1)[0];
  276. removingValues.splice(removingIndex, 1, removing)
  277. } else {
  278. throw errors.log("E1021", inflector.titleize(optionName.substring(0, optionName.length - 1)), removingValue)
  279. }
  280. });
  281. return this._partialArrayOptionChange(optionName, optionValue, [], removingValues)
  282. },
  283. _partialArrayOptionChange: function(optionName, optionValue, addingValues, removingValues) {
  284. return fromPromise(new Promise(function(resolve) {
  285. this._optionChangeBag = {
  286. resolve: resolve,
  287. added: addingValues,
  288. removed: removingValues
  289. };
  290. this.option(optionName, optionValue)
  291. }.bind(this)).then(function(result) {
  292. return result && 1 === result.length ? result[0] : result
  293. }), this)
  294. }
  295. });
  296. registerComponent("dxMap", Map);
  297. module.exports = Map;
  298. module.exports.default = module.exports;