UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

224 lines (223 loc) 13.3 kB
// SPDX-License-Identifier: Apache-2.0 import GeoGirafeAppComponent from './geogirafe-component.js'; import tippy from 'tippy.js'; import AboutComponent from '../../components/about/component.js'; import BasemapComponent from '../../components/basemap/component.js'; import CoordinateComponent from '../../components/coordinate/component.js'; import ContactComponent from '../../components/contact/component.js'; import MapDefaultContextMenuComponent from '../../components/context-menu/default-context-menu/component.js'; import MapCustomContextMenuComponent from '../../components/context-menu/custom-context-menu/component.js'; import CrossSectionSettingsComponent from '../../components/cross-section/cross-section-settings/component.js'; import CrossSectionViewComponent from '../../components/cross-section/cross-section-viewer/component.js'; import DrawingComponent, { DrawingStateLocation } from '../../components/drawing/component.js'; import EditComponent from '../../components/edit/component.js'; import EditFromComponent from '../../components/edit/editform/component.js'; import HelpComponent from '../../components/help/component.js'; import InfoboxComponent from '../../components/infobox/component.js'; import InfoWindowComponent from '../../components/infowindow/component.js'; import LayoutComponent from '../../components/layout/component.js'; import LRPanelComponent from '../../components/lr-panel/component.js'; import MapComponent from '../../components/map/component.js'; import MenuButtonComponent from '../../components/menubutton/component.js'; import ModalsComponent from '../../components/modals/component.js'; import NavigationComponent from '../../components/navigation/component.js'; import NewsButtonComponent from '../../components/news/news-button/component.js'; import NewsPanelComponent from '../../components/news/news-panel/component.js'; import OauthComponent from '../../components/auth/component.js'; import PrintComponent from '../../components/print/component.js'; import PrototypeBannerComponent from '../../components/prototypebanner/component.js'; import QueryBuilderComponent from '../../components/querybuilder/component.js'; import ScaleComponent from '../../components/scale/component.js'; import SearchComponent from '../../components/search/component.js'; import SelectionGridComponent from '../../components/selectiongrid/component.js'; import SelectionWindowComponent from '../../components/selectionwindow/component.js'; import ShareComponent from '../../components/share/component.js'; import ExternalLayersComponent from '../../components/extlayers/component.js'; import ThemeComponent from '../../components/themes/component.js'; import TimeRestrictionComponent from '../../components/timerestriction/component.js'; import TimePickerComponent from '../../components/timerestriction/timepicker/component.js'; import TimeSliderComponent from '../../components/timerestriction/timeslider/component.js'; import TreeViewGroupComponent from '../../components/treeview/treeviewgroup/component.js'; import TreeViewItemComponent from '../../components/treeview/treeviewitem/component.js'; import TreeViewRootComponent from '../../components/treeview/treeviewroot/component.js'; import TreeViewThemeComponent from '../../components/treeview/treeviewtheme/component.js'; import UserPreferencesComponent from '../../components/userpreferences/component.js'; import VideoRecordComponent from '../../components/videorecord/component.js'; import DrawingState from '../drawing/drawingState.js'; import DrawingSerializer from '../drawing/drawingSerializer.js'; import { ShareState, ShareStateSerializer } from '../../components/share/sharestate.js'; import SelectionToolComponent from '../../components/selectiontool/component.js'; import AdvancedFilterComponent from '../../components/advancedfilter/component.js'; import FixedDimensionComponent from '../../components/drawing/fixed-dimension/component.js'; import GetDirectionsArtifact from '../../components/getdirections/component.js'; import ServiceWorkerHelper from '../utils/swhelper.js'; import FeedbackButtonComponent from '../../components/feedbackbutton/component.js'; import AddBookmarkComponent from '../../components/addbookmark/component.js'; import DrawingToolbarComponent from '../../components/drawing-toolbar/component.js'; export default class GeoGirafeApp { readyPromise; resolveReady; mainComponent; isIframe; get context() { return this.mainComponent.getContext(); } constructor(isIframe = false) { this.isIframe = isIframe; this.readyPromise = new Promise((resolve) => { this.resolveReady = resolve; }); this.initialize(); } async isReady() { return this.readyPromise; } async initialize() { this.defineMainComponent(); await this.mainComponent.isReady(); await this.initializeServiceWorker(); this.initializeInterface(); this.addCustomSerializers(); this.context.stateManager.state.application.isCustomSerializerInitialized = true; this.defineCoreComponents(); this.resolveReady(); } defineMainComponent() { // Define custom element for the main application customElements.define('girafe-app', GeoGirafeAppComponent); this.mainComponent = document.querySelector('girafe-app'); if (!this.mainComponent) { throw new Error('Application GeoGirafe not found. Please verify that the component is present in the HTML.'); } } initializeInterface() { // Override default tooltip maxWidth: tippy.setDefaultProps({ maxWidth: '' }); } addCustomSerializers() { // Add custom state and serializers (need to be done early, because the shared state will need them) this.context.stateManager.state.extendedState[DrawingStateLocation] = new DrawingState(); this.context.stateSerializer.addSerializer(DrawingState, new DrawingSerializer(this.context)); this.context.stateManager.state.extendedState.share = new ShareState(); this.context.stateSerializer.addSerializer(ShareState, new ShareStateSerializer(this.context, this.isIframe)); } defineCoreComponents() { customElements.define('girafe-about', AboutComponent); customElements.define('girafe-advanced-filter', AdvancedFilterComponent); customElements.define('girafe-basemap', BasemapComponent); customElements.define('girafe-coordinate', CoordinateComponent); customElements.define('girafe-contact', ContactComponent); customElements.define('girafe-default-context-menu', MapDefaultContextMenuComponent); customElements.define('girafe-custom-context-menu', MapCustomContextMenuComponent); customElements.define('girafe-layout', LayoutComponent); customElements.define('girafe-help', HelpComponent); customElements.define('girafe-infobox', InfoboxComponent); customElements.define('girafe-info-window', InfoWindowComponent); customElements.define('girafe-cross-section-settings', CrossSectionSettingsComponent); customElements.define('girafe-cross-section-view', CrossSectionViewComponent); customElements.define('girafe-lr-panel', LRPanelComponent); customElements.define('girafe-map', MapComponent); customElements.define('girafe-menu-button', MenuButtonComponent); customElements.define('girafe-modals', ModalsComponent); customElements.define('girafe-nav-history', NavigationComponent); customElements.define('girafe-news-button', NewsButtonComponent); customElements.define('girafe-news', NewsPanelComponent); customElements.define('girafe-print', PrintComponent); customElements.define('girafe-prototype-banner', PrototypeBannerComponent); customElements.define('girafe-query-builder', QueryBuilderComponent); customElements.define('girafe-oauth', OauthComponent); customElements.define('girafe-drawing', DrawingComponent); customElements.define('girafe-drawing-toolbar', DrawingToolbarComponent); customElements.define('girafe-edit', EditComponent); customElements.define('girafe-edit-form', EditFromComponent); customElements.define('girafe-scale', ScaleComponent); customElements.define('girafe-search', SearchComponent); customElements.define('girafe-selection-tool', SelectionToolComponent); customElements.define('girafe-selection-grid', SelectionGridComponent); customElements.define('girafe-selection-window', SelectionWindowComponent); customElements.define('girafe-share', ShareComponent); customElements.define('girafe-ext-layer', ExternalLayersComponent); customElements.define('girafe-theme-select', ThemeComponent); customElements.define('girafe-time-restriction', TimeRestrictionComponent); customElements.define('girafe-time-picker', TimePickerComponent); customElements.define('girafe-time-slider', TimeSliderComponent); customElements.define('girafe-tree-view', TreeViewRootComponent); customElements.define('girafe-tree-view-group', TreeViewGroupComponent); customElements.define('girafe-tree-view-item', TreeViewItemComponent); customElements.define('girafe-tree-view-theme', TreeViewThemeComponent); customElements.define('girafe-user-preferences', UserPreferencesComponent); customElements.define('girafe-video-record', VideoRecordComponent); customElements.define('girafe-fixed-dimension', FixedDimensionComponent); customElements.define('girafe-get-directions', GetDirectionsArtifact); customElements.define('girafe-feedback-button', FeedbackButtonComponent); customElements.define('girafe-add-bookmark', AddBookmarkComponent); } async initializeServiceWorker() { try { const storeVersion = 6; const dbCacheName = 'geogirafe-cache'; if (globalThis.location.protocol !== 'https:') { throw new Error('Service worker registration not possible if the application is served over HTTP.'); } if (!navigator?.serviceWorker) { throw new Error('Service worker not supported by your browser.'); } let sw; try { sw = await this.waitForServiceWorkerActivation(); } catch (err) { throw new Error(`Service worker registration failed. ${err}`, { cause: err }); } const config = this.context.configManager.Config; const serviceWorkerHelper = new ServiceWorkerHelper(sw); // Communicate configuration to service-worker // NOTE REG : Important : we have to wait that the config has been correctly sent to the SW // Otherwise the first requests can be sent without the correct auhentication. const issuerConfig = config.oauth?.issuer ?? config.gmfauth; const gmfConfig = config.oauth?.geomapfish ?? config.gmfauth; if (issuerConfig && gmfConfig) { const issuerHostname = new URL(issuerConfig.url).hostname; const audience = [...issuerConfig.audience, issuerHostname]; const swConfig = { logLevel: config.general.logLevel, audience: audience, audienceExcludedPaths: issuerConfig.audienceExcludedPaths, authMode: gmfConfig.authMode, alwaysSendCookies: issuerConfig.alwaysSendCookies, refererPolicy: gmfConfig.refererPolicy }; await serviceWorkerHelper.sendMessageToServiceWorker(swConfig); } else { // If no auth, we just update the log level, but we do not have to wait sw.postMessage({ logLevel: config.general.logLevel }); } await this.context.offlineManager.setServiceWorker(sw, storeVersion, dbCacheName); await this.context.authManager.initialize(sw); } catch (e) { console.error("Service worker could not be initialized. Authentication and offline maps won't work.\n", e); // Finish auth initialization, since without a service worker auth isn't possible this.context.stateManager.state.application.isAuthInitialized = true; } } async waitForServiceWorkerActivation() { const registration = await navigator.serviceWorker.register('service-worker.js', { type: 'module' }); if (registration.active) { return registration.active; } const sw = registration.installing || registration.waiting; if (!sw) { throw new Error('No service worker is installing or waiting.'); } return new Promise((resolve) => { sw.addEventListener('statechange', function onStateChange() { if (sw.state === 'activated') { sw.removeEventListener('statechange', onStateChange); resolve(sw); } }); }); } }