Source: Widgets/Geocoder/Geocoder.js

  1. /*global define*/
  2. define([
  3. '../../Core/defined',
  4. '../../Core/defineProperties',
  5. '../../Core/destroyObject',
  6. '../../Core/DeveloperError',
  7. '../../Core/FeatureDetection',
  8. '../../ThirdParty/knockout',
  9. '../getElement',
  10. './GeocoderViewModel'
  11. ], function(
  12. defined,
  13. defineProperties,
  14. destroyObject,
  15. DeveloperError,
  16. FeatureDetection,
  17. knockout,
  18. getElement,
  19. GeocoderViewModel) {
  20. 'use strict';
  21. var startSearchPath = 'M29.772,26.433l-7.126-7.126c0.96-1.583,1.523-3.435,1.524-5.421C24.169,8.093,19.478,3.401,13.688,3.399C7.897,3.401,3.204,8.093,3.204,13.885c0,5.789,4.693,10.481,10.484,10.481c1.987,0,3.839-0.563,5.422-1.523l7.128,7.127L29.772,26.433zM7.203,13.885c0.006-3.582,2.903-6.478,6.484-6.486c3.579,0.008,6.478,2.904,6.484,6.486c-0.007,3.58-2.905,6.476-6.484,6.484C10.106,20.361,7.209,17.465,7.203,13.885z';
  22. var stopSearchPath = 'M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248z';
  23. /**
  24. * A widget for finding addresses and landmarks, and flying the camera to them. Geocoding is
  25. * performed using the {@link http://msdn.microsoft.com/en-us/library/ff701715.aspx|Bing Maps Locations API}.
  26. *
  27. * @alias Geocoder
  28. * @constructor
  29. *
  30. * @param {Object} options Object with the following properties:
  31. * @param {Element|String} options.container The DOM element or ID that will contain the widget.
  32. * @param {Scene} options.scene The Scene instance to use.
  33. * @param {String} [options.url='https://dev.virtualearth.net'] The base URL of the Bing Maps API.
  34. * @param {String} [options.key] The Bing Maps key for your application, which can be
  35. * created at {@link https://www.bingmapsportal.com}.
  36. * If this parameter is not provided, {@link BingMapsApi.defaultKey} is used.
  37. * If {@link BingMapsApi.defaultKey} is undefined as well, a message is
  38. * written to the console reminding you that you must create and supply a Bing Maps
  39. * key as soon as possible. Please do not deploy an application that uses
  40. * this widget without creating a separate key for your application.
  41. * @param {Number} [options.flightDuration=1.5] The duration of the camera flight to an entered location, in seconds.
  42. */
  43. function Geocoder(options) {
  44. //>>includeStart('debug', pragmas.debug);
  45. if (!defined(options) || !defined(options.container)) {
  46. throw new DeveloperError('options.container is required.');
  47. }
  48. if (!defined(options.scene)) {
  49. throw new DeveloperError('options.scene is required.');
  50. }
  51. //>>includeEnd('debug');
  52. var container = getElement(options.container);
  53. var viewModel = new GeocoderViewModel(options);
  54. viewModel._startSearchPath = startSearchPath;
  55. viewModel._stopSearchPath = stopSearchPath;
  56. var form = document.createElement('form');
  57. form.setAttribute('data-bind', 'submit: search');
  58. var textBox = document.createElement('input');
  59. textBox.type = 'search';
  60. textBox.className = 'cesium-geocoder-input';
  61. textBox.setAttribute('placeholder', 'Enter an address or landmark...');
  62. textBox.setAttribute('data-bind', '\
  63. value: searchText,\
  64. valueUpdate: "afterkeydown",\
  65. disable: isSearchInProgress,\
  66. css: { "cesium-geocoder-input-wide" : keepExpanded || searchText.length > 0 }');
  67. this._onTextBoxFocus = function() {
  68. // as of 2016-10-19, setTimeout is required to ensure that the
  69. // text is focused on Safari 10
  70. setTimeout(function() {
  71. textBox.select();
  72. }, 0);
  73. };
  74. textBox.addEventListener('focus', this._onTextBoxFocus, false);
  75. form.appendChild(textBox);
  76. this._textBox = textBox;
  77. var searchButton = document.createElement('span');
  78. searchButton.className = 'cesium-geocoder-searchButton';
  79. searchButton.setAttribute('data-bind', '\
  80. click: search,\
  81. cesiumSvgPath: { path: isSearchInProgress ? _stopSearchPath : _startSearchPath, width: 32, height: 32 }');
  82. form.appendChild(searchButton);
  83. container.appendChild(form);
  84. knockout.applyBindings(viewModel, form);
  85. this._container = container;
  86. this._viewModel = viewModel;
  87. this._form = form;
  88. this._onInputBegin = function(e) {
  89. if (!container.contains(e.target)) {
  90. textBox.blur();
  91. }
  92. };
  93. this._onInputEnd = function(e) {
  94. if (container.contains(e.target)) {
  95. textBox.focus();
  96. }
  97. };
  98. //We subscribe to both begin and end events in order to give the text box
  99. //focus no matter where on the widget is clicked.
  100. if (FeatureDetection.supportsPointerEvents()) {
  101. document.addEventListener('pointerdown', this._onInputBegin, true);
  102. document.addEventListener('pointerup', this._onInputEnd, true);
  103. document.addEventListener('pointercancel', this._onInputEnd, true);
  104. } else {
  105. document.addEventListener('mousedown', this._onInputBegin, true);
  106. document.addEventListener('mouseup', this._onInputEnd, true);
  107. document.addEventListener('touchstart', this._onInputBegin, true);
  108. document.addEventListener('touchend', this._onInputEnd, true);
  109. document.addEventListener('touchcancel', this._onInputEnd, true);
  110. }
  111. }
  112. defineProperties(Geocoder.prototype, {
  113. /**
  114. * Gets the parent container.
  115. * @memberof Geocoder.prototype
  116. *
  117. * @type {Element}
  118. */
  119. container : {
  120. get : function() {
  121. return this._container;
  122. }
  123. },
  124. /**
  125. * Gets the view model.
  126. * @memberof Geocoder.prototype
  127. *
  128. * @type {GeocoderViewModel}
  129. */
  130. viewModel : {
  131. get : function() {
  132. return this._viewModel;
  133. }
  134. }
  135. });
  136. /**
  137. * @returns {Boolean} true if the object has been destroyed, false otherwise.
  138. */
  139. Geocoder.prototype.isDestroyed = function() {
  140. return false;
  141. };
  142. /**
  143. * Destroys the widget. Should be called if permanently
  144. * removing the widget from layout.
  145. */
  146. Geocoder.prototype.destroy = function() {
  147. if (FeatureDetection.supportsPointerEvents()) {
  148. document.removeEventListener('pointerdown', this._onInputBegin, true);
  149. document.removeEventListener('pointerup', this._onInputEnd, true);
  150. } else {
  151. document.removeEventListener('mousedown', this._onInputBegin, true);
  152. document.removeEventListener('mouseup', this._onInputEnd, true);
  153. document.removeEventListener('touchstart', this._onInputBegin, true);
  154. document.removeEventListener('touchend', this._onInputEnd, true);
  155. }
  156. knockout.cleanNode(this._form);
  157. this._container.removeChild(this._form);
  158. this._textBox.removeEventListener('focus', this._onTextBoxFocus, false);
  159. return destroyObject(this);
  160. };
  161. return Geocoder;
  162. });