@geogirafe/lib-geoportal
Version:
GeoGirafe is a flexible application to build online geoportals.
224 lines (223 loc) • 13.3 kB
JavaScript
// 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);
}
});
});
}
}