/* eslint-disable require-jsdoc */ import getCoordinateInputName from "./coordinate_input" /** * You can use this method to "attach" front-end geocoding to any forms in the * front-end which have address fields with geocoding autocompletion * functionality already applied to them. * * To learn more about the front-end geocoding autocompletion, please refer to * the maps documentation at: /docs/customization/maps.md. * * When the geocoding autocompletion finishes, most of the times, its results * will also contain the geocoordinate information for the selected address. * This method allows you to pass these coordinates (latitude and longitude) * to the same front-end form where the geocoding autocompletion address field * is located at (which is the $input you pass to this method). The latitude * and longitude coordinates will be added or "attached" to the form once the * user selects one of the suggested addresses. * * Therefore, if there was the following geocoding autocompletion field at * your form: * * * You would then "attach" the geocoding result coordinates to the same form * where this input is at as follows: * $(document).ready(function() { * window.Decidim.attachGeocoding($("#record_address")); * }); * * Now, after the user selects one of the suggested geocoding autocompletion * addresses and the geocoding autocompletion API provides the coordinates in * the results, you would have the following fields automatically generated * to your form: * * * * * If you would not do the attachment, these hidden longitude and latitude * fields would not be generated and the geocoding would have to happen at the * server-side when the form is submitted. The problem with that approach * would be that the server-side address geocoding could potentially result in * different coordinates than what the user actually selected in the front-end * because the autocompletion API can return different coordinates than the * geocoding API. Another reason is to avoid unnecessary calls to the * geocoding API as the front-end geocoding suggestion already returned the * coordinate values we need. * * @param {jQuery} $input The input jQuery element for the geocoded address * field. * @param {Object} options (optional) Extra options if you want to customize * the latitude and longitude element IDs or names from the default. * @param {function} callback (optional) Callback to run when updating the coordinates values * @returns {void} */ export default function attachGeocoding($input, options, callback) { const attachOptions = $.extend({}, options); const inputIdParts = $input.attr("id").split("_"); inputIdParts.pop(); const idPrefix = `${inputIdParts.join("_")}`; let latitudeName = "latitude"; let longitudeName = "longitude"; if ($input.length > 0) { latitudeName = getCoordinateInputName("latitude", $input, attachOptions); longitudeName = getCoordinateInputName("longitude", $input, attachOptions); } const config = $.extend({ latitudeId: `${idPrefix}_latitude`, longitudeId: `${idPrefix}_longitude`, latitudeName: latitudeName, longitudeName: longitudeName }, options); let geocoded = false; const createCoordinateFields = () => { let $latitude = $(`#${config.latitudeId}`); if ($latitude.length < 1) { $latitude = $(``); $input.after($latitude); } let $longitude = $(`#${config.longitudeId}`); if ($longitude.length < 1) { $longitude = $(``); $input.after($longitude); } } const clearCoordinateFields = () => { if (geocoded) { return; } $(`#${config.latitudeId}`).val("").removeAttr("value"); $(`#${config.longitudeId}`).val("").removeAttr("value"); }; const setCoordinates = (coordinates) => { createCoordinateFields(); $(`#${config.latitudeId}`).val(coordinates[0]).attr("value", coordinates[0]); $(`#${config.longitudeId}`).val(coordinates[1]).attr("value", coordinates[1]); } // When the user changes the value of the coordinate field without selecting // any of the geocoding autocomplete results, clear the current latitude and // longitude values to let the backend do the geocoding. Once a geocoding // autocomplete value has been selected, assume the user just wants to // refine the address formatting without changing the location point value. // If they want, they can still modify the point in the next step of the // proposal creation/editing. $input.on("change.decidim", () => { clearCoordinateFields(); }); // When we receive the geocoding event on the field, update the coordinate // values. $input.on("geocoder-suggest-coordinates.decidim", (_ev, coordinates) => { setCoordinates(coordinates); geocoded = true; if (typeof callback === "function") { callback(coordinates); return; } }); // Set the initial values if the field defines the coordinates const coordinates = `${$input.data("coordinates")}`.split(",").map(parseFloat); if (Array.isArray(coordinates) && coordinates.length === 2) { setCoordinates(coordinates); } }