UNPKG

geopf-extensions-openlayers

Version:

French Geoportal Extensions for OpenLayers libraries

1,378 lines (1,274 loc) 55.4 kB
// import CSS import "../../CSS/Controls/Reporting/GPFreporting.css"; // import OpenLayers import Control from "../Control"; import Widget from "../Widget"; import Map from "ol/Map"; import { unByKey as olObservableUnByKey } from "ol/Observable"; import Overlay from "ol/Overlay"; // import local import Utils from "../../Utils/Helper"; import SelectorID from "../../Utils/SelectorID"; import Logger from "../../Utils/LoggerByDefault"; import Draggable from "../../Utils/Draggable"; // DOM import ReportingDOM from "./ReportingDOM"; import Drawing from "../Drawing/Drawing"; var logger = Logger.getLogger("reporting"); // ################################################################### // // ########################## IoC actions ############################ // // ################################################################### // /** * @typedef {InputActionByDefaut} IocInput * @property {function(Map):void} setMap - Définit la carte. * @property {function(string):void} setIcon - Définit l'icône. * @property {function():Object} getData - Retourne les données. * @property {function():void} clear - Réinitialise l'action. * @property {function():void} active - Active l'action. * @property {function():void} disable - Désactive l'action. * @description Instance d'action d'entrée pour le contrôle Reporting (IoC). */ class InputActionByDefaut { /** * @classdesc * Input action for the Reporting control. * This class handles user input on the map, specifically capturing single click events * to set coordinates for a reporting action. * @constructor * @alias InputActionByDefaut * @param {Map} [map] - Optional OpenLayers map instance. * @description * The constructor initializes the action with an optional map instance. * If no map is provided, it defaults to null. * It also initializes properties for data, coordinates, and event listeners. */ constructor (map /* optionnel !!! */) { logger.info("InputActionByDefaut constructor"); this.map = map || null; // will be set by the IoC this.data = null; this.coordinate = null; this.listener = null; this.icon = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAzNiIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBmaWxsPSIjMDAwMDkxIiBkPSJNMTguMzY0IDMuNjM2YTkgOSAwIDAgMSAwIDEyLjcyOEwxMiAyMi43MjhsLTYuMzY0LTYuMzY0QTkgOSAwIDAgMSAxOC4zNjQgMy42MzZaTTEyIDhhMiAyIDAgMSAwIDAgNCAyIDIgMCAwIDAgMC00WiIvPjwvc3ZnPg=="; } // ######################################################## // // ########################## API ######################### // /** * Set the map for this action * @api * @param {Map} map - Map. */ setMap (map) { logger.info("InputActionByDefaut map"); this.map = map; } /** * Set the icon for this action * @api * @param {String} icon - Icon URL or base64 encoded string. * @description * This method sets the icon for the input action. * It updates the icon property with the provided value. * If the icon is not provided, it defaults to a base64 encoded SVG string. */ setIcon (icon) { logger.info("InputActionByDefaut icon"); if (icon) { this.icon = icon; } } /** * Get the data for this action * @api * @returns {Object} data - Data for this action. * @description * This method returns a GeoJSON FeatureCollection with a single Point feature. * The Point's coordinates are set to the last clicked coordinate on the map. * The FeatureCollection also includes a CRS (Coordinate Reference System) definition * based on the map's current projection. * @example * var data = inputAction.getData(); * console.log(data); * // Output: * // { * // location: { * // type: "FeatureCollection", * // crs: { * // type: "name", * // properties: { * // name: "EPSG:4326" * // } * // }, * // features: [ * // { * // type: "Feature", * // geometry: { * // type: "Point", * // coordinates: [longitude, latitude] * // }, * // properties: { * // description: "Point de signalement", * // date: "2023-10-01T12:00:00Z", * // }, * // } * // ] * // } * // } */ getData () { logger.info("InputActionByDefaut data"); var projection = this.map.getView().getProjection(); if (!this.coordinate) { this.coordinate = this.map.getView().getCenter(); } var geometry = { type : "FeatureCollection", crs : { type : "name", properties : { name : projection.getCode() } }, features : [ { type : "Feature", crs : { type : "name", properties : { name : projection.getCode() } }, geometry : { type : "Point", coordinates : this.coordinate || [0, 0] }, properties : { description : "Point de signalement", date : new Date().toISOString(), author : "Anonyme" } } ] }; this.data = { location : geometry }; return this.data || { location : null }; } /** * Clear the data and remove the event listener * @api * @description * This method resets the data and coordinate properties to null, * and removes the event listener if it exists. */ clear () { logger.info("InputActionByDefaut clear"); this.data = null; this.coordinate = null; if (this.listener) { olObservableUnByKey(this.listener); this.listener = null; } if (this.marker != null) { this.map.removeOverlay(this.marker); this.marker = null; } } /** * Activate the action by adding event listeners * @api * @description * This method sets up the action to listen for single click events on the map. * When a single click occurs, it triggers the handler method to capture the coordinates. */ active () { this._addEventsListeners(); } /** * Disable the action by removing event listeners * @api * @description * This method removes the event listeners that were added during activation. */ disable () { this._removeEventsListeners(); } // ######################################################## // // ######################### privates ##################### // _addEventsListeners () { logger.info("InputActionByDefaut active"); if (!this.map) { return; } this.listener = this.map.on("singleclick", this._handler.bind(this)); } _removeEventsListeners () { logger.info("InputActionByDefaut disable"); if (!this.map) { return; } olObservableUnByKey(this.listener); } _handler (e) { logger.info("InputActionByDefaut handler", e); if (!this.map) { return; } this.coordinate = e.coordinate; // on supprime le marqueur précédent if (this.marker != null) { this.map.removeOverlay(this.marker); this.marker = null; } // on ajoute un marqueur sur la carte var markerDiv = document.createElement("img"); markerDiv.src = this.icon; this.marker = new Overlay({ position : this.coordinate, positioning : "center-center", element : markerDiv, stopEvent : false }); this.map.addOverlay(this.marker); } } /** * @typedef {FormActionByDefaut} IocForm * @property {function(HTMLElement):void} setForm - Définit le formulaire. * @property {function():Object} getData - Retourne les données du formulaire. * @property {function():void} clear - Réinitialise le formulaire. * @property {function():void} active - Active l'action. * @property {function():void} disable - Désactive l'action. * @description Instance d'action de formulaire pour le contrôle Reporting (IoC). */ class FormActionByDefaut { /** * @classdesc * Form action for the Reporting control. * This class handles form submission and captures data from the form fields. * @constructor * @alias FormActionByDefaut * @param {HTMLFormElement} form - The form element to be managed by this action. * @description * The constructor initializes the action with a form element. * It sets up properties for data, form, and submit button. * The form and submit button will be set by the IoC (Inversion of Control) container. * The data property is initialized to null, indicating no data has been captured yet. */ constructor (form) { logger.info("FormActionByDefaut constructor"); this.data = null; this.form = null; // will be set by the IoC this.submit = null; this.error = null; this.message = null; } /** * Activate the action by adding event listeners * @api * @description * This method sets up the action to listen for the form submission event. * When the form is submitted, it triggers the handler method to capture the form data. */ active () { logger.info("FormActionByDefaut active"); this._addEventsListeners(); } /** * Disable the action by removing event listeners * @api * @description * This method does not perform any action in this implementation. * It is intended to be overridden in subclasses if needed. */ disable () { logger.info("FormActionByDefaut disable"); } /** * Set the form for this action * @api * @param {HTMLElement} form DOM element - The form element to be set for this action. */ setForm (form) { logger.info("FormActionByDefaut form"); this.form = form; this.submit = this.form.querySelector("input[type=\"submit\"]"); this.error = this.form.querySelector("#GPreportingFormFieldset"); this.message = this.form.querySelector("#GPreportingFormFieldsetMessages"); } /** * Get the data for this action * @api * @returns {Object} data - Data for this action. * @description * This method retrieves the data captured from the form submission. * It simulates a form submission by clicking the submit button, * and returns the data as an object. * @example * var data = formAction.getData(); * console.log(data); * // Output: * // { * // name: "Report Name", * // desc: "Report Description", * // theme: "Selected Theme" * // } */ getData () { logger.info("FormActionByDefaut data"); this.submit.click(); // simulate form submission if (!this.data) { this.error.classList.add("fr-fieldset--error"); this.message.classList.replace("gpf-hidden", "gpf-visible"); } else { this.error.classList.remove("fr-fieldset--error"); this.message.classList.replace("gpf-visible", "gpf-hidden"); } return this.data; } /** * Clear the data and reset the form * @api * @description * This method resets the data property to null, * effectively clearing any previously captured form data. * It is intended to be called when the form needs to be reset or cleared. */ clear () { logger.info("FormActionByDefaut clear"); this.data = null; } // ######################################################## // // ######################### privates ##################### // _addEventsListeners () { this.form.addEventListener("submit", this._handler.bind(this), {once : true}); } _handler (e) { logger.info("FormActionByDefaut handler", e); e.preventDefault(); // on récupère les données du formulaire var formData = new FormData(e.target); var name = formData.get("GPreportingLabelName"); var desc = formData.get("GPreportingTextDesc"); var theme = formData.get("GPreportingSelectTheme"); // on transforme les données du formulaire en objet if (name && desc && theme) { this.data = { name : name, desc : desc, theme : theme }; } return false; } } /** * @typedef {DrawingActionByDefaut} IocDrawing * @property {function(Map):void} setMap - Définit la carte. * @property {function(HTMLElement):void} setTarget - Définit le conteneur DOM. * @property {function(String):void} setFormat - Définit le format d'export. * @property {function():Object} getData - Retourne les données du dessin. * @property {function():void} clear - Réinitialise le dessin. * @property {function():void} active - Active l'action. * @property {function():void} disable - Désactive l'action. * @description Instance d'action de dessin pour le contrôle Reporting (IoC). */ class DrawingActionByDefaut { /** * @classdesc * Drawing action for the Reporting control. * This class handles drawing actions on the map, allowing users to create and manage drawings. * @constructor * @alias DrawingActionByDefaut * @param {Map} [map] - Optional OpenLayers map instance. * @description * The constructor initializes the action with an optional map instance. * If no map is provided, it defaults to null. * It also initializes properties for data, map, DOM elements, and the Drawing instance. * The Drawing instance is used to manage the drawing functionality on the map. * The DOM elements for the drawing button and panel will be set by the IoC (Inversion of Control) container. */ constructor (map) { logger.info("DrawingActionByDefaut constructor"); this.data = null; this.format = "geojson"; // default format for export this.map = map || null; // will be set by the IoC this.dom = null; // will be set by the IoC // containers this.Drawing = null; // widget this.DrawingBtn = null; // bouton picto this.DrawingPanel = null; // panneau } /** * Activate the action by adding event listeners * @api * @description * This method initializes the Drawing instance if it is not already created. * It sets the Drawing instance to be active and configures its panel. * The Drawing instance allows users to create and manage drawings on the map. * It also sets the position of the Drawing panel to be relative, allowing it to be positioned within its parent container. * If the Drawing instance is already initialized, it simply makes the Drawing panel visible. * This method is typically called when the user wants to start drawing on the map. */ active () { logger.info("ServiceActionByDefaut active"); if (!this.Drawing) { this._initializeDrawing(); } // this.DrawingBtn.setAttribute("aria-pressed", true); this.Drawing.setCollapsed(false); this.DrawingPanel.style.position = "relative"; this.DrawingPanel.style.left = "0px"; this.DrawingPanel.style.top = "0px"; this.DrawingPanel.style.bottom = "unset"; this.DrawingPanel.style.right = "unset"; } /** * Disable the action by removing event listeners * @api * @description * This method collapses the Drawing instance, effectively hiding the drawing panel. * It is typically called when the user wants to stop drawing or hide the drawing panel. * If the Drawing instance is not initialized, it simply returns without performing any action. * This method is intended to be called when the drawing functionality is no longer needed. * It ensures that the Drawing panel is hidden and the Drawing instance is not active. */ disable () { logger.info("DrawingActionByDefaut disable"); if (!this.Drawing) { return; } this.Drawing.setCollapsed(true); // this.DrawingBtn.setAttribute("aria-pressed", false); } /** * Clear the data * @api * @description * This method resets the data property to null, * effectively clearing any previously captured drawing data. * It is intended to be called when the drawing needs to be reset or cleared. * It does not perform any action on the Drawing instance itself. * This method is useful when the user wants to start a new drawing or discard the current drawing. * It ensures that the data property is cleared, allowing for a fresh start on the next drawing action. * It does not remove the Drawing instance or its event listeners, allowing the user to continue drawing if desired. */ clear () { logger.info("DrawingActionByDefaut clear"); this.data = null; } /** * Get the data for this action * @api * @returns {Object} data - Data for this action. * @description * This method retrieves the data captured from the drawing. * It exports the features from the Drawing instance * and returns them as an object. * If no drawing data is available, it returns an empty object. * @example * var data = drawingAction.getData(); * console.log(data); * // Output: * // { * // drawing: { * // type: "FeatureCollection", * // features: [...] * // } * // } */ getData () { logger.info("DrawingActionByDefaut data"); // on récupère les données du dessin if (this.Drawing) { var content = this.Drawing.exportFeatures(); this.data = { drawing : (this.format.toLowerCase() === "geojson") ? JSON.parse(content) : content }; } return this.data || { drawing : null }; } /** * Set the map for this action * @api * @param {Map} map - Map. * @description * This method sets the map instance for the Drawing action. * It initializes the Drawing instance if it is not already created. * If the Drawing instance is already initialized, it simply updates the map property. * This method is typically called when the map is ready or when the Drawing action needs to be associated with a specific map instance. */ setMap (map) { logger.info("DrawingActionByDefaut map"); this.map = map; } /** * Set the target DOM element for the Drawing action * @api * @param {HTMLElement} dom - The DOM element to set as the target for the Drawing action. */ setTarget (dom) { logger.info("DrawingActionByDefaut target"); if (!dom) { logger.warn("DrawingActionByDefaut target is null"); return; } this.dom = dom; } /** * Set the format for exporting drawings * @api * @param {String} format - The format to set for exporting drawings. */ setFormat (format) { logger.info("DrawingActionByDefaut format"); if (format) { this.format = format; } } // ######################################################## // // ######################### privates ##################### // _initializeDrawing () { logger.info("DrawingActionByDefaut initializeDrawing"); if (!this.map) { return; } var id = 1000; this.Drawing = new Drawing({ target : this.dom, id : id, layerDescription : { title : "Signalement", description : "Dessin de signalement", }, popup : { display : false }, tools : { export : false } }); this.Drawing.setExportFormat(this.format); var container = this.Drawing.getContainer(); container.className = ""; var button = this.DrawingBtn = container.querySelector("#GPshowDrawingPicto-" + id); if (button) { button.style.display = "none"; } var panel = this.DrawingPanel = container.querySelector("#GPdrawingPanel-" + id); if (panel) { panel.style.position = "relative"; panel.style.left = "0px"; panel.style.top = "0px"; panel.style.bottom = "unset"; panel.style.right = "unset"; panel.style.margin = "auto"; } var header = container.querySelector(".gpf-panel__header"); if (header) { header.style.display = "none"; } // on ajoute le widget de dessin à la carte this.map.addControl(this.Drawing); } } /** * @typedef {ServiceActionByDefaut} IocService * @property {function(Object):Promise} send - Envoie les données au service. * @property {function():void} clear - Réinitialise l'action. * @property {function():void} active - Active l'action. * @property {function():void} disable - Désactive l'action. * @description Instance d'action de service pour le contrôle Reporting (IoC). */ class ServiceActionByDefaut { constructor () { logger.info("ServiceActionByDefaut constructor"); } /** * Activate the action by adding event listeners * @api * @description * This method does not perform any action in this implementation. * It is intended to be overridden in subclasses if needed. */ active () { logger.info("ServiceActionByDefaut active"); } /** * Disable the action by removing event listeners * @api * @description * This method does not perform any action in this implementation. * It is intended to be overridden in subclasses if needed. */ disable () { logger.info("ServiceActionByDefaut disable"); } /** * Send data to the service * @api * @param {Object} data - Data to send. * @returns {Promise} - A promise that resolves when the data is sent. * @description * This method is intended to send data to a service. * It currently throws an error indicating that the method is not implemented. * @example * var serviceAction = new ServiceActionByDefaut(); * serviceAction.send(data); * // Output data to the service : * // { * // location: { ... }, // GeoJSON FeatureCollection with Point geometry * // name: "Report Name", * // desc: "Report Description", * // theme: "Selected Theme", * // drawing: { ... } // GeoJSON FeatureCollection with drawing features * // } */ send (data) { logger.info("ServiceActionByDefaut send"); return Promise.reject(new Error("ServiceActionByDefaut send not implemented")); } /** * Clear the data * @api * @description * This method does not perform any action in this implementation. * It is intended to be overridden in subclasses if needed. */ clear () { logger.info("ServiceActionByDefaut clear"); } } // ################################################################### // // ########################## Class Reporting ######################## // // ################################################################### // /** * @typedef {Object} ReportingOptions * @property {boolean} [collapsed=true] - Définit si le widget est replié au chargement. * @property {boolean} [draggable=false] - Permet de déplacer le panneau du widget. * @property {boolean} [auto=true] - Active l’ajout automatique des événements sur la carte. * @property {Array<string>} [thematics] - Liste des thématiques proposées dans le formulaire. * @property {string} [format="geojson"] - Format d’export des dessins (ex : "geojson", "kml"). * @property {string} [icon] - Icône utilisée pour le point de signalement (URL ou base64). * @property {HTMLElement} [element] - Élément DOM à utiliser comme conteneur principal. * @property {string} [target] - Sélecteur ou identifiant du conteneur cible. * @property {Function} [render] - Fonction de rendu personnalisée. * @property {string} [position] - Position CSS du widget sur la carte. * @property {boolean} [gutter] - Ajoute ou retire l’espace autour du panneau. */ /** * @classdesc * * Reporting control. * This control allows users to report issues or provide feedback on the map. * * @alias ol.control.Reporting * @module Reporting */ class Reporting extends Control { /** * @constructor * @param {Object} [options] - options * @param {Boolean} [options.collapsed=true] - specify if control is collapsed (true) or not (false) * @param {Boolean} [options.draggable=false] - specify if control is draggable (true) or not (false) * @param {Boolean} [options.auto=true] - specify if control add some stuff auto * @param {Array} [options.thematics] - specify the list of thematics * @param {String} [options.format="geojson"] - specify the format for export (default: "geojson") * @param {String} [options.icon] - specify the icon for point entry (default: base64 encoded SVG) * @param {HTMLElement} [options.element] - specify the DOM element to append the control * @param {String} [options.target] - specify the target element to append the control * @param {Function} [options.render] - specify the render function * @description * The Reporting control is a custom OpenLayers control that allows users to report issues or provide * feedback on the map. It provides a user interface for inputting details about the report, including * the location, description, and thematic category of the issue. * @fires reporting:sending * @fires reporting:opened * @example * var reporting = new ol.control.Reporting(); * map.addControl(reporting); */ constructor (options) { options = options || {}; // call ol.control.Control constructor super({ element : options.element, target : options.target, render : options.render }); if (!(this instanceof Reporting)) { throw new TypeError("ERROR CLASS_CONSTRUCTOR"); } // initialisation du composant this.#initialize(options); // Reporting 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) { // on selectionne le panneau de la 1ère étape this.setStep(0); // on initialise les actions IoC // (Input, Form, Service) this._setComponents(map); // mode "draggable" if (this.draggable) { Draggable.dragElement( this.panelReportingContainer, this.panelReportingHeaderContainer, this.options.position ? null : map.getTargetElement() ); } // mode "collapsed" if (!this.collapsed) { this.buttonReportingShow.click(); } // ajout des evenements sur la carte if (this.auto) { this._addEventsListeners(map); } } else { // suppression des evenements sur la carte // pour les futurs suppressions de couche if (this.auto) { this._removeEventsListeners(); } } // 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"); } } /** * Get container * * @returns {HTMLElement} container */ getContainer () { return this.container; } /** * 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) { return; } if ((collapsed && this.collapsed) || (!collapsed && !this.collapsed)) { return; } if (collapsed) { this.buttonReportingClose.click(); } else { this.buttonReportingShow.click(); } this.collapsed = collapsed; } // ################################################################### // // ##################### public methods IoC ########################## // // ################################################################### // /** * Set the components for the Reporting control. * @private * @param {Map} map - The OpenLayers map instance to set for the components. * @description * This method initializes the IoC (Inversion of Control) components for the Reporting control. * It sets up the input, form, service, and drawing actions by creating instances of the respective classes. */ _setComponents (map) { // on initialise les actions IoC if (!this.iocInput) { this.iocInput = new InputActionByDefaut(); } this.iocInput.setMap(map); this.iocInput.setIcon(this.options.icon || null); if (!this.iocForm) { this.iocForm = new FormActionByDefaut(); } this.iocForm.setForm(this.formReportingContainer); if (!this.iocService) { this.iocService = new ServiceActionByDefaut(); } if (!this.iocDrawing) { this.iocDrawing = new DrawingActionByDefaut(); } this.iocDrawing.setFormat(this.options.format || "geojson"); this.iocDrawing.setMap(map); this.iocDrawing.setTarget(this.drawingReportingContainer); this.stepContainer[0].action = this.iocInput; this.stepContainer[1].action = this.iocForm; this.stepContainer[2].action = this.iocService; this.stepContainer[3].action = this.iocDrawing; } /** * Add ioc * @param {IocInput} input - instance */ setComponentInput (input) { // TODO // verifier si l'input est une instance de InputActionByDefaut // if (input instanceof InputActionByDefaut) this.iocInput = input; } /** * Add ioc * @param {IocForm} form - instance */ setComponentForm (form) { this.iocForm = form; } /** * Add ioc * @param {IocService} service - instance */ setComponentService (service) { this.iocService = service; } /** * Add ioc * @param {IocDrawing} drawing - instance */ setComponentDrawing (drawing) { this.iocDrawing = drawing; } // ################################################################### // // #################### privates methods ############################# // // ################################################################### // /** * Initialize Reporting control (called by Reporting constructor) * * @param {Object} options - constructor options * @private */ #initialize (options) { this.uid = SelectorID.generate(); // set default options this.options = { collapsed : true, draggable : false, auto : true, thematics : [ "Réseau routier", "Bâtiment", "Toponyme", "Touristique/randonnée", "Borne cadastrale", "Hydrographie", "Végétation", "Autre" ] }; // merge with user options Utils.assign(this.options, options); /** {Boolean} specify if control is collapsed (true) or not (false) */ this.collapsed = this.options.collapsed; /** {Boolean} specify if control is draggable (true) or not (false) */ this.draggable = this.options.draggable; /** {Boolean} specify if control add some stuff auto */ this.auto = this.options.auto; /** @private */ this.buttonReportingShow = null; /** @private */ this.panelReportingContainer = null; /** @private */ this.panelReportingHeaderContainer = null; // usefull for the dragNdrop /** @private */ this.panelReportingFooterContainer = null; /** @private */ this.reportingBtnAnnulerFooter = null; /** @private */ this.reportingBtnSuivantFooter = null; /** @private */ this.buttonReportingClose = null; /** @private */ this.divReportingTitle = null; /** @private */ this.labelReportingIcon = null; /** @private */ this.buttonReportingSubmit = null; /** @private */ this.spanReportingError = null; /** @private */ this.inputReportingContainer = null; /** @private */ this.formReportingContainer = null; /** @private */ this.sendReportingContainer = null; /** @private */ this.drawingReportingContainer = null; this.step = 0; this.stepContainer = [ { name : "", footer : true, header : true, next : 1, prev : -1, action : null, // this.iocInput container : null // this.inputReportingContainer }, { name : "Décrire le signalement", footer : true, header : true, next : 2, prev : 0, action : null, // this.iocForm container : null // this.formReportingContainer }, { name : "Envoyer un signalement", footer : false, header : true, next : -1, prev : 1, action : null, // this.iocService container : null // this.sendReportingContainer }, { name : "Dessiner sur la carte", footer : true, header : true, next : 1, prev : 1, action : null, // this.iocDrawing container : null // this.drawingReportingContainer }, ]; // will be set by the IoC /** @type {IocInput} */ this.iocInput = null; /** @type {IocForm} */ this.iocForm = null; /** @type {IocService} */ this.iocService = null; /** @type {IocDrawing} */ this.iocDrawing = null; /** {Object} raw data */ this.data = null; /** {Array} specify some events listener */ this.eventsListeners = []; /** * event triggered when the reporting panel is opened * @event reporting:opened * @defaultValue "reporting:opened" * @group Events * @description * This event is dispatched when the reporting panel is opened. * It indicates that the reporting process has started and the user can begin inputting data. * This event can be used to perform additional actions when the reporting panel is opened, * such as initializing the input fields or updating the UI to reflect the reporting state. */ this.OPENED_REPORTING_EVENT = "reporting:opened"; /** * event triggered at the end of the reporting process * @event reporting:sending * @defaultValue "reporting:sending" * @group Events * @description * This event is dispatched when the reporting data is successfully sent. * It contains the reporting data that was sent. * This event can be used to perform additional actions after the reporting data is sent, * such as updating the UI or notifying other components. */ this.SEND_REPORTING_EVENT = "reporting:sending"; } /** * Create control main container (DOM initialize) * * @returns {HTMLElement} DOM element * @private */ #initContainer () { // create main container var container = this._createMainContainerElement(); var picto = this.buttonReportingShow = this._createShowReportingPictoElement(); container.appendChild(picto); // panel var reportingPanel = this.panelReportingContainer = this._createReportingPanelElement(); var reportingPanelDiv = this._createReportingPanelDivElement(); reportingPanel.appendChild(reportingPanelDiv); // header var reportingPanelHeader = this.panelReportingHeaderContainer = this._createReportingPanelHeaderElement(); // icone var reportingPanelIcon = this.labelReportingIcon = this._createReportingPanelIconElement(); reportingPanelHeader.appendChild(reportingPanelIcon); // title var reportingPanelTitle = this.divReportingTitle = this._createReportingPanelTitleElement(); reportingPanelHeader.appendChild(reportingPanelTitle); // close picto var reportingCloseBtn = this.buttonReportingClose = this._createReportingPanelCloseElement(); reportingPanelHeader.appendChild(reportingCloseBtn); // footer var reportingPanelFooter = this.panelReportingFooterContainer = this._createReportingPanelFooterElement(); var BtnAnnulerFooter = this.reportingBtnAnnulerFooter = this._createReportingButtonAnnulerFooterElement(); var BtnSuivantFooter = this.reportingBtnSuivantFooter = this._createReportingButtonSuivantFooterElement(); reportingPanelFooter.appendChild(BtnAnnulerFooter); reportingPanelFooter.appendChild(BtnSuivantFooter); // step container for the custom code var input = this.inputReportingContainer = this._createReportingPanelInputElement(); this.stepContainer[0].container = input; var form = this.formReportingContainer = this._createReportingPanelFormElement(this.options.thematics); this.stepContainer[1].container = form; var send = this.sendReportingContainer = this._createReportingPanelSendElement(); this.stepContainer[2].container = send; var draw = this.drawingReportingContainer = this._createReportingPanelDrawingElement(); this.stepContainer[3].container = draw; var submit = this.buttonReportingSubmit = this._createReportingSubmitFormElement(); form.appendChild(submit); var error = this.spanReportingError = this._createReportingErrorSendElement(); send.appendChild(error); reportingPanelDiv.appendChild(reportingPanelHeader); reportingPanelDiv.appendChild(input); reportingPanelDiv.appendChild(form); reportingPanelDiv.appendChild(send); reportingPanelDiv.appendChild(draw); reportingPanelDiv.appendChild(reportingPanelFooter); container.appendChild(reportingPanel); logger.log(container); return container; } /** * Add events listener on map (called by setMap) * * @param {Map} map - map * @private */ _addEventsListeners (map) { var self = this; this.eventsListeners["custom:action"] = function (e) { logger.trace(e); }; // the event custom:action is associate with an openlayers event map.getLayers().on("some:event", this.eventsListeners["custom:action"]); } /** * Remove events listener on map (called by setMap) * @private */ _removeEventsListeners () { var map = this.getMap(); map.getLayers().un("some:event", this.eventsListeners["custom:action"]); delete this.eventsListeners["custom:action"]; } // ################################################################### // // ############################ steps ################################ // // ################################################################### // /** * Set the current step of the reporting process. * @param {*} num - The step number to set. * @returns {void} * @description * This method sets the current step of the reporting process. * It updates the visibility of the step containers, * activates the corresponding IoC action, * and updates the reporting title and footer display. * It is typically called when the user navigates to a different step in the reporting process, * such as moving from the input step to the form step or the send step. */ setStep (num) { if (num === undefined) { return; } // on reinitialise les panneaux par defaut et on desactive les actions IoC for (let index = 0; index < this.stepContainer.length; index++) { const element = this.stepContainer[index].container; element.classList.replace("gpf-visible", "gpf-hidden"); var action = this.stepContainer[index].action; if (action) { action.disable(); action.clear(); } } // étape active this.step = num; var panel = this.stepContainer[num].container; panel.classList.replace("gpf-hidden", "gpf-visible"); // on active l'action IoC var action = this.stepContainer[num].action; if (action) { // puis, on active l'action IoC courrante action.active(); } // on modifie le titre this.divReportingTitle.innerHTML = this.stepContainer[num].name; // on modifie l'affichage du footer this.panelReportingFooterContainer.style.display = (this.stepContainer[num].footer) ? "flex" : "none"; this.reportingBtnAnnulerFooter.style.display = (this.stepContainer[num].prev === -1) ? "none" : "flex"; this.reportingBtnSuivantFooter.style.display = (this.stepContainer[num].next === -1) ? "none" : "flex"; this.labelReportingIcon.style.display = (this.stepContainer[num].prev === -1) ? "none" : "flex"; } /** * Move to the next step in the reporting process. * This method checks if there is a next step available. * @returns {void} * @description * This method is used to navigate to the next step in the reporting process. * It retrieves the next step from the stepContainer array based on the current step index. * If a next step exists (indicated by a valid index), it retrieves the action associated with the current step, * clears its data, and disables it. * The data from the current action is merged into the reporting data object. */ nextStep () { var current = this.step; var next = this.stepContainer[current].next; if (next !== -1) { var action = this.stepContainer[current].action; if (action) { // on récupère les données de l'action IoC courrante var data = action.getData(); // si les données sont vides, on ne va pas plus loin ! if (!data) { return; } logger.trace("Reporting nextStep", data); this.data = Object.assign({}, this.data, data); // puis, on desactive l'action IoC courrante action.disable(); action.clear(); } // on passe à l'étape suivante this.setStep(next); } } /** * Move to the previous step in the reporting process. * This method checks if there is a previous step available. * If there is a previous step, it clears the current action's data * and sets the step to the previous one. * @returns {void} * @description * This method is used to navigate back to the previous step in the reporting process. * It retrieves the previous step from the stepContainer array based on the current step index. * If a previous step exists (indicated by a valid index), it clears the current action's data * and sets the step to the previous one. * The current action's data is cleared to ensure that any unsaved changes are discarded. * This method is typically called when the user wants to go back to the previous step * in the reporting process, allowing them to review or modify their input before proceeding. * It is useful for scenarios where users may need to correct or change their input * before finalizing their report. * If the current step does not have a previous step (indicated by prev being -1), * the method does nothing, effectively preventing navigation to a non-existent step. * This ensures that the reporting process remains linear and prevents users from navigating * to steps that are not part of the defined workflow. */ prevStep () { var current = this.step; var prev = this.stepContainer[current].prev; if (prev !== -1) { var action = this.stepContainer[current].action; if (action) { // on ne récupère pas les données de l'action IoC courrante car on abandonne l'étape // on desactive l'action IoC action.clear(); } // on passe à l'étape précédente this.setStep(prev); } } clear () { // on reinitialise les panneaux par defaut et on desactive les actions IoC for (let index = 0; index < this.stepContainer.length; index++) { const element = this.stepContainer[index].container; element.classList.replace("gpf-visible", "gpf-hidden"); var action = this.stepContainer[index].action; if (action) { action.disable(); action.clear(); } } // on supprime la couche de signalement // créée par l'outil de dessin var drawing = this.iocDrawing.Drawing; if (drawing) { var layer = drawing.getLayer(); var map = this.getMap(); if (layer) { map.removeLayer(layer); drawing.setLayer(); } } } // ################################################################### // // ######################## event dom ################################ // // ################################################################### // /** * Handle the click event on the "Show Reporting" button. * @param {*} e - The click event object. * @private */ onShowReportingClick (e) { logger.trace("onShowReportingClick", e); var opened = this.buttonReportingShow.ariaPressed; if (opened === "true") { this.onPanelOpen(); } this.collapsed = !(opened === "true"); this.dispatchEvent("change:collapsed"); if (this.collapsed) { this.clear(); } else { // doit on revenir à la 1ére étape ? this.setStep(0); /** * event triggered when the reporting panel is opened */ this.dispatchEvent(this.OPENED_REPORTING_EVENT); } } /** * Handle the click event on the "Previous Reporting" button. * @param {*} e - The click event object. * @private */ onPrevReportingClick (e) { logger.trace("onPrevReportingClick", e); this.prevStep(); } /** * Handle the click event on the "Next Reporting" button. * @param {*} e - The click event object. * @private */ onNextReportingClick (e) { logger.trace("onNextReportingClick", e); this.nextStep(); } /** * Handle the click event on the "Close Reporting" button. * @param {*} e - The click event object. * @private */ onCloseReportingClick (e) { logger.trace("onCloseReportingClick", e); this.clear(); } /** * Handle th