function Geocoder(options) {
  //>>includeStart('debug', pragmas.debug);
  if (!defined(options) || !defined(options.container)) {
    throw new DeveloperError("options.container is required.");
  }
  if (!defined(options.scene)) {
    throw new DeveloperError("options.scene is required.");
  }
  //>>includeEnd('debug');

  const container = getElement(options.container);
  const viewModel = new GeocoderViewModel(options);

  viewModel._startSearchPath = startSearchPath;
  viewModel._stopSearchPath = stopSearchPath;

  const form = document.createElement("form");
  form.setAttribute("data-bind", "submit: search");

  const textBox = document.createElement("input");
  textBox.type = "search";
  textBox.className = "cesium-geocoder-input";
  textBox.setAttribute("placeholder", "Enter an address or landmark...");
  textBox.setAttribute(
    "data-bind",
    '\
textInput: searchText,\
disable: isSearchInProgress,\
event: { keyup: handleKeyUp, keydown: handleKeyDown, mouseover: deselectSuggestion },\
css: { "cesium-geocoder-input-wide" : keepExpanded || searchText.length > 0 },\
hasFocus: _focusTextbox',
  );

  this._onTextBoxFocus = function () {
    // as of 2016-10-19, setTimeout is required to ensure that the
    // text is focused on Safari 10
    setTimeout(function () {
      textBox.select();
    }, 0);
  };

  textBox.addEventListener("focus", this._onTextBoxFocus, false);
  form.appendChild(textBox);
  this._textBox = textBox;

  const searchButton = document.createElement("span");
  searchButton.className = "cesium-geocoder-searchButton";
  searchButton.setAttribute(
    "data-bind",
    "\
click: search,\
cesiumSvgPath: { path: isSearchInProgress ? _stopSearchPath : _startSearchPath, width: 32, height: 32 }",
  );
  form.appendChild(searchButton);

  container.appendChild(form);

  const searchSuggestionsContainer = document.createElement("div");
  searchSuggestionsContainer.className = "search-results";
  searchSuggestionsContainer.setAttribute(
    "data-bind",
    "visible: _suggestionsVisible",
  );

  const suggestionsList = document.createElement("ul");
  suggestionsList.setAttribute("data-bind", "foreach: _suggestions");
  const suggestions = document.createElement("li");
  suggestionsList.appendChild(suggestions);
  suggestions.setAttribute(
    "data-bind",
    "text: $data.displayName, \
click: $parent.activateSuggestion, \
event: { mouseover: $parent.handleMouseover}, \
css: { active: $data === $parent._selectedSuggestion }",
  );

  searchSuggestionsContainer.appendChild(suggestionsList);
  container.appendChild(searchSuggestionsContainer);

  knockout.applyBindings(viewModel, form);
  knockout.applyBindings(viewModel, searchSuggestionsContainer);

  this._container = container;
  this._searchSuggestionsContainer = searchSuggestionsContainer;
  this._viewModel = viewModel;
  this._form = form;

  this._onInputBegin = function (e) {
    // e.target will not be correct if we are inside of the Shadow DOM
    // and contains will always fail. To retrieve the correct target,
    // we need to access the first element of the composedPath.
    // This allows us to use shadow DOM if it exists and fall
    // back to legacy behavior if its not being used.

    let target = e.target;
    if (typeof e.composedPath === "function") {
      target = e.composedPath()[0];
    }

    if (!container.contains(target)) {
      viewModel._focusTextbox = false;
      viewModel.hideSuggestions();
    }
  };

  this._onInputEnd = function (e) {
    viewModel._focusTextbox = true;
    viewModel.showSuggestions();
  };

  //We subscribe to both begin and end events in order to give the text box
  //focus no matter where on the widget is clicked.

  if (FeatureDetection.supportsPointerEvents()) {
    document.addEventListener("pointerdown", this._onInputBegin, true);
    container.addEventListener("pointerup", this._onInputEnd, true);
    container.addEventListener("pointercancel", this._onInputEnd, true);
  } else {
    document.addEventListener("mousedown", this._onInputBegin, true);
    container.addEventListener("mouseup", this._onInputEnd, true);
    document.addEventListener("touchstart", this._onInputBegin, true);
    container.addEventListener("touchend", this._onInputEnd, true);
    container.addEventListener("touchcancel", this._onInputEnd, true);
  }
}
A widget for finding addresses and landmarks, and flying the camera to them.  Geocoding is
performed using {@link https://cesium.com/cesium-ion/|Cesium ion}.

@alias Geocoder
@constructor

@param {object} options Object with the following properties:
@param {Element|string} options.container The DOM element or ID that will contain the widget.
@param {Scene} options.scene The Scene instance to use.
@param {GeocoderService[]} [options.geocoderServices] The geocoder services to be used
@param {boolean} [options.autoComplete = true] True if the geocoder should query as the user types to autocomplete
@param {number} [options.flightDuration=1.5] The duration of the camera flight to an entered location, in seconds.
@param {Geocoder.DestinationFoundFunction} [options.destinationFound=GeocoderViewModel.flyToDestination] A callback function that is called after a successful geocode.  If not supplied, the default behavior is to fly the camera to the result destination.