@igo2/context
Version:
1 lines • 365 kB
Source Map (JSON)
{"version":3,"file":"igo2-context.mjs","sources":["../../../packages/context/src/lib/share-map/shared/share-map.interface.ts","../../../packages/context/src/lib/share-map/shared/share-map-definitions.ts","../../../packages/context/src/lib/share-map/shared/share-map.utils.ts","../../../packages/context/src/lib/share-map/shared/share-map-encoder.ts","../../../packages/context/src/lib/share-map/shared/share-map-legacy.service.ts","../../../packages/context/src/lib/share-map/shared/share-map-parser.ts","../../../packages/context/src/lib/share-map/shared/share-map.service.ts","../../../packages/context/src/lib/context-manager/shared/context.service.ts","../../../packages/context/src/lib/context-import-export/shared/context-export.errors.ts","../../../packages/context/src/lib/context-import-export/shared/context-export.service.ts","../../../packages/context/src/lib/context-import-export/shared/context-export.utils.ts","../../../packages/context/src/lib/context-import-export/shared/context-import.errors.ts","../../../packages/context/src/lib/context-import-export/shared/context-import.utils.ts","../../../packages/context/src/lib/context-import-export/shared/context-import.service.ts","../../../packages/context/src/lib/context-import-export/context-import-export/context-import-export.component.ts","../../../packages/context/src/lib/context-import-export/context-import-export/context-import-export.component.html","../../../packages/context/src/lib/context-import-export/context-import-export.module.ts","../../../packages/context/src/lib/context-manager/context-form/context-form.component.ts","../../../packages/context/src/lib/context-manager/context-form/context-form.component.html","../../../packages/context/src/lib/context-manager/shared/context.enum.ts","../../../packages/context/src/lib/context-manager/shared/layer-context.directive.ts","../../../packages/context/src/lib/context-manager/shared/map-context.directive.ts","../../../packages/context/src/lib/context-manager/context-edit/context-edit.component.ts","../../../packages/context/src/lib/context-manager/context-edit/context-edit.component.html","../../../packages/context/src/lib/context-manager/context-item/context-item.component.ts","../../../packages/context/src/lib/context-manager/context-item/context-item.component.html","../../../packages/context/src/lib/context-map-button/bookmark-button/bookmark-dialog.component.ts","../../../packages/context/src/lib/context-map-button/bookmark-button/bookmark-dialog.component.html","../../../packages/context/src/lib/context-manager/context-list/context-list.enum.ts","../../../packages/context/src/lib/context-manager/context-list/context-list.component.ts","../../../packages/context/src/lib/context-manager/context-list/context-list.component.html","../../../packages/context/src/lib/context-manager/context-permissions/context-permission-item/context-permission-item.component.ts","../../../packages/context/src/lib/context-manager/context-permissions/context-permission-item/context-permission-item.component.html","../../../packages/context/src/lib/context-manager/context-permissions/context-permission.service.ts","../../../packages/context/src/lib/context-manager/context-permissions/context-permissions.component.ts","../../../packages/context/src/lib/context-manager/context-permissions/context-permissions.component.html","../../../packages/context/src/lib/context-manager/context-manager.module.ts","../../../packages/context/src/lib/context-map-button/bookmark-button/bookmark-button.component.ts","../../../packages/context/src/lib/context-map-button/bookmark-button/bookmark-button.component.html","../../../packages/context/src/lib/context-map-button/poi-button/poi-dialog.component.ts","../../../packages/context/src/lib/context-map-button/poi-button/poi-dialog.component.html","../../../packages/context/src/lib/context-map-button/poi-button/shared/poi.service.ts","../../../packages/context/src/lib/context-map-button/poi-button/poi-button.component.ts","../../../packages/context/src/lib/context-map-button/poi-button/poi-button.component.html","../../../packages/context/src/lib/context-map-button/user-button/user-button.animation.ts","../../../packages/context/src/lib/context-map-button/user-button/user-dialog.component.ts","../../../packages/context/src/lib/context-map-button/user-button/user-dialog.component.html","../../../packages/context/src/lib/context-map-button/user-button/user-button.component.ts","../../../packages/context/src/lib/context-map-button/user-button/user-button.component.html","../../../packages/context/src/lib/context-map-button/context-map-button.module.ts","../../../packages/context/src/lib/share-map/share-map/share-map-url.component.ts","../../../packages/context/src/lib/share-map/share-map/share-map-url.component.html","../../../packages/context/src/lib/share-map/share-map/share-map.component.ts","../../../packages/context/src/lib/share-map/share-map/share-map.component.html","../../../packages/context/src/lib/share-map/share-map.module.ts","../../../packages/context/src/lib/sidenav/sidenav.component.ts","../../../packages/context/src/lib/sidenav/sidenav.component.html","../../../packages/context/src/lib/sidenav/sidenav.module.ts","../../../packages/context/src/lib/context.module.ts","../../../packages/context/src/lib/context-manager/context-manager.directive.ts","../../../packages/context/src/lib/context-map-button/context-map-button.directive.ts","../../../packages/context/src/public_api.ts","../../../packages/context/src/igo2-context.ts"],"sourcesContent":["import { RouteServiceOptions } from '@igo2/core/route';\nimport { LayerGroupOptions, LayerOptions } from '@igo2/geo';\n\nexport const ServiceType = [\n 'wms',\n 'wmts',\n 'arcgisrest',\n 'imagearcgisrest',\n 'tilearcgisrest'\n] as const;\nexport type ServiceType = (typeof ServiceType)[number];\n\nexport enum ServiceTypeEnum {\n wms = 0,\n wmts = 1,\n arcgisrest = 2,\n imagearcgisrest = 3,\n tilearcgisrest = 4\n}\n\nconst BaseLayerParamsKeys = [\n 'visible',\n 'opacity',\n 'zIndex',\n 'parentId'\n] as const;\n\nexport const LayerParamsKeys = [\n 'id',\n 'index',\n 'names',\n 'type',\n 'version',\n 'queryString',\n ...BaseLayerParamsKeys\n] as const;\nexport type LayerParamsKeys = (typeof LayerParamsKeys)[number];\n\nexport const GroupParamsKeys = [\n 'title',\n 'id',\n 'expanded',\n ...BaseLayerParamsKeys\n] as const;\nexport type GroupParamsKeys = (typeof GroupParamsKeys)[number];\n\nconst PositionParamsKeys = [\n 'center',\n 'zoom',\n 'rotation',\n 'projection'\n] as const;\ntype PositionParamsKeys = (typeof PositionParamsKeys)[number];\n\nexport interface ShareMapKeysDefinitions {\n urlsKey: string;\n contextKey: string;\n languageKey: string;\n pos: DefinitionKeyParams<PositionParamsKeys>;\n layers: DefinitionKeyParams<LayerParamsKeys>;\n groups: DefinitionKeyParams<GroupParamsKeys>;\n}\n\nexport interface DefinitionKeyParams<T extends string> extends BaseKeyParams {\n params: DefinitionParams<T>;\n}\n\nexport type DefinitionParams<T extends string = string> = Record<\n T,\n BaseKeyParams | undefined\n>;\n\nexport interface BaseKeyParams {\n key: string;\n parse?: (value: string) => unknown;\n stringify?: (value: unknown) => string;\n}\n\nexport interface PositionParams {\n center?: [number, number];\n zoom?: number;\n rotation?: number;\n projection?: string;\n}\n\nexport interface LayerProperties {\n zIndex: number;\n visibility: boolean;\n type: ServiceType;\n opacity: number;\n parentId: string;\n}\n\nexport interface UrlParsedParam {\n position: PositionParams;\n layersOptions: LayerOptions[];\n context: string;\n}\n\nexport type LayerParams = BaseLayerParams & {\n index: number;\n names?: string;\n type?: string;\n};\n\nexport type GroupParams = BaseLayerParams &\n Pick<LayerGroupOptions, 'title' | 'id'> & { expanded?: boolean };\n\ntype BaseLayerParams = Pick<\n LayerOptions,\n 'visible' | 'opacity' | 'zIndex' | 'parentId' | 'id'\n>;\n\nexport interface ShareMapRouteKeysOptions extends RouteServiceOptions {\n context: string;\n urls: string;\n position: string;\n layers: string;\n groups: string;\n center: string;\n zoom: string;\n projection: string;\n rotation: string;\n opacity: string;\n}\n\nexport const SHARE_MAP_KEYS_DEFAULT_OPTIONS: ShareMapRouteKeysOptions = {\n context: 'ctx',\n urls: 'urls',\n position: 'pos',\n layers: 'layers',\n groups: 'groups',\n center: 'ctr',\n zoom: 'z',\n projection: 'p',\n rotation: 'r',\n opacity: 'o'\n};\n","import {\n ServiceTypeEnum,\n ShareMapKeysDefinitions,\n ShareMapRouteKeysOptions\n} from './share-map.interface';\n\nexport function shareMapKeyDefs(\n options: ShareMapRouteKeysOptions\n): ShareMapKeysDefinitions {\n return {\n contextKey: options.context,\n urlsKey: options.urls,\n languageKey: options.languageKey,\n pos: {\n key: options.position,\n params: {\n zoom: {\n key: options.zoom,\n parse: (params) => parseIntergerParam(params, options.zoom)\n },\n center: {\n key: '@',\n parse: parseCenter,\n stringify: stringifyCenter\n },\n rotation: {\n key: options.rotation,\n parse: (params) => parseRotation(params, options.rotation),\n stringify: formatNumber\n },\n projection: {\n key: options.projection,\n parse: (params) => extractParam(params, options.projection)\n }\n }\n },\n layers: {\n key: options.layers,\n params: {\n index: undefined,\n id: {\n key: 'id',\n parse: (params) => extractParam(params, 'id'),\n stringify: (value: string) => `${value}`\n },\n names: {\n key: 'n',\n parse: (params) => {\n const param = extractParam(params, 'n');\n if (!param) return undefined;\n const unbracketed = param.slice(1, -1);\n return decodeURIComponent(unbracketed);\n },\n stringify: (value: string) => `[${encodeURIComponent(value)}]`\n },\n opacity: {\n key: 'o',\n parse: (params) => parseFloatParam(params, 'o')\n },\n visible: {\n key: 'v',\n parse: (params) => parseBooleanParam(params, 'v'),\n stringify: stringifyBoolean\n },\n type: {\n key: 't',\n parse: (params) => parseLayerType(params, 't'),\n stringify: stringifyType\n },\n zIndex: {\n key: 'z',\n parse: (params) => parseIntergerParam(params, 'z')\n },\n parentId: {\n key: 'pid',\n parse: (params) => extractParam(params, 'pid')\n },\n version: {\n key: 'vrn',\n parse: (params) => extractParam(params, 'vrn')\n },\n queryString: {\n key: 'q',\n parse: (params) => extractParam(params, 'q')\n }\n }\n },\n groups: {\n key: options.groups,\n params: {\n id: {\n key: 'id',\n parse: (params) => extractIdParam(params, 'id')\n },\n parentId: {\n key: 'pid',\n parse: (params) => extractIdParam(params, 'pid')\n },\n title: {\n key: 't',\n parse: (params) => {\n const param = extractParam(params, 't');\n return param ? decodeURIComponent(param) : undefined;\n },\n stringify: (value: string) => encodeURIComponent(value)\n },\n visible: {\n key: 'v',\n parse: (params) => parseBooleanParam(params, 'v'),\n stringify: stringifyBoolean\n },\n opacity: {\n key: 'o',\n parse: (params) => parseFloatParam(params, 'o')\n },\n zIndex: {\n key: 'z',\n parse: (params) => parseIntergerParam(params, 'z')\n },\n expanded: {\n key: 'e',\n parse: (params) => parseBooleanParam(params, 'e'),\n stringify: stringifyBoolean\n }\n }\n }\n };\n}\n\nfunction stringifyBoolean(value: boolean): string {\n return value ? '1' : '0';\n}\n\nfunction parseBoolean(params: string): boolean {\n return !!parseInt(params);\n}\n\nfunction stringifyType(value: keyof typeof ServiceTypeEnum): string {\n return String(ServiceTypeEnum[value]);\n}\n\nfunction parseLayerType(\n params: string,\n key: string\n): keyof typeof ServiceTypeEnum {\n const param = extractParam(params, key);\n if (!param) {\n return;\n }\n const type = Number(param);\n\n return ServiceTypeEnum[type] as keyof typeof ServiceTypeEnum;\n}\n\nfunction stringifyCenter(values: [number, number]): string {\n return values.map(formatNumber).join(',');\n}\n\nfunction parseCenter(params: string | undefined): [number, number] | undefined {\n if (!params?.startsWith('@')) {\n return;\n }\n const paramsSplitted = params.split(',');\n const index = paramsSplitted.findIndex((param) => param.startsWith('@'));\n return [\n Number(paramsSplitted[index].slice(1)),\n Number(paramsSplitted[index + 1])\n ];\n}\n\nfunction parseRotation(params: string | undefined, key: string): number {\n const param = extractParam(params, key);\n if (!param) {\n return;\n }\n const degree = parseInteger(param);\n return (degree * Math.PI) / 180;\n}\n\nfunction parseIntergerParam(params: string | undefined, key: string): number {\n const param = extractParam(params, key);\n if (!param) {\n return;\n }\n return parseInteger(param);\n}\n\nfunction parseBooleanParam(params: string | undefined, key: string): boolean {\n const param = extractParam(params, key);\n if (!param) {\n return;\n }\n return parseBoolean(param);\n}\n\nfunction parseFloatParam(params: string | undefined, key: string): number {\n const param = extractParam(params, key);\n if (!param) {\n return;\n }\n return parseFloat(param);\n}\n\nfunction extractParam(\n params: string | undefined,\n suffix: string\n): string | undefined {\n return params\n ?.split(',')\n .find((param) => param.endsWith(suffix))\n ?.slice(0, -suffix.length);\n}\n\nfunction extractIdParam(\n params: string,\n suffix: 'id' | 'pid'\n): string | undefined {\n const paramsSplited = params.split(',');\n // Find the parameter that ends with exact id/pid and isn't part of another word\n const param = paramsSplited.find((param) => {\n const key = param.slice(-suffix.length);\n\n if (key !== suffix) {\n return false;\n }\n\n if (suffix === 'pid') {\n return true;\n }\n\n const prefixChar = param.slice(-suffix.length - 1, -suffix.length);\n return prefixChar !== 'p';\n });\n return param?.slice(0, -suffix.length);\n}\n\nfunction parseInteger(value: string) {\n return parseInt(value, 10);\n}\n\nfunction formatNumber(value: number) {\n return value.toFixed(5).replace(/\\.([^0]+)0+$/, '.$1');\n}\n","import { Params } from '@angular/router';\n\nimport { RouteServiceOptions } from '@igo2/core/route';\nimport {\n AnyDataSourceOptions,\n AnyLayerOptions,\n QueryFormat,\n QueryableDataSourceOptions,\n isLayerGroupOptions\n} from '@igo2/geo';\n\nimport { ServiceType, ShareMapKeysDefinitions } from './share-map.interface';\n\nexport function buildDataSourceOptions(\n type: ServiceType,\n url: string,\n layers: string[],\n version: string\n): AnyDataSourceOptions {\n const isLayerType = [\n 'wmts',\n 'arcgisrest',\n 'imagearcgisrest',\n 'tilearcgisrest'\n ].includes(type);\n const arcgisClause =\n type === 'arcgisrest' ||\n type === 'imagearcgisrest' ||\n type === 'tilearcgisrest';\n const params =\n type === 'wms' ? { LAYERS: layers.join(','), VERSION: version } : undefined;\n\n const layer = isLayerType ? layers.join(',') : undefined;\n const baseParams = {\n type: type,\n url,\n params,\n layer,\n version: type === 'wmts' ? '1.0.0' : undefined\n };\n\n if (arcgisClause) {\n return {\n ...baseParams,\n queryable: true,\n queryFormat: QueryFormat.ESRIJSON\n } as QueryableDataSourceOptions;\n }\n\n return baseParams;\n}\n\nexport function getFlattenOptions(\n options: AnyLayerOptions[]\n): AnyLayerOptions[] {\n return options.reduce((accumulator, option) => {\n if (isLayerGroupOptions(option)) {\n const children = option.children\n ? getFlattenOptions(option.children)\n : [];\n accumulator.push(option, ...children);\n } else {\n accumulator.push(option);\n }\n return accumulator;\n }, []);\n}\n\n/**\n * Checks if the provided query parameters contain legacy parameter pairs\n * (e.g., layers and URLs) that indicate older configuration formats.\n */\nexport function hasLegacyParams(\n params: Params,\n optionsLegacy: RouteServiceOptions\n): boolean {\n const {\n layersKey,\n wmsUrlKey,\n wmsLayersKey,\n wmtsUrlKey,\n wmtsLayersKey,\n arcgisUrlKey,\n arcgisLayersKey,\n iarcgisUrlKey,\n iarcgisLayersKey,\n tarcgisUrlKey,\n tarcgisLayersKey\n } = optionsLegacy;\n\n // Define valid legacy parameter pairs\n const legacyPairs: [string | undefined, string | undefined][] = [\n [layersKey, wmsUrlKey],\n [wmsLayersKey, wmsUrlKey],\n [wmtsLayersKey, wmtsUrlKey],\n [arcgisLayersKey, arcgisUrlKey],\n [iarcgisLayersKey, iarcgisUrlKey],\n [tarcgisLayersKey, tarcgisUrlKey]\n ].filter(([layer, url]) => layer && url) as [string, string][];\n\n // Check if any legacy pair exists in the query parameters\n return legacyPairs.some(\n ([layer, url]) => getParamValue(params, layer) && getParamValue(params, url)\n );\n}\n\nexport function hasModernShareParams(\n params: Params,\n keysDefinitions: ShareMapKeysDefinitions\n): boolean {\n const { contextKey, groups } = keysDefinitions;\n const hasGroups = !!getParamValue(params, groups.key);\n const hasContext = !!getParamValue(params, contextKey);\n\n return hasContext || hasGroups;\n}\n\nexport function getParamValue(params: Params, key: string): string | undefined {\n const value = params[key];\n return value !== '' ? value : undefined;\n}\n","import {\n AnyDataSourceOptions,\n AnyLayer,\n AnyLayerOptions,\n ID_GROUP_PREFIX,\n IgoMap,\n Layer,\n LayerGroup,\n LayerId,\n MapViewController,\n WMSDataSourceOptions,\n findParentId,\n getLayerOptionIdentifier,\n isLayerGroup,\n isLayerGroupOptions,\n isLayerItem\n} from '@igo2/geo';\nimport { ObjectUtils, OptionalRequired } from '@igo2/utils';\n\nimport type { DetailedContext } from '../../context-manager';\nimport {\n BaseKeyParams,\n DefinitionParams,\n GroupParams,\n LayerParams,\n PositionParams,\n ServiceType,\n ShareMapKeysDefinitions\n} from './share-map.interface';\nimport { getFlattenOptions } from './share-map.utils';\n\nexport class ShareMapEncoder {\n private context: DetailedContext | undefined;\n language: string;\n\n constructor(\n private SHARE_MAP_DEFS: ShareMapKeysDefinitions,\n private document: Document\n ) {}\n\n generateUrl(map: IgoMap, context: DetailedContext): string {\n this.context = context;\n\n const layers = [\n map.layerController.baseLayer,\n ...map.layerController.layersFlattened\n ].filter(Boolean);\n\n const urlParams = this.getBaseUrlConfig(map.viewController);\n this.buildQueryUrl(layers, urlParams);\n\n const [baseUrl] = this.document.location.href.split('?');\n const queryString = urlParams.toString();\n return queryString !== '' ? `${baseUrl}?${queryString}` : baseUrl;\n }\n\n /**\n * Replaces local group IDs with unique IDs to avoid conflicts and have short URL.\n * This is necessary for sharing the map context.\n */\n private replaceGroupLocalIds(layers: AnyLayer[]): void {\n const idMap = new Map<LayerId, LayerId>();\n const existingIds = new Set(\n layers.map((layer) => layer.id).filter(Boolean)\n );\n\n // eslint-disable-next-line prefer-const\n let counter = 1;\n layers.forEach((layer) => {\n if (layer.id && layer.id.toString().includes(ID_GROUP_PREFIX)) {\n const newId = this.getUniqueId(existingIds, counter);\n idMap.set(layer.id, newId);\n layer.options.id = newId;\n }\n });\n }\n\n private getUniqueId(existingIds: Set<string | number>, counter: number) {\n while (existingIds.has(String(counter))) {\n counter++;\n }\n existingIds.add(String(counter));\n return counter;\n }\n\n private getCurrentContext(): {\n layers: AnyLayerOptions[];\n center?: [number, number];\n projection?: string;\n zoom?: number;\n rotation?: number;\n } {\n return ObjectUtils.removeUndefined({\n layers: this.context?.layers,\n center: this.context?.map.view.center,\n projection: this.context?.map.view.projection,\n zoom: this.context?.map.view.zoom,\n rotation: this.context?.map.view.rotation\n });\n }\n\n /**\n * Filters layers to include only LayerGroups or LayerItems with a valid ServiceType.\n */\n private isLayerSharable(layers: AnyLayer[]): AnyLayer[] {\n return layers.filter(\n (layer) =>\n isLayerGroup(layer) ||\n (isLayerItem(layer) &&\n ServiceType.includes(layer.dataSource?.options?.type as ServiceType))\n );\n }\n\n /**\n * Extracts only LayerGroup items from the filtered layers.\n */\n private getLayerGroups(layers: AnyLayer[]): LayerGroup[] {\n return layers.filter(isLayerGroup) as LayerGroup[];\n }\n\n /**\n * Extracts only LayerItem items from the filtered layers.\n */\n private getLayerItems(layers: AnyLayer[]): Layer[] {\n return layers.filter(isLayerItem) as Layer[];\n }\n\n private buildQueryUrl(layers: AnyLayer[], urlParams: URLSearchParams) {\n const layersSharable = this.isLayerSharable(layers);\n this.replaceGroupLocalIds(layersSharable);\n const layersChanged = this.getFilteredMapLayers(layersSharable);\n\n const groups = this.getLayerGroups(layersChanged);\n const groupsQueryValue = this.buildGroupsQueryValue(groups);\n\n const layersByService = this.generateLayersOptionsByService(\n this.getLayerItems(layersChanged)\n );\n const [urls, layerParams] = this.buildLayersQueryValues(layersByService);\n\n if (urls.length > 0) {\n urlParams.set(this.SHARE_MAP_DEFS.urlsKey, urls.join(','));\n }\n if (layerParams.length > 0) {\n urlParams.set(this.SHARE_MAP_DEFS.layers.key, layerParams.join(';'));\n }\n if (groupsQueryValue) {\n urlParams.set(this.SHARE_MAP_DEFS.groups.key, groupsQueryValue);\n }\n }\n\n /**\n * Retrieves the current context layers and maps them by identifier.\n */\n private getContextLayersMap(): Map<string, AnyLayerOptions> {\n const ctxLayers = this.getCurrentContext()?.layers || [];\n const ctxFlattened = getFlattenOptions(ctxLayers);\n\n return new Map(\n ctxFlattened.map((lctx) => {\n const identifier = getLayerOptionIdentifier(lctx);\n return [identifier, lctx] as const;\n })\n );\n }\n\n /**\n * Filters layers and context layers based on visibility, opacity, and expanded state.\n */\n private getFilteredMapLayers(filteredLayers: AnyLayer[]): AnyLayer[] {\n const ctxLayersMap = this.getContextLayersMap();\n\n return filteredLayers.filter((layer) => {\n const mapLayerIdentifier = getLayerOptionIdentifier(layer.options);\n if (!mapLayerIdentifier) return false;\n const ctxLayer = ctxLayersMap.get(mapLayerIdentifier);\n if (!ctxLayer) return true;\n\n const visibilityChange = layer.visible !== (ctxLayer.visible ?? true);\n const opacityChange = layer.opacity !== (ctxLayer.opacity ?? 1);\n const layerParentId = this.getIdsNestedParent(layer)?.join('.');\n const ctxParentId =\n ctxLayer.parentId ?? findParentId(this.context.layers, ctxLayer);\n const parentIdChange = ctxParentId !== layerParentId;\n\n let expandedChange = false;\n let titleChange = false;\n if (isLayerGroup(layer) && isLayerGroupOptions(ctxLayer)) {\n // Check if the layer group was assign an local id\n // If so, we don't support any change for this case\n if (String(ctxLayer.id).includes(ID_GROUP_PREFIX)) {\n return false;\n }\n expandedChange = (ctxLayer.expanded ?? false) !== layer.expanded;\n titleChange = (ctxLayer.title ?? false) !== layer.title;\n }\n\n return (\n visibilityChange ||\n opacityChange ||\n expandedChange ||\n titleChange ||\n parentIdChange\n );\n });\n }\n\n private getIdsNestedParent(\n node: Layer | LayerGroup,\n ids?: LayerId[]\n ): LayerId[] | undefined {\n if (!node.parent) return ids;\n\n if (node.parent) {\n if (!ids) {\n ids = [node.parent.id];\n } else {\n ids.unshift(node.parent.id);\n }\n return this.getIdsNestedParent(node.parent, ids);\n }\n\n return ids;\n }\n\n private generateLayersOptionsByService(\n layers: Layer[]\n ): [url: string, layers: LayerParams[]][] {\n const layersByUrl = new Map<string, LayerParams[]>();\n\n layers.forEach((layer) => {\n const [url, params] = this.generateLayerOption(layer);\n if (!layersByUrl.has(url)) {\n layersByUrl.set(url, [params]);\n } else {\n layersByUrl.get(url)!.push(params);\n }\n });\n\n let customIndex = 0;\n return Array.from(layersByUrl.entries()).map(([url, layerParams]) => {\n const hasNoId = layerParams.some((p) => !p.id);\n if (hasNoId) {\n layerParams.forEach((params) => {\n if (!params.id) {\n params.index = customIndex;\n }\n });\n customIndex++;\n }\n return [url, layerParams];\n });\n }\n\n private generateLayerOption(\n layer: Layer\n ): [url: string, layers: LayerParams] {\n const dataSourceOptions = layer.dataSource.options as AnyDataSourceOptions;\n return [\n this.concatUrlWithVersion(dataSourceOptions),\n this.getLayerParams(layer)\n ];\n }\n\n private getLayerNames(dataSourceOptions: AnyDataSourceOptions): string {\n const type = dataSourceOptions.type.toLowerCase() as ServiceType;\n if (type === 'wms') {\n const params = (dataSourceOptions as Partial<WMSDataSourceOptions>)\n .params;\n return params.LAYERS;\n }\n\n return 'layer' in dataSourceOptions ? dataSourceOptions.layer : '';\n }\n\n private getWmsVersion(\n dataSourceOptions: AnyDataSourceOptions\n ): string | undefined {\n const params = (dataSourceOptions as Partial<WMSDataSourceOptions>)?.params;\n return params?.VERSION && params.VERSION !== '1.3.0'\n ? params.VERSION\n : undefined;\n }\n\n private concatUrlWithVersion(\n dataSourceOptions: AnyDataSourceOptions\n ): string {\n const url = dataSourceOptions.url;\n\n if ((dataSourceOptions.type.toLowerCase() as ServiceType) === 'wms') {\n const version = this.getWmsVersion(dataSourceOptions);\n if (version) {\n const operator = url.includes('?') ? '&' : '?';\n const { version: versionDef } = this.SHARE_MAP_DEFS.layers.params;\n return `${url}${operator}${versionDef.key}=${version}`;\n }\n }\n\n return dataSourceOptions.url;\n }\n\n private getLayerParams(layer: Layer): LayerParams {\n const dataSourceOptions = layer.dataSource.options;\n const isExisting = this.context?.layers\n ? this.hasLayerId(this.context.layers, layer.id)\n : false;\n return {\n index: undefined,\n ...(isExisting\n ? { id: layer.id }\n : {\n names: this.getLayerNames(dataSourceOptions),\n type: dataSourceOptions?.type\n }),\n opacity: this.getOpacity(layer.opacity),\n parentId: layer.parent?.id,\n visible: this.getVisibility(layer.visible),\n zIndex: layer.zIndex\n };\n }\n\n /** Recursive */\n private hasLayerId(\n layersOptions: AnyLayerOptions[],\n targetId: string | number\n ): boolean {\n if (targetId == null) return false;\n return layersOptions.some((l) => {\n if (l.id == null) return false;\n if (!isLayerGroupOptions(l)) {\n return String(l.id) === String(targetId);\n }\n\n if (isLayerGroupOptions(l) && Array.isArray(l.children)) {\n return this.hasLayerId(l.children, targetId);\n }\n return false;\n });\n }\n\n private getOpacity(opacity: number): number | undefined {\n return opacity === 1 ? undefined : opacity;\n }\n\n private getVisibility(visibility: boolean | undefined): boolean {\n return !!visibility;\n }\n\n private buildLayersQueryValues(\n layersByService: [url: string, params: LayerParams[]][]\n ): [urls: string[], layerParams: string[]] {\n const urls: string[] = [];\n const layerParams: string[] = [];\n for (const [url, layer] of layersByService) {\n let needUrl = false;\n\n for (const param of layer) {\n layerParams.push(this.stringifyLayerParams(param));\n if (param.id == null) {\n needUrl = true;\n }\n }\n\n // If some layer have no id push the url service.\n if (needUrl) {\n urls.push(url);\n }\n }\n\n return [urls, layerParams];\n }\n\n private stringifyLayerParams(params: LayerParams): string {\n const { index, ...restParams } = params;\n\n const stringifiedParams = this.stringifyDefinitions(\n restParams,\n this.SHARE_MAP_DEFS.layers.params\n );\n\n return restParams.id != null\n ? `${stringifiedParams}`\n : `${index},${stringifiedParams}`;\n }\n\n private getBaseUrlConfig(viewController: MapViewController): URLSearchParams {\n const { pos, contextKey, languageKey } = this.SHARE_MAP_DEFS;\n const href = this.document.location.href;\n const urlParams = this.getSanitizedParams(href);\n\n if (pos) {\n const positionStringified = this.stringifyPosition(\n this.getPosition(viewController)\n );\n urlParams.set(pos.key, positionStringified);\n }\n const contextUri = this.context?.uri;\n if (contextUri) urlParams.set(contextKey, contextUri);\n\n if (this.language && !urlParams.has(languageKey))\n urlParams.set(languageKey, this.language);\n\n return urlParams;\n }\n\n getSanitizedParams(baseUrl: string): URLSearchParams {\n const [, queryString] = baseUrl.split('?');\n const params = new URLSearchParams(queryString);\n\n const keys = this.extractKeys(this.SHARE_MAP_DEFS);\n keys.forEach((key) => {\n params.delete(key);\n });\n\n return params;\n }\n\n private extractKeys(defs: ShareMapKeysDefinitions): string[] {\n const keys: string[] = [];\n for (const key in defs) {\n if (Object.prototype.hasOwnProperty.call(defs, key)) {\n const value = defs[key];\n if (typeof value === 'string') {\n keys.push(value);\n } else if (typeof value === 'object' && value !== null) {\n keys.push(value.key);\n }\n }\n }\n\n return keys;\n }\n\n private getPosition(viewController: MapViewController): PositionParams {\n return ObjectUtils.removeUndefined({\n center: viewController.getCenter('EPSG:4326'),\n zoom: this.getZoom(viewController),\n rotation: this.getRotation(viewController),\n projection: this.getProjection(viewController)\n });\n }\n\n private getProjection(viewController: MapViewController): string | undefined {\n const ctxProjection = this.getCurrentContext().projection;\n const mapProjection = viewController.getOlProjection().getCode();\n return ctxProjection === mapProjection ? undefined : mapProjection;\n }\n\n private getZoom(viewController: MapViewController): number | undefined {\n const mapZoom = viewController.getZoom();\n const ctxZoom = this.getCurrentContext().zoom;\n return ctxZoom === mapZoom ? undefined : mapZoom;\n }\n\n private getRotation(viewController: MapViewController): number | undefined {\n const rotationRadians = viewController.getRotation();\n const rotationDegree = (rotationRadians * 180) / Math.PI;\n const ctxRotation = this.getCurrentContext().rotation;\n return rotationDegree === ctxRotation || rotationDegree === 0\n ? undefined\n : rotationDegree;\n }\n\n private stringifyPosition(position: PositionParams): string {\n const definitions = this.SHARE_MAP_DEFS.pos.params;\n const { center, ...restPosition } = position;\n const stringifiedParams = this.stringifyDefinitions(\n restPosition,\n definitions\n );\n\n const result = [\n `${definitions.center.key}${definitions.center.stringify(center)}`,\n stringifiedParams\n ].filter(Boolean);\n\n return result.join(',');\n }\n\n private stringifyDefinitions(\n values: Record<string, unknown>,\n definitions: DefinitionParams\n ): string | undefined {\n const result = Object.keys(ObjectUtils.removeUndefined(values))\n .map((key) => {\n const { key: defKey, stringify } = definitions[key] as BaseKeyParams;\n const value = stringify ? stringify(values[key]) : values[key];\n return `${value}${defKey}`;\n })\n .join(',');\n return result === '' ? undefined : result;\n }\n\n private buildGroupsQueryValue(layers: LayerGroup[]): string | undefined {\n if (layers.length === 0) return undefined;\n\n return layers\n .map((layer) => {\n const params = this.getLayerGroupParams(layer);\n return this.stringifyGroupParams(params);\n })\n .join(';');\n }\n\n private getLayerGroupParams(layer: LayerGroup): GroupParams {\n return {\n id: layer.id,\n title: layer.title,\n zIndex: layer.zIndex,\n parentId: layer.parent?.id,\n visible: this.getVisibility(layer.visible),\n opacity: this.getOpacity(layer.opacity),\n expanded: layer.expanded\n } satisfies OptionalRequired<GroupParams>;\n }\n\n private stringifyGroupParams(params: GroupParams): string {\n return this.stringifyDefinitions(params, this.SHARE_MAP_DEFS.groups.params);\n }\n}\n","import { HttpParams } from '@angular/common/http';\nimport { Params } from '@angular/router';\n\nimport { RouteServiceOptions } from '@igo2/core/route';\nimport { LayerOptions, generateIdFromSourceOptions } from '@igo2/geo';\nimport { ObjectUtils } from '@igo2/utils';\n\nimport { PositionParams, ServiceType } from './share-map.interface';\nimport { buildDataSourceOptions } from './share-map.utils';\n\ninterface SharedLayerConfig {\n layers: string[];\n zIndex: number | undefined;\n}\n\nexport class ShareMapLegacyParser {\n constructor(private options: RouteServiceOptions) {}\n\n parseUrl(params: Params): LayerOptions[] | undefined {\n const layerOptions = ServiceType.flatMap((type) =>\n this.readLayersQueryParamsByType(params, type)\n ).filter(Boolean);\n\n return layerOptions;\n }\n\n parsePosition(params: Params): PositionParams {\n const center = params[this.options.centerKey];\n const projection = params[this.options.projectionKey];\n const zoom = params[this.options.zoomKey];\n const rotation = params[this.options.rotationKey];\n\n return ObjectUtils.removeUndefined({\n center: center?.split(',').map(Number),\n projection,\n zoom: zoom ? Number(zoom) : undefined,\n rotation: rotation ? Number(rotation) : undefined\n });\n }\n\n private readLayersQueryParamsByType(\n params: Params,\n type: ServiceType\n ): LayerOptions[] | undefined {\n const [nameParamLayersKey, urlsKey] = this.getQueryKeyByType(params, type);\n if (!nameParamLayersKey || !urlsKey) {\n return undefined;\n }\n\n const layersByService: string[] = params[nameParamLayersKey].split('),(');\n const urls: string[] = params[urlsKey].split(',');\n\n return urls\n .map((urlSrc, index) => {\n // Sanitize URL and extract version\n const [url, version] = this.sanitizeUrl(urlSrc);\n\n const layersConfig = this.extractLayersByService(\n this.removeParenthesis(layersByService[index])\n );\n\n // Generate layer options for the current service\n return this.extractLayersOptions(\n layersConfig,\n url,\n type,\n version,\n params\n );\n })\n .flat();\n }\n\n private getQueryKeyByType(\n params: Params,\n type: string\n ): [nameParamLayersKey: string | undefined, urlsKey: string | undefined] {\n let nameParamLayersKey;\n let urlsKey;\n\n const {\n layersKey,\n wmsUrlKey,\n wmsLayersKey,\n wmtsUrlKey,\n wmtsLayersKey,\n arcgisUrlKey,\n arcgisLayersKey,\n iarcgisUrlKey,\n iarcgisLayersKey,\n tarcgisUrlKey,\n tarcgisLayersKey\n } = this.options;\n\n switch (type) {\n case 'wms':\n if ((params[layersKey] || params[wmsLayersKey]) && params[wmsUrlKey]) {\n urlsKey = wmsUrlKey;\n nameParamLayersKey = params[wmsLayersKey] ? wmsLayersKey : layersKey;\n }\n break;\n case 'wmts':\n if (params[wmtsLayersKey] && params[wmtsUrlKey]) {\n urlsKey = wmtsUrlKey;\n nameParamLayersKey = wmtsLayersKey;\n }\n break;\n case 'arcgisrest':\n if (params[arcgisLayersKey] && params[arcgisUrlKey]) {\n urlsKey = arcgisUrlKey;\n nameParamLayersKey = arcgisLayersKey;\n }\n break;\n case 'imagearcgisrest':\n if (params[iarcgisLayersKey] && params[iarcgisUrlKey]) {\n urlsKey = iarcgisUrlKey;\n nameParamLayersKey = iarcgisLayersKey;\n }\n break;\n case 'tilearcgisrest':\n if (params[tarcgisLayersKey] && params[tarcgisUrlKey]) {\n urlsKey = tarcgisUrlKey;\n nameParamLayersKey = tarcgisLayersKey;\n }\n break;\n }\n if (!nameParamLayersKey || !urlsKey) {\n return [undefined, undefined];\n }\n\n return [nameParamLayersKey, urlsKey];\n }\n\n private sanitizeUrl(url: string): [url: string, version: string | undefined] {\n const version =\n this.getQueryParam('version', url.toLocaleLowerCase()) || undefined;\n\n if (version) {\n const versionRegex = new RegExp(`[?&]version=${version}`, 'i');\n url = url.replace(versionRegex, '').replace(/[?&]$/, '');\n }\n\n if (url.endsWith('?')) {\n url = url.substring(0, url.length - 1);\n }\n return [url, version];\n }\n\n private getQueryParam(name: string, url: string): string | undefined {\n let paramValue;\n if (url.includes('?')) {\n const httpParams = new HttpParams({ fromString: url.split('?')[1] });\n paramValue = httpParams.get(name);\n }\n return paramValue;\n }\n\n private extractLayersByService(layersByService: string): SharedLayerConfig[] {\n if (!layersByService.includes(':igoz')) {\n return [\n {\n layers: layersByService.split(','),\n zIndex: null\n }\n ];\n }\n\n const layers = layersByService.match(\n /([^(),:]+(?:,[^(),:]+)*(:[^(),:]+(?:[:][^(),:]+)*)?)/g\n );\n\n return layers.map((layer) => {\n const [names, zIndex] = layer.split(':igoz');\n return {\n layers: names.split(','),\n zIndex: parseInt(zIndex)\n };\n });\n }\n\n private removeParenthesis(value: string) {\n if (value.startsWith('(')) {\n value = value.substr(1);\n }\n\n if (value.endsWith(')')) {\n value = value.slice(0, -1);\n }\n\n return value;\n }\n\n private extractLayersOptions(\n layersConfig: SharedLayerConfig[],\n url: string,\n type: ServiceType,\n version: string,\n params: Params\n ): LayerOptions[] {\n return layersConfig.map((layerConfig) => {\n const sourceOptions = buildDataSourceOptions(\n type,\n url,\n layerConfig.layers,\n version\n );\n const id = generateIdFromSourceOptions(sourceOptions);\n\n const visible = this.computeLayerVisibilityFromUrl(params, id);\n\n return ObjectUtils.removeUndefined({\n id,\n visible: visible,\n zIndex: layerConfig.zIndex,\n sourceOptions\n });\n });\n }\n\n private computeLayerVisibilityFromUrl(\n params: Params,\n currentLayerid: string\n ): boolean {\n const queryParams = params;\n let visible = true;\n if (!queryParams || !currentLayerid) {\n return visible;\n }\n let visibleOnLayersParams = '';\n let visibleOffLayersParams = '';\n let visiblelayers: string[] = [];\n let invisiblelayers: string[] = [];\n if (queryParams['visiblelayers']) {\n visibleOnLayersParams = queryParams['visiblelayers'];\n }\n if (queryParams['invisiblelayers']) {\n visibleOffLayersParams = queryParams['invisiblelayers'];\n }\n\n /* This order is important because to control whichever\n the order of * param. First whe open and close everything.*/\n if (visibleOnLayersParams === '*') {\n visible = true;\n }\n if (visibleOffLayersParams === '*') {\n visible = false;\n }\n\n // After, managing named layer by id (context.json OR id from datasource)\n visiblelayers = visibleOnLayersParams.split(',');\n invisiblelayers = visibleOffLayersParams.split(',');\n if (\n visiblelayers.indexOf(currentLayerid) > -1 ||\n visiblelayers.indexOf(currentLayerid.toString()) > -1\n ) {\n visible = true;\n }\n if (\n invisiblelayers.indexOf(currentLayerid) > -1 ||\n invisiblelayers.indexOf(currentLayerid.toString()) > -1\n ) {\n visible = false;\n }\n return visible;\n }\n}\n","import { Params } from '@angular/router';\n\nimport { RouteServiceOptions } from '@igo2/core/route';\nimport {\n type AnyLayerOptions,\n type LayerGroupOptions,\n type LayerOptions\n} from '@igo2/geo';\nimport { ObjectUtils, OptionalRequired } from '@igo2/utils';\n\nimport { ShareMapLegacyParser } from './share-map-legacy.service';\nimport {\n LayerProperties,\n PositionParams,\n ServiceType,\n ShareMapKeysDefinitions\n} from './share-map.interface';\nimport {\n buildDataSourceOptions,\n getParamValue,\n hasLegacyParams,\n hasModernShareParams\n} from './share-map.utils';\n\ntype BaseLayerOptionsParsed = Pick<\n LayerOptions,\n 'visible' | 'zIndex' | 'opacity' | 'parentId'\n> &\n Partial<Pick<LayerOptions, 'id'>>;\n\ntype LayerOptionsParsed = BaseLayerOptionsParsed &\n Pick<LayerOptions, 'sourceOptions'>;\n\ntype LayerGroupOptionsParserd = BaseLayerOptionsParsed &\n Pick<LayerGroupOptions, 'id' | 'title' | 'expanded' | 'type'>;\n\nexport class ShareMapParser {\n legacy: ShareMapLegacyParser;\n\n constructor(\n private keysDefinitions: ShareMapKeysDefinitions,\n private legacyOptions: RouteServiceOptions\n ) {\n this.legacy = new ShareMapLegacyParser(legacyOptions);\n }\n\n parseLayers(params: Params): AnyLayerOptions[] | undefined {\n if (\n !hasModernShareParams(params, this.keysDefinitions) &&\n hasLegacyParams(params, this.legacyOptions)\n ) {\n return this.legacy.parseUrl(params);\n }\n\n const {\n urlsKey,\n layers: layersDef,\n groups: groupsDef\n } = this.keysDefinitions;\n\n const layersArray = this.splitParam(\n getParamValue(params, layersDef.key),\n ';'\n );\n const urlsArray = this.splitParam(getParamValue(params, urlsKey), ',');\n const groupsArray = this.splitParam(\n getParamValue(params, groupsDef.key),\n ';'\n );\n\n const groupsOptions = groupsArray.map((layer) => this.parseGroup(layer));\n const layersOptions = layersArray\n .map((layer) => this.parseLayer(layer, urlsArray))\n .filter(Boolean);\n return [...groupsOptions, ...layersOptions];\n }\n\n private splitParam(value: string | undefined, delimiter: string): string[] {\n return value ? value.split(delimiter) : [];\n }\n\n parsePosition(params: Params): PositionParams | undefined {\n const position = decodeURIComponent(params[this.keysDefinitions.pos.key]);\n if (!position) {\n return this.legacy.parsePosition(params);\n }\n\n const { center, zoom, rotation, projection } =\n this.keysDefinitions.pos.params;\n\n return ObjectUtils.removeUndefined({\n center: center.parse(position) as [number, number],\n zoom: zoom.parse(position) as number,\n rotation: rotation.parse(position) as number,\n projection: projection.parse(position) as string\n } satisfies OptionalRequired<PositionParams>);\n }\n\n private parseLayer(\n layer: string,\n urls: string[]\n ): LayerOptionsParsed | undefined {\n const { zIndex, visibility, type, opacity, parentId } =\n this.extractLayerProperties(layer);\n const base = {\n visible: visibility,\n zIndex,\n opacity,\n parentId\n } satisfies BaseLayerOptionsParsed;\n\n const layerNames = this.extractLayerNames(layer);\n\n if (layerNames) {\n const urlIndex = this.extractUrlIndex(layer);\n if (urlIndex === undefined) return undefined;\n\n const url = urls[urlIndex];\n const version = this.extractVersionFromUrl(url);\n\n const sourceOptions = buildDataSourceOptions(\n type,\n url,\n layerNames,\n version\n );\n\n return ObjectUtils.removeUndefined({\n id: undefined,\n sourceOptions,\n ...base\n } satisfies OptionalRequired<LayerOptionsParsed>);\n }\n\n const id = this.extractLayerId(layer);\n if (!id) return undefined;\n return ObjectUtils.removeUndefined({\n id,\n sourceOptions: undefined,\n ...base\n } satisfies OptionalRequired<LayerOptionsParsed>);\n }\n\n private parseGroup(properties: string): LayerGroupOptionsParserd {\n const { params } = this.keysDefinitions.groups;\n return ObjectUtils.removeUndefined({\n id: params.id.parse(properties) as string,\n title: params.title.parse(properties) as string,\n zIndex: params.zIndex.parse(properties) as number,\n visible: params.visible.parse(properties) as boolean,\n opacity: params.opacity.parse(properties) as number,\n parentId: params.parentId.parse(properties) as string,\n expanded: params.expanded.parse(properties) as boolean,\n type: 'group'\n } satisfies OptionalRequired<LayerGroupOptionsParserd>);\n }\n\n private extractVersionFromUrl(url: string): string | undefined {\n const versionDef = this.keysDefinitions.layers.params.version;\n return versionDef.parse(url) as string;\n }\n\n private extractLayerId(layer: string): string | undefined {\n const { id } = this.keysDefinitions.layers.params;\n const regex = new RegExp(`([a-zA-Z0-9_]+)${id.key}\\\\b`);\n const match = layer.match(regex);\n return match ? match[1] : undefined;\n }\n\n private extractUrlIndex(layer: string): number | undefined {\n const { index } = this.keysDefinitions.layers.params;\n const regex = index ? new RegExp(`([\\\\d.]+)${index}`) : /([\\d.]+)/;\n const match = layer.match(regex);\n return match ? parseInt(match[1], 10) : undefined;\n }\n\n private extractLayerNames(layer: string): string[] | undefined {\n const { names } = this.keysDefinitions.layers.params;\n const pattern = new RegExp(`\\\\[.*?\\\\]${names.key}`, 'g');\n\n const matches = layer.match(pattern);\n if (!matches) return undefined;\n\n return matches\n .map((match) =>\n decodeURIComponent(match.slice(1, -`]${names.key}`.length))\n )\n .join('\\n')\n .split(',');\n }\n\n private extractLayerProperties(properties: string): LayerProperties {\n const { params } = this.keysDefinitions.layers;\n return {\n zIndex: params.zIndex.parse(properties) as number,\n visibility: params.visible.parse(properties) as boolean,\n type: params.type.parse(properties) as ServiceType,\n opacity: params.opacity.parse(properties) as number,\n parentId: params.parentId.parse(properties) as string\n } satisfies OptionalRequired<LayerProperties>;\n }\n}\n","import { DOCUMENT } from '@angular/common';\nimport { Injectable, inject } from '@angular/core';\nimport { Params } from '@angular/router';\n\nimport { RouteService, RouteServiceOptions } from '@igo2/core/route';\nimport { AnyLayerOptions, IgoMap } from '@igo2/geo';\n\nimport { DetailedContext } from '../../context-manager/shared/context.interface';\nimport { shareMapKeyDefs } from './share-map-definitions';\nimport { ShareMapEncoder } from './share-map-encoder';\nimport { ShareMapParser } from './share-map-parser';\nimport {\n PositionParams,\n SHARE_MAP_KEYS_DEFAULT_OPTIONS,\n ShareMapKeysDefinitions,\n ShareMapRouteKeysOptions\n} from './share-map.interface';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class ShareMapService {\n routeService = inject(RouteService);\n document = inject<Document>(DOCUMENT);\n\n get language(): string {\n return this._language;\n }\n set language(value: string) {\n this._language = value;\n if (this.encoder) {\n this.encoder.language = value;\n }\n }\n private _language = '';\n\n options: ShareMapRouteKeysOptions;\n optionsLegacy: RouteServiceOptions;\n keysDefinitions: ShareMapKeysDefinitions;\n private encoder: ShareMapEncoder;\n private parser: ShareMapParser;\n\n constructor() {\n this.options = SHARE_MAP_KEYS_DEFAULT_OPTIONS;\n this.optionsLegacy = this.routeService.legacyOptions;\n this.keysDefinitions = shareMapKeyDefs({\n ...SHARE_MAP_KEYS_DEFAULT_OPTIONS,\n languageKey: this.routeService.options.languageKey\n });\n\n this.encoder = new ShareMapEncoder(this.keysDefinitions, this.document);\n\n this.parser = new Shar