geolocation-8.x-3.x-dev/modules/geolocation_address/js/WidgetSubscriber/GeolocationAddressWidget.js

modules/geolocation_address/js/WidgetSubscriber/GeolocationAddressWidget.js
/**
 * @typedef {Object} AddressIntegrationSettings

 * @prop {String} geocoder
 * @prop {Object} settings
 * @prop {String} address_field
 * @prop {String} direction
 * @prop {String} sync_mode
 * @prop {String} geocoder_url
 * @prop {String} geocoder_reverse_url
 */

import { FieldWidgetBase } from "../../../../js/WidgetSubscriber/FieldWidgetBase.js";
import { GeolocationAddress } from "../GeolocationAddress.js";
import { GeolocationCoordinates } from "../../../../js/Base/GeolocationCoordinates.js";

export default class GeolocationAddressWidget extends FieldWidgetBase {
  initialize() {
    super.initialize();

    /**
     *
     * @type {Map<int, Function>}
     */
    this.ajaxAddressElementResolvesByIndex = new Map();

    jQuery(document).ajaxComplete(() => {
      if (!this.ajaxAddressElementResolvesByIndex.size) return;
      this.ajaxAddressElementResolvesByIndex.forEach((resolve, index) => {
        const element = this.getElementByIndex(index);
        if (element) {
          resolve(element);
          this.ajaxAddressElementResolvesByIndex.delete(index);
        }
      });
    });
  }

  onFormChange(element, index) {
    let requiredSet = true;
    element.querySelectorAll(".form-element.required").forEach((formElement) => {
      if (!formElement.value) requiredSet = false;
    });

    if (!requiredSet) return;

    super.onFormChange(element, index);
  }

  getAllInputElements(returnElements = false) {
    const hyphenatedFieldName = this.settings.field_name.replaceAll("_", "-");
    const pattern = new RegExp(`${hyphenatedFieldName}-\\d+$`);

    const elements = Array.from(this.form.querySelectorAll(`[data-drupal-selector*="${hyphenatedFieldName}-"]`)).filter((element) => {
      return pattern.test(element.getAttribute("data-drupal-selector"));
    });

    if (returnElements) {
      return elements;
    }

    const map = new Map();
    elements.forEach((element) => {
      map.set(this.getIndexByElement(element), element);
    });

    return map;
  }

  triggerAddMoreElement() {
    let error = false;
    this.getAllInputElements().forEach((element) => {
      if (element.querySelector(".form-element.error")) {
        console.error("Cannot add new element while errors present.");
        error = true;
      }
    });
    if (error) return;
    this.form.querySelector(`[name="${this.settings.field_name}_add_more"]`)?.dispatchEvent(new Event("mousedown"));
  }

  /**
   *
   * @param {GeolocationAddress} address
   *   Address.
   * @return {Promise<GeolocationCoordinates|null>}
   *   Coordinates.
   */
  addressToCoordinates(address) {
    return new Promise((resolve, reject) => {
      if (!address.countryCode) {
        resolve(null);
        return;
      }
      fetch(this.settings.geocoder_url, {
        method: "POST",
        mode: "cors",
        cache: "no-cache",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/json",
        },
        redirect: "follow",
        referrerPolicy: "no-referrer",
        body: JSON.stringify({
          geocoder: this.settings.geocoder,
          geocoder_settings: this.settings.settings,
          field_name: this.settings.field_name,
          address,
        }),
      }).then((response) => {
        if (response.ok) {
          response.json().then((coordinates) => {
            if (coordinates) {
              resolve(new GeolocationCoordinates(coordinates.lat, coordinates.lng));
            } else {
              resolve(null);
            }
          });
          return;
        }
        reject(new Error(`GeolocationAddressWidget: ${response.status} ${response.statusText}`));
      });
    });
  }

  /**
   * @param {float} latitude
   *   Latitude.
   * @param {float} longitude
   *   Longitude.
   *
   * @return {Promise<GeolocationAddress>}
   *   Address.
   */
  coordinatesToAddress(latitude, longitude) {
    return new Promise((resolve, reject) => {
      fetch(this.settings.geocoder_reverse_url, {
        method: "POST",
        mode: "cors",
        cache: "no-cache",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/json",
        },
        redirect: "follow",
        referrerPolicy: "no-referrer",
        body: JSON.stringify({
          geocoder: this.settings.geocoder,
          geocoder_settings: this.settings.settings,
          field_name: this.settings.field_name,
          latitude,
          longitude,
        }),
      }).then((response) => {
        if (response.ok) {
          response.json().then((address) => {
            resolve(new GeolocationAddress(address));
          });

          return;
        }
        reject(new Error(`GeolocationAddressWidget: ${response.status} ${response.statusText}`));
      });
    });
  }

  /**
   * @param {GeolocationAddress} address
   *   Address.
   * @param {Element} element
   *   Element.
   */
  setAddressByElement(address, element) {
    const countrySelect = element.querySelector("select.country");

    if (!address || (!address.countryCode ?? false)) {
      countrySelect.value = "";
      countrySelect.dispatchEvent(new Event("change"));
      return;
    }

    if (countrySelect.value === address.countryCode) {
      address.addressWidgetElementSelectors.forEach((selector, property) => {
        if (property === "countryCode") return;
        if (element.querySelector(selector)) element.querySelector(selector).value = address[property] ?? "";
      });

      return;
    }

    new Promise((resolve) => {
      const index = this.getIndexByElement(element);
      this.ajaxAddressElementResolvesByIndex.set(index, resolve);
    }).then((addressElement) => {
      this.setAddressByElement(address, addressElement);
    });

    countrySelect.value = address.countryCode;
    countrySelect.dispatchEvent(new Event("change"));
  }

  setCoordinatesByElement(coordinates, element) {
    if (!element) {
      console.error("Cannot set address by coordinates on non-existing element.");
    }

    if (!coordinates) {
      const countrySelect = element.querySelector("select.country");

      countrySelect.value = "";
      countrySelect.dispatchEvent(new Event("change"));

      return;
    }

    this.coordinatesToAddress(coordinates.lat, coordinates.lng).then((address) => {
      if (address.isEmpty()) {
        const warning = document.createElement("div");
        warning.innerHTML = `<p>${Drupal.t("No address found for given coordinates. Address field will stay empty for given field.")}</p>`;
        Drupal.dialog(warning, {
          title: Drupal.t("Address synchronization"),
        }).showModal();
      }
      this.setAddressByElement(address, element);
    });
  }

  getCoordinatesByElement(element) {
    if (!element) {
      return Promise.reject(new Error("GeolocationAddressWidget: Cannot get coordinates of non-existing element."));
    }

    const countryCode = element.querySelector("select.country")?.value ?? null;

    if (!countryCode) {
      return new Promise((resolve) => {
        resolve(new GeolocationCoordinates());
      });
    }

    const address = this.getAddressByElement(element);

    if (address) {
      return this.addressToCoordinates(address);
    }

    return Promise.reject(new Error("GeolocationAddressWidget: Cannot get coordinates of element without an address."));
  }

  /**
   * @param {Element} element
   *   Element.
   *
   * @return {GeolocationAddress}
   *   Address.
   */
  getAddressByElement(element) {
    const address = new GeolocationAddress();

    address.addressWidgetElementSelectors.forEach((selector, property) => {
      address[property] = element.querySelector(selector)?.value.trim() ?? null;
    });

    return address;
  }

  reorder(newOrder, source) {
    if (this.settings.direction === "one_way") return;
    super.reorder(newOrder, source);
  }

  addCoordinates(coordinates, index, source) {
    if (this.settings.direction === "one_way") return;
    super.addCoordinates(coordinates, index, source);
  }

  removeCoordinates(index, source) {
    if (this.settings.direction === "one_way") return;
    super.removeCoordinates(index, source);
  }

  alterCoordinates(coordinates, index, source) {
    if (this.settings.direction === "one_way") return;
    super.alterCoordinates(coordinates, index, source);
  }
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc