UNPKG

geopf-extensions-openlayers

Version:

French Geoportal Extensions for OpenLayers libraries

1,189 lines (1,073 loc) 113 kB
// import CSS import "../../CSS/Controls/SearchEngine/GPFsearchEngine.css"; // import "../../CSS/Controls/SearchEngine/GPFsearchEngineStyle.css"; // import OpenLayers // import Control from "ol/control/Control"; import Control from "../Control"; import Widget from "../Widget"; import Map from "ol/Map"; import Overlay from "ol/Overlay"; import { transform as olProjTransform, get as olProjGet, transformExtent as olProjTransformExtent } from "ol/proj"; import GeoJSON from "ol/format/GeoJSON"; // import geoportal library access import Gp from "geoportal-access-lib"; // import local import Config from "../../Utils/Config"; import Logger from "../../Utils/LoggerByDefault"; import Utils from "../../Utils/Helper"; import Markers from "../Utils/Markers"; import Interactions from "../Utils/Interactions"; import SelectorID from "../../Utils/SelectorID"; import MathUtils from "../../Utils/MathUtils"; import SearchEngineUtils from "../../Utils/SearchEngineUtils"; import GeocodeUtils from "../../Utils/GeocodeUtils"; import CRS from "../../CRS/CRS"; // import local des layers import GeoportalWMS from "../../Layers/LayerWMS"; import GeoportalWMTS from "../../Layers/LayerWMTS"; import GeoportalWFS from "../../Layers/LayerWFS"; import GeoportalMapBox from "../../Layers/LayerMapBox"; // Service import Search from "../../Services/Search"; // DOM import SearchEngineDOM from "./SearchEngineDOM"; import checkDsfr from "../Utils/CheckDsfr"; var logger = Logger.getLogger("searchengine"); /** * @typedef {Object} SearchEngineOptions * @property {number} [id] - Identifiant du widget (option avancée) * @property {string} [apiKey] - Clé API. "calcul" par défaut. * @property {boolean} [ssl=true] - Utilisation du protocole https (true par défaut) * @property {boolean} [collapsed=true] - Mode réduit (true par défaut) * @property {boolean} [collapsible=true] - Contrôle pliable ou non (true par défaut) * @property {string} [direction="start"] - Position du picto (loupe), "start" par défaut * @property {string} [placeholder="Rechercher un lieu, une adresse"] - Placeholder de la barre de recherche * @property {boolean} [displayMarker=true] - Afficher un marqueur sur le résultat (true par défaut) * @property {string} [markerStyle="lightOrange"] - Style du marqueur ("lightOrange", "darkOrange", "red", "turquoiseBlue") * @property {string} [markerUrl=""] - URL du marqueur (prioritaire sur markerStyle) * @property {boolean} [splitResults=false] - Désactiver la recherche par couches (false par défaut) * @property {boolean} [displayButtonAdvancedSearch=false] - Afficher le bouton de recherche avancée (false par défaut) * @property {boolean} [displayButtonGeolocate=false] - Afficher le bouton de géolocalisation (false par défaut) * @property {boolean} [displayButtonCoordinateSearch=false] - Afficher le bouton de recherche par coordonnées (false par défaut) * @property {boolean} [coordinateSearchInAdvancedSearch=false] - Afficher la recherche par coordonnées dans la recherche avancée * @property {boolean} [displayButtonClose=true] - Afficher le bouton de fermeture (true par défaut) * @property {Object} [coordinateSearch] - Options de recherche par coordonnées * @property {HTMLElement} [coordinateSearch.target=null] - Cible d'affichage des résultats * @property {Array} [coordinateSearch.units] - Unités de coordonnées à afficher ("DEC", "DMS", "M", "KM") * Values may be "DEC" (decimal degrees), "DMS" (sexagecimal) for geographical coordinates, * and "M" or "KM" for metric coordinates * @property {Array} [coordinateSearch.systems] - Systèmes de projection à afficher (objet avec crs, label, type) * @property {Object} [advancedSearch] - Options de recherche avancée (voir geocodeOptions.filterOptions) * @property {HTMLElement} [advancedSearch.target=null] - Cible d'affichage des résultats * @property {Object} [resources] - Ressources utilisées par les services * @property {string|string[]} [resources.geocode="location"] - Ressources de géocodage * @property {string[]} [resources.autocomplete] - Ressources d'autocomplétion * @property {boolean} [resources.search=false] - Activer le service de recherche (false par défaut) * @property {Object} [searchOptions={}] - Options du service de recherche * @property {boolean} [searchOptions.addToMap=true] - Ajouter la couche automatiquement à la carte * @property {string[]} [searchOptions.filterServices] - Filtrer sur une liste de services ("WMTS,TMS" par défaut) * @property {string[]} [searchOptions.filterWMTSPriority] - Filtrer sur les couches WMTS prioritaires * @property {string[]} [searchOptions.filterProjections] - Filtrer sur une liste de projections * @property {boolean} [searchOptions.filterLayersPriority=false] - Filtrer sur les couches prioritaires * @property {boolean} [searchOptions.filterLayers=true] - Activer le filtrage automatique des couches * @property {Object} [searchOptions.filterLayersList] - Liste des couches à filtrer {"layerName": "service"} * @property {boolean} [searchOptions.filterTMS=true] - Garder les TMS avec style dans les métadonnées * @property {Object} [searchOptions.serviceOptions] - Options du service de recherche * @property {string} [searchOptions.serviceOptions.url] - URL du service * @property {string} [searchOptions.serviceOptions.index="standard"] - Index de recherche * @property {string[]} [searchOptions.serviceOptions.fields=["title","layer_name"]] - Champs de recherche * @property {number} [searchOptions.serviceOptions.size=1000] - Nombre de réponses du service * @property {number} [searchOptions.serviceOptions.maximumResponses=10] - Nombre de résultats à afficher * @property {number} [searchOptions.maximumEntries] - Nombre maximum de résultats à afficher * @property {Object} [geocodeOptions={}] - Options du service de géocodage (voir Gp.Services.geocode {@link http://ignf.github.io/geoportal-access-lib/latest/jsdoc/module-Services.html#~geocode Gp.Services.geocode})) * @property {Object} [geocodeOptions.serviceOptions] - Options du service de géocodage * @property {Object} [autocompleteOptions={}] - Options du service d'autocomplétion (voir Gp.Services.autoComplete {@link http://ignf.github.io/geoportal-access-lib/latest/jsdoc/module-Services.html#~autoComplete Gp.Services.autoComplete}) * @property {Object} [autocompleteOptions.serviceOptions] - Options du service d'autocomplétion * @property {boolean} [autocompleteOptions.triggerGeocode=false] - Déclencher une requête de géocodage si aucune suggestion * @property {number} [autocompleteOptions.triggerDelay=1000] - Délai avant la requête de géocodage (ms) * @property {number} [autocompleteOptions.maximumEntries] - Nombre maximum de résultats d'autocomplétion à afficher * @property {boolean} [autocompleteOptions.prettifyResults=false] - Nettoyer/embellir les résultats d'autocomplétion * @property {string|number|Function} [zoomTo] - Niveau de zoom à appliquer sur le résultat ("auto", niveau, ou fonction) * Value possible : auto or zoom level. * Possible to overload it with a function : * zoomTo : function (info) { * // do some stuff... * return zoom; * } */ /** * @classdesc * SearchEngine control * * @alias ol.control.SearchEngine * @module SearchEngine */ class SearchEngine extends Control { /** * @constructor * @param {SearchEngineOptions} options - control options * @fires searchengine:autocomplete:click * @fires searchengine:geocode:click * @fires searchengine:search:click * @fires searchengine:geolocation:click * @fires searchengine:geolocation:remove * @fires searchengine:coordinates:click * @todo option : direction (start|end) de la position du picto (loupe) * @todo option : choix du target pour les fenetres geocodage ou recherche par coordonnées * @example * var SearchEngine = ol.control.SearchEngine({ * apiKey : "CLEAPI", * collapsed : true, * collapsible : true, * displayButtonAdvancedSearch : true, * displayButtonGeolocate : true, * displayButtonCoordinateSearch : true, * markerStyle : "lightOrange" // "http://..." or "data/base64..." * resources : { * geocode : ["StreetAddress", "PositionOfInterest"], * autocomplete : ["StreetAddress"], * search : false * }, * advancedSearch : { * target : document.getElementById("dialog"), * PositionOfInterest : [{name : "municipality", title : "Ville"}], * StreetAddress : [{...}] * }, * coordinateSearch : { * target : null * systems : [ * { * "crs" : "EPSG:3857", * "label" : "Web Mercator", * "type" : "Metric" * }, * { * "crs" : "EPSG:4326", * "label" : "Géographiques", * "type" : "Geographical" * } * ], * units : ["DEC", "DMS"] * }, * geocodeOptions : {}, * autocompleteOptions : {}, * searchOptions : {} * }); * * SearchEngine.on("searchengine:autocomplete:click", function (e) { * console.warn("autocomplete", e.location); * }); * SearchEngine.on("searchengine:search:click", function (e) { * console.warn("search", e.suggest); * }); * SearchEngine.on("searchengine:geocode:click", function (e) { * console.warn("geocode", e.location); * }); * SearchEngine.on("searchengine:geolocation:click", function (e) { * console.warn("geolocation", e.); * }); * SearchEngine.on("searchengine:coordinate:click", function (e) { * console.warn("coordinate", e.); * }); */ constructor (options) { options = options || {}; // call ol.control.Control constructor super(options); if (!(this instanceof SearchEngine)) { throw new TypeError("ERROR CLASS_CONSTRUCTOR"); } /** * Nom de la classe (heritage) * @private */ this.CLASSNAME = "SearchEngine"; // initialisation du composant this.initialize(options); // // Widget main DOM container this.container = this._initContainer(); // ajout du container (this.element) ? this.element.appendChild(this.container) : this.element = this.container; return this; } // ################################################################### // // ##################### public methods ############################## // // ################################################################### // /** * Overwrite OpenLayers setMap method * * @param {Map} map - Map. */ setMap (map) { if (!map) { this._clearResults(); } // mode "collapsed" if (!this.collapsed) { this._showSearchEngineButton.setAttribute("aria-pressed", true); } // on appelle la méthode setMap originale d'OpenLayers super.setMap(map); // position if (this.options.position) { this.setPosition(this.options.position); } // reunion du bouton avec le précédent if (this.options.gutter === false) { this.getContainer().classList.add("gpf-button-no-gutter"); } } /** * Returns true if widget is collapsed (minimized), false otherwise * * @returns {Boolean} collapsed - true if widget is collapsed */ getCollapsed () { return this.collapsed; } /** * Collapse or display widget main container * * @param {Boolean} collapsed - True to collapse widget, False to display it */ setCollapsed (collapsed) { if (collapsed === undefined) { logger.log("[ERROR] SearchEngine:setCollapsed - missing collapsed parameter"); return; } if (!this.options.collapsible) { return; // on interdit le mode pliable ! } if ((collapsed && this.collapsed) || (!collapsed && !this.collapsed)) { return; } this._showSearchEngineButton.click(); this.collapsed = collapsed; } /** * Get locations data from geocode service * * @returns {Object} data - locations */ getData () { return this._geocodedLocations; } /** * Get container * * @returns {HTMLElement} container */ getContainer () { return this.container; } // ################################################################### // // ##################### init component ############################## // // ################################################################### // /** * Initialize SearchEngine control (called by SearchEngine constructor) * * @param {Object} options - constructor options * @private */ initialize (options) { this._checkInputOptions(options); // define default options this.options = { collapsed : true, collapsible : true, zoomTo : "", resources : { geocode : [], autocomplete : [], search : false }, displayButtonClose : true, displayButtonAdvancedSearch : false, displayButtonGeolocate : false, displayButtonCoordinateSearch : false, coordinateSearchInAdvancedSearch : false, advancedSearch : {}, coordinateSearch : {}, searchOptions : { addToMap : true, maximumEntries : 5, serviceOptions : { maximumResponses : 10, }, filterLayers : true }, geocodeOptions : { serviceOptions : {} }, autocompleteOptions : { serviceOptions : { maximumResponses : 5, }, triggerGeocode : false, triggerDelay : 1000, prettifyResults : false }, displayMarker : true, markerStyle : "lightOrange", markerUrl : "", placeholder : "Rechercher un lieu, une adresse", splitResults : false, }; // merge with user options Utils.mergeParams(this.options, options); if (this.options.resources.geocode === "") { this.options.resources.geocode = ["PositionOfInterest", "StreetAddress"]; } if (this.options.resources.autocomplete.length === 0) { this.options.resources.autocomplete = ["PositionOfInterest", "StreetAddress"]; } if (this.options.resources.search) { // configuration avec gestion des options surchargées du service if (this.options.searchOptions) { if (this.options.searchOptions.serviceOptions) { if (this.options.searchOptions.serviceOptions.url) { Search.setUrl(this.options.searchOptions.serviceOptions.url); } if (this.options.searchOptions.serviceOptions.fields) { Search.setFields(this.options.searchOptions.serviceOptions.fields); } if (this.options.searchOptions.serviceOptions.index) { Search.setIndex(this.options.searchOptions.serviceOptions.index); } if (this.options.searchOptions.serviceOptions.size) { Search.setSize(this.options.searchOptions.serviceOptions.size); } if (this.options.searchOptions.serviceOptions.maximumResponses) { Search.setMaximumResponses(this.options.searchOptions.serviceOptions.maximumResponses); } } if (this.options.searchOptions.filterServices) { Search.setFiltersByService(this.options.searchOptions.filterServices); } if (this.options.searchOptions.filterLayersPriority) { Search.setFiltersByLayerPriority(this.options.searchOptions.filterLayersPriority); } if (this.options.searchOptions.filterWMTSPriority) { Search.setFilterWMTSPriority(this.options.searchOptions.filterWMTSPriority); } if (this.options.searchOptions.filterTMS === false) { Search.setFilterTMS(this.options.searchOptions.filterTMS); } if (this.options.searchOptions.filterProjections) { Search.setFiltersByProjection(this.options.searchOptions.filterProjections); } } // abonnement au service Search.target.addEventListener("suggest", (e) => { logger.debug(e); let suggestResults = e.detail; // filtre des suggestions selon la configuration ou l'option filterLayersList suggestResults = this._filterResultsFromConfigLayers(suggestResults); this._fillSearchedSuggestListContainer(suggestResults); }); } if (!this.options.collapsible) { this.options.collapsed = false; // on interdit le mode pliable ! } /** {Boolean} specify if searchEngine control is collapsed (true) or not (false) */ this.collapsed = this.options.collapsed; // identifiant du contrôle : utile pour suffixer les identifiants CSS (pour gérer le cas où il y en a plusieurs dans la même page) this._uid = this.options.id || SelectorID.generate(); this._showSearchEngineButton = null; this._showSearchEngineAdvancedButton = null; // container de l'input de recherche /** @private */ this._inputSearchContainer = null; // container des reponses de l'autocompletion / du service de recherche /** @private */ this._autocompleteContainer = null; /** @private */ this._containerResultsLocation = null; /** @private */ this._containerResultsSuggest = null; // Radio buttons correspondants /** @private */ this._radioButtonLocation = null; /** @private */ this._radioButtonSuggest = null; // listes des reponses de l'autocompletion /** @private */ this._suggestedLocations = []; // container des reponses du geocodage /** @private */ this._geocodedContainer = null; // liste des reponses du geocodage /** @private */ this._geocodedLocations = []; // container des filtres du geocodage /** @private */ this._filterContainer = null; // ressource de geocodage selectionnée pour le geocodage avancé /** @private */ this._currentGeocodingCode = null; // localisant /** @private */ this._currentGeocodingLocation = null; // liste des filtres du geocodage pour le geocodage avancé /** @private */ this._advancedSearchFilters = {}; this._initAdvancedSearchFilters(); // liste des ressources du geocodage pour le geocodage avancé /** @private */ this._advancedSearchCodes = []; this._initAdvancedSearchCodes(); // recherche par coordonnées : systemes de projections /** @private */ this._coordinateSearchSystems = []; if (this.options.displayButtonCoordinateSearch) { this._initCoordinateSearchSystems(); this._currentCoordinateSearchSystems = this._coordinateSearchSystems[0]; // epsg:4326 this._currentCoordinateSearchType = this._coordinateSearchSystems[0].type; // geographical ou metric } // recherche par coordonnées : unités /** @private */ this._coordinateSearchUnits = []; if (this.options.displayButtonCoordinateSearch) { this._initCoordinateSearchUnits(); this._currentCoordinateSearchUnits = this._coordinateSearchUnits[this._currentCoordinateSearchType][0].code; // decimal } /** @private */ this._coordinateSearchLngInput = null; /** @private */ this._coordinateSearchLatInput = null; // marker /** @private */ this._marker = null; // marker style or url /** @private */ var _markerStyle = this.options.markerStyle; /** @private */ var _markerUrl = this.options.markerUrl; if (_markerUrl) { this._markerUrl = _markerUrl; } else { this._markerUrl = (Object.keys(Markers).indexOf(_markerStyle) === -1) ? Markers["lightOrange"] : Markers[_markerStyle]; } // marker display /** @private */ this._displayMarker = this.options.displayMarker; // popup /** @private */ this._popupContent = null; /** @private */ this._popupDiv = this._initPopupDiv(); /** @private */ this._popupOverlay = null; // trigger geocode /** @private */ this._triggerHandler = null; } /** * this method is called by this.initialize() * and makes sure input options are correctly formated * * @param {Object} options - options * * @private */ _checkInputOptions (options) { var i; if (options.resources) { // on vérifie que resources est bien un objet if (typeof options.resources === "object") { // ressources de geocodage var geocodeResources = options.resources.geocode; if (geocodeResources) { // on vérifie que la liste des ressources de geocodage est bien un tableau if (Array.isArray(geocodeResources)) { var geocodeResourcesList = ["StreetAddress", "PositionOfInterest", "CadastralParcel", "Administratif"]; for (i = 0; i < geocodeResources.length; i++) { if (geocodeResourcesList.indexOf(geocodeResources[i]) === -1) { // si la resource n'est pas référencée, on l'enlève // geocodeResources.splice(i, 1); logger.log("[SearchEngine] options.resources.geocode : " + geocodeResources[i] + " is not a resource for geocode"); } } } else { logger.log("[SearchEngine] 'options.resources.geocode' parameter should be an array"); geocodeResources = null; } } // ressources d'autocompletion var autocompleteResources = options.resources.autocomplete; if (autocompleteResources) { // on vérifie que la liste des ressources d'autocompletion est bien un tableau if (Array.isArray(autocompleteResources)) { var autocompleteResourcesList = ["StreetAddress", "PositionOfInterest"]; for (i = 0; i < autocompleteResources.length; i++) { if (autocompleteResourcesList.indexOf(autocompleteResources[i]) === -1) { // si la resource n'est pas référencée, on l'enlève // autocompleteResources.splice(i, 1); logger.log("[SearchEngine] options.resources.autocomplete : " + autocompleteResources[i] + " is not a resource for autocomplete"); } } } else { logger.log("[SearchEngine] 'options.resources.autocomplete' parameter should be an array"); autocompleteResources = null; } } } else { logger.log("[SearchEngine] 'resources' parameter should be an object"); options.resources = null; } } } /** * this method is called by this.initialize() * and initialize the geocoding resources titles. * * @private */ _initAdvancedSearchCodes () { // INFORMATION // on y ajoute les filtres attributaires pour une table de ressources // selectionnée via un evenement (onchange) de la liste deroulante du // menu avancé du geocodage. // cf. onGeocodingAdvancedSearchCodeChange() pour la selection de la // ressource de geocodage à afficher var geocodeResources = this.options.resources.geocode; if (geocodeResources === "location") { geocodeResources = ["PositionOfInterest", "StreetAddress", "CadastralParcel"]; } if (!Array.isArray(geocodeResources)) { geocodeResources = [geocodeResources]; } for (var i = 0; i < geocodeResources.length; i++) { switch (geocodeResources[i]) { case "PositionOfInterest": this._advancedSearchCodes.push({ id : "PositionOfInterest", title : "Lieux/toponymes" }); break; case "StreetAddress": this._advancedSearchCodes.push({ id : "StreetAddress", title : "Adresses" }); break; case "CadastralParcel": this._advancedSearchCodes.push({ id : "CadastralParcel", title : "Parcelles cadastrales" }); break; default: break; } } // par défaut, au cas où aucune ressource passée en option ne correspond à celles attendues if (this._advancedSearchCodes.length === 0) { this._advancedSearchCodes = [{ id : "StreetAddress", title : "Adresses" }, { id : "PositionOfInterest", title : "Lieux/toponymes" }, { id : "CadastralParcel", title : "Cadastre" }]; } logger.log("advancedSearchCodes", this._advancedSearchCodes); } /** * this method is called by this.onAdd() * and initialize the advanced geocoding filters. * * @private */ _initAdvancedSearchFilters () { // liste des filtres par defauts pour toutes les ressources this._advancedSearchFilters = SearchEngineUtils.advancedSearchFiltersByDefault; // on merge les options avancées avec celles par defaut var advancedSearchFiltersCustom = this.options.advancedSearch; Utils.assign(this._advancedSearchFilters, advancedSearchFiltersCustom); logger.log("advancedSearchFilters", this._advancedSearchFilters); } /** * this method is called by the constructor and initialize the projection * systems. * getting coordinates in the requested projection : * see this.onCoordinateSearchSystemChange() * * @private */ _initCoordinateSearchSystems () { // on donne la possibilité à l'utilisateur de modifier // la liste des systèmes à afficher // Ex. this.options.coordinateSearch.systems // systemes de projection disponible par defaut var projectionSystemsByDefault = [{ label : "G\u00e9ographique", crs : "EPSG:4326", type : "Geographical" }, { label : "Web Mercator", crs : "EPSG:3857", type : "Metric" }, { label : "Lambert 93", crs : "EPSG:2154", type : "Metric" }]; var systems = this.options.coordinateSearch.systems; if (systems) { // on ajoute les definitions d'un systeme de reference fournies par l'utilisateur for (var i = 0; i < systems.length; i++) { var sys = systems[i]; this._setSystem(sys); } } // on ajoute les systèmes de projections par défaut if (this._coordinateSearchSystems.length === 0) { for (var j = 0; j < projectionSystemsByDefault.length; j++) { this._setSystem(projectionSystemsByDefault[j]); } } } /** * this method is called by the constructor and initialize the units. * getting coordinates in the requested units : * see this.onCoordinateSearchUnitsChange() * * @private */ _initCoordinateSearchUnits () { // on donne la possibilité à l'utilisateur de modifier // la liste des unités à afficher // Ex. // this.options.units : ["DEC", "DMS"] // unités disponible par defaut var projectionUnitsByDefault = { Geographical : [{ code : "DEC", label : "degrés décimaux", format : MathUtils.coordinateToDecimal }, { code : "DMS", label : "degrés sexagésimaux", format : MathUtils.coordinateToDMS }], Metric : [{ code : "M", label : "mètres", format : MathUtils.coordinateToMeter }, { code : "KM", label : "kilomètres", format : MathUtils.coordinateToKMeter }] }; var units = this.options.coordinateSearch.units; if (units) { for (var type in projectionUnitsByDefault) { if (projectionUnitsByDefault.hasOwnProperty(type)) { var found = false; for (var j = 0; j < projectionUnitsByDefault[type].length; j++) { var obj = projectionUnitsByDefault[type][j]; for (var i = 0; i < units.length; i++) { var unit = units[i]; if (obj.code === unit) { found = true; if (!this._coordinateSearchUnits[type]) { this._coordinateSearchUnits[type] = []; } this._coordinateSearchUnits[type].push(obj); } } } if (!found) { this._coordinateSearchUnits[type] = projectionUnitsByDefault[type]; } } } } // au cas où... if (typeof this._coordinateSearchUnits === "object" && Object.keys(this._coordinateSearchUnits).length === 0) { this._coordinateSearchUnits = projectionUnitsByDefault; } } /** * this method is called by this.initialize() and initialize popup div * (to display results information on marker click) * * @returns {Object} element - DOM element for popup * @private */ _initPopupDiv () { var context = this; var element = document.createElement("div"); element.className = "gp-feature-info-div gpf-widget-color"; // bouton de suppression de la pop-up / marker // var span = document.createElement("span"); // span.className = "GPelementHidden gpf-visible"; // afficher en dsfr // span.innerText = "Supprimer"; var remove = document.createElement("button"); remove.title = "Supprimer le marqueur"; remove.className = "gp-styling-button remove gpf-btn gpf-btn-icon-remove fr-btn--remove fr-btn fr-btn--tertiary-no-outline fr-mt-1v fr-mr-2v"; // on remove click : remove marker remove.onclick = function () { var map = context.getMap(); if (context._marker) { map.removeOverlay(context._marker); context._marker = null; } if (context._popupOverlay != null) { context._popupOverlay.setPosition(undefined); } /** * event triggered when i want a remove geolocation popup * * @event searchengine:geolocation:remove * @property {Object} type - event * @property {Object} target - instance SearchEngine * @example * SearchEngine.on("searchengine:geolocation:remove", function (e) { * console.log(e.coordinates); * }) */ context.dispatchEvent({ type : "searchengine:geolocation:remove" }); }; // remove.appendChild(span); // bouton de fermeture de la pop-up var closer = document.createElement("button"); closer.title = "Fermer la pop-up"; closer.className = "gp-styling-button closer gpf-btn gpf-btn-icon-close fr-btn--close fr-btn fr-btn--tertiary-no-outline fr-mt-1v fr-mr-2v"; // on closer click : remove popup closer.onclick = function () { if (context._popupOverlay != null) { context._popupOverlay.setPosition(undefined); } return false; }; this._popupContent = document.createElement("div"); this._popupContent.className = "gp-features-content-div"; this._popupContent.style["min-width"] = "200px"; element.appendChild(closer); element.appendChild(this._popupContent); element.appendChild(remove); return element; } // ################################################################### // // ######################## DOM initialize ########################### // // ################################################################### // /** * Create control main container * * @returns {HTMLElement} DOM element * * @private */ _initContainer () { // create main container var container = this._createMainContainerElement(); var searchDiv = this._createSearchDivElement(); // create search engine picto var picto = this._showSearchEngineButton = this._createShowSearchEnginePictoElement(this.options.collapsible); searchDiv.appendChild(picto); // only dsfr : on applique un fond blanc sur une barre de recherche fixe if (!this.options.collapsible) { container.classList.add("gpf-widget-color", "gpf-widget-padding"); } var search = this._inputSearchContainer = this._createSearchInputElement(this.options.placeholder); if (this.options.displayButtonClose) { search.appendChild(this._createSearchResetElement()); } var context = this; if (search.addEventListener) { search.addEventListener("click", function () { context.onAutoCompleteInputClick(); }); } else if (search.attachEvent) { search.attachEvent("onclick", function () { context.onAutoCompleteInputClick(); }); } searchDiv.appendChild(search); var buttonsContainer = this._createButtonsElement(); var firstLineWrapper = this._createFirstLineWrapper(); firstLineWrapper.appendChild(searchDiv); firstLineWrapper.appendChild(buttonsContainer); container.appendChild(firstLineWrapper); if (checkDsfr() && this.options.splitResults) { var radioContainer = this._createRadioContainer(); container.appendChild(radioContainer); } if (checkDsfr() && this.options.splitResults) { var radioElements; [radioElements, this._radioButtonLocation, this._radioButtonSuggest] = this._createRadioElements(); radioContainer.appendChild(radioElements); } if (this.options.displayButtonGeolocate) { var geolocateShow = this._createShowGeolocateElement(); buttonsContainer.appendChild(geolocateShow); } if (this.options.displayButtonCoordinateSearch || this.options.coordinateSearchInAdvancedSearch) { var searchByCoordinateShow = this._createShowSearchByCoordinateElement(); if (!this.options.coordinateSearchInAdvancedSearch) { buttonsContainer.appendChild(searchByCoordinateShow); } var coordinatePanel = this._createCoordinateSearchPanelElement(); var coordinatePanelDiv = this._createCoordinateSearchPanelDivElement(); var coordinateHeader = this._createCoordinateSearchPanelHeaderElement(); var coordinateForm = this._createCoordinateSearchPanelFormElement(); var div = null; div = this._containerSystems = this.__createCoordinateSearchDivElement(); coordinateForm.appendChild(div); var labelSystems = this._createCoordinateSearchSystemsLabelElement(); var systems = this._setCoordinateSearchSystemsSelectElement(this._coordinateSearchSystems); div.appendChild(labelSystems); div.appendChild(systems); div = this._containerUnits = this.__createCoordinateSearchDivElement(); coordinateForm.appendChild(div); var labelUnits = this._createCoordinateSearchUnitsLabelElement(); var units = this._setCoordinateSearchUnitsSelectElement(this._coordinateSearchUnits[this._currentCoordinateSearchType]); div.appendChild(labelUnits); div.appendChild(units); div = this._containerCoordinateLat = this.__createCoordinateSearchDivElement(); coordinateForm.appendChild(div); var coordinateLat = this._setCoordinateSearchLatLabelElement(this._currentCoordinateSearchType); var coordinateInputLat = this._coordinateSearchLatInput = this._setCoordinateSearchLatInputElement(this._currentCoordinateSearchUnits); div.appendChild(coordinateLat); div.appendChild(coordinateInputLat); div = this._containerCoordinateLng = this.__createCoordinateSearchDivElement(); coordinateForm.appendChild(div); var coordinateLng = this._setCoordinateSearchLngLabelElement(this._currentCoordinateSearchType); var coordinateInputLng = this._coordinateSearchLngInput = this._setCoordinateSearchLngInputElement(this._currentCoordinateSearchUnits); div.appendChild(coordinateLng); div.appendChild(coordinateInputLng); var submit = this._createCoordinateSearchSubmitElement(); coordinateForm.appendChild(submit); coordinatePanelDiv.appendChild(coordinateHeader); coordinatePanelDiv.appendChild(coordinateForm); coordinatePanel.appendChild(coordinatePanelDiv); if (!this.options.coordinateSearchInAdvancedSearch) { container.appendChild(coordinatePanel); } } if (this.options.displayButtonAdvancedSearch) { var advancedShow = this._showSearchEngineAdvancedButton = this._createShowAdvancedSearchElement(); buttonsContainer.appendChild(advancedShow); // INFO je decompose les appels car j'ai besoin de recuperer le container // des filtres var advancedPanel = this._createAdvancedSearchPanelElement(); var advancedPanelDiv = this._createAdvancedSearchPanelDivElement(); var advancedHeader = this._createAdvancedSearchPanelHeaderElement(); var advancedForm = this._createAdvancedSearchPanelFormElement(this._advancedSearchCodes, this.options.coordinateSearchInAdvancedSearch); var advancedFormFilters = this._filterContainer = this._createAdvancedSearchFormFiltersElement(); this._setFilter(this._advancedSearchCodes[0].id); // ex "PositionOfInterest" var advancedFormInput = this._createAdvancedSearchFormInputElement(); advancedForm.appendChild(advancedFormFilters); if (this.options.coordinateSearchInAdvancedSearch) { advancedForm.appendChild(coordinateForm); } advancedForm.appendChild(advancedFormInput); advancedPanelDiv.appendChild(advancedHeader); advancedPanelDiv.appendChild(advancedForm); advancedPanel.appendChild(advancedPanelDiv); container.appendChild(advancedPanel); } // INFO je decompose les appels car j'ai besoin de recuperer le container // des resultats de l'autocompletion var autocomplete = this._autocompleteContainer = this._createAutoCompleteElement(); var autocompleteList = this._createAutoCompleteListElement(); var containerResultsLocation = this._containerResultsLocation = this._createAutoCompletedLocationContainer(); var containerResultsSuggest = this._containerResultsSuggest = this._createSearchedSuggestContainer(); autocompleteList.appendChild(containerResultsLocation); autocompleteList.appendChild(containerResultsSuggest); autocomplete.appendChild(autocompleteList); container.appendChild(autocomplete); // INFO je decompose les appels car j'ai besoin de recuperer le container // des resultats du geocodage var geocode = this._createGeocodeResultsElement(); var geocodeDiv = this._createGeocodeResultsDivElement(); geocode.appendChild(geocodeDiv); var geocodeList = this._geocodedContainer = this._createGeocodeResultsListElement(); geocodeDiv.appendChild(geocodeList); container.appendChild(geocode); return container; } /** * this method is called by : * - this._initContainer() : ... * - this.onGeocodingAdvancedSearchCodeChoice() : ... * and initialize or create the filters container HTMLElement * to the geocoding advanced menu. * * @param {String} code - resource geocoding name * * @returns {HTMLElement} DOM element * @private */ _setFilter (code) { // INFORMATION // Nous avons 2 solutions possibles pour la mise en place des filtres. // 1. Soit on decide de creer tous les filtres pour chaque ressource // de geocodage à l'initialisation du composant, et on joue sur le // mode 'hidden' pour n'afficher que la ressource selectionnée. // 2. Soit on decide de creer à chaque fois les filtres pour la // ressource selectionnée. // Chaque solution a ses inconvenients/avantages. // Implementation du choix 2 car elle offre plus de souplesse pour // recuperer les 'form-data'... var container = this._filterContainer; var codeFound = false; for (var i = 0; i < this._advancedSearchCodes.length; i++) { if (this._advancedSearchCodes[i].id === code) { codeFound = true; break; } } if (!codeFound) { // cette ressource n'est pas disponible, // on supprime les anciens enfants... while (container.firstChild) { container.removeChild(container.firstChild); } return; } // on sauvegarde la ressource de geocodage sélectionnée this._currentGeocodingCode = code; // on supprime les enfants... while (container.firstChild) { container.removeChild(container.firstChild); } var lstAttributs = this._advancedSearchFilters[code]; if (!lstAttributs || lstAttributs.length === 0) { // cette ressource n'est pas parametrable return; } var divTable = this._createAdvancedSearchFiltersTableElement(code, true); for (var j = 0; j < lstAttributs.length; j++) { var divFilter = this._createAdvancedSearchFiltersAttributElement(lstAttributs[j]); divTable.appendChild(divFilter); } container.appendChild(divTable); return container; } // ################################################################### // // ################ methods to request and results ################### // // ################################################################### // /** * this method is called by this.onAutoCompleteSearch() * and executes a request to the service. * * @param {Object} settings - service settings * @param {String} settings.text - text * @param {Function} settings.onSuccess - callback * @param {Function} settings.onFailure - callback * @private */ _requestAutoComplete (settings) { // on ne fait pas de requête si on n'a pas renseigné de parametres ! if (!settings || (typeof settings === "object" && Object.keys(settings).length === 0)) { return; } // on ne fait pas de requête si la parametre 'text' est vide ! if (!settings.text) { return; } logger.log(settings); var options = {}; // on recupere les options du service Utils.assign(options, this.options.autocompleteOptions.serviceOptions); // ainsi que la recherche et les callbacks Utils.assign(options, settings); // on ajoute le paramètre filterOptions.type spécifiant les ressources. var resources = this.options.resources.autocomplete; if (resources && Array.isArray(resources)) { // il se peut que l'utilisateur ait surchargé ce paramètre dans geocodeOptions, if (!options.type) { options.type = resources; } } // cas où la clef API n'est pas renseignée dans les options du service, // on utilise celle renseignée au niveau du controle ou la clé "calcul" par défaut. options.apiKey = options.apiKey || this.options.apiKey; // si l'utilisateur a spécifié le paramètre ssl au niveau du control, on s'en sert // true par défaut (https) if (typeof options.ssl !== "boolean") { if (typeof this.options.ssl === "boolean") { options.ssl = this.options.ssl; } else { options.ssl = true; } } logger.log(options); Gp.Services.autoComplete(options); } /** * this method is called by this.onAutoCompleteSearchText() (case of success) * and fills the container of the location list. * it creates a HTML Element per location * * @param {Array} locations - Array of Gp.Services.AutoComplete.SuggestedLocation corresponding to autocomplete results list * @private */ _fillAutoCompletedLocationListContainer (locations) { if (!locations || locations.length === 0) { return; } // on vide la liste avant de la construire var element = this._containerResultsLocation; if (element.childElementCount) { while (element.firstChild) { element.removeChild(element.firstChild); } } element.classList.add("GPelementHidden", "gpf-hidden"); if (locations.length) { if (!this._radioButtonLocation || (this._radioButtonLocation && this._radioButtonLocation.checked)) { element.classList.remove("GPelementHidden", "gpf-hidden"); } this._displaySuggestedLocation(); if (!checkDsfr() || !this.options.splitResults) { this._createAutoCompletedLocationTitleElement(); } for (var i = 0; i < locations.length; i++) { // Proposals are dynamically filled in Javascript by autocomplete service this._createAutoCompletedLocationElement(locations[i], i); } } } /** * this method is called by this.() (case of success) * and fills the container of the suggest list. * it creates a HTML Element per suggest * * @param {Array} suggests - Array of suggested corresponding to search results list * @private */ _fillSearchedSuggestListContainer (suggests) { // on réduit la liste de suggests au nombre d'éléments maximum que l'on veut afficher if (this.options.searchOptions.maximumEntries) { suggests = suggests.slice(0, this.options.searchOptions.maximumEntries); } // on vide la liste avant de la construire var element = this._containerResultsSuggest; if (element.childElementCount) { while (element.firstChild) { element.removeChild(element.firstChild); } } element.classList.add("GPelementHidden", "gpf-hidden"); if (suggests.length) { if (!this._radioButtonSuggest || (this._radioButtonSuggest && this._radioButtonSuggest.checked)) { element.classList.remove("GPelementHidden", "gpf-hidden"); } if (!checkDsfr() || !this.options.splitResults) { this._createSearchedSuggestTitleElement(); } for (let i = 0; i < suggests.length;