maplibre-gl-nasa-earthdata 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{NasaEarthdataControl-BiDau2KA.js → NasaEarthdataControl-CMD7orkJ.js} +6 -4
- package/dist/NasaEarthdataControl-CMD7orkJ.js.map +1 -0
- package/dist/{NasaEarthdataControl-6FgeYQNn.cjs → NasaEarthdataControl-DahZNxUI.cjs} +6 -4
- package/dist/NasaEarthdataControl-DahZNxUI.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/react.cjs +1 -1
- package/dist/react.mjs +1 -1
- package/package.json +1 -1
- package/dist/NasaEarthdataControl-6FgeYQNn.cjs.map +0 -1
- package/dist/NasaEarthdataControl-BiDau2KA.js.map +0 -1
|
@@ -920,9 +920,11 @@ var NasaEarthdataControl = class {
|
|
|
920
920
|
toggleBtn.innerHTML = `
|
|
921
921
|
<span class="plugin-control-icon">
|
|
922
922
|
<svg viewBox="0 0 24 24" width="22" height="22" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
923
|
-
<
|
|
924
|
-
<path d="
|
|
925
|
-
<path d="
|
|
923
|
+
<path d="M13 7 9 3 5 7l4 4"/>
|
|
924
|
+
<path d="m17 11 4 4-4 4-4-4"/>
|
|
925
|
+
<path d="m8 12 4 4 6-6-4-4Z"/>
|
|
926
|
+
<path d="m16 8 3-3"/>
|
|
927
|
+
<path d="M9 21a6 6 0 0 0-6-6"/>
|
|
926
928
|
</svg>
|
|
927
929
|
</span>
|
|
928
930
|
`;
|
|
@@ -1483,4 +1485,4 @@ var NasaEarthdataControl = class {
|
|
|
1483
1485
|
//#endregion
|
|
1484
1486
|
export { formatNumericValue as a, buildTileUrl as c, searchLayers as d, parseCapabilities as f, debounce as i, DEFAULT_CAPABILITIES_URL as l, clamp as n, generateId as o, classNames as r, throttle as s, NasaEarthdataControl as t, GibsClient as u };
|
|
1485
1487
|
|
|
1486
|
-
//# sourceMappingURL=NasaEarthdataControl-
|
|
1488
|
+
//# sourceMappingURL=NasaEarthdataControl-CMD7orkJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NasaEarthdataControl-CMD7orkJ.js","names":[],"sources":["../src/lib/gibs/parseCapabilities.ts","../src/lib/gibs/search.ts","../src/lib/gibs/GibsClient.ts","../src/lib/gibs/tileUrl.ts","../src/lib/utils/helpers.ts","../src/lib/core/NasaEarthdataControl.ts"],"sourcesContent":["import type {\n GibsCapabilities,\n GibsLayer,\n GibsLayerFormat,\n GibsTimeDimension,\n ParseOptions,\n} from \"./types\";\n\nconst FORMAT_MAP: Record<string, GibsLayerFormat> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpeg\",\n \"application/vnd.mapbox-vector-tile\": \"mvt\",\n};\n\nconst MIME_MAP: Record<GibsLayerFormat, string> = {\n png: \"image/png\",\n jpeg: \"image/jpeg\",\n mvt: \"application/vnd.mapbox-vector-tile\",\n};\n\n/**\n * Returns the direct child elements of a node matching a local tag name,\n * ignoring XML namespaces.\n */\nfunction childrenByLocalName(parent: Element, localName: string): Element[] {\n const result: Element[] = [];\n for (let i = 0; i < parent.children.length; i++) {\n const child = parent.children[i];\n if (child.localName === localName) {\n result.push(child);\n }\n }\n return result;\n}\n\n/**\n * Returns the text content of the first direct child element matching a local tag name.\n */\nfunction childText(parent: Element, localName: string): string | undefined {\n const child = childrenByLocalName(parent, localName)[0];\n return child?.textContent?.trim() || undefined;\n}\n\n/**\n * Parses the WGS84 bounding box of a layer element.\n */\nfunction parseBbox(\n layerEl: Element,\n): [number, number, number, number] | undefined {\n const bboxEl = childrenByLocalName(layerEl, \"WGS84BoundingBox\")[0];\n if (!bboxEl) return undefined;\n\n const lower = childText(bboxEl, \"LowerCorner\")?.split(/\\s+/).map(Number);\n const upper = childText(bboxEl, \"UpperCorner\")?.split(/\\s+/).map(Number);\n if (!lower || !upper || lower.length < 2 || upper.length < 2)\n return undefined;\n if ([...lower, ...upper].some((n) => Number.isNaN(n))) return undefined;\n\n return [lower[0], lower[1], upper[0], upper[1]];\n}\n\n/**\n * Parses the Time dimension of a layer element, if present.\n */\nfunction parseTimeDimension(layerEl: Element): GibsTimeDimension | undefined {\n const dimensions = childrenByLocalName(layerEl, \"Dimension\");\n for (const dim of dimensions) {\n if (childText(dim, \"Identifier\") !== \"Time\") continue;\n\n const defaultValue = childText(dim, \"Default\");\n if (!defaultValue) return undefined;\n\n const values = childrenByLocalName(dim, \"Value\")\n .map((v) => v.textContent?.trim())\n .filter((v): v is string => Boolean(v));\n\n return { default: defaultValue, values };\n }\n return undefined;\n}\n\n/**\n * Parses the first legend URL advertised in a layer's default style.\n */\nfunction parseLegendUrl(layerEl: Element): string | undefined {\n const styles = childrenByLocalName(layerEl, \"Style\");\n for (const style of styles) {\n const legend = childrenByLocalName(style, \"LegendURL\")[0];\n const href =\n legend?.getAttribute(\"xlink:href\") ??\n legend?.getAttributeNS(null, \"href\");\n if (href) return href;\n }\n return undefined;\n}\n\n/**\n * Parses a single WMTS Layer element into a GibsLayer.\n * Returns undefined for layers that cannot be represented (missing\n * identifier, no tile resource template, or unsupported format).\n */\nfunction parseLayer(\n layerEl: Element,\n includeVector: boolean,\n): GibsLayer | undefined {\n const id = childText(layerEl, \"Identifier\");\n if (!id) return undefined;\n\n const title = childText(layerEl, \"Title\") ?? id;\n\n // Collect supported formats; prefer png > jpeg > mvt.\n const formats = childrenByLocalName(layerEl, \"Format\")\n .map((f) => FORMAT_MAP[f.textContent?.trim() ?? \"\"])\n .filter((f): f is GibsLayerFormat => Boolean(f));\n const format = ([\"png\", \"jpeg\", \"mvt\"] as const).find((f) =>\n formats.includes(f),\n );\n if (!format) return undefined;\n if (format === \"mvt\" && !includeVector) return undefined;\n\n // Tile matrix set link, e.g. \"GoogleMapsCompatible_Level9\" -> maxZoom 9.\n const tmsLink = childrenByLocalName(layerEl, \"TileMatrixSetLink\")[0];\n const tileMatrixSet = tmsLink\n ? childText(tmsLink, \"TileMatrixSet\")\n : undefined;\n if (!tileMatrixSet) return undefined;\n const levelMatch = /Level(\\d+)$/.exec(tileMatrixSet);\n const maxZoom = levelMatch ? parseInt(levelMatch[1], 10) : 9;\n\n // Tile resource template. Only consider templates whose advertised format\n // matches the chosen layer format, then prefer the template containing\n // {Time} when the layer is time-enabled so dates can be substituted.\n const resourceUrls = childrenByLocalName(layerEl, \"ResourceURL\").filter(\n (r) => r.getAttribute(\"resourceType\") === \"tile\",\n );\n if (resourceUrls.length === 0) return undefined;\n\n const selectedMime = MIME_MAP[format];\n const formatMatched = resourceUrls.filter((r) => {\n const mime = r.getAttribute(\"format\")?.trim().toLowerCase();\n return !mime || mime === selectedMime;\n });\n\n const templates = (formatMatched.length > 0 ? formatMatched : resourceUrls)\n .map((r) => r.getAttribute(\"template\"))\n .filter((t): t is string => Boolean(t));\n if (templates.length === 0) return undefined;\n\n const time = parseTimeDimension(layerEl);\n const resourceTemplate =\n (time ? templates.find((t) => t.includes(\"{Time}\")) : undefined) ??\n templates.find((t) => !t.includes(\"{Time}\")) ??\n templates[0];\n\n const extMatch = /\\.([a-z0-9]+)$/i.exec(resourceTemplate);\n const fileExtension = extMatch\n ? extMatch[1]\n : format === \"jpeg\"\n ? \"jpg\"\n : format;\n\n return {\n id,\n title,\n // Platform/instrument prefix of the identifier, used for grouping\n category: id.split(\"_\")[0],\n format,\n fileExtension,\n tileMatrixSet,\n maxZoom,\n bbox: parseBbox(layerEl),\n resourceTemplate,\n time,\n legendUrl: parseLegendUrl(layerEl),\n };\n}\n\n/**\n * Parses a WMTS GetCapabilities XML document into a GibsCapabilities object.\n *\n * Layers without a usable tile resource template are skipped. Vector-tile\n * (MVT) layers are skipped unless `options.includeVector` is true.\n *\n * @param xml - The raw WMTSCapabilities.xml document text\n * @param options - Parse options\n * @returns Parsed capabilities with layers sorted by title\n */\nexport function parseCapabilities(\n xml: string,\n options?: ParseOptions,\n): GibsCapabilities {\n const includeVector = options?.includeVector ?? false;\n\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const parserError = doc.getElementsByTagName(\"parsererror\")[0];\n if (parserError) {\n throw new Error(\n `Failed to parse capabilities XML: ${parserError.textContent ?? \"unknown error\"}`,\n );\n }\n\n const root = doc.documentElement;\n const contents = childrenByLocalName(root, \"Contents\")[0];\n if (!contents) {\n throw new Error(\"Invalid capabilities document: missing Contents element\");\n }\n\n const layers: GibsLayer[] = [];\n for (const layerEl of childrenByLocalName(contents, \"Layer\")) {\n const layer = parseLayer(layerEl, includeVector);\n if (layer) layers.push(layer);\n }\n\n layers.sort((a, b) => a.title.localeCompare(b.title));\n\n return { layers, fetchedAt: Date.now() };\n}\n","import type { GibsLayer } from \"./types\";\n\n/**\n * Filters GIBS layers by a free-text query.\n *\n * Performs a case-insensitive substring match against the layer title and\n * identifier. An empty or whitespace-only query returns all layers.\n *\n * @param layers - The layers to search\n * @param query - The search query\n * @returns Layers whose title or identifier contains the query\n */\nexport function searchLayers(layers: GibsLayer[], query: string): GibsLayer[] {\n const q = query.trim().toLowerCase();\n if (!q) return layers;\n\n return layers.filter(\n (layer) =>\n layer.title.toLowerCase().includes(q) ||\n layer.id.toLowerCase().includes(q),\n );\n}\n","import { parseCapabilities } from \"./parseCapabilities\";\nimport { searchLayers } from \"./search\";\nimport type { GibsCapabilities, GibsLayer } from \"./types\";\n\n/**\n * Default URL of the NASA GIBS WMTS capabilities document (EPSG:3857, \"best\" imagery).\n */\nexport const DEFAULT_CAPABILITIES_URL =\n \"https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/1.0.0/WMTSCapabilities.xml\";\n\n/**\n * Options for creating a GibsClient.\n */\nexport interface GibsClientOptions {\n /**\n * URL of the WMTS capabilities document.\n * @default DEFAULT_CAPABILITIES_URL\n */\n url?: string;\n\n /**\n * Whether to include vector-tile (MVT) layers.\n * @default false\n */\n includeVector?: boolean;\n}\n\n/**\n * Fetches, parses, and caches the NASA GIBS WMTS capabilities document.\n *\n * Concurrent calls to {@link GibsClient.getCapabilities} are deduplicated:\n * the document is fetched and parsed at most once unless `force` is passed.\n *\n * @example\n * ```typescript\n * const client = new GibsClient();\n * const { layers } = await client.getCapabilities();\n * const matches = client.search('temperature');\n * ```\n */\nexport class GibsClient {\n private _url: string;\n private _includeVector: boolean;\n private _capabilities?: GibsCapabilities;\n private _pending?: Promise<GibsCapabilities>;\n\n /**\n * Creates a new GibsClient.\n *\n * @param options - Client options\n */\n constructor(options?: GibsClientOptions) {\n this._url = options?.url ?? DEFAULT_CAPABILITIES_URL;\n this._includeVector = options?.includeVector ?? false;\n }\n\n /**\n * Fetches and parses the capabilities document, caching the result.\n *\n * @param force - If true, refetch even if a cached result exists\n * @returns The parsed capabilities\n */\n async getCapabilities(force = false): Promise<GibsCapabilities> {\n if (this._capabilities && !force) return this._capabilities;\n if (this._pending && !force) return this._pending;\n\n this._pending = (async () => {\n const response = await fetch(this._url);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch capabilities: ${response.status} ${response.statusText}`,\n );\n }\n const xml = await response.text();\n const capabilities = parseCapabilities(xml, {\n includeVector: this._includeVector,\n });\n this._capabilities = capabilities;\n return capabilities;\n })();\n\n try {\n return await this._pending;\n } finally {\n this._pending = undefined;\n }\n }\n\n /**\n * Returns the cached capabilities, if already loaded.\n */\n getCachedCapabilities(): GibsCapabilities | undefined {\n return this._capabilities;\n }\n\n /**\n * Searches the cached layers by title or identifier.\n * Returns an empty array if capabilities have not been loaded yet.\n *\n * @param query - The search query\n */\n search(query: string): GibsLayer[] {\n return searchLayers(this._capabilities?.layers ?? [], query);\n }\n\n /**\n * Looks up a cached layer by identifier.\n *\n * @param id - The layer identifier\n */\n getLayer(id: string): GibsLayer | undefined {\n return this._capabilities?.layers.find((layer) => layer.id === id);\n }\n}\n","import type { GibsLayer } from \"./types\";\n\n/**\n * Builds a MapLibre-compatible XYZ tile URL template from a GIBS layer.\n *\n * Substitutes {Time} with the given date (or the layer's default date),\n * {TileMatrixSet} with the layer's tile matrix set, and maps the WMTS\n * placeholders {TileMatrix}/{TileRow}/{TileCol} to {z}/{y}/{x}.\n *\n * @param layer - The GIBS layer to build a URL for\n * @param time - Optional ISO 8601 date overriding the layer's default date\n * @returns A tile URL template usable in a MapLibre raster source\n */\nexport function buildTileUrl(layer: GibsLayer, time?: string): string {\n return layer.resourceTemplate\n .replace(\"{Time}\", time ?? layer.time?.default ?? \"default\")\n .replace(\"{TileMatrixSet}\", layer.tileMatrixSet)\n .replace(\"{TileMatrix}\", \"{z}\")\n .replace(\"{TileRow}\", \"{y}\")\n .replace(\"{TileCol}\", \"{x}\");\n}\n","/**\n * Clamps a value between a minimum and maximum.\n *\n * @param value - The value to clamp\n * @param min - The minimum allowed value\n * @param max - The maximum allowed value\n * @returns The clamped value\n *\n * @example\n * ```typescript\n * clamp(5, 0, 10); // returns 5\n * clamp(-5, 0, 10); // returns 0\n * clamp(15, 0, 10); // returns 10\n * ```\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Formats a numeric value with appropriate decimal places based on step size.\n *\n * @param value - The value to format\n * @param step - The step size to determine decimal places\n * @returns The formatted value as a string\n *\n * @example\n * ```typescript\n * formatNumericValue(5, 1); // returns \"5\"\n * formatNumericValue(0.5, 0.1); // returns \"0.5\"\n * formatNumericValue(0.55, 0.01); // returns \"0.55\"\n * ```\n */\nexport function formatNumericValue(value: number, step: number): string {\n if (step === 0) return value.toString();\n const decimals = Math.max(0, -Math.floor(Math.log10(step)));\n return value.toFixed(decimals);\n}\n\n/**\n * Generates a unique ID string.\n *\n * @param prefix - Optional prefix for the ID\n * @returns A unique ID string\n *\n * @example\n * ```typescript\n * generateId('control'); // returns \"control-abc123\"\n * generateId(); // returns \"abc123\"\n * ```\n */\nexport function generateId(prefix?: string): string {\n const id = Math.random().toString(36).substring(2, 9);\n return prefix ? `${prefix}-${id}` : id;\n}\n\n/**\n * Debounces a function call.\n *\n * @param fn - The function to debounce\n * @param delay - The delay in milliseconds\n * @returns A debounced version of the function\n *\n * @example\n * ```typescript\n * const debouncedUpdate = debounce(() => updateMap(), 100);\n * window.addEventListener('resize', debouncedUpdate);\n * ```\n */\nexport function debounce<T extends (...args: unknown[]) => void>(\n fn: T,\n delay: number,\n): (...args: Parameters<T>) => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n return (...args: Parameters<T>) => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n timeoutId = setTimeout(() => {\n fn(...args);\n timeoutId = null;\n }, delay);\n };\n}\n\n/**\n * Throttles a function call.\n *\n * @param fn - The function to throttle\n * @param limit - The minimum time between calls in milliseconds\n * @returns A throttled version of the function\n *\n * @example\n * ```typescript\n * const throttledScroll = throttle(() => handleScroll(), 100);\n * window.addEventListener('scroll', throttledScroll);\n * ```\n */\nexport function throttle<T extends (...args: unknown[]) => void>(\n fn: T,\n limit: number,\n): (...args: Parameters<T>) => void {\n let inThrottle = false;\n\n return (...args: Parameters<T>) => {\n if (!inThrottle) {\n fn(...args);\n inThrottle = true;\n setTimeout(() => {\n inThrottle = false;\n }, limit);\n }\n };\n}\n\n/**\n * Creates a CSS class string from an object of class names.\n *\n * @param classes - Object with class names as keys and boolean values\n * @returns A space-separated string of class names\n *\n * @example\n * ```typescript\n * classNames({ active: true, disabled: false, visible: true });\n * // returns \"active visible\"\n * ```\n */\nexport function classNames(classes: Record<string, boolean>): string {\n return Object.entries(classes)\n .filter(([, value]) => value)\n .map(([key]) => key)\n .join(\" \");\n}\n","import type {\n IControl,\n Map as MapLibreMap,\n RasterTileSource,\n} from \"maplibre-gl\";\nimport { GibsClient, DEFAULT_CAPABILITIES_URL } from \"../gibs/GibsClient\";\nimport { buildTileUrl } from \"../gibs/tileUrl\";\nimport { searchLayers } from \"../gibs/search\";\nimport type { GibsCapabilities, GibsLayer } from \"../gibs/types\";\nimport { clamp, debounce } from \"../utils\";\nimport type {\n NasaEarthdataControlOptions,\n NasaEarthdataState,\n NasaEarthdataEvent,\n NasaEarthdataEventHandler,\n NasaEarthdataEventPayload,\n AddedLayerState,\n} from \"./types\";\n\n/**\n * Default options for the NasaEarthdataControl\n */\nconst DEFAULT_OPTIONS: Required<NasaEarthdataControlOptions> = {\n collapsed: true,\n position: \"top-right\",\n title: \"NASA Earthdata\",\n panelWidth: 320,\n className: \"\",\n capabilitiesUrl: DEFAULT_CAPABILITIES_URL,\n includeVector: false,\n showOpacity: true,\n attribution:\n '<a href=\"https://earthdata.nasa.gov/gibs\" target=\"_blank\">NASA EOSDIS GIBS</a>',\n theme: \"auto\",\n};\n\n/**\n * Prefix used for source and layer ids added to the map\n */\nconst LAYER_ID_PREFIX = \"nasa-gibs-\";\n\n/**\n * Event handlers map type\n */\ntype EventHandlersMap = globalThis.Map<\n NasaEarthdataEvent,\n Set<NasaEarthdataEventHandler>\n>;\n\n/**\n * A MapLibre GL control for searching and adding NASA GIBS (Global Imagery\n * Browse Services) WMTS layers to the map.\n *\n * The control renders a collapsible button. When expanded, it fetches the\n * GIBS capabilities document, lets the user search the layer catalog, and\n * add/remove raster layers with per-layer date and opacity controls.\n *\n * @example\n * ```typescript\n * const control = new NasaEarthdataControl({\n * title: 'NASA Earthdata',\n * collapsed: false,\n * });\n * map.addControl(control, 'top-right');\n * control.on('layeradd', (e) => console.log('Added', e.layer?.id));\n * ```\n */\nexport class NasaEarthdataControl implements IControl {\n private _map?: MapLibreMap;\n private _mapContainer?: HTMLElement;\n private _container?: HTMLElement;\n private _panel?: HTMLElement;\n private _options: Required<NasaEarthdataControlOptions>;\n private _state: NasaEarthdataState;\n private _eventHandlers: EventHandlersMap = new globalThis.Map();\n\n private _client: GibsClient;\n private _capabilities?: GibsCapabilities;\n private _loading = false;\n private _addedLayers: globalThis.Map<string, AddedLayerState> =\n new globalThis.Map();\n\n // Panel content elements\n private _searchInput?: HTMLInputElement;\n private _metaEl?: HTMLElement;\n private _resultsEl?: HTMLElement;\n private _addedEl?: HTMLElement;\n private _insertSelect?: HTMLSelectElement;\n\n // UI state: expanded category groups, open legends, insertion position\n private _expandedCategories = new Set<string>();\n private _openLegends = new Set<string>();\n private _insertBefore = \"\";\n\n // Panel positioning handlers\n private _resizeHandler: (() => void) | null = null;\n private _mapResizeHandler: (() => void) | null = null;\n private _clickOutsideHandler: ((e: MouseEvent) => void) | null = null;\n\n /**\n * Creates a new NasaEarthdataControl instance.\n *\n * @param options - Configuration options for the control\n */\n constructor(options?: Partial<NasaEarthdataControlOptions>) {\n this._options = { ...DEFAULT_OPTIONS, ...options };\n this._state = {\n collapsed: this._options.collapsed,\n panelWidth: this._options.panelWidth,\n query: \"\",\n addedLayers: [],\n };\n this._client = new GibsClient({\n url: this._options.capabilitiesUrl,\n includeVector: this._options.includeVector,\n });\n }\n\n /**\n * Called when the control is added to the map.\n * Implements the IControl interface.\n *\n * @param map - The MapLibre GL map instance\n * @returns The control's container element\n */\n onAdd(map: MapLibreMap): HTMLElement {\n this._map = map;\n this._mapContainer = map.getContainer();\n this._container = this._createContainer();\n this._panel = this._createPanel();\n\n // Append panel to map container for independent positioning (avoids overlap with other controls)\n this._mapContainer.appendChild(this._panel);\n\n // Setup event listeners for panel positioning and click-outside\n this._setupEventListeners();\n\n // Set initial panel state\n if (!this._state.collapsed) {\n this._panel.classList.add(\"expanded\");\n this._loadCapabilities();\n // Update position after control is added to DOM\n requestAnimationFrame(() => {\n this._updatePanelPosition();\n });\n }\n\n return this._container;\n }\n\n /**\n * Called when the control is removed from the map.\n * Implements the IControl interface.\n */\n onRemove(): void {\n // Remove all added GIBS layers and sources from the map\n for (const layerId of Array.from(this._addedLayers.keys())) {\n this._removeMapLayer(layerId);\n }\n this._addedLayers.clear();\n this._state.addedLayers = [];\n\n // Remove event listeners\n if (this._resizeHandler) {\n window.removeEventListener(\"resize\", this._resizeHandler);\n this._resizeHandler = null;\n }\n if (this._mapResizeHandler && this._map) {\n this._map.off(\"resize\", this._mapResizeHandler);\n this._mapResizeHandler = null;\n }\n if (this._clickOutsideHandler) {\n document.removeEventListener(\"click\", this._clickOutsideHandler);\n this._clickOutsideHandler = null;\n }\n\n // Remove panel from map container\n this._panel?.parentNode?.removeChild(this._panel);\n\n // Remove button container from control stack\n this._container?.parentNode?.removeChild(this._container);\n\n this._map = undefined;\n this._mapContainer = undefined;\n this._container = undefined;\n this._panel = undefined;\n this._searchInput = undefined;\n this._metaEl = undefined;\n this._resultsEl = undefined;\n this._addedEl = undefined;\n this._insertSelect = undefined;\n this._openLegends.clear();\n this._eventHandlers.clear();\n }\n\n /**\n * Gets the current state of the control.\n *\n * @returns The current control state\n */\n getState(): NasaEarthdataState {\n return {\n ...this._state,\n addedLayers: this._state.addedLayers.map((l) => ({ ...l })),\n };\n }\n\n /**\n * Updates the control state. Changes to `addedLayers` are reconciled\n * against the map: missing layers are added, extra layers are removed,\n * and date/opacity changes are applied.\n *\n * @param newState - Partial state to merge with current state\n */\n setState(newState: Partial<NasaEarthdataState>): void {\n if (\n newState.collapsed !== undefined &&\n newState.collapsed !== this._state.collapsed\n ) {\n if (newState.collapsed) {\n this.collapse();\n } else {\n this.expand();\n }\n }\n\n if (newState.panelWidth !== undefined) {\n this._state.panelWidth = newState.panelWidth;\n if (this._panel) {\n this._panel.style.width = `${newState.panelWidth}px`;\n }\n }\n\n if (newState.query !== undefined && newState.query !== this._state.query) {\n this._state.query = newState.query;\n if (this._searchInput) {\n this._searchInput.value = newState.query;\n }\n this._renderResults();\n }\n\n let deferStateEmit = false;\n if (newState.addedLayers) {\n // Reconciliation needs the layer catalog; defer it until the\n // capabilities are loaded so state restoration works before the\n // panel is first expanded. An empty list needs no catalog data.\n const desired = newState.addedLayers.map((l) => ({ ...l }));\n if (desired.length === 0 || this._capabilities) {\n this._reconcileAddedLayers(desired);\n } else {\n // Emit statechange only after reconciliation so listeners never\n // see a stale addedLayers snapshot.\n deferStateEmit = true;\n void this.getCapabilities()\n .then(() => {\n this._reconcileAddedLayers(desired);\n this._renderResults();\n this._renderAddedSection();\n this._emit(\"statechange\");\n })\n .catch((error: unknown) => {\n const err =\n error instanceof Error ? error : new Error(String(error));\n this._emitError(err);\n });\n }\n }\n\n if (!deferStateEmit) {\n this._emit(\"statechange\");\n }\n }\n\n /**\n * Toggles the collapsed state of the control panel.\n */\n toggle(): void {\n this._state.collapsed = !this._state.collapsed;\n\n if (this._panel) {\n if (this._state.collapsed) {\n this._panel.classList.remove(\"expanded\");\n this._emit(\"collapse\");\n } else {\n this._panel.classList.add(\"expanded\");\n this._updatePanelPosition();\n this._loadCapabilities();\n this._refreshInsertOptions();\n this._emit(\"expand\");\n }\n }\n\n this._emit(\"statechange\");\n }\n\n /**\n * Expands the control panel.\n */\n expand(): void {\n if (this._state.collapsed) {\n this.toggle();\n }\n }\n\n /**\n * Collapses the control panel.\n */\n collapse(): void {\n if (!this._state.collapsed) {\n this.toggle();\n }\n }\n\n /**\n * Registers an event handler.\n *\n * @param event - The event type to listen for\n * @param handler - The callback function\n */\n on(event: NasaEarthdataEvent, handler: NasaEarthdataEventHandler): void {\n if (!this._eventHandlers.has(event)) {\n this._eventHandlers.set(event, new Set());\n }\n this._eventHandlers.get(event)!.add(handler);\n }\n\n /**\n * Removes an event handler.\n *\n * @param event - The event type\n * @param handler - The callback function to remove\n */\n off(event: NasaEarthdataEvent, handler: NasaEarthdataEventHandler): void {\n this._eventHandlers.get(event)?.delete(handler);\n }\n\n /**\n * Gets the map instance.\n *\n * @returns The MapLibre GL map instance or undefined if not added to a map\n */\n getMap(): MapLibreMap | undefined {\n return this._map;\n }\n\n /**\n * Gets the control container element.\n *\n * @returns The container element or undefined if not added to a map\n */\n getContainer(): HTMLElement | undefined {\n return this._container;\n }\n\n /**\n * Fetches and caches the GIBS capabilities document.\n *\n * @param force - If true, refetch even if cached\n * @returns The parsed capabilities\n */\n async getCapabilities(force = false): Promise<GibsCapabilities> {\n const capabilities = await this._client.getCapabilities(force);\n this._capabilities = capabilities;\n return capabilities;\n }\n\n /**\n * Searches the loaded GIBS layers by title or identifier.\n * Returns an empty array if the capabilities have not been loaded yet.\n *\n * @param query - The search query\n * @returns Matching layers\n */\n search(query: string): GibsLayer[] {\n return searchLayers(this._capabilities?.layers ?? [], query);\n }\n\n /**\n * Gets the state of all layers currently added to the map.\n *\n * @returns Added layer states\n */\n getAddedLayers(): AddedLayerState[] {\n return Array.from(this._addedLayers.values()).map((l) => ({ ...l }));\n }\n\n /**\n * Adds a GIBS layer to the map as a raster source and layer.\n * Requires the capabilities to be loaded (call getCapabilities() first\n * when using the control programmatically).\n *\n * @param layerId - The GIBS layer identifier\n * @param options - Optional date, opacity, visibility, and insertion position\n */\n addLayer(\n layerId: string,\n options?: {\n date?: string;\n opacity?: number;\n visible?: boolean;\n before?: string;\n key?: string;\n },\n ): void {\n if (!this._map) return;\n\n const layer = this._client.getLayer(layerId);\n if (!layer) {\n this._emitError(\n new Error(`Unknown GIBS layer: ${layerId} (capabilities not loaded?)`),\n );\n return;\n }\n\n const date = options?.date ?? layer.time?.default;\n const opacity = clamp(options?.opacity ?? 1, 0, 1);\n const visible = options?.visible ?? true;\n const before = options?.before ?? this._insertBefore;\n\n // Restoring a known instance key is a no-op if it is already on the map\n if (options?.key && this._addedLayers.has(options.key)) return;\n const key = options?.key ?? this._instanceKey(layer, date);\n const mapId = LAYER_ID_PREFIX + key;\n\n // Host applications that persist and restore layers may have already\n // recreated the native source/layer from saved state. Reuse them when\n // present instead of re-adding (which would throw on a duplicate id),\n // and apply the requested opacity/visibility so control state and map\n // agree.\n if (!this._map.getSource(mapId)) {\n this._map.addSource(mapId, {\n type: \"raster\",\n tiles: [buildTileUrl(layer, date)],\n tileSize: 256,\n maxzoom: layer.maxZoom,\n attribution: this._options.attribution,\n });\n }\n if (!this._map.getLayer(mapId)) {\n this._map.addLayer(\n {\n id: mapId,\n type: \"raster\",\n source: mapId,\n paint: { \"raster-opacity\": opacity },\n layout: { visibility: visible ? \"visible\" : \"none\" },\n },\n before && this._map.getLayer(before) ? before : undefined,\n );\n } else {\n this._map.setPaintProperty(mapId, \"raster-opacity\", opacity);\n this._map.setLayoutProperty(\n mapId,\n \"visibility\",\n visible ? \"visible\" : \"none\",\n );\n }\n\n const added: AddedLayerState = { key, id: layerId, date, opacity, visible };\n this._addedLayers.set(key, added);\n this._syncAddedLayersState();\n this._renderResults();\n this._renderAddedSection();\n this._refreshInsertOptions();\n this._emit(\"layeradd\", { layer });\n this._emit(\"statechange\");\n }\n\n /**\n * Builds a unique instance key for a layer/date pair. Non-time layers can\n * only be added once; time-enabled layers get one instance per addition,\n * so a numeric suffix disambiguates repeated adds at the same date.\n */\n private _instanceKey(layer: GibsLayer, date?: string): string {\n if (!layer.time) return layer.id;\n\n const base = `${layer.id}@${date ?? layer.time.default}`;\n if (!this._addedLayers.has(base)) return base;\n let n = 2;\n while (this._addedLayers.has(`${base}#${n}`)) n++;\n return `${base}#${n}`;\n }\n\n /**\n * Resolves an instance key or a GIBS layer identifier to instances.\n * An exact key match wins; otherwise all instances of the layer match.\n */\n private _resolveInstances(keyOrId: string): AddedLayerState[] {\n const exact = this._addedLayers.get(keyOrId);\n if (exact) return [exact];\n return Array.from(this._addedLayers.values()).filter(\n (l) => l.id === keyOrId,\n );\n }\n\n /**\n * Removes added GIBS layer instances from the map. Pass an instance key\n * to remove a single instance, or a GIBS layer identifier to remove all\n * instances of that layer.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n */\n removeLayer(keyOrId: string): void {\n const instances = this._resolveInstances(keyOrId);\n if (instances.length === 0) return;\n\n for (const instance of instances) {\n this._removeMapLayer(instance.key);\n this._addedLayers.delete(instance.key);\n this._openLegends.delete(instance.key);\n this._emit(\"layerremove\", { layer: this._client.getLayer(instance.id) });\n }\n this._syncAddedLayersState();\n this._renderResults();\n this._renderAddedSection();\n this._refreshInsertOptions();\n this._emit(\"statechange\");\n }\n\n /**\n * Changes the date of an added time-enabled layer instance.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n * @param date - The new ISO 8601 date\n */\n setLayerDate(keyOrId: string, date: string): void {\n const added = this._resolveInstances(keyOrId)[0];\n const layer = added ? this._client.getLayer(added.id) : undefined;\n if (!this._map || !added || !layer) return;\n\n added.date = date;\n const mapId = LAYER_ID_PREFIX + added.key;\n const source = this._map.getSource(mapId) as RasterTileSource | undefined;\n const tiles = [buildTileUrl(layer, date)];\n\n if (source && typeof source.setTiles === \"function\") {\n source.setTiles(tiles);\n } else {\n // Fallback: re-create the source and layer\n this._removeMapLayer(added.key);\n this._addedLayers.delete(added.key);\n this.addLayer(added.id, {\n date,\n opacity: added.opacity,\n visible: added.visible,\n key: added.key,\n });\n return;\n }\n\n this._syncAddedLayersState();\n this._emit(\"statechange\");\n }\n\n /**\n * Changes the opacity of an added layer instance.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n * @param opacity - The new opacity (0 to 1)\n */\n setLayerOpacity(keyOrId: string, opacity: number): void {\n const added = this._resolveInstances(keyOrId)[0];\n if (!this._map || !added) return;\n\n added.opacity = clamp(opacity, 0, 1);\n this._map.setPaintProperty(\n LAYER_ID_PREFIX + added.key,\n \"raster-opacity\",\n added.opacity,\n );\n this._syncAddedLayersState();\n this._emit(\"statechange\");\n }\n\n /**\n * Toggles the visibility of an added layer instance on the map.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n * @param visible - Whether the layer should be visible\n */\n setLayerVisibility(keyOrId: string, visible: boolean): void {\n const added = this._resolveInstances(keyOrId)[0];\n if (!this._map || !added) return;\n\n added.visible = visible;\n this._map.setLayoutProperty(\n LAYER_ID_PREFIX + added.key,\n \"visibility\",\n visible ? \"visible\" : \"none\",\n );\n this._syncAddedLayersState();\n this._emit(\"statechange\");\n }\n\n /**\n * Removes the map source and layer for an instance key, if present.\n */\n private _removeMapLayer(key: string): void {\n if (!this._map) return;\n const mapId = LAYER_ID_PREFIX + key;\n if (this._map.getLayer(mapId)) {\n this._map.removeLayer(mapId);\n }\n if (this._map.getSource(mapId)) {\n this._map.removeSource(mapId);\n }\n }\n\n /**\n * Mirrors the internal added-layers map into the serializable state.\n */\n private _syncAddedLayersState(): void {\n this._state.addedLayers = this.getAddedLayers();\n }\n\n /**\n * Reconciles a desired added-layers list against the map:\n * adds missing layers, removes extras, and applies date/opacity changes.\n */\n private _reconcileAddedLayers(desired: AddedLayerState[]): void {\n const desiredKeys = new Set(desired.map((l) => l.key));\n\n for (const existingKey of Array.from(this._addedLayers.keys())) {\n if (!desiredKeys.has(existingKey)) {\n this.removeLayer(existingKey);\n }\n }\n\n for (const target of desired) {\n const existing = this._addedLayers.get(target.key);\n if (!existing) {\n this.addLayer(target.id, {\n date: target.date,\n opacity: target.opacity,\n visible: target.visible,\n key: target.key,\n });\n } else {\n if (target.date && target.date !== existing.date) {\n this.setLayerDate(target.key, target.date);\n }\n if (\n target.opacity !== undefined &&\n target.opacity !== existing.opacity\n ) {\n this.setLayerOpacity(target.key, target.opacity);\n }\n if (\n target.visible !== undefined &&\n target.visible !== existing.visible\n ) {\n this.setLayerVisibility(target.key, target.visible);\n }\n }\n }\n }\n\n /**\n * Loads the capabilities document (once) and renders the results list.\n */\n private _loadCapabilities(): void {\n if (this._capabilities || this._loading) return;\n\n this._loading = true;\n this._renderStatus(\"Loading NASA GIBS layer catalog…\");\n\n this.getCapabilities()\n .then(() => {\n this._loading = false;\n this._renderResults();\n this._renderAddedSection();\n this._refreshInsertOptions();\n this._emit(\"capabilitiesload\");\n })\n .catch((error: unknown) => {\n this._loading = false;\n const err = error instanceof Error ? error : new Error(String(error));\n this._renderStatus(\n `Failed to load layer catalog: ${err.message}`,\n true,\n );\n this._emitError(err);\n });\n }\n\n /**\n * Emits an event to all registered handlers.\n *\n * @param event - The event type to emit\n * @param extra - Extra payload fields (layer, error)\n */\n private _emit(\n event: NasaEarthdataEvent,\n extra?: Partial<NasaEarthdataEventPayload>,\n ): void {\n const handlers = this._eventHandlers.get(event);\n if (handlers) {\n const eventData: NasaEarthdataEventPayload = {\n type: event,\n state: this.getState(),\n ...extra,\n };\n handlers.forEach((handler) => handler(eventData));\n }\n }\n\n /**\n * Emits an 'error' event.\n */\n private _emitError(error: Error): void {\n this._emit(\"error\", { error });\n }\n\n /**\n * Returns the theme class for the configured theme, if explicit.\n */\n private _themeClass(): string {\n if (this._options.theme === \"dark\") return \" ne-theme-dark\";\n if (this._options.theme === \"light\") return \" ne-theme-light\";\n return \"\";\n }\n\n /**\n * Creates the main container element for the control.\n * Contains a toggle button (29x29) matching navigation control size.\n *\n * @returns The container element\n */\n private _createContainer(): HTMLElement {\n const container = document.createElement(\"div\");\n container.className = `maplibregl-ctrl maplibregl-ctrl-group plugin-control maplibre-gl-nasa-earthdata${this._themeClass()}${\n this._options.className ? ` ${this._options.className}` : \"\"\n }`;\n\n // Create toggle button (29x29 to match navigation control)\n const toggleBtn = document.createElement(\"button\");\n toggleBtn.className = \"plugin-control-toggle\";\n toggleBtn.type = \"button\";\n toggleBtn.setAttribute(\"aria-label\", this._options.title);\n // Satellite icon. A distinct, recognizable mark for NASA Earthdata's\n // satellite imagery that avoids colliding with the generic globe icon used\n // by core map functionality in host apps.\n toggleBtn.innerHTML = `\n <span class=\"plugin-control-icon\">\n <svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M13 7 9 3 5 7l4 4\"/>\n <path d=\"m17 11 4 4-4 4-4-4\"/>\n <path d=\"m8 12 4 4 6-6-4-4Z\"/>\n <path d=\"m16 8 3-3\"/>\n <path d=\"M9 21a6 6 0 0 0-6-6\"/>\n </svg>\n </span>\n `;\n toggleBtn.addEventListener(\"click\", () => this.toggle());\n\n container.appendChild(toggleBtn);\n\n return container;\n }\n\n /**\n * Creates the panel element with header, search box, and results list.\n * Panel is positioned as a dropdown below the toggle button.\n *\n * @returns The panel element\n */\n private _createPanel(): HTMLElement {\n const panel = document.createElement(\"div\");\n panel.className = `plugin-control-panel maplibre-gl-nasa-earthdata${this._themeClass()}`;\n panel.style.width = `${this._options.panelWidth}px`;\n\n // Create header with title and close button\n const header = document.createElement(\"div\");\n header.className = \"plugin-control-header\";\n\n const title = document.createElement(\"span\");\n title.className = \"plugin-control-title\";\n title.textContent = this._options.title;\n\n const closeBtn = document.createElement(\"button\");\n closeBtn.className = \"plugin-control-close\";\n closeBtn.type = \"button\";\n closeBtn.setAttribute(\"aria-label\", \"Close panel\");\n closeBtn.innerHTML = \"×\";\n closeBtn.addEventListener(\"click\", () => this.collapse());\n\n header.appendChild(title);\n header.appendChild(closeBtn);\n\n // Create content area: search input, insert-before row, category list,\n // and the added-layers management section\n const content = document.createElement(\"div\");\n content.className = \"plugin-control-content nasa-content\";\n\n const search = document.createElement(\"input\");\n search.className = \"plugin-control-input nasa-search\";\n search.type = \"search\";\n search.placeholder = \"Search layers (e.g. temperature)\";\n search.setAttribute(\"aria-label\", \"Search NASA GIBS layers\");\n search.value = this._state.query;\n const onSearch = debounce(() => {\n this._state.query = search.value;\n this._renderResults();\n this._emit(\"statechange\");\n }, 250);\n search.addEventListener(\"input\", onSearch);\n this._searchInput = search;\n\n // \"Insert before\" row: choose where new layers are inserted in the\n // map's layer stack\n const insertRow = document.createElement(\"div\");\n insertRow.className = \"nasa-insert-row\";\n\n const insertLabel = document.createElement(\"span\");\n insertLabel.className = \"nasa-insert-label\";\n insertLabel.textContent = \"Insert before\";\n\n const insertSelect = document.createElement(\"select\");\n insertSelect.className = \"nasa-insert-select\";\n insertSelect.setAttribute(\"aria-label\", \"Insert new layers before\");\n insertSelect.addEventListener(\"change\", () => {\n this._insertBefore = insertSelect.value;\n });\n this._insertSelect = insertSelect;\n\n insertRow.appendChild(insertLabel);\n insertRow.appendChild(insertSelect);\n\n const meta = document.createElement(\"div\");\n meta.className = \"nasa-meta\";\n this._metaEl = meta;\n\n // Scrollable body holding the category groups and the added layers\n const body = document.createElement(\"div\");\n body.className = \"nasa-body\";\n\n const results = document.createElement(\"div\");\n results.className = \"nasa-results\";\n this._resultsEl = results;\n\n const added = document.createElement(\"div\");\n added.className = \"nasa-added-section\";\n this._addedEl = added;\n\n body.appendChild(results);\n body.appendChild(added);\n\n content.appendChild(search);\n content.appendChild(insertRow);\n content.appendChild(meta);\n content.appendChild(body);\n\n panel.appendChild(header);\n panel.appendChild(content);\n\n // Resize handle on the panel's outer edge for adjusting the width by\n // dragging. _updatePanelPosition() moves it to the correct side for the\n // control corner (left edge when right-anchored, right edge when\n // left-anchored).\n const resizeHandle = document.createElement(\"div\");\n resizeHandle.className = \"nasa-resize-handle\";\n resizeHandle.setAttribute(\"aria-hidden\", \"true\");\n resizeHandle.addEventListener(\"pointerdown\", (e) =>\n this._startResize(e, resizeHandle),\n );\n panel.appendChild(resizeHandle);\n\n return panel;\n }\n\n /**\n * Returns the maximum panel width that fits the map container.\n */\n private _maxPanelWidth(): number {\n return Math.max(\n 240,\n (this._mapContainer?.getBoundingClientRect().width ?? window.innerWidth) -\n 20,\n );\n }\n\n /**\n * Clamps the panel width to the map container. Applied whenever the panel\n * is (re)positioned so a saved width survives map shrinking.\n */\n private _applyPanelWidthBounds(): void {\n if (!this._panel) return;\n const width = Math.round(\n clamp(this._state.panelWidth, 240, this._maxPanelWidth()),\n );\n this._state.panelWidth = width;\n this._panel.style.width = `${width}px`;\n }\n\n /**\n * Starts a panel width drag-resize. The drag direction is derived from\n * the control corner so resizing works whether the panel is anchored to\n * the left or the right edge of the map.\n */\n private _startResize(e: PointerEvent, handle: HTMLElement): void {\n if (!this._panel) return;\n e.preventDefault();\n\n const startX = e.clientX;\n const startWidth = this._panel.getBoundingClientRect().width;\n // Right-anchored panels grow leftward, so invert the pointer delta\n const anchoredRight = !this._getControlPosition().endsWith(\"left\");\n const maxWidth = this._maxPanelWidth();\n\n const onMove = (ev: PointerEvent) => {\n const delta = anchoredRight ? startX - ev.clientX : ev.clientX - startX;\n const width = Math.round(clamp(startWidth + delta, 240, maxWidth));\n this._state.panelWidth = width;\n if (this._panel) {\n this._panel.style.width = `${width}px`;\n }\n };\n const onEnd = (ev: PointerEvent) => {\n handle.removeEventListener(\"pointermove\", onMove);\n handle.removeEventListener(\"pointerup\", onEnd);\n handle.removeEventListener(\"pointercancel\", onEnd);\n if (handle.hasPointerCapture(ev.pointerId)) {\n handle.releasePointerCapture(ev.pointerId);\n }\n this._panel?.classList.remove(\"nasa-resizing\");\n this._emit(\"statechange\");\n };\n\n handle.setPointerCapture(e.pointerId);\n handle.addEventListener(\"pointermove\", onMove);\n handle.addEventListener(\"pointerup\", onEnd);\n handle.addEventListener(\"pointercancel\", onEnd);\n this._panel.classList.add(\"nasa-resizing\");\n }\n\n /**\n * Shows a status message (loading or error) in the results area.\n */\n private _renderStatus(message: string, isError = false): void {\n if (!this._resultsEl || !this._metaEl) return;\n this._metaEl.textContent = \"\";\n this._resultsEl.innerHTML = \"\";\n const status = document.createElement(\"div\");\n status.className = `nasa-status${isError ? \" nasa-status-error\" : \"\"}`;\n status.textContent = message;\n this._resultsEl.appendChild(status);\n }\n\n /**\n * Groups layers by category, merging categories with fewer layers than\n * the threshold into \"Other\". Returns entries sorted alphabetically\n * with \"Other\" last.\n */\n private _groupByCategory(layers: GibsLayer[]): [string, GibsLayer[]][] {\n const MIN_CATEGORY_SIZE = 3;\n const all = this._capabilities?.layers ?? [];\n\n // Count category sizes over the FULL catalog so a category does not\n // flip into \"Other\" while filtering\n const totals = new globalThis.Map<string, number>();\n for (const layer of all) {\n totals.set(layer.category, (totals.get(layer.category) ?? 0) + 1);\n }\n\n const groups = new globalThis.Map<string, GibsLayer[]>();\n for (const layer of layers) {\n const total = totals.get(layer.category) ?? 0;\n const key = total >= MIN_CATEGORY_SIZE ? layer.category : \"Other\";\n const group = groups.get(key);\n if (group) {\n group.push(layer);\n } else {\n groups.set(key, [layer]);\n }\n }\n\n return Array.from(groups.entries()).sort(([a], [b]) => {\n if (a === \"Other\") return 1;\n if (b === \"Other\") return -1;\n return a.localeCompare(b);\n });\n }\n\n /**\n * Renders the (filtered) layer catalog grouped by category.\n */\n private _renderResults(): void {\n if (!this._resultsEl || !this._metaEl) return;\n if (!this._capabilities) {\n if (!this._loading) {\n this._renderStatus(\"Expand the panel to load the layer catalog.\");\n }\n return;\n }\n\n const query = this._state.query.trim();\n const matches = searchLayers(this._capabilities.layers, query);\n const groups = this._groupByCategory(matches);\n\n const layerWord = matches.length === 1 ? \"layer\" : \"layers\";\n const categoryWord = groups.length === 1 ? \"category\" : \"categories\";\n this._metaEl.textContent = `${matches.length} ${layerWord} in ${groups.length} ${categoryWord}`;\n\n this._resultsEl.innerHTML = \"\";\n if (matches.length === 0) {\n const status = document.createElement(\"div\");\n status.className = \"nasa-status\";\n status.textContent = \"No layers match your search.\";\n this._resultsEl.appendChild(status);\n return;\n }\n\n for (const [category, layers] of groups) {\n // While searching, auto-expand the matching categories\n const expanded = query ? true : this._expandedCategories.has(category);\n this._resultsEl.appendChild(\n this._createCategoryGroup(category, layers, expanded),\n );\n }\n }\n\n /**\n * Creates a collapsible category group with a count badge.\n */\n private _createCategoryGroup(\n category: string,\n layers: GibsLayer[],\n expanded: boolean,\n ): HTMLElement {\n const group = document.createElement(\"div\");\n group.className = \"nasa-category\";\n\n const header = document.createElement(\"button\");\n header.type = \"button\";\n header.className = `nasa-category-header${expanded ? \" expanded\" : \"\"}`;\n header.setAttribute(\"aria-expanded\", String(expanded));\n\n const chevron = document.createElement(\"span\");\n chevron.className = \"nasa-category-chevron\";\n chevron.innerHTML = `\n <svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9 6l6 6-6 6\"/>\n </svg>\n `;\n\n const name = document.createElement(\"span\");\n name.className = \"nasa-category-name\";\n name.textContent = category;\n\n const count = document.createElement(\"span\");\n count.className = \"nasa-category-count\";\n count.textContent = String(layers.length);\n\n header.appendChild(chevron);\n header.appendChild(name);\n header.appendChild(count);\n header.addEventListener(\"click\", () => {\n if (this._expandedCategories.has(category)) {\n this._expandedCategories.delete(category);\n } else {\n this._expandedCategories.add(category);\n }\n this._renderResults();\n });\n\n group.appendChild(header);\n\n if (expanded) {\n const list = document.createElement(\"div\");\n list.className = \"nasa-category-layers\";\n list.setAttribute(\"role\", \"list\");\n\n for (const layer of layers) {\n list.appendChild(this._createLayerRow(layer));\n }\n\n group.appendChild(list);\n }\n\n return group;\n }\n\n /**\n * Creates a single catalog layer row with title, badges, and an action\n * button. Non-time layers toggle add/remove; time-enabled layers always\n * offer \"Add\" so additional instances with different dates can be added.\n * Per-layer controls live in the added-layers section.\n */\n private _createLayerRow(layer: GibsLayer): HTMLElement {\n const instanceCount = Array.from(this._addedLayers.values()).filter(\n (l) => l.id === layer.id,\n ).length;\n const added = instanceCount > 0;\n\n const row = document.createElement(\"div\");\n row.className = `nasa-layer-row${added ? \" nasa-layer-row-added\" : \"\"}`;\n row.setAttribute(\"role\", \"listitem\");\n\n const main = document.createElement(\"div\");\n main.className = \"nasa-layer-main\";\n\n const info = document.createElement(\"div\");\n info.className = \"nasa-layer-info\";\n\n const titleEl = document.createElement(\"div\");\n titleEl.className = \"nasa-layer-title\";\n titleEl.textContent = layer.title;\n titleEl.title = layer.id;\n\n const badges = document.createElement(\"div\");\n badges.className = \"nasa-layer-badges\";\n const formatBadge = document.createElement(\"span\");\n formatBadge.className = \"nasa-badge\";\n formatBadge.textContent = layer.format;\n badges.appendChild(formatBadge);\n if (layer.time) {\n const timeBadge = document.createElement(\"span\");\n timeBadge.className = \"nasa-badge nasa-badge-time\";\n timeBadge.textContent = \"time\";\n timeBadge.title = `Default date: ${layer.time.default}`;\n badges.appendChild(timeBadge);\n }\n if (instanceCount > 1) {\n const countBadge = document.createElement(\"span\");\n countBadge.className = \"nasa-badge nasa-badge-time\";\n countBadge.textContent = `${instanceCount} added`;\n badges.appendChild(countBadge);\n }\n\n info.appendChild(titleEl);\n info.appendChild(badges);\n\n // Time-enabled layers can be added repeatedly (one instance per date),\n // so their button always reads \"Add\"\n const isRemove = added && !layer.time;\n const actionBtn = document.createElement(\"button\");\n actionBtn.type = \"button\";\n actionBtn.className = `nasa-layer-action${isRemove ? \" nasa-layer-action-remove\" : \"\"}`;\n actionBtn.textContent = isRemove ? \"Remove\" : \"Add\";\n actionBtn.setAttribute(\n \"aria-label\",\n isRemove\n ? `Remove layer ${layer.title}`\n : added\n ? `Add layer ${layer.title} for another date`\n : `Add layer ${layer.title}`,\n );\n actionBtn.addEventListener(\"click\", () => {\n if (!layer.time && this._resolveInstances(layer.id).length > 0) {\n this.removeLayer(layer.id);\n } else {\n this.addLayer(layer.id);\n }\n });\n\n main.appendChild(info);\n main.appendChild(actionBtn);\n row.appendChild(main);\n\n return row;\n }\n\n /**\n * Renders the \"Added layers\" management section: visibility checkbox,\n * legend toggle, remove button, opacity slider, and date picker.\n */\n private _renderAddedSection(): void {\n if (!this._addedEl) return;\n\n this._addedEl.innerHTML = \"\";\n if (this._addedLayers.size === 0) return;\n\n const heading = document.createElement(\"div\");\n heading.className = \"nasa-added-heading\";\n heading.textContent = \"Added layers\";\n this._addedEl.appendChild(heading);\n\n for (const added of this._addedLayers.values()) {\n const layer = this._client.getLayer(added.id);\n if (layer) {\n this._addedEl.appendChild(this._createAddedRow(layer, added));\n }\n }\n }\n\n /**\n * Creates a management card for one added layer.\n */\n private _createAddedRow(\n layer: GibsLayer,\n added: AddedLayerState,\n ): HTMLElement {\n const card = document.createElement(\"div\");\n card.className = \"nasa-added-card\";\n\n // Header row: visibility checkbox, title, legend toggle, remove\n const head = document.createElement(\"div\");\n head.className = \"nasa-added-head\";\n\n const visLabel = document.createElement(\"label\");\n visLabel.className = \"nasa-added-vis\";\n visLabel.title = \"Toggle layer visibility\";\n\n const visCheckbox = document.createElement(\"input\");\n visCheckbox.type = \"checkbox\";\n visCheckbox.className = \"nasa-added-checkbox\";\n visCheckbox.checked = added.visible;\n visCheckbox.setAttribute(\n \"aria-label\",\n `Toggle visibility of ${layer.title}`,\n );\n visCheckbox.addEventListener(\"change\", () => {\n this.setLayerVisibility(added.key, visCheckbox.checked);\n });\n\n const titleEl = document.createElement(\"span\");\n titleEl.className = \"nasa-added-title\";\n titleEl.textContent = layer.title;\n titleEl.title = added.key;\n\n visLabel.appendChild(visCheckbox);\n visLabel.appendChild(titleEl);\n\n // Date chip distinguishes multiple instances of the same time layer\n let dateChip: HTMLElement | undefined;\n if (layer.time && added.date) {\n dateChip = document.createElement(\"span\");\n dateChip.className = \"nasa-badge nasa-badge-time\";\n dateChip.textContent = added.date.slice(0, 10);\n visLabel.appendChild(dateChip);\n }\n\n const actions = document.createElement(\"div\");\n actions.className = \"nasa-added-actions\";\n\n if (layer.legendUrl) {\n const legendBtn = document.createElement(\"button\");\n legendBtn.type = \"button\";\n legendBtn.className = `nasa-icon-button${\n this._openLegends.has(added.key) ? \" active\" : \"\"\n }`;\n legendBtn.title = \"Toggle legend\";\n legendBtn.setAttribute(\"aria-label\", `Toggle legend for ${layer.title}`);\n legendBtn.innerHTML = `\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M8 6h13M8 12h13M8 18h13\"/>\n <rect x=\"3\" y=\"4.5\" width=\"3\" height=\"3\" rx=\"0.5\"/>\n <rect x=\"3\" y=\"10.5\" width=\"3\" height=\"3\" rx=\"0.5\"/>\n <rect x=\"3\" y=\"16.5\" width=\"3\" height=\"3\" rx=\"0.5\"/>\n </svg>\n `;\n legendBtn.addEventListener(\"click\", () => {\n if (this._openLegends.has(added.key)) {\n this._openLegends.delete(added.key);\n } else {\n this._openLegends.add(added.key);\n }\n this._renderAddedSection();\n });\n actions.appendChild(legendBtn);\n }\n\n const removeBtn = document.createElement(\"button\");\n removeBtn.type = \"button\";\n removeBtn.className = \"nasa-icon-button nasa-icon-button-danger\";\n removeBtn.title = \"Remove layer\";\n removeBtn.setAttribute(\"aria-label\", `Remove layer ${layer.title}`);\n removeBtn.innerHTML = `\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\"/>\n <path d=\"M10 11v6M14 11v6\"/>\n </svg>\n `;\n removeBtn.addEventListener(\"click\", () => {\n this.removeLayer(added.key);\n });\n actions.appendChild(removeBtn);\n\n head.appendChild(visLabel);\n head.appendChild(actions);\n card.appendChild(head);\n\n // Opacity slider with live percentage readout\n if (this._options.showOpacity) {\n const opacityRow = document.createElement(\"label\");\n opacityRow.className = \"nasa-control-label\";\n opacityRow.textContent = \"Opacity\";\n\n const opacityInput = document.createElement(\"input\");\n opacityInput.type = \"range\";\n opacityInput.className = \"nasa-opacity\";\n opacityInput.min = \"0\";\n opacityInput.max = \"1\";\n opacityInput.step = \"0.05\";\n opacityInput.value = String(added.opacity);\n opacityInput.setAttribute(\"aria-label\", `Opacity for ${layer.title}`);\n\n const opacityValue = document.createElement(\"span\");\n opacityValue.className = \"nasa-opacity-value\";\n opacityValue.textContent = `${Math.round(added.opacity * 100)}%`;\n\n opacityInput.addEventListener(\"input\", () => {\n const value = Number(opacityInput.value);\n this.setLayerOpacity(added.key, value);\n opacityValue.textContent = `${Math.round(value * 100)}%`;\n });\n\n opacityRow.appendChild(opacityInput);\n opacityRow.appendChild(opacityValue);\n card.appendChild(opacityRow);\n }\n\n // Date picker for time-enabled layers\n if (layer.time) {\n const dateLabel = document.createElement(\"label\");\n dateLabel.className = \"nasa-control-label\";\n dateLabel.textContent = \"Date\";\n\n const dateInput = document.createElement(\"input\");\n dateInput.type = \"date\";\n dateInput.className = \"nasa-date\";\n dateInput.value = (added.date ?? layer.time.default).slice(0, 10);\n const range = this._timeRange(layer);\n if (range.min) dateInput.min = range.min;\n if (range.max) dateInput.max = range.max;\n dateInput.addEventListener(\"change\", () => {\n if (dateInput.value) {\n this.setLayerDate(added.key, dateInput.value);\n if (dateChip) {\n dateChip.textContent = dateInput.value;\n }\n }\n });\n\n dateLabel.appendChild(dateInput);\n card.appendChild(dateLabel);\n }\n\n // Legend image (GIBS horizontal legend SVG)\n if (layer.legendUrl && this._openLegends.has(added.key)) {\n const legend = document.createElement(\"img\");\n legend.className = \"nasa-legend-image\";\n legend.src = layer.legendUrl;\n legend.alt = `Legend for ${layer.title}`;\n legend.loading = \"lazy\";\n card.appendChild(legend);\n }\n\n return card;\n }\n\n /**\n * Refreshes the \"Insert before\" dropdown with the map's current layers.\n */\n private _refreshInsertOptions(): void {\n if (!this._insertSelect || !this._map) return;\n\n const layers = this._map.getStyle()?.layers ?? [];\n const select = this._insertSelect;\n select.innerHTML = \"\";\n\n const topOption = document.createElement(\"option\");\n topOption.value = \"\";\n topOption.textContent = \"Top of map\";\n select.appendChild(topOption);\n\n for (const layer of layers) {\n const option = document.createElement(\"option\");\n option.value = layer.id;\n option.textContent = layer.id;\n select.appendChild(option);\n }\n\n // Restore the previous selection if the layer still exists\n if (this._insertBefore && layers.some((l) => l.id === this._insertBefore)) {\n select.value = this._insertBefore;\n } else {\n this._insertBefore = \"\";\n select.value = \"\";\n }\n }\n\n /**\n * Derives the min/max selectable dates from a layer's time domain values.\n */\n private _timeRange(layer: GibsLayer): { min?: string; max?: string } {\n const time = layer.time;\n if (!time) return {};\n\n let min: string | undefined;\n let max: string | undefined;\n for (const value of time.values) {\n const [start, end] = value.split(\"/\");\n const startDate = start?.slice(0, 10);\n const endDate = (end ?? start)?.slice(0, 10);\n if (startDate && (!min || startDate < min)) min = startDate;\n if (endDate && (!max || endDate > max)) max = endDate;\n }\n const defaultDate = time.default.slice(0, 10);\n if (!max || defaultDate > max) max = defaultDate;\n return { min, max };\n }\n\n /**\n * Setup event listeners for panel positioning and click-outside behavior.\n */\n private _setupEventListeners(): void {\n // Click outside to close (check both container and panel since they're now separate).\n // Ignore targets that are no longer connected to the DOM: clicking an\n // Add/Remove button re-renders the results list, detaching the button\n // before this document-level handler runs.\n this._clickOutsideHandler = (e: MouseEvent) => {\n const target = e.target as Node;\n if (\n this._container &&\n this._panel &&\n target.isConnected &&\n !this._container.contains(target) &&\n !this._panel.contains(target)\n ) {\n this.collapse();\n }\n };\n document.addEventListener(\"click\", this._clickOutsideHandler);\n\n // Update panel position on window resize\n this._resizeHandler = () => {\n if (!this._state.collapsed) {\n this._updatePanelPosition();\n }\n };\n window.addEventListener(\"resize\", this._resizeHandler);\n\n // Update panel position on map resize (e.g., sidebar toggle)\n this._mapResizeHandler = () => {\n if (!this._state.collapsed) {\n this._updatePanelPosition();\n }\n };\n this._map?.on(\"resize\", this._mapResizeHandler);\n }\n\n /**\n * Detect which corner the control is positioned in.\n *\n * @returns The position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n */\n private _getControlPosition():\n | \"top-left\"\n | \"top-right\"\n | \"bottom-left\"\n | \"bottom-right\" {\n const parent = this._container?.parentElement;\n if (!parent) return \"top-right\"; // Default\n\n if (parent.classList.contains(\"maplibregl-ctrl-top-left\"))\n return \"top-left\";\n if (parent.classList.contains(\"maplibregl-ctrl-top-right\"))\n return \"top-right\";\n if (parent.classList.contains(\"maplibregl-ctrl-bottom-left\"))\n return \"bottom-left\";\n if (parent.classList.contains(\"maplibregl-ctrl-bottom-right\"))\n return \"bottom-right\";\n\n return \"top-right\"; // Default\n }\n\n /**\n * Update the panel position based on button location and control corner.\n * Positions the panel next to the button, expanding in the appropriate direction.\n */\n private _updatePanelPosition(): void {\n if (!this._container || !this._panel || !this._mapContainer) return;\n\n // Get the toggle button (first child of container)\n const button = this._container.querySelector(\".plugin-control-toggle\");\n if (!button) return;\n\n // Keep the panel within the map even if it shrank since the width was set\n this._applyPanelWidthBounds();\n\n const buttonRect = button.getBoundingClientRect();\n const mapRect = this._mapContainer.getBoundingClientRect();\n const position = this._getControlPosition();\n\n // Calculate button position relative to map container\n const buttonTop = buttonRect.top - mapRect.top;\n const buttonBottom = mapRect.bottom - buttonRect.bottom;\n const buttonLeft = buttonRect.left - mapRect.left;\n const buttonRight = mapRect.right - buttonRect.right;\n\n const panelGap = 5; // Gap between button and panel\n\n // Reset all positioning\n this._panel.style.top = \"\";\n this._panel.style.bottom = \"\";\n this._panel.style.left = \"\";\n this._panel.style.right = \"\";\n\n // Keep the resize handle on the panel's outer (growing) edge:\n // right edge for left-anchored panels, left edge for right-anchored ones\n this._panel\n .querySelector(\".nasa-resize-handle\")\n ?.classList.toggle(\"nasa-resize-handle-right\", position.endsWith(\"left\"));\n\n switch (position) {\n case \"top-left\":\n // Panel expands down and to the right\n this._panel.style.top = `${buttonTop + buttonRect.height + panelGap}px`;\n this._panel.style.left = `${buttonLeft}px`;\n break;\n\n case \"top-right\":\n // Panel expands down and to the left\n this._panel.style.top = `${buttonTop + buttonRect.height + panelGap}px`;\n this._panel.style.right = `${buttonRight}px`;\n break;\n\n case \"bottom-left\":\n // Panel expands up and to the right\n this._panel.style.bottom = `${buttonBottom + buttonRect.height + panelGap}px`;\n this._panel.style.left = `${buttonLeft}px`;\n break;\n\n case \"bottom-right\":\n // Panel expands up and to the left\n this._panel.style.bottom = `${buttonBottom + buttonRect.height + panelGap}px`;\n this._panel.style.right = `${buttonRight}px`;\n break;\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -920,9 +920,11 @@ var NasaEarthdataControl = class {
|
|
|
920
920
|
toggleBtn.innerHTML = `
|
|
921
921
|
<span class="plugin-control-icon">
|
|
922
922
|
<svg viewBox="0 0 24 24" width="22" height="22" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
923
|
-
<
|
|
924
|
-
<path d="
|
|
925
|
-
<path d="
|
|
923
|
+
<path d="M13 7 9 3 5 7l4 4"/>
|
|
924
|
+
<path d="m17 11 4 4-4 4-4-4"/>
|
|
925
|
+
<path d="m8 12 4 4 6-6-4-4Z"/>
|
|
926
|
+
<path d="m16 8 3-3"/>
|
|
927
|
+
<path d="M9 21a6 6 0 0 0-6-6"/>
|
|
926
928
|
</svg>
|
|
927
929
|
</span>
|
|
928
930
|
`;
|
|
@@ -1554,4 +1556,4 @@ Object.defineProperty(exports, "throttle", {
|
|
|
1554
1556
|
}
|
|
1555
1557
|
});
|
|
1556
1558
|
|
|
1557
|
-
//# sourceMappingURL=NasaEarthdataControl-
|
|
1559
|
+
//# sourceMappingURL=NasaEarthdataControl-DahZNxUI.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NasaEarthdataControl-DahZNxUI.cjs","names":[],"sources":["../src/lib/gibs/parseCapabilities.ts","../src/lib/gibs/search.ts","../src/lib/gibs/GibsClient.ts","../src/lib/gibs/tileUrl.ts","../src/lib/utils/helpers.ts","../src/lib/core/NasaEarthdataControl.ts"],"sourcesContent":["import type {\n GibsCapabilities,\n GibsLayer,\n GibsLayerFormat,\n GibsTimeDimension,\n ParseOptions,\n} from \"./types\";\n\nconst FORMAT_MAP: Record<string, GibsLayerFormat> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpeg\",\n \"application/vnd.mapbox-vector-tile\": \"mvt\",\n};\n\nconst MIME_MAP: Record<GibsLayerFormat, string> = {\n png: \"image/png\",\n jpeg: \"image/jpeg\",\n mvt: \"application/vnd.mapbox-vector-tile\",\n};\n\n/**\n * Returns the direct child elements of a node matching a local tag name,\n * ignoring XML namespaces.\n */\nfunction childrenByLocalName(parent: Element, localName: string): Element[] {\n const result: Element[] = [];\n for (let i = 0; i < parent.children.length; i++) {\n const child = parent.children[i];\n if (child.localName === localName) {\n result.push(child);\n }\n }\n return result;\n}\n\n/**\n * Returns the text content of the first direct child element matching a local tag name.\n */\nfunction childText(parent: Element, localName: string): string | undefined {\n const child = childrenByLocalName(parent, localName)[0];\n return child?.textContent?.trim() || undefined;\n}\n\n/**\n * Parses the WGS84 bounding box of a layer element.\n */\nfunction parseBbox(\n layerEl: Element,\n): [number, number, number, number] | undefined {\n const bboxEl = childrenByLocalName(layerEl, \"WGS84BoundingBox\")[0];\n if (!bboxEl) return undefined;\n\n const lower = childText(bboxEl, \"LowerCorner\")?.split(/\\s+/).map(Number);\n const upper = childText(bboxEl, \"UpperCorner\")?.split(/\\s+/).map(Number);\n if (!lower || !upper || lower.length < 2 || upper.length < 2)\n return undefined;\n if ([...lower, ...upper].some((n) => Number.isNaN(n))) return undefined;\n\n return [lower[0], lower[1], upper[0], upper[1]];\n}\n\n/**\n * Parses the Time dimension of a layer element, if present.\n */\nfunction parseTimeDimension(layerEl: Element): GibsTimeDimension | undefined {\n const dimensions = childrenByLocalName(layerEl, \"Dimension\");\n for (const dim of dimensions) {\n if (childText(dim, \"Identifier\") !== \"Time\") continue;\n\n const defaultValue = childText(dim, \"Default\");\n if (!defaultValue) return undefined;\n\n const values = childrenByLocalName(dim, \"Value\")\n .map((v) => v.textContent?.trim())\n .filter((v): v is string => Boolean(v));\n\n return { default: defaultValue, values };\n }\n return undefined;\n}\n\n/**\n * Parses the first legend URL advertised in a layer's default style.\n */\nfunction parseLegendUrl(layerEl: Element): string | undefined {\n const styles = childrenByLocalName(layerEl, \"Style\");\n for (const style of styles) {\n const legend = childrenByLocalName(style, \"LegendURL\")[0];\n const href =\n legend?.getAttribute(\"xlink:href\") ??\n legend?.getAttributeNS(null, \"href\");\n if (href) return href;\n }\n return undefined;\n}\n\n/**\n * Parses a single WMTS Layer element into a GibsLayer.\n * Returns undefined for layers that cannot be represented (missing\n * identifier, no tile resource template, or unsupported format).\n */\nfunction parseLayer(\n layerEl: Element,\n includeVector: boolean,\n): GibsLayer | undefined {\n const id = childText(layerEl, \"Identifier\");\n if (!id) return undefined;\n\n const title = childText(layerEl, \"Title\") ?? id;\n\n // Collect supported formats; prefer png > jpeg > mvt.\n const formats = childrenByLocalName(layerEl, \"Format\")\n .map((f) => FORMAT_MAP[f.textContent?.trim() ?? \"\"])\n .filter((f): f is GibsLayerFormat => Boolean(f));\n const format = ([\"png\", \"jpeg\", \"mvt\"] as const).find((f) =>\n formats.includes(f),\n );\n if (!format) return undefined;\n if (format === \"mvt\" && !includeVector) return undefined;\n\n // Tile matrix set link, e.g. \"GoogleMapsCompatible_Level9\" -> maxZoom 9.\n const tmsLink = childrenByLocalName(layerEl, \"TileMatrixSetLink\")[0];\n const tileMatrixSet = tmsLink\n ? childText(tmsLink, \"TileMatrixSet\")\n : undefined;\n if (!tileMatrixSet) return undefined;\n const levelMatch = /Level(\\d+)$/.exec(tileMatrixSet);\n const maxZoom = levelMatch ? parseInt(levelMatch[1], 10) : 9;\n\n // Tile resource template. Only consider templates whose advertised format\n // matches the chosen layer format, then prefer the template containing\n // {Time} when the layer is time-enabled so dates can be substituted.\n const resourceUrls = childrenByLocalName(layerEl, \"ResourceURL\").filter(\n (r) => r.getAttribute(\"resourceType\") === \"tile\",\n );\n if (resourceUrls.length === 0) return undefined;\n\n const selectedMime = MIME_MAP[format];\n const formatMatched = resourceUrls.filter((r) => {\n const mime = r.getAttribute(\"format\")?.trim().toLowerCase();\n return !mime || mime === selectedMime;\n });\n\n const templates = (formatMatched.length > 0 ? formatMatched : resourceUrls)\n .map((r) => r.getAttribute(\"template\"))\n .filter((t): t is string => Boolean(t));\n if (templates.length === 0) return undefined;\n\n const time = parseTimeDimension(layerEl);\n const resourceTemplate =\n (time ? templates.find((t) => t.includes(\"{Time}\")) : undefined) ??\n templates.find((t) => !t.includes(\"{Time}\")) ??\n templates[0];\n\n const extMatch = /\\.([a-z0-9]+)$/i.exec(resourceTemplate);\n const fileExtension = extMatch\n ? extMatch[1]\n : format === \"jpeg\"\n ? \"jpg\"\n : format;\n\n return {\n id,\n title,\n // Platform/instrument prefix of the identifier, used for grouping\n category: id.split(\"_\")[0],\n format,\n fileExtension,\n tileMatrixSet,\n maxZoom,\n bbox: parseBbox(layerEl),\n resourceTemplate,\n time,\n legendUrl: parseLegendUrl(layerEl),\n };\n}\n\n/**\n * Parses a WMTS GetCapabilities XML document into a GibsCapabilities object.\n *\n * Layers without a usable tile resource template are skipped. Vector-tile\n * (MVT) layers are skipped unless `options.includeVector` is true.\n *\n * @param xml - The raw WMTSCapabilities.xml document text\n * @param options - Parse options\n * @returns Parsed capabilities with layers sorted by title\n */\nexport function parseCapabilities(\n xml: string,\n options?: ParseOptions,\n): GibsCapabilities {\n const includeVector = options?.includeVector ?? false;\n\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const parserError = doc.getElementsByTagName(\"parsererror\")[0];\n if (parserError) {\n throw new Error(\n `Failed to parse capabilities XML: ${parserError.textContent ?? \"unknown error\"}`,\n );\n }\n\n const root = doc.documentElement;\n const contents = childrenByLocalName(root, \"Contents\")[0];\n if (!contents) {\n throw new Error(\"Invalid capabilities document: missing Contents element\");\n }\n\n const layers: GibsLayer[] = [];\n for (const layerEl of childrenByLocalName(contents, \"Layer\")) {\n const layer = parseLayer(layerEl, includeVector);\n if (layer) layers.push(layer);\n }\n\n layers.sort((a, b) => a.title.localeCompare(b.title));\n\n return { layers, fetchedAt: Date.now() };\n}\n","import type { GibsLayer } from \"./types\";\n\n/**\n * Filters GIBS layers by a free-text query.\n *\n * Performs a case-insensitive substring match against the layer title and\n * identifier. An empty or whitespace-only query returns all layers.\n *\n * @param layers - The layers to search\n * @param query - The search query\n * @returns Layers whose title or identifier contains the query\n */\nexport function searchLayers(layers: GibsLayer[], query: string): GibsLayer[] {\n const q = query.trim().toLowerCase();\n if (!q) return layers;\n\n return layers.filter(\n (layer) =>\n layer.title.toLowerCase().includes(q) ||\n layer.id.toLowerCase().includes(q),\n );\n}\n","import { parseCapabilities } from \"./parseCapabilities\";\nimport { searchLayers } from \"./search\";\nimport type { GibsCapabilities, GibsLayer } from \"./types\";\n\n/**\n * Default URL of the NASA GIBS WMTS capabilities document (EPSG:3857, \"best\" imagery).\n */\nexport const DEFAULT_CAPABILITIES_URL =\n \"https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/1.0.0/WMTSCapabilities.xml\";\n\n/**\n * Options for creating a GibsClient.\n */\nexport interface GibsClientOptions {\n /**\n * URL of the WMTS capabilities document.\n * @default DEFAULT_CAPABILITIES_URL\n */\n url?: string;\n\n /**\n * Whether to include vector-tile (MVT) layers.\n * @default false\n */\n includeVector?: boolean;\n}\n\n/**\n * Fetches, parses, and caches the NASA GIBS WMTS capabilities document.\n *\n * Concurrent calls to {@link GibsClient.getCapabilities} are deduplicated:\n * the document is fetched and parsed at most once unless `force` is passed.\n *\n * @example\n * ```typescript\n * const client = new GibsClient();\n * const { layers } = await client.getCapabilities();\n * const matches = client.search('temperature');\n * ```\n */\nexport class GibsClient {\n private _url: string;\n private _includeVector: boolean;\n private _capabilities?: GibsCapabilities;\n private _pending?: Promise<GibsCapabilities>;\n\n /**\n * Creates a new GibsClient.\n *\n * @param options - Client options\n */\n constructor(options?: GibsClientOptions) {\n this._url = options?.url ?? DEFAULT_CAPABILITIES_URL;\n this._includeVector = options?.includeVector ?? false;\n }\n\n /**\n * Fetches and parses the capabilities document, caching the result.\n *\n * @param force - If true, refetch even if a cached result exists\n * @returns The parsed capabilities\n */\n async getCapabilities(force = false): Promise<GibsCapabilities> {\n if (this._capabilities && !force) return this._capabilities;\n if (this._pending && !force) return this._pending;\n\n this._pending = (async () => {\n const response = await fetch(this._url);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch capabilities: ${response.status} ${response.statusText}`,\n );\n }\n const xml = await response.text();\n const capabilities = parseCapabilities(xml, {\n includeVector: this._includeVector,\n });\n this._capabilities = capabilities;\n return capabilities;\n })();\n\n try {\n return await this._pending;\n } finally {\n this._pending = undefined;\n }\n }\n\n /**\n * Returns the cached capabilities, if already loaded.\n */\n getCachedCapabilities(): GibsCapabilities | undefined {\n return this._capabilities;\n }\n\n /**\n * Searches the cached layers by title or identifier.\n * Returns an empty array if capabilities have not been loaded yet.\n *\n * @param query - The search query\n */\n search(query: string): GibsLayer[] {\n return searchLayers(this._capabilities?.layers ?? [], query);\n }\n\n /**\n * Looks up a cached layer by identifier.\n *\n * @param id - The layer identifier\n */\n getLayer(id: string): GibsLayer | undefined {\n return this._capabilities?.layers.find((layer) => layer.id === id);\n }\n}\n","import type { GibsLayer } from \"./types\";\n\n/**\n * Builds a MapLibre-compatible XYZ tile URL template from a GIBS layer.\n *\n * Substitutes {Time} with the given date (or the layer's default date),\n * {TileMatrixSet} with the layer's tile matrix set, and maps the WMTS\n * placeholders {TileMatrix}/{TileRow}/{TileCol} to {z}/{y}/{x}.\n *\n * @param layer - The GIBS layer to build a URL for\n * @param time - Optional ISO 8601 date overriding the layer's default date\n * @returns A tile URL template usable in a MapLibre raster source\n */\nexport function buildTileUrl(layer: GibsLayer, time?: string): string {\n return layer.resourceTemplate\n .replace(\"{Time}\", time ?? layer.time?.default ?? \"default\")\n .replace(\"{TileMatrixSet}\", layer.tileMatrixSet)\n .replace(\"{TileMatrix}\", \"{z}\")\n .replace(\"{TileRow}\", \"{y}\")\n .replace(\"{TileCol}\", \"{x}\");\n}\n","/**\n * Clamps a value between a minimum and maximum.\n *\n * @param value - The value to clamp\n * @param min - The minimum allowed value\n * @param max - The maximum allowed value\n * @returns The clamped value\n *\n * @example\n * ```typescript\n * clamp(5, 0, 10); // returns 5\n * clamp(-5, 0, 10); // returns 0\n * clamp(15, 0, 10); // returns 10\n * ```\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Formats a numeric value with appropriate decimal places based on step size.\n *\n * @param value - The value to format\n * @param step - The step size to determine decimal places\n * @returns The formatted value as a string\n *\n * @example\n * ```typescript\n * formatNumericValue(5, 1); // returns \"5\"\n * formatNumericValue(0.5, 0.1); // returns \"0.5\"\n * formatNumericValue(0.55, 0.01); // returns \"0.55\"\n * ```\n */\nexport function formatNumericValue(value: number, step: number): string {\n if (step === 0) return value.toString();\n const decimals = Math.max(0, -Math.floor(Math.log10(step)));\n return value.toFixed(decimals);\n}\n\n/**\n * Generates a unique ID string.\n *\n * @param prefix - Optional prefix for the ID\n * @returns A unique ID string\n *\n * @example\n * ```typescript\n * generateId('control'); // returns \"control-abc123\"\n * generateId(); // returns \"abc123\"\n * ```\n */\nexport function generateId(prefix?: string): string {\n const id = Math.random().toString(36).substring(2, 9);\n return prefix ? `${prefix}-${id}` : id;\n}\n\n/**\n * Debounces a function call.\n *\n * @param fn - The function to debounce\n * @param delay - The delay in milliseconds\n * @returns A debounced version of the function\n *\n * @example\n * ```typescript\n * const debouncedUpdate = debounce(() => updateMap(), 100);\n * window.addEventListener('resize', debouncedUpdate);\n * ```\n */\nexport function debounce<T extends (...args: unknown[]) => void>(\n fn: T,\n delay: number,\n): (...args: Parameters<T>) => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n return (...args: Parameters<T>) => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n timeoutId = setTimeout(() => {\n fn(...args);\n timeoutId = null;\n }, delay);\n };\n}\n\n/**\n * Throttles a function call.\n *\n * @param fn - The function to throttle\n * @param limit - The minimum time between calls in milliseconds\n * @returns A throttled version of the function\n *\n * @example\n * ```typescript\n * const throttledScroll = throttle(() => handleScroll(), 100);\n * window.addEventListener('scroll', throttledScroll);\n * ```\n */\nexport function throttle<T extends (...args: unknown[]) => void>(\n fn: T,\n limit: number,\n): (...args: Parameters<T>) => void {\n let inThrottle = false;\n\n return (...args: Parameters<T>) => {\n if (!inThrottle) {\n fn(...args);\n inThrottle = true;\n setTimeout(() => {\n inThrottle = false;\n }, limit);\n }\n };\n}\n\n/**\n * Creates a CSS class string from an object of class names.\n *\n * @param classes - Object with class names as keys and boolean values\n * @returns A space-separated string of class names\n *\n * @example\n * ```typescript\n * classNames({ active: true, disabled: false, visible: true });\n * // returns \"active visible\"\n * ```\n */\nexport function classNames(classes: Record<string, boolean>): string {\n return Object.entries(classes)\n .filter(([, value]) => value)\n .map(([key]) => key)\n .join(\" \");\n}\n","import type {\n IControl,\n Map as MapLibreMap,\n RasterTileSource,\n} from \"maplibre-gl\";\nimport { GibsClient, DEFAULT_CAPABILITIES_URL } from \"../gibs/GibsClient\";\nimport { buildTileUrl } from \"../gibs/tileUrl\";\nimport { searchLayers } from \"../gibs/search\";\nimport type { GibsCapabilities, GibsLayer } from \"../gibs/types\";\nimport { clamp, debounce } from \"../utils\";\nimport type {\n NasaEarthdataControlOptions,\n NasaEarthdataState,\n NasaEarthdataEvent,\n NasaEarthdataEventHandler,\n NasaEarthdataEventPayload,\n AddedLayerState,\n} from \"./types\";\n\n/**\n * Default options for the NasaEarthdataControl\n */\nconst DEFAULT_OPTIONS: Required<NasaEarthdataControlOptions> = {\n collapsed: true,\n position: \"top-right\",\n title: \"NASA Earthdata\",\n panelWidth: 320,\n className: \"\",\n capabilitiesUrl: DEFAULT_CAPABILITIES_URL,\n includeVector: false,\n showOpacity: true,\n attribution:\n '<a href=\"https://earthdata.nasa.gov/gibs\" target=\"_blank\">NASA EOSDIS GIBS</a>',\n theme: \"auto\",\n};\n\n/**\n * Prefix used for source and layer ids added to the map\n */\nconst LAYER_ID_PREFIX = \"nasa-gibs-\";\n\n/**\n * Event handlers map type\n */\ntype EventHandlersMap = globalThis.Map<\n NasaEarthdataEvent,\n Set<NasaEarthdataEventHandler>\n>;\n\n/**\n * A MapLibre GL control for searching and adding NASA GIBS (Global Imagery\n * Browse Services) WMTS layers to the map.\n *\n * The control renders a collapsible button. When expanded, it fetches the\n * GIBS capabilities document, lets the user search the layer catalog, and\n * add/remove raster layers with per-layer date and opacity controls.\n *\n * @example\n * ```typescript\n * const control = new NasaEarthdataControl({\n * title: 'NASA Earthdata',\n * collapsed: false,\n * });\n * map.addControl(control, 'top-right');\n * control.on('layeradd', (e) => console.log('Added', e.layer?.id));\n * ```\n */\nexport class NasaEarthdataControl implements IControl {\n private _map?: MapLibreMap;\n private _mapContainer?: HTMLElement;\n private _container?: HTMLElement;\n private _panel?: HTMLElement;\n private _options: Required<NasaEarthdataControlOptions>;\n private _state: NasaEarthdataState;\n private _eventHandlers: EventHandlersMap = new globalThis.Map();\n\n private _client: GibsClient;\n private _capabilities?: GibsCapabilities;\n private _loading = false;\n private _addedLayers: globalThis.Map<string, AddedLayerState> =\n new globalThis.Map();\n\n // Panel content elements\n private _searchInput?: HTMLInputElement;\n private _metaEl?: HTMLElement;\n private _resultsEl?: HTMLElement;\n private _addedEl?: HTMLElement;\n private _insertSelect?: HTMLSelectElement;\n\n // UI state: expanded category groups, open legends, insertion position\n private _expandedCategories = new Set<string>();\n private _openLegends = new Set<string>();\n private _insertBefore = \"\";\n\n // Panel positioning handlers\n private _resizeHandler: (() => void) | null = null;\n private _mapResizeHandler: (() => void) | null = null;\n private _clickOutsideHandler: ((e: MouseEvent) => void) | null = null;\n\n /**\n * Creates a new NasaEarthdataControl instance.\n *\n * @param options - Configuration options for the control\n */\n constructor(options?: Partial<NasaEarthdataControlOptions>) {\n this._options = { ...DEFAULT_OPTIONS, ...options };\n this._state = {\n collapsed: this._options.collapsed,\n panelWidth: this._options.panelWidth,\n query: \"\",\n addedLayers: [],\n };\n this._client = new GibsClient({\n url: this._options.capabilitiesUrl,\n includeVector: this._options.includeVector,\n });\n }\n\n /**\n * Called when the control is added to the map.\n * Implements the IControl interface.\n *\n * @param map - The MapLibre GL map instance\n * @returns The control's container element\n */\n onAdd(map: MapLibreMap): HTMLElement {\n this._map = map;\n this._mapContainer = map.getContainer();\n this._container = this._createContainer();\n this._panel = this._createPanel();\n\n // Append panel to map container for independent positioning (avoids overlap with other controls)\n this._mapContainer.appendChild(this._panel);\n\n // Setup event listeners for panel positioning and click-outside\n this._setupEventListeners();\n\n // Set initial panel state\n if (!this._state.collapsed) {\n this._panel.classList.add(\"expanded\");\n this._loadCapabilities();\n // Update position after control is added to DOM\n requestAnimationFrame(() => {\n this._updatePanelPosition();\n });\n }\n\n return this._container;\n }\n\n /**\n * Called when the control is removed from the map.\n * Implements the IControl interface.\n */\n onRemove(): void {\n // Remove all added GIBS layers and sources from the map\n for (const layerId of Array.from(this._addedLayers.keys())) {\n this._removeMapLayer(layerId);\n }\n this._addedLayers.clear();\n this._state.addedLayers = [];\n\n // Remove event listeners\n if (this._resizeHandler) {\n window.removeEventListener(\"resize\", this._resizeHandler);\n this._resizeHandler = null;\n }\n if (this._mapResizeHandler && this._map) {\n this._map.off(\"resize\", this._mapResizeHandler);\n this._mapResizeHandler = null;\n }\n if (this._clickOutsideHandler) {\n document.removeEventListener(\"click\", this._clickOutsideHandler);\n this._clickOutsideHandler = null;\n }\n\n // Remove panel from map container\n this._panel?.parentNode?.removeChild(this._panel);\n\n // Remove button container from control stack\n this._container?.parentNode?.removeChild(this._container);\n\n this._map = undefined;\n this._mapContainer = undefined;\n this._container = undefined;\n this._panel = undefined;\n this._searchInput = undefined;\n this._metaEl = undefined;\n this._resultsEl = undefined;\n this._addedEl = undefined;\n this._insertSelect = undefined;\n this._openLegends.clear();\n this._eventHandlers.clear();\n }\n\n /**\n * Gets the current state of the control.\n *\n * @returns The current control state\n */\n getState(): NasaEarthdataState {\n return {\n ...this._state,\n addedLayers: this._state.addedLayers.map((l) => ({ ...l })),\n };\n }\n\n /**\n * Updates the control state. Changes to `addedLayers` are reconciled\n * against the map: missing layers are added, extra layers are removed,\n * and date/opacity changes are applied.\n *\n * @param newState - Partial state to merge with current state\n */\n setState(newState: Partial<NasaEarthdataState>): void {\n if (\n newState.collapsed !== undefined &&\n newState.collapsed !== this._state.collapsed\n ) {\n if (newState.collapsed) {\n this.collapse();\n } else {\n this.expand();\n }\n }\n\n if (newState.panelWidth !== undefined) {\n this._state.panelWidth = newState.panelWidth;\n if (this._panel) {\n this._panel.style.width = `${newState.panelWidth}px`;\n }\n }\n\n if (newState.query !== undefined && newState.query !== this._state.query) {\n this._state.query = newState.query;\n if (this._searchInput) {\n this._searchInput.value = newState.query;\n }\n this._renderResults();\n }\n\n let deferStateEmit = false;\n if (newState.addedLayers) {\n // Reconciliation needs the layer catalog; defer it until the\n // capabilities are loaded so state restoration works before the\n // panel is first expanded. An empty list needs no catalog data.\n const desired = newState.addedLayers.map((l) => ({ ...l }));\n if (desired.length === 0 || this._capabilities) {\n this._reconcileAddedLayers(desired);\n } else {\n // Emit statechange only after reconciliation so listeners never\n // see a stale addedLayers snapshot.\n deferStateEmit = true;\n void this.getCapabilities()\n .then(() => {\n this._reconcileAddedLayers(desired);\n this._renderResults();\n this._renderAddedSection();\n this._emit(\"statechange\");\n })\n .catch((error: unknown) => {\n const err =\n error instanceof Error ? error : new Error(String(error));\n this._emitError(err);\n });\n }\n }\n\n if (!deferStateEmit) {\n this._emit(\"statechange\");\n }\n }\n\n /**\n * Toggles the collapsed state of the control panel.\n */\n toggle(): void {\n this._state.collapsed = !this._state.collapsed;\n\n if (this._panel) {\n if (this._state.collapsed) {\n this._panel.classList.remove(\"expanded\");\n this._emit(\"collapse\");\n } else {\n this._panel.classList.add(\"expanded\");\n this._updatePanelPosition();\n this._loadCapabilities();\n this._refreshInsertOptions();\n this._emit(\"expand\");\n }\n }\n\n this._emit(\"statechange\");\n }\n\n /**\n * Expands the control panel.\n */\n expand(): void {\n if (this._state.collapsed) {\n this.toggle();\n }\n }\n\n /**\n * Collapses the control panel.\n */\n collapse(): void {\n if (!this._state.collapsed) {\n this.toggle();\n }\n }\n\n /**\n * Registers an event handler.\n *\n * @param event - The event type to listen for\n * @param handler - The callback function\n */\n on(event: NasaEarthdataEvent, handler: NasaEarthdataEventHandler): void {\n if (!this._eventHandlers.has(event)) {\n this._eventHandlers.set(event, new Set());\n }\n this._eventHandlers.get(event)!.add(handler);\n }\n\n /**\n * Removes an event handler.\n *\n * @param event - The event type\n * @param handler - The callback function to remove\n */\n off(event: NasaEarthdataEvent, handler: NasaEarthdataEventHandler): void {\n this._eventHandlers.get(event)?.delete(handler);\n }\n\n /**\n * Gets the map instance.\n *\n * @returns The MapLibre GL map instance or undefined if not added to a map\n */\n getMap(): MapLibreMap | undefined {\n return this._map;\n }\n\n /**\n * Gets the control container element.\n *\n * @returns The container element or undefined if not added to a map\n */\n getContainer(): HTMLElement | undefined {\n return this._container;\n }\n\n /**\n * Fetches and caches the GIBS capabilities document.\n *\n * @param force - If true, refetch even if cached\n * @returns The parsed capabilities\n */\n async getCapabilities(force = false): Promise<GibsCapabilities> {\n const capabilities = await this._client.getCapabilities(force);\n this._capabilities = capabilities;\n return capabilities;\n }\n\n /**\n * Searches the loaded GIBS layers by title or identifier.\n * Returns an empty array if the capabilities have not been loaded yet.\n *\n * @param query - The search query\n * @returns Matching layers\n */\n search(query: string): GibsLayer[] {\n return searchLayers(this._capabilities?.layers ?? [], query);\n }\n\n /**\n * Gets the state of all layers currently added to the map.\n *\n * @returns Added layer states\n */\n getAddedLayers(): AddedLayerState[] {\n return Array.from(this._addedLayers.values()).map((l) => ({ ...l }));\n }\n\n /**\n * Adds a GIBS layer to the map as a raster source and layer.\n * Requires the capabilities to be loaded (call getCapabilities() first\n * when using the control programmatically).\n *\n * @param layerId - The GIBS layer identifier\n * @param options - Optional date, opacity, visibility, and insertion position\n */\n addLayer(\n layerId: string,\n options?: {\n date?: string;\n opacity?: number;\n visible?: boolean;\n before?: string;\n key?: string;\n },\n ): void {\n if (!this._map) return;\n\n const layer = this._client.getLayer(layerId);\n if (!layer) {\n this._emitError(\n new Error(`Unknown GIBS layer: ${layerId} (capabilities not loaded?)`),\n );\n return;\n }\n\n const date = options?.date ?? layer.time?.default;\n const opacity = clamp(options?.opacity ?? 1, 0, 1);\n const visible = options?.visible ?? true;\n const before = options?.before ?? this._insertBefore;\n\n // Restoring a known instance key is a no-op if it is already on the map\n if (options?.key && this._addedLayers.has(options.key)) return;\n const key = options?.key ?? this._instanceKey(layer, date);\n const mapId = LAYER_ID_PREFIX + key;\n\n // Host applications that persist and restore layers may have already\n // recreated the native source/layer from saved state. Reuse them when\n // present instead of re-adding (which would throw on a duplicate id),\n // and apply the requested opacity/visibility so control state and map\n // agree.\n if (!this._map.getSource(mapId)) {\n this._map.addSource(mapId, {\n type: \"raster\",\n tiles: [buildTileUrl(layer, date)],\n tileSize: 256,\n maxzoom: layer.maxZoom,\n attribution: this._options.attribution,\n });\n }\n if (!this._map.getLayer(mapId)) {\n this._map.addLayer(\n {\n id: mapId,\n type: \"raster\",\n source: mapId,\n paint: { \"raster-opacity\": opacity },\n layout: { visibility: visible ? \"visible\" : \"none\" },\n },\n before && this._map.getLayer(before) ? before : undefined,\n );\n } else {\n this._map.setPaintProperty(mapId, \"raster-opacity\", opacity);\n this._map.setLayoutProperty(\n mapId,\n \"visibility\",\n visible ? \"visible\" : \"none\",\n );\n }\n\n const added: AddedLayerState = { key, id: layerId, date, opacity, visible };\n this._addedLayers.set(key, added);\n this._syncAddedLayersState();\n this._renderResults();\n this._renderAddedSection();\n this._refreshInsertOptions();\n this._emit(\"layeradd\", { layer });\n this._emit(\"statechange\");\n }\n\n /**\n * Builds a unique instance key for a layer/date pair. Non-time layers can\n * only be added once; time-enabled layers get one instance per addition,\n * so a numeric suffix disambiguates repeated adds at the same date.\n */\n private _instanceKey(layer: GibsLayer, date?: string): string {\n if (!layer.time) return layer.id;\n\n const base = `${layer.id}@${date ?? layer.time.default}`;\n if (!this._addedLayers.has(base)) return base;\n let n = 2;\n while (this._addedLayers.has(`${base}#${n}`)) n++;\n return `${base}#${n}`;\n }\n\n /**\n * Resolves an instance key or a GIBS layer identifier to instances.\n * An exact key match wins; otherwise all instances of the layer match.\n */\n private _resolveInstances(keyOrId: string): AddedLayerState[] {\n const exact = this._addedLayers.get(keyOrId);\n if (exact) return [exact];\n return Array.from(this._addedLayers.values()).filter(\n (l) => l.id === keyOrId,\n );\n }\n\n /**\n * Removes added GIBS layer instances from the map. Pass an instance key\n * to remove a single instance, or a GIBS layer identifier to remove all\n * instances of that layer.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n */\n removeLayer(keyOrId: string): void {\n const instances = this._resolveInstances(keyOrId);\n if (instances.length === 0) return;\n\n for (const instance of instances) {\n this._removeMapLayer(instance.key);\n this._addedLayers.delete(instance.key);\n this._openLegends.delete(instance.key);\n this._emit(\"layerremove\", { layer: this._client.getLayer(instance.id) });\n }\n this._syncAddedLayersState();\n this._renderResults();\n this._renderAddedSection();\n this._refreshInsertOptions();\n this._emit(\"statechange\");\n }\n\n /**\n * Changes the date of an added time-enabled layer instance.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n * @param date - The new ISO 8601 date\n */\n setLayerDate(keyOrId: string, date: string): void {\n const added = this._resolveInstances(keyOrId)[0];\n const layer = added ? this._client.getLayer(added.id) : undefined;\n if (!this._map || !added || !layer) return;\n\n added.date = date;\n const mapId = LAYER_ID_PREFIX + added.key;\n const source = this._map.getSource(mapId) as RasterTileSource | undefined;\n const tiles = [buildTileUrl(layer, date)];\n\n if (source && typeof source.setTiles === \"function\") {\n source.setTiles(tiles);\n } else {\n // Fallback: re-create the source and layer\n this._removeMapLayer(added.key);\n this._addedLayers.delete(added.key);\n this.addLayer(added.id, {\n date,\n opacity: added.opacity,\n visible: added.visible,\n key: added.key,\n });\n return;\n }\n\n this._syncAddedLayersState();\n this._emit(\"statechange\");\n }\n\n /**\n * Changes the opacity of an added layer instance.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n * @param opacity - The new opacity (0 to 1)\n */\n setLayerOpacity(keyOrId: string, opacity: number): void {\n const added = this._resolveInstances(keyOrId)[0];\n if (!this._map || !added) return;\n\n added.opacity = clamp(opacity, 0, 1);\n this._map.setPaintProperty(\n LAYER_ID_PREFIX + added.key,\n \"raster-opacity\",\n added.opacity,\n );\n this._syncAddedLayersState();\n this._emit(\"statechange\");\n }\n\n /**\n * Toggles the visibility of an added layer instance on the map.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n * @param visible - Whether the layer should be visible\n */\n setLayerVisibility(keyOrId: string, visible: boolean): void {\n const added = this._resolveInstances(keyOrId)[0];\n if (!this._map || !added) return;\n\n added.visible = visible;\n this._map.setLayoutProperty(\n LAYER_ID_PREFIX + added.key,\n \"visibility\",\n visible ? \"visible\" : \"none\",\n );\n this._syncAddedLayersState();\n this._emit(\"statechange\");\n }\n\n /**\n * Removes the map source and layer for an instance key, if present.\n */\n private _removeMapLayer(key: string): void {\n if (!this._map) return;\n const mapId = LAYER_ID_PREFIX + key;\n if (this._map.getLayer(mapId)) {\n this._map.removeLayer(mapId);\n }\n if (this._map.getSource(mapId)) {\n this._map.removeSource(mapId);\n }\n }\n\n /**\n * Mirrors the internal added-layers map into the serializable state.\n */\n private _syncAddedLayersState(): void {\n this._state.addedLayers = this.getAddedLayers();\n }\n\n /**\n * Reconciles a desired added-layers list against the map:\n * adds missing layers, removes extras, and applies date/opacity changes.\n */\n private _reconcileAddedLayers(desired: AddedLayerState[]): void {\n const desiredKeys = new Set(desired.map((l) => l.key));\n\n for (const existingKey of Array.from(this._addedLayers.keys())) {\n if (!desiredKeys.has(existingKey)) {\n this.removeLayer(existingKey);\n }\n }\n\n for (const target of desired) {\n const existing = this._addedLayers.get(target.key);\n if (!existing) {\n this.addLayer(target.id, {\n date: target.date,\n opacity: target.opacity,\n visible: target.visible,\n key: target.key,\n });\n } else {\n if (target.date && target.date !== existing.date) {\n this.setLayerDate(target.key, target.date);\n }\n if (\n target.opacity !== undefined &&\n target.opacity !== existing.opacity\n ) {\n this.setLayerOpacity(target.key, target.opacity);\n }\n if (\n target.visible !== undefined &&\n target.visible !== existing.visible\n ) {\n this.setLayerVisibility(target.key, target.visible);\n }\n }\n }\n }\n\n /**\n * Loads the capabilities document (once) and renders the results list.\n */\n private _loadCapabilities(): void {\n if (this._capabilities || this._loading) return;\n\n this._loading = true;\n this._renderStatus(\"Loading NASA GIBS layer catalog…\");\n\n this.getCapabilities()\n .then(() => {\n this._loading = false;\n this._renderResults();\n this._renderAddedSection();\n this._refreshInsertOptions();\n this._emit(\"capabilitiesload\");\n })\n .catch((error: unknown) => {\n this._loading = false;\n const err = error instanceof Error ? error : new Error(String(error));\n this._renderStatus(\n `Failed to load layer catalog: ${err.message}`,\n true,\n );\n this._emitError(err);\n });\n }\n\n /**\n * Emits an event to all registered handlers.\n *\n * @param event - The event type to emit\n * @param extra - Extra payload fields (layer, error)\n */\n private _emit(\n event: NasaEarthdataEvent,\n extra?: Partial<NasaEarthdataEventPayload>,\n ): void {\n const handlers = this._eventHandlers.get(event);\n if (handlers) {\n const eventData: NasaEarthdataEventPayload = {\n type: event,\n state: this.getState(),\n ...extra,\n };\n handlers.forEach((handler) => handler(eventData));\n }\n }\n\n /**\n * Emits an 'error' event.\n */\n private _emitError(error: Error): void {\n this._emit(\"error\", { error });\n }\n\n /**\n * Returns the theme class for the configured theme, if explicit.\n */\n private _themeClass(): string {\n if (this._options.theme === \"dark\") return \" ne-theme-dark\";\n if (this._options.theme === \"light\") return \" ne-theme-light\";\n return \"\";\n }\n\n /**\n * Creates the main container element for the control.\n * Contains a toggle button (29x29) matching navigation control size.\n *\n * @returns The container element\n */\n private _createContainer(): HTMLElement {\n const container = document.createElement(\"div\");\n container.className = `maplibregl-ctrl maplibregl-ctrl-group plugin-control maplibre-gl-nasa-earthdata${this._themeClass()}${\n this._options.className ? ` ${this._options.className}` : \"\"\n }`;\n\n // Create toggle button (29x29 to match navigation control)\n const toggleBtn = document.createElement(\"button\");\n toggleBtn.className = \"plugin-control-toggle\";\n toggleBtn.type = \"button\";\n toggleBtn.setAttribute(\"aria-label\", this._options.title);\n // Satellite icon. A distinct, recognizable mark for NASA Earthdata's\n // satellite imagery that avoids colliding with the generic globe icon used\n // by core map functionality in host apps.\n toggleBtn.innerHTML = `\n <span class=\"plugin-control-icon\">\n <svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M13 7 9 3 5 7l4 4\"/>\n <path d=\"m17 11 4 4-4 4-4-4\"/>\n <path d=\"m8 12 4 4 6-6-4-4Z\"/>\n <path d=\"m16 8 3-3\"/>\n <path d=\"M9 21a6 6 0 0 0-6-6\"/>\n </svg>\n </span>\n `;\n toggleBtn.addEventListener(\"click\", () => this.toggle());\n\n container.appendChild(toggleBtn);\n\n return container;\n }\n\n /**\n * Creates the panel element with header, search box, and results list.\n * Panel is positioned as a dropdown below the toggle button.\n *\n * @returns The panel element\n */\n private _createPanel(): HTMLElement {\n const panel = document.createElement(\"div\");\n panel.className = `plugin-control-panel maplibre-gl-nasa-earthdata${this._themeClass()}`;\n panel.style.width = `${this._options.panelWidth}px`;\n\n // Create header with title and close button\n const header = document.createElement(\"div\");\n header.className = \"plugin-control-header\";\n\n const title = document.createElement(\"span\");\n title.className = \"plugin-control-title\";\n title.textContent = this._options.title;\n\n const closeBtn = document.createElement(\"button\");\n closeBtn.className = \"plugin-control-close\";\n closeBtn.type = \"button\";\n closeBtn.setAttribute(\"aria-label\", \"Close panel\");\n closeBtn.innerHTML = \"×\";\n closeBtn.addEventListener(\"click\", () => this.collapse());\n\n header.appendChild(title);\n header.appendChild(closeBtn);\n\n // Create content area: search input, insert-before row, category list,\n // and the added-layers management section\n const content = document.createElement(\"div\");\n content.className = \"plugin-control-content nasa-content\";\n\n const search = document.createElement(\"input\");\n search.className = \"plugin-control-input nasa-search\";\n search.type = \"search\";\n search.placeholder = \"Search layers (e.g. temperature)\";\n search.setAttribute(\"aria-label\", \"Search NASA GIBS layers\");\n search.value = this._state.query;\n const onSearch = debounce(() => {\n this._state.query = search.value;\n this._renderResults();\n this._emit(\"statechange\");\n }, 250);\n search.addEventListener(\"input\", onSearch);\n this._searchInput = search;\n\n // \"Insert before\" row: choose where new layers are inserted in the\n // map's layer stack\n const insertRow = document.createElement(\"div\");\n insertRow.className = \"nasa-insert-row\";\n\n const insertLabel = document.createElement(\"span\");\n insertLabel.className = \"nasa-insert-label\";\n insertLabel.textContent = \"Insert before\";\n\n const insertSelect = document.createElement(\"select\");\n insertSelect.className = \"nasa-insert-select\";\n insertSelect.setAttribute(\"aria-label\", \"Insert new layers before\");\n insertSelect.addEventListener(\"change\", () => {\n this._insertBefore = insertSelect.value;\n });\n this._insertSelect = insertSelect;\n\n insertRow.appendChild(insertLabel);\n insertRow.appendChild(insertSelect);\n\n const meta = document.createElement(\"div\");\n meta.className = \"nasa-meta\";\n this._metaEl = meta;\n\n // Scrollable body holding the category groups and the added layers\n const body = document.createElement(\"div\");\n body.className = \"nasa-body\";\n\n const results = document.createElement(\"div\");\n results.className = \"nasa-results\";\n this._resultsEl = results;\n\n const added = document.createElement(\"div\");\n added.className = \"nasa-added-section\";\n this._addedEl = added;\n\n body.appendChild(results);\n body.appendChild(added);\n\n content.appendChild(search);\n content.appendChild(insertRow);\n content.appendChild(meta);\n content.appendChild(body);\n\n panel.appendChild(header);\n panel.appendChild(content);\n\n // Resize handle on the panel's outer edge for adjusting the width by\n // dragging. _updatePanelPosition() moves it to the correct side for the\n // control corner (left edge when right-anchored, right edge when\n // left-anchored).\n const resizeHandle = document.createElement(\"div\");\n resizeHandle.className = \"nasa-resize-handle\";\n resizeHandle.setAttribute(\"aria-hidden\", \"true\");\n resizeHandle.addEventListener(\"pointerdown\", (e) =>\n this._startResize(e, resizeHandle),\n );\n panel.appendChild(resizeHandle);\n\n return panel;\n }\n\n /**\n * Returns the maximum panel width that fits the map container.\n */\n private _maxPanelWidth(): number {\n return Math.max(\n 240,\n (this._mapContainer?.getBoundingClientRect().width ?? window.innerWidth) -\n 20,\n );\n }\n\n /**\n * Clamps the panel width to the map container. Applied whenever the panel\n * is (re)positioned so a saved width survives map shrinking.\n */\n private _applyPanelWidthBounds(): void {\n if (!this._panel) return;\n const width = Math.round(\n clamp(this._state.panelWidth, 240, this._maxPanelWidth()),\n );\n this._state.panelWidth = width;\n this._panel.style.width = `${width}px`;\n }\n\n /**\n * Starts a panel width drag-resize. The drag direction is derived from\n * the control corner so resizing works whether the panel is anchored to\n * the left or the right edge of the map.\n */\n private _startResize(e: PointerEvent, handle: HTMLElement): void {\n if (!this._panel) return;\n e.preventDefault();\n\n const startX = e.clientX;\n const startWidth = this._panel.getBoundingClientRect().width;\n // Right-anchored panels grow leftward, so invert the pointer delta\n const anchoredRight = !this._getControlPosition().endsWith(\"left\");\n const maxWidth = this._maxPanelWidth();\n\n const onMove = (ev: PointerEvent) => {\n const delta = anchoredRight ? startX - ev.clientX : ev.clientX - startX;\n const width = Math.round(clamp(startWidth + delta, 240, maxWidth));\n this._state.panelWidth = width;\n if (this._panel) {\n this._panel.style.width = `${width}px`;\n }\n };\n const onEnd = (ev: PointerEvent) => {\n handle.removeEventListener(\"pointermove\", onMove);\n handle.removeEventListener(\"pointerup\", onEnd);\n handle.removeEventListener(\"pointercancel\", onEnd);\n if (handle.hasPointerCapture(ev.pointerId)) {\n handle.releasePointerCapture(ev.pointerId);\n }\n this._panel?.classList.remove(\"nasa-resizing\");\n this._emit(\"statechange\");\n };\n\n handle.setPointerCapture(e.pointerId);\n handle.addEventListener(\"pointermove\", onMove);\n handle.addEventListener(\"pointerup\", onEnd);\n handle.addEventListener(\"pointercancel\", onEnd);\n this._panel.classList.add(\"nasa-resizing\");\n }\n\n /**\n * Shows a status message (loading or error) in the results area.\n */\n private _renderStatus(message: string, isError = false): void {\n if (!this._resultsEl || !this._metaEl) return;\n this._metaEl.textContent = \"\";\n this._resultsEl.innerHTML = \"\";\n const status = document.createElement(\"div\");\n status.className = `nasa-status${isError ? \" nasa-status-error\" : \"\"}`;\n status.textContent = message;\n this._resultsEl.appendChild(status);\n }\n\n /**\n * Groups layers by category, merging categories with fewer layers than\n * the threshold into \"Other\". Returns entries sorted alphabetically\n * with \"Other\" last.\n */\n private _groupByCategory(layers: GibsLayer[]): [string, GibsLayer[]][] {\n const MIN_CATEGORY_SIZE = 3;\n const all = this._capabilities?.layers ?? [];\n\n // Count category sizes over the FULL catalog so a category does not\n // flip into \"Other\" while filtering\n const totals = new globalThis.Map<string, number>();\n for (const layer of all) {\n totals.set(layer.category, (totals.get(layer.category) ?? 0) + 1);\n }\n\n const groups = new globalThis.Map<string, GibsLayer[]>();\n for (const layer of layers) {\n const total = totals.get(layer.category) ?? 0;\n const key = total >= MIN_CATEGORY_SIZE ? layer.category : \"Other\";\n const group = groups.get(key);\n if (group) {\n group.push(layer);\n } else {\n groups.set(key, [layer]);\n }\n }\n\n return Array.from(groups.entries()).sort(([a], [b]) => {\n if (a === \"Other\") return 1;\n if (b === \"Other\") return -1;\n return a.localeCompare(b);\n });\n }\n\n /**\n * Renders the (filtered) layer catalog grouped by category.\n */\n private _renderResults(): void {\n if (!this._resultsEl || !this._metaEl) return;\n if (!this._capabilities) {\n if (!this._loading) {\n this._renderStatus(\"Expand the panel to load the layer catalog.\");\n }\n return;\n }\n\n const query = this._state.query.trim();\n const matches = searchLayers(this._capabilities.layers, query);\n const groups = this._groupByCategory(matches);\n\n const layerWord = matches.length === 1 ? \"layer\" : \"layers\";\n const categoryWord = groups.length === 1 ? \"category\" : \"categories\";\n this._metaEl.textContent = `${matches.length} ${layerWord} in ${groups.length} ${categoryWord}`;\n\n this._resultsEl.innerHTML = \"\";\n if (matches.length === 0) {\n const status = document.createElement(\"div\");\n status.className = \"nasa-status\";\n status.textContent = \"No layers match your search.\";\n this._resultsEl.appendChild(status);\n return;\n }\n\n for (const [category, layers] of groups) {\n // While searching, auto-expand the matching categories\n const expanded = query ? true : this._expandedCategories.has(category);\n this._resultsEl.appendChild(\n this._createCategoryGroup(category, layers, expanded),\n );\n }\n }\n\n /**\n * Creates a collapsible category group with a count badge.\n */\n private _createCategoryGroup(\n category: string,\n layers: GibsLayer[],\n expanded: boolean,\n ): HTMLElement {\n const group = document.createElement(\"div\");\n group.className = \"nasa-category\";\n\n const header = document.createElement(\"button\");\n header.type = \"button\";\n header.className = `nasa-category-header${expanded ? \" expanded\" : \"\"}`;\n header.setAttribute(\"aria-expanded\", String(expanded));\n\n const chevron = document.createElement(\"span\");\n chevron.className = \"nasa-category-chevron\";\n chevron.innerHTML = `\n <svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9 6l6 6-6 6\"/>\n </svg>\n `;\n\n const name = document.createElement(\"span\");\n name.className = \"nasa-category-name\";\n name.textContent = category;\n\n const count = document.createElement(\"span\");\n count.className = \"nasa-category-count\";\n count.textContent = String(layers.length);\n\n header.appendChild(chevron);\n header.appendChild(name);\n header.appendChild(count);\n header.addEventListener(\"click\", () => {\n if (this._expandedCategories.has(category)) {\n this._expandedCategories.delete(category);\n } else {\n this._expandedCategories.add(category);\n }\n this._renderResults();\n });\n\n group.appendChild(header);\n\n if (expanded) {\n const list = document.createElement(\"div\");\n list.className = \"nasa-category-layers\";\n list.setAttribute(\"role\", \"list\");\n\n for (const layer of layers) {\n list.appendChild(this._createLayerRow(layer));\n }\n\n group.appendChild(list);\n }\n\n return group;\n }\n\n /**\n * Creates a single catalog layer row with title, badges, and an action\n * button. Non-time layers toggle add/remove; time-enabled layers always\n * offer \"Add\" so additional instances with different dates can be added.\n * Per-layer controls live in the added-layers section.\n */\n private _createLayerRow(layer: GibsLayer): HTMLElement {\n const instanceCount = Array.from(this._addedLayers.values()).filter(\n (l) => l.id === layer.id,\n ).length;\n const added = instanceCount > 0;\n\n const row = document.createElement(\"div\");\n row.className = `nasa-layer-row${added ? \" nasa-layer-row-added\" : \"\"}`;\n row.setAttribute(\"role\", \"listitem\");\n\n const main = document.createElement(\"div\");\n main.className = \"nasa-layer-main\";\n\n const info = document.createElement(\"div\");\n info.className = \"nasa-layer-info\";\n\n const titleEl = document.createElement(\"div\");\n titleEl.className = \"nasa-layer-title\";\n titleEl.textContent = layer.title;\n titleEl.title = layer.id;\n\n const badges = document.createElement(\"div\");\n badges.className = \"nasa-layer-badges\";\n const formatBadge = document.createElement(\"span\");\n formatBadge.className = \"nasa-badge\";\n formatBadge.textContent = layer.format;\n badges.appendChild(formatBadge);\n if (layer.time) {\n const timeBadge = document.createElement(\"span\");\n timeBadge.className = \"nasa-badge nasa-badge-time\";\n timeBadge.textContent = \"time\";\n timeBadge.title = `Default date: ${layer.time.default}`;\n badges.appendChild(timeBadge);\n }\n if (instanceCount > 1) {\n const countBadge = document.createElement(\"span\");\n countBadge.className = \"nasa-badge nasa-badge-time\";\n countBadge.textContent = `${instanceCount} added`;\n badges.appendChild(countBadge);\n }\n\n info.appendChild(titleEl);\n info.appendChild(badges);\n\n // Time-enabled layers can be added repeatedly (one instance per date),\n // so their button always reads \"Add\"\n const isRemove = added && !layer.time;\n const actionBtn = document.createElement(\"button\");\n actionBtn.type = \"button\";\n actionBtn.className = `nasa-layer-action${isRemove ? \" nasa-layer-action-remove\" : \"\"}`;\n actionBtn.textContent = isRemove ? \"Remove\" : \"Add\";\n actionBtn.setAttribute(\n \"aria-label\",\n isRemove\n ? `Remove layer ${layer.title}`\n : added\n ? `Add layer ${layer.title} for another date`\n : `Add layer ${layer.title}`,\n );\n actionBtn.addEventListener(\"click\", () => {\n if (!layer.time && this._resolveInstances(layer.id).length > 0) {\n this.removeLayer(layer.id);\n } else {\n this.addLayer(layer.id);\n }\n });\n\n main.appendChild(info);\n main.appendChild(actionBtn);\n row.appendChild(main);\n\n return row;\n }\n\n /**\n * Renders the \"Added layers\" management section: visibility checkbox,\n * legend toggle, remove button, opacity slider, and date picker.\n */\n private _renderAddedSection(): void {\n if (!this._addedEl) return;\n\n this._addedEl.innerHTML = \"\";\n if (this._addedLayers.size === 0) return;\n\n const heading = document.createElement(\"div\");\n heading.className = \"nasa-added-heading\";\n heading.textContent = \"Added layers\";\n this._addedEl.appendChild(heading);\n\n for (const added of this._addedLayers.values()) {\n const layer = this._client.getLayer(added.id);\n if (layer) {\n this._addedEl.appendChild(this._createAddedRow(layer, added));\n }\n }\n }\n\n /**\n * Creates a management card for one added layer.\n */\n private _createAddedRow(\n layer: GibsLayer,\n added: AddedLayerState,\n ): HTMLElement {\n const card = document.createElement(\"div\");\n card.className = \"nasa-added-card\";\n\n // Header row: visibility checkbox, title, legend toggle, remove\n const head = document.createElement(\"div\");\n head.className = \"nasa-added-head\";\n\n const visLabel = document.createElement(\"label\");\n visLabel.className = \"nasa-added-vis\";\n visLabel.title = \"Toggle layer visibility\";\n\n const visCheckbox = document.createElement(\"input\");\n visCheckbox.type = \"checkbox\";\n visCheckbox.className = \"nasa-added-checkbox\";\n visCheckbox.checked = added.visible;\n visCheckbox.setAttribute(\n \"aria-label\",\n `Toggle visibility of ${layer.title}`,\n );\n visCheckbox.addEventListener(\"change\", () => {\n this.setLayerVisibility(added.key, visCheckbox.checked);\n });\n\n const titleEl = document.createElement(\"span\");\n titleEl.className = \"nasa-added-title\";\n titleEl.textContent = layer.title;\n titleEl.title = added.key;\n\n visLabel.appendChild(visCheckbox);\n visLabel.appendChild(titleEl);\n\n // Date chip distinguishes multiple instances of the same time layer\n let dateChip: HTMLElement | undefined;\n if (layer.time && added.date) {\n dateChip = document.createElement(\"span\");\n dateChip.className = \"nasa-badge nasa-badge-time\";\n dateChip.textContent = added.date.slice(0, 10);\n visLabel.appendChild(dateChip);\n }\n\n const actions = document.createElement(\"div\");\n actions.className = \"nasa-added-actions\";\n\n if (layer.legendUrl) {\n const legendBtn = document.createElement(\"button\");\n legendBtn.type = \"button\";\n legendBtn.className = `nasa-icon-button${\n this._openLegends.has(added.key) ? \" active\" : \"\"\n }`;\n legendBtn.title = \"Toggle legend\";\n legendBtn.setAttribute(\"aria-label\", `Toggle legend for ${layer.title}`);\n legendBtn.innerHTML = `\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M8 6h13M8 12h13M8 18h13\"/>\n <rect x=\"3\" y=\"4.5\" width=\"3\" height=\"3\" rx=\"0.5\"/>\n <rect x=\"3\" y=\"10.5\" width=\"3\" height=\"3\" rx=\"0.5\"/>\n <rect x=\"3\" y=\"16.5\" width=\"3\" height=\"3\" rx=\"0.5\"/>\n </svg>\n `;\n legendBtn.addEventListener(\"click\", () => {\n if (this._openLegends.has(added.key)) {\n this._openLegends.delete(added.key);\n } else {\n this._openLegends.add(added.key);\n }\n this._renderAddedSection();\n });\n actions.appendChild(legendBtn);\n }\n\n const removeBtn = document.createElement(\"button\");\n removeBtn.type = \"button\";\n removeBtn.className = \"nasa-icon-button nasa-icon-button-danger\";\n removeBtn.title = \"Remove layer\";\n removeBtn.setAttribute(\"aria-label\", `Remove layer ${layer.title}`);\n removeBtn.innerHTML = `\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\"/>\n <path d=\"M10 11v6M14 11v6\"/>\n </svg>\n `;\n removeBtn.addEventListener(\"click\", () => {\n this.removeLayer(added.key);\n });\n actions.appendChild(removeBtn);\n\n head.appendChild(visLabel);\n head.appendChild(actions);\n card.appendChild(head);\n\n // Opacity slider with live percentage readout\n if (this._options.showOpacity) {\n const opacityRow = document.createElement(\"label\");\n opacityRow.className = \"nasa-control-label\";\n opacityRow.textContent = \"Opacity\";\n\n const opacityInput = document.createElement(\"input\");\n opacityInput.type = \"range\";\n opacityInput.className = \"nasa-opacity\";\n opacityInput.min = \"0\";\n opacityInput.max = \"1\";\n opacityInput.step = \"0.05\";\n opacityInput.value = String(added.opacity);\n opacityInput.setAttribute(\"aria-label\", `Opacity for ${layer.title}`);\n\n const opacityValue = document.createElement(\"span\");\n opacityValue.className = \"nasa-opacity-value\";\n opacityValue.textContent = `${Math.round(added.opacity * 100)}%`;\n\n opacityInput.addEventListener(\"input\", () => {\n const value = Number(opacityInput.value);\n this.setLayerOpacity(added.key, value);\n opacityValue.textContent = `${Math.round(value * 100)}%`;\n });\n\n opacityRow.appendChild(opacityInput);\n opacityRow.appendChild(opacityValue);\n card.appendChild(opacityRow);\n }\n\n // Date picker for time-enabled layers\n if (layer.time) {\n const dateLabel = document.createElement(\"label\");\n dateLabel.className = \"nasa-control-label\";\n dateLabel.textContent = \"Date\";\n\n const dateInput = document.createElement(\"input\");\n dateInput.type = \"date\";\n dateInput.className = \"nasa-date\";\n dateInput.value = (added.date ?? layer.time.default).slice(0, 10);\n const range = this._timeRange(layer);\n if (range.min) dateInput.min = range.min;\n if (range.max) dateInput.max = range.max;\n dateInput.addEventListener(\"change\", () => {\n if (dateInput.value) {\n this.setLayerDate(added.key, dateInput.value);\n if (dateChip) {\n dateChip.textContent = dateInput.value;\n }\n }\n });\n\n dateLabel.appendChild(dateInput);\n card.appendChild(dateLabel);\n }\n\n // Legend image (GIBS horizontal legend SVG)\n if (layer.legendUrl && this._openLegends.has(added.key)) {\n const legend = document.createElement(\"img\");\n legend.className = \"nasa-legend-image\";\n legend.src = layer.legendUrl;\n legend.alt = `Legend for ${layer.title}`;\n legend.loading = \"lazy\";\n card.appendChild(legend);\n }\n\n return card;\n }\n\n /**\n * Refreshes the \"Insert before\" dropdown with the map's current layers.\n */\n private _refreshInsertOptions(): void {\n if (!this._insertSelect || !this._map) return;\n\n const layers = this._map.getStyle()?.layers ?? [];\n const select = this._insertSelect;\n select.innerHTML = \"\";\n\n const topOption = document.createElement(\"option\");\n topOption.value = \"\";\n topOption.textContent = \"Top of map\";\n select.appendChild(topOption);\n\n for (const layer of layers) {\n const option = document.createElement(\"option\");\n option.value = layer.id;\n option.textContent = layer.id;\n select.appendChild(option);\n }\n\n // Restore the previous selection if the layer still exists\n if (this._insertBefore && layers.some((l) => l.id === this._insertBefore)) {\n select.value = this._insertBefore;\n } else {\n this._insertBefore = \"\";\n select.value = \"\";\n }\n }\n\n /**\n * Derives the min/max selectable dates from a layer's time domain values.\n */\n private _timeRange(layer: GibsLayer): { min?: string; max?: string } {\n const time = layer.time;\n if (!time) return {};\n\n let min: string | undefined;\n let max: string | undefined;\n for (const value of time.values) {\n const [start, end] = value.split(\"/\");\n const startDate = start?.slice(0, 10);\n const endDate = (end ?? start)?.slice(0, 10);\n if (startDate && (!min || startDate < min)) min = startDate;\n if (endDate && (!max || endDate > max)) max = endDate;\n }\n const defaultDate = time.default.slice(0, 10);\n if (!max || defaultDate > max) max = defaultDate;\n return { min, max };\n }\n\n /**\n * Setup event listeners for panel positioning and click-outside behavior.\n */\n private _setupEventListeners(): void {\n // Click outside to close (check both container and panel since they're now separate).\n // Ignore targets that are no longer connected to the DOM: clicking an\n // Add/Remove button re-renders the results list, detaching the button\n // before this document-level handler runs.\n this._clickOutsideHandler = (e: MouseEvent) => {\n const target = e.target as Node;\n if (\n this._container &&\n this._panel &&\n target.isConnected &&\n !this._container.contains(target) &&\n !this._panel.contains(target)\n ) {\n this.collapse();\n }\n };\n document.addEventListener(\"click\", this._clickOutsideHandler);\n\n // Update panel position on window resize\n this._resizeHandler = () => {\n if (!this._state.collapsed) {\n this._updatePanelPosition();\n }\n };\n window.addEventListener(\"resize\", this._resizeHandler);\n\n // Update panel position on map resize (e.g., sidebar toggle)\n this._mapResizeHandler = () => {\n if (!this._state.collapsed) {\n this._updatePanelPosition();\n }\n };\n this._map?.on(\"resize\", this._mapResizeHandler);\n }\n\n /**\n * Detect which corner the control is positioned in.\n *\n * @returns The position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n */\n private _getControlPosition():\n | \"top-left\"\n | \"top-right\"\n | \"bottom-left\"\n | \"bottom-right\" {\n const parent = this._container?.parentElement;\n if (!parent) return \"top-right\"; // Default\n\n if (parent.classList.contains(\"maplibregl-ctrl-top-left\"))\n return \"top-left\";\n if (parent.classList.contains(\"maplibregl-ctrl-top-right\"))\n return \"top-right\";\n if (parent.classList.contains(\"maplibregl-ctrl-bottom-left\"))\n return \"bottom-left\";\n if (parent.classList.contains(\"maplibregl-ctrl-bottom-right\"))\n return \"bottom-right\";\n\n return \"top-right\"; // Default\n }\n\n /**\n * Update the panel position based on button location and control corner.\n * Positions the panel next to the button, expanding in the appropriate direction.\n */\n private _updatePanelPosition(): void {\n if (!this._container || !this._panel || !this._mapContainer) return;\n\n // Get the toggle button (first child of container)\n const button = this._container.querySelector(\".plugin-control-toggle\");\n if (!button) return;\n\n // Keep the panel within the map even if it shrank since the width was set\n this._applyPanelWidthBounds();\n\n const buttonRect = button.getBoundingClientRect();\n const mapRect = this._mapContainer.getBoundingClientRect();\n const position = this._getControlPosition();\n\n // Calculate button position relative to map container\n const buttonTop = buttonRect.top - mapRect.top;\n const buttonBottom = mapRect.bottom - buttonRect.bottom;\n const buttonLeft = buttonRect.left - mapRect.left;\n const buttonRight = mapRect.right - buttonRect.right;\n\n const panelGap = 5; // Gap between button and panel\n\n // Reset all positioning\n this._panel.style.top = \"\";\n this._panel.style.bottom = \"\";\n this._panel.style.left = \"\";\n this._panel.style.right = \"\";\n\n // Keep the resize handle on the panel's outer (growing) edge:\n // right edge for left-anchored panels, left edge for right-anchored ones\n this._panel\n .querySelector(\".nasa-resize-handle\")\n ?.classList.toggle(\"nasa-resize-handle-right\", position.endsWith(\"left\"));\n\n switch (position) {\n case \"top-left\":\n // Panel expands down and to the right\n this._panel.style.top = `${buttonTop + buttonRect.height + panelGap}px`;\n this._panel.style.left = `${buttonLeft}px`;\n break;\n\n case \"top-right\":\n // Panel expands down and to the left\n this._panel.style.top = `${buttonTop + buttonRect.height + panelGap}px`;\n this._panel.style.right = `${buttonRight}px`;\n break;\n\n case \"bottom-left\":\n // Panel expands up and to the right\n this._panel.style.bottom = `${buttonBottom + buttonRect.height + panelGap}px`;\n this._panel.style.left = `${buttonLeft}px`;\n break;\n\n case \"bottom-right\":\n // Panel expands up and to the left\n this._panel.style.bottom = `${buttonBottom + buttonRect.height + panelGap}px`;\n this._panel.style.right = `${buttonRight}px`;\n break;\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_NasaEarthdataControl = require("./NasaEarthdataControl-
|
|
2
|
+
const require_NasaEarthdataControl = require("./NasaEarthdataControl-DahZNxUI.cjs");
|
|
3
3
|
exports.DEFAULT_CAPABILITIES_URL = require_NasaEarthdataControl.DEFAULT_CAPABILITIES_URL;
|
|
4
4
|
exports.GibsClient = require_NasaEarthdataControl.GibsClient;
|
|
5
5
|
exports.NasaEarthdataControl = require_NasaEarthdataControl.NasaEarthdataControl;
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as formatNumericValue, c as buildTileUrl, d as searchLayers, f as parseCapabilities, i as debounce, l as DEFAULT_CAPABILITIES_URL, n as clamp, o as generateId, r as classNames, s as throttle, t as NasaEarthdataControl, u as GibsClient } from "./NasaEarthdataControl-
|
|
1
|
+
import { a as formatNumericValue, c as buildTileUrl, d as searchLayers, f as parseCapabilities, i as debounce, l as DEFAULT_CAPABILITIES_URL, n as clamp, o as generateId, r as classNames, s as throttle, t as NasaEarthdataControl, u as GibsClient } from "./NasaEarthdataControl-CMD7orkJ.js";
|
|
2
2
|
export { DEFAULT_CAPABILITIES_URL, GibsClient, NasaEarthdataControl, buildTileUrl, clamp, classNames, debounce, formatNumericValue, generateId, parseCapabilities, searchLayers, throttle };
|
package/dist/react.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_NasaEarthdataControl = require("./NasaEarthdataControl-
|
|
2
|
+
const require_NasaEarthdataControl = require("./NasaEarthdataControl-DahZNxUI.cjs");
|
|
3
3
|
let react = require("react");
|
|
4
4
|
//#region src/lib/core/NasaEarthdataControlReact.tsx
|
|
5
5
|
/**
|
package/dist/react.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as NasaEarthdataControl } from "./NasaEarthdataControl-
|
|
1
|
+
import { t as NasaEarthdataControl } from "./NasaEarthdataControl-CMD7orkJ.js";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
//#region src/lib/core/NasaEarthdataControlReact.tsx
|
|
4
4
|
/**
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"NasaEarthdataControl-6FgeYQNn.cjs","names":[],"sources":["../src/lib/gibs/parseCapabilities.ts","../src/lib/gibs/search.ts","../src/lib/gibs/GibsClient.ts","../src/lib/gibs/tileUrl.ts","../src/lib/utils/helpers.ts","../src/lib/core/NasaEarthdataControl.ts"],"sourcesContent":["import type {\n GibsCapabilities,\n GibsLayer,\n GibsLayerFormat,\n GibsTimeDimension,\n ParseOptions,\n} from \"./types\";\n\nconst FORMAT_MAP: Record<string, GibsLayerFormat> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpeg\",\n \"application/vnd.mapbox-vector-tile\": \"mvt\",\n};\n\nconst MIME_MAP: Record<GibsLayerFormat, string> = {\n png: \"image/png\",\n jpeg: \"image/jpeg\",\n mvt: \"application/vnd.mapbox-vector-tile\",\n};\n\n/**\n * Returns the direct child elements of a node matching a local tag name,\n * ignoring XML namespaces.\n */\nfunction childrenByLocalName(parent: Element, localName: string): Element[] {\n const result: Element[] = [];\n for (let i = 0; i < parent.children.length; i++) {\n const child = parent.children[i];\n if (child.localName === localName) {\n result.push(child);\n }\n }\n return result;\n}\n\n/**\n * Returns the text content of the first direct child element matching a local tag name.\n */\nfunction childText(parent: Element, localName: string): string | undefined {\n const child = childrenByLocalName(parent, localName)[0];\n return child?.textContent?.trim() || undefined;\n}\n\n/**\n * Parses the WGS84 bounding box of a layer element.\n */\nfunction parseBbox(\n layerEl: Element,\n): [number, number, number, number] | undefined {\n const bboxEl = childrenByLocalName(layerEl, \"WGS84BoundingBox\")[0];\n if (!bboxEl) return undefined;\n\n const lower = childText(bboxEl, \"LowerCorner\")?.split(/\\s+/).map(Number);\n const upper = childText(bboxEl, \"UpperCorner\")?.split(/\\s+/).map(Number);\n if (!lower || !upper || lower.length < 2 || upper.length < 2)\n return undefined;\n if ([...lower, ...upper].some((n) => Number.isNaN(n))) return undefined;\n\n return [lower[0], lower[1], upper[0], upper[1]];\n}\n\n/**\n * Parses the Time dimension of a layer element, if present.\n */\nfunction parseTimeDimension(layerEl: Element): GibsTimeDimension | undefined {\n const dimensions = childrenByLocalName(layerEl, \"Dimension\");\n for (const dim of dimensions) {\n if (childText(dim, \"Identifier\") !== \"Time\") continue;\n\n const defaultValue = childText(dim, \"Default\");\n if (!defaultValue) return undefined;\n\n const values = childrenByLocalName(dim, \"Value\")\n .map((v) => v.textContent?.trim())\n .filter((v): v is string => Boolean(v));\n\n return { default: defaultValue, values };\n }\n return undefined;\n}\n\n/**\n * Parses the first legend URL advertised in a layer's default style.\n */\nfunction parseLegendUrl(layerEl: Element): string | undefined {\n const styles = childrenByLocalName(layerEl, \"Style\");\n for (const style of styles) {\n const legend = childrenByLocalName(style, \"LegendURL\")[0];\n const href =\n legend?.getAttribute(\"xlink:href\") ??\n legend?.getAttributeNS(null, \"href\");\n if (href) return href;\n }\n return undefined;\n}\n\n/**\n * Parses a single WMTS Layer element into a GibsLayer.\n * Returns undefined for layers that cannot be represented (missing\n * identifier, no tile resource template, or unsupported format).\n */\nfunction parseLayer(\n layerEl: Element,\n includeVector: boolean,\n): GibsLayer | undefined {\n const id = childText(layerEl, \"Identifier\");\n if (!id) return undefined;\n\n const title = childText(layerEl, \"Title\") ?? id;\n\n // Collect supported formats; prefer png > jpeg > mvt.\n const formats = childrenByLocalName(layerEl, \"Format\")\n .map((f) => FORMAT_MAP[f.textContent?.trim() ?? \"\"])\n .filter((f): f is GibsLayerFormat => Boolean(f));\n const format = ([\"png\", \"jpeg\", \"mvt\"] as const).find((f) =>\n formats.includes(f),\n );\n if (!format) return undefined;\n if (format === \"mvt\" && !includeVector) return undefined;\n\n // Tile matrix set link, e.g. \"GoogleMapsCompatible_Level9\" -> maxZoom 9.\n const tmsLink = childrenByLocalName(layerEl, \"TileMatrixSetLink\")[0];\n const tileMatrixSet = tmsLink\n ? childText(tmsLink, \"TileMatrixSet\")\n : undefined;\n if (!tileMatrixSet) return undefined;\n const levelMatch = /Level(\\d+)$/.exec(tileMatrixSet);\n const maxZoom = levelMatch ? parseInt(levelMatch[1], 10) : 9;\n\n // Tile resource template. Only consider templates whose advertised format\n // matches the chosen layer format, then prefer the template containing\n // {Time} when the layer is time-enabled so dates can be substituted.\n const resourceUrls = childrenByLocalName(layerEl, \"ResourceURL\").filter(\n (r) => r.getAttribute(\"resourceType\") === \"tile\",\n );\n if (resourceUrls.length === 0) return undefined;\n\n const selectedMime = MIME_MAP[format];\n const formatMatched = resourceUrls.filter((r) => {\n const mime = r.getAttribute(\"format\")?.trim().toLowerCase();\n return !mime || mime === selectedMime;\n });\n\n const templates = (formatMatched.length > 0 ? formatMatched : resourceUrls)\n .map((r) => r.getAttribute(\"template\"))\n .filter((t): t is string => Boolean(t));\n if (templates.length === 0) return undefined;\n\n const time = parseTimeDimension(layerEl);\n const resourceTemplate =\n (time ? templates.find((t) => t.includes(\"{Time}\")) : undefined) ??\n templates.find((t) => !t.includes(\"{Time}\")) ??\n templates[0];\n\n const extMatch = /\\.([a-z0-9]+)$/i.exec(resourceTemplate);\n const fileExtension = extMatch\n ? extMatch[1]\n : format === \"jpeg\"\n ? \"jpg\"\n : format;\n\n return {\n id,\n title,\n // Platform/instrument prefix of the identifier, used for grouping\n category: id.split(\"_\")[0],\n format,\n fileExtension,\n tileMatrixSet,\n maxZoom,\n bbox: parseBbox(layerEl),\n resourceTemplate,\n time,\n legendUrl: parseLegendUrl(layerEl),\n };\n}\n\n/**\n * Parses a WMTS GetCapabilities XML document into a GibsCapabilities object.\n *\n * Layers without a usable tile resource template are skipped. Vector-tile\n * (MVT) layers are skipped unless `options.includeVector` is true.\n *\n * @param xml - The raw WMTSCapabilities.xml document text\n * @param options - Parse options\n * @returns Parsed capabilities with layers sorted by title\n */\nexport function parseCapabilities(\n xml: string,\n options?: ParseOptions,\n): GibsCapabilities {\n const includeVector = options?.includeVector ?? false;\n\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const parserError = doc.getElementsByTagName(\"parsererror\")[0];\n if (parserError) {\n throw new Error(\n `Failed to parse capabilities XML: ${parserError.textContent ?? \"unknown error\"}`,\n );\n }\n\n const root = doc.documentElement;\n const contents = childrenByLocalName(root, \"Contents\")[0];\n if (!contents) {\n throw new Error(\"Invalid capabilities document: missing Contents element\");\n }\n\n const layers: GibsLayer[] = [];\n for (const layerEl of childrenByLocalName(contents, \"Layer\")) {\n const layer = parseLayer(layerEl, includeVector);\n if (layer) layers.push(layer);\n }\n\n layers.sort((a, b) => a.title.localeCompare(b.title));\n\n return { layers, fetchedAt: Date.now() };\n}\n","import type { GibsLayer } from \"./types\";\n\n/**\n * Filters GIBS layers by a free-text query.\n *\n * Performs a case-insensitive substring match against the layer title and\n * identifier. An empty or whitespace-only query returns all layers.\n *\n * @param layers - The layers to search\n * @param query - The search query\n * @returns Layers whose title or identifier contains the query\n */\nexport function searchLayers(layers: GibsLayer[], query: string): GibsLayer[] {\n const q = query.trim().toLowerCase();\n if (!q) return layers;\n\n return layers.filter(\n (layer) =>\n layer.title.toLowerCase().includes(q) ||\n layer.id.toLowerCase().includes(q),\n );\n}\n","import { parseCapabilities } from \"./parseCapabilities\";\nimport { searchLayers } from \"./search\";\nimport type { GibsCapabilities, GibsLayer } from \"./types\";\n\n/**\n * Default URL of the NASA GIBS WMTS capabilities document (EPSG:3857, \"best\" imagery).\n */\nexport const DEFAULT_CAPABILITIES_URL =\n \"https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/1.0.0/WMTSCapabilities.xml\";\n\n/**\n * Options for creating a GibsClient.\n */\nexport interface GibsClientOptions {\n /**\n * URL of the WMTS capabilities document.\n * @default DEFAULT_CAPABILITIES_URL\n */\n url?: string;\n\n /**\n * Whether to include vector-tile (MVT) layers.\n * @default false\n */\n includeVector?: boolean;\n}\n\n/**\n * Fetches, parses, and caches the NASA GIBS WMTS capabilities document.\n *\n * Concurrent calls to {@link GibsClient.getCapabilities} are deduplicated:\n * the document is fetched and parsed at most once unless `force` is passed.\n *\n * @example\n * ```typescript\n * const client = new GibsClient();\n * const { layers } = await client.getCapabilities();\n * const matches = client.search('temperature');\n * ```\n */\nexport class GibsClient {\n private _url: string;\n private _includeVector: boolean;\n private _capabilities?: GibsCapabilities;\n private _pending?: Promise<GibsCapabilities>;\n\n /**\n * Creates a new GibsClient.\n *\n * @param options - Client options\n */\n constructor(options?: GibsClientOptions) {\n this._url = options?.url ?? DEFAULT_CAPABILITIES_URL;\n this._includeVector = options?.includeVector ?? false;\n }\n\n /**\n * Fetches and parses the capabilities document, caching the result.\n *\n * @param force - If true, refetch even if a cached result exists\n * @returns The parsed capabilities\n */\n async getCapabilities(force = false): Promise<GibsCapabilities> {\n if (this._capabilities && !force) return this._capabilities;\n if (this._pending && !force) return this._pending;\n\n this._pending = (async () => {\n const response = await fetch(this._url);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch capabilities: ${response.status} ${response.statusText}`,\n );\n }\n const xml = await response.text();\n const capabilities = parseCapabilities(xml, {\n includeVector: this._includeVector,\n });\n this._capabilities = capabilities;\n return capabilities;\n })();\n\n try {\n return await this._pending;\n } finally {\n this._pending = undefined;\n }\n }\n\n /**\n * Returns the cached capabilities, if already loaded.\n */\n getCachedCapabilities(): GibsCapabilities | undefined {\n return this._capabilities;\n }\n\n /**\n * Searches the cached layers by title or identifier.\n * Returns an empty array if capabilities have not been loaded yet.\n *\n * @param query - The search query\n */\n search(query: string): GibsLayer[] {\n return searchLayers(this._capabilities?.layers ?? [], query);\n }\n\n /**\n * Looks up a cached layer by identifier.\n *\n * @param id - The layer identifier\n */\n getLayer(id: string): GibsLayer | undefined {\n return this._capabilities?.layers.find((layer) => layer.id === id);\n }\n}\n","import type { GibsLayer } from \"./types\";\n\n/**\n * Builds a MapLibre-compatible XYZ tile URL template from a GIBS layer.\n *\n * Substitutes {Time} with the given date (or the layer's default date),\n * {TileMatrixSet} with the layer's tile matrix set, and maps the WMTS\n * placeholders {TileMatrix}/{TileRow}/{TileCol} to {z}/{y}/{x}.\n *\n * @param layer - The GIBS layer to build a URL for\n * @param time - Optional ISO 8601 date overriding the layer's default date\n * @returns A tile URL template usable in a MapLibre raster source\n */\nexport function buildTileUrl(layer: GibsLayer, time?: string): string {\n return layer.resourceTemplate\n .replace(\"{Time}\", time ?? layer.time?.default ?? \"default\")\n .replace(\"{TileMatrixSet}\", layer.tileMatrixSet)\n .replace(\"{TileMatrix}\", \"{z}\")\n .replace(\"{TileRow}\", \"{y}\")\n .replace(\"{TileCol}\", \"{x}\");\n}\n","/**\n * Clamps a value between a minimum and maximum.\n *\n * @param value - The value to clamp\n * @param min - The minimum allowed value\n * @param max - The maximum allowed value\n * @returns The clamped value\n *\n * @example\n * ```typescript\n * clamp(5, 0, 10); // returns 5\n * clamp(-5, 0, 10); // returns 0\n * clamp(15, 0, 10); // returns 10\n * ```\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Formats a numeric value with appropriate decimal places based on step size.\n *\n * @param value - The value to format\n * @param step - The step size to determine decimal places\n * @returns The formatted value as a string\n *\n * @example\n * ```typescript\n * formatNumericValue(5, 1); // returns \"5\"\n * formatNumericValue(0.5, 0.1); // returns \"0.5\"\n * formatNumericValue(0.55, 0.01); // returns \"0.55\"\n * ```\n */\nexport function formatNumericValue(value: number, step: number): string {\n if (step === 0) return value.toString();\n const decimals = Math.max(0, -Math.floor(Math.log10(step)));\n return value.toFixed(decimals);\n}\n\n/**\n * Generates a unique ID string.\n *\n * @param prefix - Optional prefix for the ID\n * @returns A unique ID string\n *\n * @example\n * ```typescript\n * generateId('control'); // returns \"control-abc123\"\n * generateId(); // returns \"abc123\"\n * ```\n */\nexport function generateId(prefix?: string): string {\n const id = Math.random().toString(36).substring(2, 9);\n return prefix ? `${prefix}-${id}` : id;\n}\n\n/**\n * Debounces a function call.\n *\n * @param fn - The function to debounce\n * @param delay - The delay in milliseconds\n * @returns A debounced version of the function\n *\n * @example\n * ```typescript\n * const debouncedUpdate = debounce(() => updateMap(), 100);\n * window.addEventListener('resize', debouncedUpdate);\n * ```\n */\nexport function debounce<T extends (...args: unknown[]) => void>(\n fn: T,\n delay: number,\n): (...args: Parameters<T>) => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n return (...args: Parameters<T>) => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n timeoutId = setTimeout(() => {\n fn(...args);\n timeoutId = null;\n }, delay);\n };\n}\n\n/**\n * Throttles a function call.\n *\n * @param fn - The function to throttle\n * @param limit - The minimum time between calls in milliseconds\n * @returns A throttled version of the function\n *\n * @example\n * ```typescript\n * const throttledScroll = throttle(() => handleScroll(), 100);\n * window.addEventListener('scroll', throttledScroll);\n * ```\n */\nexport function throttle<T extends (...args: unknown[]) => void>(\n fn: T,\n limit: number,\n): (...args: Parameters<T>) => void {\n let inThrottle = false;\n\n return (...args: Parameters<T>) => {\n if (!inThrottle) {\n fn(...args);\n inThrottle = true;\n setTimeout(() => {\n inThrottle = false;\n }, limit);\n }\n };\n}\n\n/**\n * Creates a CSS class string from an object of class names.\n *\n * @param classes - Object with class names as keys and boolean values\n * @returns A space-separated string of class names\n *\n * @example\n * ```typescript\n * classNames({ active: true, disabled: false, visible: true });\n * // returns \"active visible\"\n * ```\n */\nexport function classNames(classes: Record<string, boolean>): string {\n return Object.entries(classes)\n .filter(([, value]) => value)\n .map(([key]) => key)\n .join(\" \");\n}\n","import type {\n IControl,\n Map as MapLibreMap,\n RasterTileSource,\n} from \"maplibre-gl\";\nimport { GibsClient, DEFAULT_CAPABILITIES_URL } from \"../gibs/GibsClient\";\nimport { buildTileUrl } from \"../gibs/tileUrl\";\nimport { searchLayers } from \"../gibs/search\";\nimport type { GibsCapabilities, GibsLayer } from \"../gibs/types\";\nimport { clamp, debounce } from \"../utils\";\nimport type {\n NasaEarthdataControlOptions,\n NasaEarthdataState,\n NasaEarthdataEvent,\n NasaEarthdataEventHandler,\n NasaEarthdataEventPayload,\n AddedLayerState,\n} from \"./types\";\n\n/**\n * Default options for the NasaEarthdataControl\n */\nconst DEFAULT_OPTIONS: Required<NasaEarthdataControlOptions> = {\n collapsed: true,\n position: \"top-right\",\n title: \"NASA Earthdata\",\n panelWidth: 320,\n className: \"\",\n capabilitiesUrl: DEFAULT_CAPABILITIES_URL,\n includeVector: false,\n showOpacity: true,\n attribution:\n '<a href=\"https://earthdata.nasa.gov/gibs\" target=\"_blank\">NASA EOSDIS GIBS</a>',\n theme: \"auto\",\n};\n\n/**\n * Prefix used for source and layer ids added to the map\n */\nconst LAYER_ID_PREFIX = \"nasa-gibs-\";\n\n/**\n * Event handlers map type\n */\ntype EventHandlersMap = globalThis.Map<\n NasaEarthdataEvent,\n Set<NasaEarthdataEventHandler>\n>;\n\n/**\n * A MapLibre GL control for searching and adding NASA GIBS (Global Imagery\n * Browse Services) WMTS layers to the map.\n *\n * The control renders a collapsible button. When expanded, it fetches the\n * GIBS capabilities document, lets the user search the layer catalog, and\n * add/remove raster layers with per-layer date and opacity controls.\n *\n * @example\n * ```typescript\n * const control = new NasaEarthdataControl({\n * title: 'NASA Earthdata',\n * collapsed: false,\n * });\n * map.addControl(control, 'top-right');\n * control.on('layeradd', (e) => console.log('Added', e.layer?.id));\n * ```\n */\nexport class NasaEarthdataControl implements IControl {\n private _map?: MapLibreMap;\n private _mapContainer?: HTMLElement;\n private _container?: HTMLElement;\n private _panel?: HTMLElement;\n private _options: Required<NasaEarthdataControlOptions>;\n private _state: NasaEarthdataState;\n private _eventHandlers: EventHandlersMap = new globalThis.Map();\n\n private _client: GibsClient;\n private _capabilities?: GibsCapabilities;\n private _loading = false;\n private _addedLayers: globalThis.Map<string, AddedLayerState> =\n new globalThis.Map();\n\n // Panel content elements\n private _searchInput?: HTMLInputElement;\n private _metaEl?: HTMLElement;\n private _resultsEl?: HTMLElement;\n private _addedEl?: HTMLElement;\n private _insertSelect?: HTMLSelectElement;\n\n // UI state: expanded category groups, open legends, insertion position\n private _expandedCategories = new Set<string>();\n private _openLegends = new Set<string>();\n private _insertBefore = \"\";\n\n // Panel positioning handlers\n private _resizeHandler: (() => void) | null = null;\n private _mapResizeHandler: (() => void) | null = null;\n private _clickOutsideHandler: ((e: MouseEvent) => void) | null = null;\n\n /**\n * Creates a new NasaEarthdataControl instance.\n *\n * @param options - Configuration options for the control\n */\n constructor(options?: Partial<NasaEarthdataControlOptions>) {\n this._options = { ...DEFAULT_OPTIONS, ...options };\n this._state = {\n collapsed: this._options.collapsed,\n panelWidth: this._options.panelWidth,\n query: \"\",\n addedLayers: [],\n };\n this._client = new GibsClient({\n url: this._options.capabilitiesUrl,\n includeVector: this._options.includeVector,\n });\n }\n\n /**\n * Called when the control is added to the map.\n * Implements the IControl interface.\n *\n * @param map - The MapLibre GL map instance\n * @returns The control's container element\n */\n onAdd(map: MapLibreMap): HTMLElement {\n this._map = map;\n this._mapContainer = map.getContainer();\n this._container = this._createContainer();\n this._panel = this._createPanel();\n\n // Append panel to map container for independent positioning (avoids overlap with other controls)\n this._mapContainer.appendChild(this._panel);\n\n // Setup event listeners for panel positioning and click-outside\n this._setupEventListeners();\n\n // Set initial panel state\n if (!this._state.collapsed) {\n this._panel.classList.add(\"expanded\");\n this._loadCapabilities();\n // Update position after control is added to DOM\n requestAnimationFrame(() => {\n this._updatePanelPosition();\n });\n }\n\n return this._container;\n }\n\n /**\n * Called when the control is removed from the map.\n * Implements the IControl interface.\n */\n onRemove(): void {\n // Remove all added GIBS layers and sources from the map\n for (const layerId of Array.from(this._addedLayers.keys())) {\n this._removeMapLayer(layerId);\n }\n this._addedLayers.clear();\n this._state.addedLayers = [];\n\n // Remove event listeners\n if (this._resizeHandler) {\n window.removeEventListener(\"resize\", this._resizeHandler);\n this._resizeHandler = null;\n }\n if (this._mapResizeHandler && this._map) {\n this._map.off(\"resize\", this._mapResizeHandler);\n this._mapResizeHandler = null;\n }\n if (this._clickOutsideHandler) {\n document.removeEventListener(\"click\", this._clickOutsideHandler);\n this._clickOutsideHandler = null;\n }\n\n // Remove panel from map container\n this._panel?.parentNode?.removeChild(this._panel);\n\n // Remove button container from control stack\n this._container?.parentNode?.removeChild(this._container);\n\n this._map = undefined;\n this._mapContainer = undefined;\n this._container = undefined;\n this._panel = undefined;\n this._searchInput = undefined;\n this._metaEl = undefined;\n this._resultsEl = undefined;\n this._addedEl = undefined;\n this._insertSelect = undefined;\n this._openLegends.clear();\n this._eventHandlers.clear();\n }\n\n /**\n * Gets the current state of the control.\n *\n * @returns The current control state\n */\n getState(): NasaEarthdataState {\n return {\n ...this._state,\n addedLayers: this._state.addedLayers.map((l) => ({ ...l })),\n };\n }\n\n /**\n * Updates the control state. Changes to `addedLayers` are reconciled\n * against the map: missing layers are added, extra layers are removed,\n * and date/opacity changes are applied.\n *\n * @param newState - Partial state to merge with current state\n */\n setState(newState: Partial<NasaEarthdataState>): void {\n if (\n newState.collapsed !== undefined &&\n newState.collapsed !== this._state.collapsed\n ) {\n if (newState.collapsed) {\n this.collapse();\n } else {\n this.expand();\n }\n }\n\n if (newState.panelWidth !== undefined) {\n this._state.panelWidth = newState.panelWidth;\n if (this._panel) {\n this._panel.style.width = `${newState.panelWidth}px`;\n }\n }\n\n if (newState.query !== undefined && newState.query !== this._state.query) {\n this._state.query = newState.query;\n if (this._searchInput) {\n this._searchInput.value = newState.query;\n }\n this._renderResults();\n }\n\n let deferStateEmit = false;\n if (newState.addedLayers) {\n // Reconciliation needs the layer catalog; defer it until the\n // capabilities are loaded so state restoration works before the\n // panel is first expanded. An empty list needs no catalog data.\n const desired = newState.addedLayers.map((l) => ({ ...l }));\n if (desired.length === 0 || this._capabilities) {\n this._reconcileAddedLayers(desired);\n } else {\n // Emit statechange only after reconciliation so listeners never\n // see a stale addedLayers snapshot.\n deferStateEmit = true;\n void this.getCapabilities()\n .then(() => {\n this._reconcileAddedLayers(desired);\n this._renderResults();\n this._renderAddedSection();\n this._emit(\"statechange\");\n })\n .catch((error: unknown) => {\n const err =\n error instanceof Error ? error : new Error(String(error));\n this._emitError(err);\n });\n }\n }\n\n if (!deferStateEmit) {\n this._emit(\"statechange\");\n }\n }\n\n /**\n * Toggles the collapsed state of the control panel.\n */\n toggle(): void {\n this._state.collapsed = !this._state.collapsed;\n\n if (this._panel) {\n if (this._state.collapsed) {\n this._panel.classList.remove(\"expanded\");\n this._emit(\"collapse\");\n } else {\n this._panel.classList.add(\"expanded\");\n this._updatePanelPosition();\n this._loadCapabilities();\n this._refreshInsertOptions();\n this._emit(\"expand\");\n }\n }\n\n this._emit(\"statechange\");\n }\n\n /**\n * Expands the control panel.\n */\n expand(): void {\n if (this._state.collapsed) {\n this.toggle();\n }\n }\n\n /**\n * Collapses the control panel.\n */\n collapse(): void {\n if (!this._state.collapsed) {\n this.toggle();\n }\n }\n\n /**\n * Registers an event handler.\n *\n * @param event - The event type to listen for\n * @param handler - The callback function\n */\n on(event: NasaEarthdataEvent, handler: NasaEarthdataEventHandler): void {\n if (!this._eventHandlers.has(event)) {\n this._eventHandlers.set(event, new Set());\n }\n this._eventHandlers.get(event)!.add(handler);\n }\n\n /**\n * Removes an event handler.\n *\n * @param event - The event type\n * @param handler - The callback function to remove\n */\n off(event: NasaEarthdataEvent, handler: NasaEarthdataEventHandler): void {\n this._eventHandlers.get(event)?.delete(handler);\n }\n\n /**\n * Gets the map instance.\n *\n * @returns The MapLibre GL map instance or undefined if not added to a map\n */\n getMap(): MapLibreMap | undefined {\n return this._map;\n }\n\n /**\n * Gets the control container element.\n *\n * @returns The container element or undefined if not added to a map\n */\n getContainer(): HTMLElement | undefined {\n return this._container;\n }\n\n /**\n * Fetches and caches the GIBS capabilities document.\n *\n * @param force - If true, refetch even if cached\n * @returns The parsed capabilities\n */\n async getCapabilities(force = false): Promise<GibsCapabilities> {\n const capabilities = await this._client.getCapabilities(force);\n this._capabilities = capabilities;\n return capabilities;\n }\n\n /**\n * Searches the loaded GIBS layers by title or identifier.\n * Returns an empty array if the capabilities have not been loaded yet.\n *\n * @param query - The search query\n * @returns Matching layers\n */\n search(query: string): GibsLayer[] {\n return searchLayers(this._capabilities?.layers ?? [], query);\n }\n\n /**\n * Gets the state of all layers currently added to the map.\n *\n * @returns Added layer states\n */\n getAddedLayers(): AddedLayerState[] {\n return Array.from(this._addedLayers.values()).map((l) => ({ ...l }));\n }\n\n /**\n * Adds a GIBS layer to the map as a raster source and layer.\n * Requires the capabilities to be loaded (call getCapabilities() first\n * when using the control programmatically).\n *\n * @param layerId - The GIBS layer identifier\n * @param options - Optional date, opacity, visibility, and insertion position\n */\n addLayer(\n layerId: string,\n options?: {\n date?: string;\n opacity?: number;\n visible?: boolean;\n before?: string;\n key?: string;\n },\n ): void {\n if (!this._map) return;\n\n const layer = this._client.getLayer(layerId);\n if (!layer) {\n this._emitError(\n new Error(`Unknown GIBS layer: ${layerId} (capabilities not loaded?)`),\n );\n return;\n }\n\n const date = options?.date ?? layer.time?.default;\n const opacity = clamp(options?.opacity ?? 1, 0, 1);\n const visible = options?.visible ?? true;\n const before = options?.before ?? this._insertBefore;\n\n // Restoring a known instance key is a no-op if it is already on the map\n if (options?.key && this._addedLayers.has(options.key)) return;\n const key = options?.key ?? this._instanceKey(layer, date);\n const mapId = LAYER_ID_PREFIX + key;\n\n // Host applications that persist and restore layers may have already\n // recreated the native source/layer from saved state. Reuse them when\n // present instead of re-adding (which would throw on a duplicate id),\n // and apply the requested opacity/visibility so control state and map\n // agree.\n if (!this._map.getSource(mapId)) {\n this._map.addSource(mapId, {\n type: \"raster\",\n tiles: [buildTileUrl(layer, date)],\n tileSize: 256,\n maxzoom: layer.maxZoom,\n attribution: this._options.attribution,\n });\n }\n if (!this._map.getLayer(mapId)) {\n this._map.addLayer(\n {\n id: mapId,\n type: \"raster\",\n source: mapId,\n paint: { \"raster-opacity\": opacity },\n layout: { visibility: visible ? \"visible\" : \"none\" },\n },\n before && this._map.getLayer(before) ? before : undefined,\n );\n } else {\n this._map.setPaintProperty(mapId, \"raster-opacity\", opacity);\n this._map.setLayoutProperty(\n mapId,\n \"visibility\",\n visible ? \"visible\" : \"none\",\n );\n }\n\n const added: AddedLayerState = { key, id: layerId, date, opacity, visible };\n this._addedLayers.set(key, added);\n this._syncAddedLayersState();\n this._renderResults();\n this._renderAddedSection();\n this._refreshInsertOptions();\n this._emit(\"layeradd\", { layer });\n this._emit(\"statechange\");\n }\n\n /**\n * Builds a unique instance key for a layer/date pair. Non-time layers can\n * only be added once; time-enabled layers get one instance per addition,\n * so a numeric suffix disambiguates repeated adds at the same date.\n */\n private _instanceKey(layer: GibsLayer, date?: string): string {\n if (!layer.time) return layer.id;\n\n const base = `${layer.id}@${date ?? layer.time.default}`;\n if (!this._addedLayers.has(base)) return base;\n let n = 2;\n while (this._addedLayers.has(`${base}#${n}`)) n++;\n return `${base}#${n}`;\n }\n\n /**\n * Resolves an instance key or a GIBS layer identifier to instances.\n * An exact key match wins; otherwise all instances of the layer match.\n */\n private _resolveInstances(keyOrId: string): AddedLayerState[] {\n const exact = this._addedLayers.get(keyOrId);\n if (exact) return [exact];\n return Array.from(this._addedLayers.values()).filter(\n (l) => l.id === keyOrId,\n );\n }\n\n /**\n * Removes added GIBS layer instances from the map. Pass an instance key\n * to remove a single instance, or a GIBS layer identifier to remove all\n * instances of that layer.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n */\n removeLayer(keyOrId: string): void {\n const instances = this._resolveInstances(keyOrId);\n if (instances.length === 0) return;\n\n for (const instance of instances) {\n this._removeMapLayer(instance.key);\n this._addedLayers.delete(instance.key);\n this._openLegends.delete(instance.key);\n this._emit(\"layerremove\", { layer: this._client.getLayer(instance.id) });\n }\n this._syncAddedLayersState();\n this._renderResults();\n this._renderAddedSection();\n this._refreshInsertOptions();\n this._emit(\"statechange\");\n }\n\n /**\n * Changes the date of an added time-enabled layer instance.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n * @param date - The new ISO 8601 date\n */\n setLayerDate(keyOrId: string, date: string): void {\n const added = this._resolveInstances(keyOrId)[0];\n const layer = added ? this._client.getLayer(added.id) : undefined;\n if (!this._map || !added || !layer) return;\n\n added.date = date;\n const mapId = LAYER_ID_PREFIX + added.key;\n const source = this._map.getSource(mapId) as RasterTileSource | undefined;\n const tiles = [buildTileUrl(layer, date)];\n\n if (source && typeof source.setTiles === \"function\") {\n source.setTiles(tiles);\n } else {\n // Fallback: re-create the source and layer\n this._removeMapLayer(added.key);\n this._addedLayers.delete(added.key);\n this.addLayer(added.id, {\n date,\n opacity: added.opacity,\n visible: added.visible,\n key: added.key,\n });\n return;\n }\n\n this._syncAddedLayersState();\n this._emit(\"statechange\");\n }\n\n /**\n * Changes the opacity of an added layer instance.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n * @param opacity - The new opacity (0 to 1)\n */\n setLayerOpacity(keyOrId: string, opacity: number): void {\n const added = this._resolveInstances(keyOrId)[0];\n if (!this._map || !added) return;\n\n added.opacity = clamp(opacity, 0, 1);\n this._map.setPaintProperty(\n LAYER_ID_PREFIX + added.key,\n \"raster-opacity\",\n added.opacity,\n );\n this._syncAddedLayersState();\n this._emit(\"statechange\");\n }\n\n /**\n * Toggles the visibility of an added layer instance on the map.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n * @param visible - Whether the layer should be visible\n */\n setLayerVisibility(keyOrId: string, visible: boolean): void {\n const added = this._resolveInstances(keyOrId)[0];\n if (!this._map || !added) return;\n\n added.visible = visible;\n this._map.setLayoutProperty(\n LAYER_ID_PREFIX + added.key,\n \"visibility\",\n visible ? \"visible\" : \"none\",\n );\n this._syncAddedLayersState();\n this._emit(\"statechange\");\n }\n\n /**\n * Removes the map source and layer for an instance key, if present.\n */\n private _removeMapLayer(key: string): void {\n if (!this._map) return;\n const mapId = LAYER_ID_PREFIX + key;\n if (this._map.getLayer(mapId)) {\n this._map.removeLayer(mapId);\n }\n if (this._map.getSource(mapId)) {\n this._map.removeSource(mapId);\n }\n }\n\n /**\n * Mirrors the internal added-layers map into the serializable state.\n */\n private _syncAddedLayersState(): void {\n this._state.addedLayers = this.getAddedLayers();\n }\n\n /**\n * Reconciles a desired added-layers list against the map:\n * adds missing layers, removes extras, and applies date/opacity changes.\n */\n private _reconcileAddedLayers(desired: AddedLayerState[]): void {\n const desiredKeys = new Set(desired.map((l) => l.key));\n\n for (const existingKey of Array.from(this._addedLayers.keys())) {\n if (!desiredKeys.has(existingKey)) {\n this.removeLayer(existingKey);\n }\n }\n\n for (const target of desired) {\n const existing = this._addedLayers.get(target.key);\n if (!existing) {\n this.addLayer(target.id, {\n date: target.date,\n opacity: target.opacity,\n visible: target.visible,\n key: target.key,\n });\n } else {\n if (target.date && target.date !== existing.date) {\n this.setLayerDate(target.key, target.date);\n }\n if (\n target.opacity !== undefined &&\n target.opacity !== existing.opacity\n ) {\n this.setLayerOpacity(target.key, target.opacity);\n }\n if (\n target.visible !== undefined &&\n target.visible !== existing.visible\n ) {\n this.setLayerVisibility(target.key, target.visible);\n }\n }\n }\n }\n\n /**\n * Loads the capabilities document (once) and renders the results list.\n */\n private _loadCapabilities(): void {\n if (this._capabilities || this._loading) return;\n\n this._loading = true;\n this._renderStatus(\"Loading NASA GIBS layer catalog…\");\n\n this.getCapabilities()\n .then(() => {\n this._loading = false;\n this._renderResults();\n this._renderAddedSection();\n this._refreshInsertOptions();\n this._emit(\"capabilitiesload\");\n })\n .catch((error: unknown) => {\n this._loading = false;\n const err = error instanceof Error ? error : new Error(String(error));\n this._renderStatus(\n `Failed to load layer catalog: ${err.message}`,\n true,\n );\n this._emitError(err);\n });\n }\n\n /**\n * Emits an event to all registered handlers.\n *\n * @param event - The event type to emit\n * @param extra - Extra payload fields (layer, error)\n */\n private _emit(\n event: NasaEarthdataEvent,\n extra?: Partial<NasaEarthdataEventPayload>,\n ): void {\n const handlers = this._eventHandlers.get(event);\n if (handlers) {\n const eventData: NasaEarthdataEventPayload = {\n type: event,\n state: this.getState(),\n ...extra,\n };\n handlers.forEach((handler) => handler(eventData));\n }\n }\n\n /**\n * Emits an 'error' event.\n */\n private _emitError(error: Error): void {\n this._emit(\"error\", { error });\n }\n\n /**\n * Returns the theme class for the configured theme, if explicit.\n */\n private _themeClass(): string {\n if (this._options.theme === \"dark\") return \" ne-theme-dark\";\n if (this._options.theme === \"light\") return \" ne-theme-light\";\n return \"\";\n }\n\n /**\n * Creates the main container element for the control.\n * Contains a toggle button (29x29) matching navigation control size.\n *\n * @returns The container element\n */\n private _createContainer(): HTMLElement {\n const container = document.createElement(\"div\");\n container.className = `maplibregl-ctrl maplibregl-ctrl-group plugin-control maplibre-gl-nasa-earthdata${this._themeClass()}${\n this._options.className ? ` ${this._options.className}` : \"\"\n }`;\n\n // Create toggle button (29x29 to match navigation control)\n const toggleBtn = document.createElement(\"button\");\n toggleBtn.className = \"plugin-control-toggle\";\n toggleBtn.type = \"button\";\n toggleBtn.setAttribute(\"aria-label\", this._options.title);\n // Globe icon\n toggleBtn.innerHTML = `\n <span class=\"plugin-control-icon\">\n <svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"9\"/>\n <path d=\"M3 12h18\"/>\n <path d=\"M12 3a13.5 13.5 0 0 1 0 18a13.5 13.5 0 0 1 0-18\"/>\n </svg>\n </span>\n `;\n toggleBtn.addEventListener(\"click\", () => this.toggle());\n\n container.appendChild(toggleBtn);\n\n return container;\n }\n\n /**\n * Creates the panel element with header, search box, and results list.\n * Panel is positioned as a dropdown below the toggle button.\n *\n * @returns The panel element\n */\n private _createPanel(): HTMLElement {\n const panel = document.createElement(\"div\");\n panel.className = `plugin-control-panel maplibre-gl-nasa-earthdata${this._themeClass()}`;\n panel.style.width = `${this._options.panelWidth}px`;\n\n // Create header with title and close button\n const header = document.createElement(\"div\");\n header.className = \"plugin-control-header\";\n\n const title = document.createElement(\"span\");\n title.className = \"plugin-control-title\";\n title.textContent = this._options.title;\n\n const closeBtn = document.createElement(\"button\");\n closeBtn.className = \"plugin-control-close\";\n closeBtn.type = \"button\";\n closeBtn.setAttribute(\"aria-label\", \"Close panel\");\n closeBtn.innerHTML = \"×\";\n closeBtn.addEventListener(\"click\", () => this.collapse());\n\n header.appendChild(title);\n header.appendChild(closeBtn);\n\n // Create content area: search input, insert-before row, category list,\n // and the added-layers management section\n const content = document.createElement(\"div\");\n content.className = \"plugin-control-content nasa-content\";\n\n const search = document.createElement(\"input\");\n search.className = \"plugin-control-input nasa-search\";\n search.type = \"search\";\n search.placeholder = \"Search layers (e.g. temperature)\";\n search.setAttribute(\"aria-label\", \"Search NASA GIBS layers\");\n search.value = this._state.query;\n const onSearch = debounce(() => {\n this._state.query = search.value;\n this._renderResults();\n this._emit(\"statechange\");\n }, 250);\n search.addEventListener(\"input\", onSearch);\n this._searchInput = search;\n\n // \"Insert before\" row: choose where new layers are inserted in the\n // map's layer stack\n const insertRow = document.createElement(\"div\");\n insertRow.className = \"nasa-insert-row\";\n\n const insertLabel = document.createElement(\"span\");\n insertLabel.className = \"nasa-insert-label\";\n insertLabel.textContent = \"Insert before\";\n\n const insertSelect = document.createElement(\"select\");\n insertSelect.className = \"nasa-insert-select\";\n insertSelect.setAttribute(\"aria-label\", \"Insert new layers before\");\n insertSelect.addEventListener(\"change\", () => {\n this._insertBefore = insertSelect.value;\n });\n this._insertSelect = insertSelect;\n\n insertRow.appendChild(insertLabel);\n insertRow.appendChild(insertSelect);\n\n const meta = document.createElement(\"div\");\n meta.className = \"nasa-meta\";\n this._metaEl = meta;\n\n // Scrollable body holding the category groups and the added layers\n const body = document.createElement(\"div\");\n body.className = \"nasa-body\";\n\n const results = document.createElement(\"div\");\n results.className = \"nasa-results\";\n this._resultsEl = results;\n\n const added = document.createElement(\"div\");\n added.className = \"nasa-added-section\";\n this._addedEl = added;\n\n body.appendChild(results);\n body.appendChild(added);\n\n content.appendChild(search);\n content.appendChild(insertRow);\n content.appendChild(meta);\n content.appendChild(body);\n\n panel.appendChild(header);\n panel.appendChild(content);\n\n // Resize handle on the panel's outer edge for adjusting the width by\n // dragging. _updatePanelPosition() moves it to the correct side for the\n // control corner (left edge when right-anchored, right edge when\n // left-anchored).\n const resizeHandle = document.createElement(\"div\");\n resizeHandle.className = \"nasa-resize-handle\";\n resizeHandle.setAttribute(\"aria-hidden\", \"true\");\n resizeHandle.addEventListener(\"pointerdown\", (e) =>\n this._startResize(e, resizeHandle),\n );\n panel.appendChild(resizeHandle);\n\n return panel;\n }\n\n /**\n * Returns the maximum panel width that fits the map container.\n */\n private _maxPanelWidth(): number {\n return Math.max(\n 240,\n (this._mapContainer?.getBoundingClientRect().width ?? window.innerWidth) -\n 20,\n );\n }\n\n /**\n * Clamps the panel width to the map container. Applied whenever the panel\n * is (re)positioned so a saved width survives map shrinking.\n */\n private _applyPanelWidthBounds(): void {\n if (!this._panel) return;\n const width = Math.round(\n clamp(this._state.panelWidth, 240, this._maxPanelWidth()),\n );\n this._state.panelWidth = width;\n this._panel.style.width = `${width}px`;\n }\n\n /**\n * Starts a panel width drag-resize. The drag direction is derived from\n * the control corner so resizing works whether the panel is anchored to\n * the left or the right edge of the map.\n */\n private _startResize(e: PointerEvent, handle: HTMLElement): void {\n if (!this._panel) return;\n e.preventDefault();\n\n const startX = e.clientX;\n const startWidth = this._panel.getBoundingClientRect().width;\n // Right-anchored panels grow leftward, so invert the pointer delta\n const anchoredRight = !this._getControlPosition().endsWith(\"left\");\n const maxWidth = this._maxPanelWidth();\n\n const onMove = (ev: PointerEvent) => {\n const delta = anchoredRight ? startX - ev.clientX : ev.clientX - startX;\n const width = Math.round(clamp(startWidth + delta, 240, maxWidth));\n this._state.panelWidth = width;\n if (this._panel) {\n this._panel.style.width = `${width}px`;\n }\n };\n const onEnd = (ev: PointerEvent) => {\n handle.removeEventListener(\"pointermove\", onMove);\n handle.removeEventListener(\"pointerup\", onEnd);\n handle.removeEventListener(\"pointercancel\", onEnd);\n if (handle.hasPointerCapture(ev.pointerId)) {\n handle.releasePointerCapture(ev.pointerId);\n }\n this._panel?.classList.remove(\"nasa-resizing\");\n this._emit(\"statechange\");\n };\n\n handle.setPointerCapture(e.pointerId);\n handle.addEventListener(\"pointermove\", onMove);\n handle.addEventListener(\"pointerup\", onEnd);\n handle.addEventListener(\"pointercancel\", onEnd);\n this._panel.classList.add(\"nasa-resizing\");\n }\n\n /**\n * Shows a status message (loading or error) in the results area.\n */\n private _renderStatus(message: string, isError = false): void {\n if (!this._resultsEl || !this._metaEl) return;\n this._metaEl.textContent = \"\";\n this._resultsEl.innerHTML = \"\";\n const status = document.createElement(\"div\");\n status.className = `nasa-status${isError ? \" nasa-status-error\" : \"\"}`;\n status.textContent = message;\n this._resultsEl.appendChild(status);\n }\n\n /**\n * Groups layers by category, merging categories with fewer layers than\n * the threshold into \"Other\". Returns entries sorted alphabetically\n * with \"Other\" last.\n */\n private _groupByCategory(layers: GibsLayer[]): [string, GibsLayer[]][] {\n const MIN_CATEGORY_SIZE = 3;\n const all = this._capabilities?.layers ?? [];\n\n // Count category sizes over the FULL catalog so a category does not\n // flip into \"Other\" while filtering\n const totals = new globalThis.Map<string, number>();\n for (const layer of all) {\n totals.set(layer.category, (totals.get(layer.category) ?? 0) + 1);\n }\n\n const groups = new globalThis.Map<string, GibsLayer[]>();\n for (const layer of layers) {\n const total = totals.get(layer.category) ?? 0;\n const key = total >= MIN_CATEGORY_SIZE ? layer.category : \"Other\";\n const group = groups.get(key);\n if (group) {\n group.push(layer);\n } else {\n groups.set(key, [layer]);\n }\n }\n\n return Array.from(groups.entries()).sort(([a], [b]) => {\n if (a === \"Other\") return 1;\n if (b === \"Other\") return -1;\n return a.localeCompare(b);\n });\n }\n\n /**\n * Renders the (filtered) layer catalog grouped by category.\n */\n private _renderResults(): void {\n if (!this._resultsEl || !this._metaEl) return;\n if (!this._capabilities) {\n if (!this._loading) {\n this._renderStatus(\"Expand the panel to load the layer catalog.\");\n }\n return;\n }\n\n const query = this._state.query.trim();\n const matches = searchLayers(this._capabilities.layers, query);\n const groups = this._groupByCategory(matches);\n\n const layerWord = matches.length === 1 ? \"layer\" : \"layers\";\n const categoryWord = groups.length === 1 ? \"category\" : \"categories\";\n this._metaEl.textContent = `${matches.length} ${layerWord} in ${groups.length} ${categoryWord}`;\n\n this._resultsEl.innerHTML = \"\";\n if (matches.length === 0) {\n const status = document.createElement(\"div\");\n status.className = \"nasa-status\";\n status.textContent = \"No layers match your search.\";\n this._resultsEl.appendChild(status);\n return;\n }\n\n for (const [category, layers] of groups) {\n // While searching, auto-expand the matching categories\n const expanded = query ? true : this._expandedCategories.has(category);\n this._resultsEl.appendChild(\n this._createCategoryGroup(category, layers, expanded),\n );\n }\n }\n\n /**\n * Creates a collapsible category group with a count badge.\n */\n private _createCategoryGroup(\n category: string,\n layers: GibsLayer[],\n expanded: boolean,\n ): HTMLElement {\n const group = document.createElement(\"div\");\n group.className = \"nasa-category\";\n\n const header = document.createElement(\"button\");\n header.type = \"button\";\n header.className = `nasa-category-header${expanded ? \" expanded\" : \"\"}`;\n header.setAttribute(\"aria-expanded\", String(expanded));\n\n const chevron = document.createElement(\"span\");\n chevron.className = \"nasa-category-chevron\";\n chevron.innerHTML = `\n <svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9 6l6 6-6 6\"/>\n </svg>\n `;\n\n const name = document.createElement(\"span\");\n name.className = \"nasa-category-name\";\n name.textContent = category;\n\n const count = document.createElement(\"span\");\n count.className = \"nasa-category-count\";\n count.textContent = String(layers.length);\n\n header.appendChild(chevron);\n header.appendChild(name);\n header.appendChild(count);\n header.addEventListener(\"click\", () => {\n if (this._expandedCategories.has(category)) {\n this._expandedCategories.delete(category);\n } else {\n this._expandedCategories.add(category);\n }\n this._renderResults();\n });\n\n group.appendChild(header);\n\n if (expanded) {\n const list = document.createElement(\"div\");\n list.className = \"nasa-category-layers\";\n list.setAttribute(\"role\", \"list\");\n\n for (const layer of layers) {\n list.appendChild(this._createLayerRow(layer));\n }\n\n group.appendChild(list);\n }\n\n return group;\n }\n\n /**\n * Creates a single catalog layer row with title, badges, and an action\n * button. Non-time layers toggle add/remove; time-enabled layers always\n * offer \"Add\" so additional instances with different dates can be added.\n * Per-layer controls live in the added-layers section.\n */\n private _createLayerRow(layer: GibsLayer): HTMLElement {\n const instanceCount = Array.from(this._addedLayers.values()).filter(\n (l) => l.id === layer.id,\n ).length;\n const added = instanceCount > 0;\n\n const row = document.createElement(\"div\");\n row.className = `nasa-layer-row${added ? \" nasa-layer-row-added\" : \"\"}`;\n row.setAttribute(\"role\", \"listitem\");\n\n const main = document.createElement(\"div\");\n main.className = \"nasa-layer-main\";\n\n const info = document.createElement(\"div\");\n info.className = \"nasa-layer-info\";\n\n const titleEl = document.createElement(\"div\");\n titleEl.className = \"nasa-layer-title\";\n titleEl.textContent = layer.title;\n titleEl.title = layer.id;\n\n const badges = document.createElement(\"div\");\n badges.className = \"nasa-layer-badges\";\n const formatBadge = document.createElement(\"span\");\n formatBadge.className = \"nasa-badge\";\n formatBadge.textContent = layer.format;\n badges.appendChild(formatBadge);\n if (layer.time) {\n const timeBadge = document.createElement(\"span\");\n timeBadge.className = \"nasa-badge nasa-badge-time\";\n timeBadge.textContent = \"time\";\n timeBadge.title = `Default date: ${layer.time.default}`;\n badges.appendChild(timeBadge);\n }\n if (instanceCount > 1) {\n const countBadge = document.createElement(\"span\");\n countBadge.className = \"nasa-badge nasa-badge-time\";\n countBadge.textContent = `${instanceCount} added`;\n badges.appendChild(countBadge);\n }\n\n info.appendChild(titleEl);\n info.appendChild(badges);\n\n // Time-enabled layers can be added repeatedly (one instance per date),\n // so their button always reads \"Add\"\n const isRemove = added && !layer.time;\n const actionBtn = document.createElement(\"button\");\n actionBtn.type = \"button\";\n actionBtn.className = `nasa-layer-action${isRemove ? \" nasa-layer-action-remove\" : \"\"}`;\n actionBtn.textContent = isRemove ? \"Remove\" : \"Add\";\n actionBtn.setAttribute(\n \"aria-label\",\n isRemove\n ? `Remove layer ${layer.title}`\n : added\n ? `Add layer ${layer.title} for another date`\n : `Add layer ${layer.title}`,\n );\n actionBtn.addEventListener(\"click\", () => {\n if (!layer.time && this._resolveInstances(layer.id).length > 0) {\n this.removeLayer(layer.id);\n } else {\n this.addLayer(layer.id);\n }\n });\n\n main.appendChild(info);\n main.appendChild(actionBtn);\n row.appendChild(main);\n\n return row;\n }\n\n /**\n * Renders the \"Added layers\" management section: visibility checkbox,\n * legend toggle, remove button, opacity slider, and date picker.\n */\n private _renderAddedSection(): void {\n if (!this._addedEl) return;\n\n this._addedEl.innerHTML = \"\";\n if (this._addedLayers.size === 0) return;\n\n const heading = document.createElement(\"div\");\n heading.className = \"nasa-added-heading\";\n heading.textContent = \"Added layers\";\n this._addedEl.appendChild(heading);\n\n for (const added of this._addedLayers.values()) {\n const layer = this._client.getLayer(added.id);\n if (layer) {\n this._addedEl.appendChild(this._createAddedRow(layer, added));\n }\n }\n }\n\n /**\n * Creates a management card for one added layer.\n */\n private _createAddedRow(\n layer: GibsLayer,\n added: AddedLayerState,\n ): HTMLElement {\n const card = document.createElement(\"div\");\n card.className = \"nasa-added-card\";\n\n // Header row: visibility checkbox, title, legend toggle, remove\n const head = document.createElement(\"div\");\n head.className = \"nasa-added-head\";\n\n const visLabel = document.createElement(\"label\");\n visLabel.className = \"nasa-added-vis\";\n visLabel.title = \"Toggle layer visibility\";\n\n const visCheckbox = document.createElement(\"input\");\n visCheckbox.type = \"checkbox\";\n visCheckbox.className = \"nasa-added-checkbox\";\n visCheckbox.checked = added.visible;\n visCheckbox.setAttribute(\n \"aria-label\",\n `Toggle visibility of ${layer.title}`,\n );\n visCheckbox.addEventListener(\"change\", () => {\n this.setLayerVisibility(added.key, visCheckbox.checked);\n });\n\n const titleEl = document.createElement(\"span\");\n titleEl.className = \"nasa-added-title\";\n titleEl.textContent = layer.title;\n titleEl.title = added.key;\n\n visLabel.appendChild(visCheckbox);\n visLabel.appendChild(titleEl);\n\n // Date chip distinguishes multiple instances of the same time layer\n let dateChip: HTMLElement | undefined;\n if (layer.time && added.date) {\n dateChip = document.createElement(\"span\");\n dateChip.className = \"nasa-badge nasa-badge-time\";\n dateChip.textContent = added.date.slice(0, 10);\n visLabel.appendChild(dateChip);\n }\n\n const actions = document.createElement(\"div\");\n actions.className = \"nasa-added-actions\";\n\n if (layer.legendUrl) {\n const legendBtn = document.createElement(\"button\");\n legendBtn.type = \"button\";\n legendBtn.className = `nasa-icon-button${\n this._openLegends.has(added.key) ? \" active\" : \"\"\n }`;\n legendBtn.title = \"Toggle legend\";\n legendBtn.setAttribute(\"aria-label\", `Toggle legend for ${layer.title}`);\n legendBtn.innerHTML = `\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M8 6h13M8 12h13M8 18h13\"/>\n <rect x=\"3\" y=\"4.5\" width=\"3\" height=\"3\" rx=\"0.5\"/>\n <rect x=\"3\" y=\"10.5\" width=\"3\" height=\"3\" rx=\"0.5\"/>\n <rect x=\"3\" y=\"16.5\" width=\"3\" height=\"3\" rx=\"0.5\"/>\n </svg>\n `;\n legendBtn.addEventListener(\"click\", () => {\n if (this._openLegends.has(added.key)) {\n this._openLegends.delete(added.key);\n } else {\n this._openLegends.add(added.key);\n }\n this._renderAddedSection();\n });\n actions.appendChild(legendBtn);\n }\n\n const removeBtn = document.createElement(\"button\");\n removeBtn.type = \"button\";\n removeBtn.className = \"nasa-icon-button nasa-icon-button-danger\";\n removeBtn.title = \"Remove layer\";\n removeBtn.setAttribute(\"aria-label\", `Remove layer ${layer.title}`);\n removeBtn.innerHTML = `\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\"/>\n <path d=\"M10 11v6M14 11v6\"/>\n </svg>\n `;\n removeBtn.addEventListener(\"click\", () => {\n this.removeLayer(added.key);\n });\n actions.appendChild(removeBtn);\n\n head.appendChild(visLabel);\n head.appendChild(actions);\n card.appendChild(head);\n\n // Opacity slider with live percentage readout\n if (this._options.showOpacity) {\n const opacityRow = document.createElement(\"label\");\n opacityRow.className = \"nasa-control-label\";\n opacityRow.textContent = \"Opacity\";\n\n const opacityInput = document.createElement(\"input\");\n opacityInput.type = \"range\";\n opacityInput.className = \"nasa-opacity\";\n opacityInput.min = \"0\";\n opacityInput.max = \"1\";\n opacityInput.step = \"0.05\";\n opacityInput.value = String(added.opacity);\n opacityInput.setAttribute(\"aria-label\", `Opacity for ${layer.title}`);\n\n const opacityValue = document.createElement(\"span\");\n opacityValue.className = \"nasa-opacity-value\";\n opacityValue.textContent = `${Math.round(added.opacity * 100)}%`;\n\n opacityInput.addEventListener(\"input\", () => {\n const value = Number(opacityInput.value);\n this.setLayerOpacity(added.key, value);\n opacityValue.textContent = `${Math.round(value * 100)}%`;\n });\n\n opacityRow.appendChild(opacityInput);\n opacityRow.appendChild(opacityValue);\n card.appendChild(opacityRow);\n }\n\n // Date picker for time-enabled layers\n if (layer.time) {\n const dateLabel = document.createElement(\"label\");\n dateLabel.className = \"nasa-control-label\";\n dateLabel.textContent = \"Date\";\n\n const dateInput = document.createElement(\"input\");\n dateInput.type = \"date\";\n dateInput.className = \"nasa-date\";\n dateInput.value = (added.date ?? layer.time.default).slice(0, 10);\n const range = this._timeRange(layer);\n if (range.min) dateInput.min = range.min;\n if (range.max) dateInput.max = range.max;\n dateInput.addEventListener(\"change\", () => {\n if (dateInput.value) {\n this.setLayerDate(added.key, dateInput.value);\n if (dateChip) {\n dateChip.textContent = dateInput.value;\n }\n }\n });\n\n dateLabel.appendChild(dateInput);\n card.appendChild(dateLabel);\n }\n\n // Legend image (GIBS horizontal legend SVG)\n if (layer.legendUrl && this._openLegends.has(added.key)) {\n const legend = document.createElement(\"img\");\n legend.className = \"nasa-legend-image\";\n legend.src = layer.legendUrl;\n legend.alt = `Legend for ${layer.title}`;\n legend.loading = \"lazy\";\n card.appendChild(legend);\n }\n\n return card;\n }\n\n /**\n * Refreshes the \"Insert before\" dropdown with the map's current layers.\n */\n private _refreshInsertOptions(): void {\n if (!this._insertSelect || !this._map) return;\n\n const layers = this._map.getStyle()?.layers ?? [];\n const select = this._insertSelect;\n select.innerHTML = \"\";\n\n const topOption = document.createElement(\"option\");\n topOption.value = \"\";\n topOption.textContent = \"Top of map\";\n select.appendChild(topOption);\n\n for (const layer of layers) {\n const option = document.createElement(\"option\");\n option.value = layer.id;\n option.textContent = layer.id;\n select.appendChild(option);\n }\n\n // Restore the previous selection if the layer still exists\n if (this._insertBefore && layers.some((l) => l.id === this._insertBefore)) {\n select.value = this._insertBefore;\n } else {\n this._insertBefore = \"\";\n select.value = \"\";\n }\n }\n\n /**\n * Derives the min/max selectable dates from a layer's time domain values.\n */\n private _timeRange(layer: GibsLayer): { min?: string; max?: string } {\n const time = layer.time;\n if (!time) return {};\n\n let min: string | undefined;\n let max: string | undefined;\n for (const value of time.values) {\n const [start, end] = value.split(\"/\");\n const startDate = start?.slice(0, 10);\n const endDate = (end ?? start)?.slice(0, 10);\n if (startDate && (!min || startDate < min)) min = startDate;\n if (endDate && (!max || endDate > max)) max = endDate;\n }\n const defaultDate = time.default.slice(0, 10);\n if (!max || defaultDate > max) max = defaultDate;\n return { min, max };\n }\n\n /**\n * Setup event listeners for panel positioning and click-outside behavior.\n */\n private _setupEventListeners(): void {\n // Click outside to close (check both container and panel since they're now separate).\n // Ignore targets that are no longer connected to the DOM: clicking an\n // Add/Remove button re-renders the results list, detaching the button\n // before this document-level handler runs.\n this._clickOutsideHandler = (e: MouseEvent) => {\n const target = e.target as Node;\n if (\n this._container &&\n this._panel &&\n target.isConnected &&\n !this._container.contains(target) &&\n !this._panel.contains(target)\n ) {\n this.collapse();\n }\n };\n document.addEventListener(\"click\", this._clickOutsideHandler);\n\n // Update panel position on window resize\n this._resizeHandler = () => {\n if (!this._state.collapsed) {\n this._updatePanelPosition();\n }\n };\n window.addEventListener(\"resize\", this._resizeHandler);\n\n // Update panel position on map resize (e.g., sidebar toggle)\n this._mapResizeHandler = () => {\n if (!this._state.collapsed) {\n this._updatePanelPosition();\n }\n };\n this._map?.on(\"resize\", this._mapResizeHandler);\n }\n\n /**\n * Detect which corner the control is positioned in.\n *\n * @returns The position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n */\n private _getControlPosition():\n | \"top-left\"\n | \"top-right\"\n | \"bottom-left\"\n | \"bottom-right\" {\n const parent = this._container?.parentElement;\n if (!parent) return \"top-right\"; // Default\n\n if (parent.classList.contains(\"maplibregl-ctrl-top-left\"))\n return \"top-left\";\n if (parent.classList.contains(\"maplibregl-ctrl-top-right\"))\n return \"top-right\";\n if (parent.classList.contains(\"maplibregl-ctrl-bottom-left\"))\n return \"bottom-left\";\n if (parent.classList.contains(\"maplibregl-ctrl-bottom-right\"))\n return \"bottom-right\";\n\n return \"top-right\"; // Default\n }\n\n /**\n * Update the panel position based on button location and control corner.\n * Positions the panel next to the button, expanding in the appropriate direction.\n */\n private _updatePanelPosition(): void {\n if (!this._container || !this._panel || !this._mapContainer) return;\n\n // Get the toggle button (first child of container)\n const button = this._container.querySelector(\".plugin-control-toggle\");\n if (!button) return;\n\n // Keep the panel within the map even if it shrank since the width was set\n this._applyPanelWidthBounds();\n\n const buttonRect = button.getBoundingClientRect();\n const mapRect = this._mapContainer.getBoundingClientRect();\n const position = this._getControlPosition();\n\n // Calculate button position relative to map container\n const buttonTop = buttonRect.top - mapRect.top;\n const buttonBottom = mapRect.bottom - buttonRect.bottom;\n const buttonLeft = buttonRect.left - mapRect.left;\n const buttonRight = mapRect.right - buttonRect.right;\n\n const panelGap = 5; // Gap between button and panel\n\n // Reset all positioning\n this._panel.style.top = \"\";\n this._panel.style.bottom = \"\";\n this._panel.style.left = \"\";\n this._panel.style.right = \"\";\n\n // Keep the resize handle on the panel's outer (growing) edge:\n // right edge for left-anchored panels, left edge for right-anchored ones\n this._panel\n .querySelector(\".nasa-resize-handle\")\n ?.classList.toggle(\"nasa-resize-handle-right\", position.endsWith(\"left\"));\n\n switch (position) {\n case \"top-left\":\n // Panel expands down and to the right\n this._panel.style.top = `${buttonTop + buttonRect.height + panelGap}px`;\n this._panel.style.left = `${buttonLeft}px`;\n break;\n\n case \"top-right\":\n // Panel expands down and to the left\n this._panel.style.top = `${buttonTop + buttonRect.height + panelGap}px`;\n this._panel.style.right = `${buttonRight}px`;\n break;\n\n case \"bottom-left\":\n // Panel expands up and to the right\n this._panel.style.bottom = `${buttonBottom + buttonRect.height + panelGap}px`;\n this._panel.style.left = `${buttonLeft}px`;\n break;\n\n case \"bottom-right\":\n // Panel expands up and to the left\n this._panel.style.bottom = `${buttonBottom + buttonRect.height + panelGap}px`;\n this._panel.style.right = `${buttonRight}px`;\n break;\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"NasaEarthdataControl-BiDau2KA.js","names":[],"sources":["../src/lib/gibs/parseCapabilities.ts","../src/lib/gibs/search.ts","../src/lib/gibs/GibsClient.ts","../src/lib/gibs/tileUrl.ts","../src/lib/utils/helpers.ts","../src/lib/core/NasaEarthdataControl.ts"],"sourcesContent":["import type {\n GibsCapabilities,\n GibsLayer,\n GibsLayerFormat,\n GibsTimeDimension,\n ParseOptions,\n} from \"./types\";\n\nconst FORMAT_MAP: Record<string, GibsLayerFormat> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpeg\",\n \"application/vnd.mapbox-vector-tile\": \"mvt\",\n};\n\nconst MIME_MAP: Record<GibsLayerFormat, string> = {\n png: \"image/png\",\n jpeg: \"image/jpeg\",\n mvt: \"application/vnd.mapbox-vector-tile\",\n};\n\n/**\n * Returns the direct child elements of a node matching a local tag name,\n * ignoring XML namespaces.\n */\nfunction childrenByLocalName(parent: Element, localName: string): Element[] {\n const result: Element[] = [];\n for (let i = 0; i < parent.children.length; i++) {\n const child = parent.children[i];\n if (child.localName === localName) {\n result.push(child);\n }\n }\n return result;\n}\n\n/**\n * Returns the text content of the first direct child element matching a local tag name.\n */\nfunction childText(parent: Element, localName: string): string | undefined {\n const child = childrenByLocalName(parent, localName)[0];\n return child?.textContent?.trim() || undefined;\n}\n\n/**\n * Parses the WGS84 bounding box of a layer element.\n */\nfunction parseBbox(\n layerEl: Element,\n): [number, number, number, number] | undefined {\n const bboxEl = childrenByLocalName(layerEl, \"WGS84BoundingBox\")[0];\n if (!bboxEl) return undefined;\n\n const lower = childText(bboxEl, \"LowerCorner\")?.split(/\\s+/).map(Number);\n const upper = childText(bboxEl, \"UpperCorner\")?.split(/\\s+/).map(Number);\n if (!lower || !upper || lower.length < 2 || upper.length < 2)\n return undefined;\n if ([...lower, ...upper].some((n) => Number.isNaN(n))) return undefined;\n\n return [lower[0], lower[1], upper[0], upper[1]];\n}\n\n/**\n * Parses the Time dimension of a layer element, if present.\n */\nfunction parseTimeDimension(layerEl: Element): GibsTimeDimension | undefined {\n const dimensions = childrenByLocalName(layerEl, \"Dimension\");\n for (const dim of dimensions) {\n if (childText(dim, \"Identifier\") !== \"Time\") continue;\n\n const defaultValue = childText(dim, \"Default\");\n if (!defaultValue) return undefined;\n\n const values = childrenByLocalName(dim, \"Value\")\n .map((v) => v.textContent?.trim())\n .filter((v): v is string => Boolean(v));\n\n return { default: defaultValue, values };\n }\n return undefined;\n}\n\n/**\n * Parses the first legend URL advertised in a layer's default style.\n */\nfunction parseLegendUrl(layerEl: Element): string | undefined {\n const styles = childrenByLocalName(layerEl, \"Style\");\n for (const style of styles) {\n const legend = childrenByLocalName(style, \"LegendURL\")[0];\n const href =\n legend?.getAttribute(\"xlink:href\") ??\n legend?.getAttributeNS(null, \"href\");\n if (href) return href;\n }\n return undefined;\n}\n\n/**\n * Parses a single WMTS Layer element into a GibsLayer.\n * Returns undefined for layers that cannot be represented (missing\n * identifier, no tile resource template, or unsupported format).\n */\nfunction parseLayer(\n layerEl: Element,\n includeVector: boolean,\n): GibsLayer | undefined {\n const id = childText(layerEl, \"Identifier\");\n if (!id) return undefined;\n\n const title = childText(layerEl, \"Title\") ?? id;\n\n // Collect supported formats; prefer png > jpeg > mvt.\n const formats = childrenByLocalName(layerEl, \"Format\")\n .map((f) => FORMAT_MAP[f.textContent?.trim() ?? \"\"])\n .filter((f): f is GibsLayerFormat => Boolean(f));\n const format = ([\"png\", \"jpeg\", \"mvt\"] as const).find((f) =>\n formats.includes(f),\n );\n if (!format) return undefined;\n if (format === \"mvt\" && !includeVector) return undefined;\n\n // Tile matrix set link, e.g. \"GoogleMapsCompatible_Level9\" -> maxZoom 9.\n const tmsLink = childrenByLocalName(layerEl, \"TileMatrixSetLink\")[0];\n const tileMatrixSet = tmsLink\n ? childText(tmsLink, \"TileMatrixSet\")\n : undefined;\n if (!tileMatrixSet) return undefined;\n const levelMatch = /Level(\\d+)$/.exec(tileMatrixSet);\n const maxZoom = levelMatch ? parseInt(levelMatch[1], 10) : 9;\n\n // Tile resource template. Only consider templates whose advertised format\n // matches the chosen layer format, then prefer the template containing\n // {Time} when the layer is time-enabled so dates can be substituted.\n const resourceUrls = childrenByLocalName(layerEl, \"ResourceURL\").filter(\n (r) => r.getAttribute(\"resourceType\") === \"tile\",\n );\n if (resourceUrls.length === 0) return undefined;\n\n const selectedMime = MIME_MAP[format];\n const formatMatched = resourceUrls.filter((r) => {\n const mime = r.getAttribute(\"format\")?.trim().toLowerCase();\n return !mime || mime === selectedMime;\n });\n\n const templates = (formatMatched.length > 0 ? formatMatched : resourceUrls)\n .map((r) => r.getAttribute(\"template\"))\n .filter((t): t is string => Boolean(t));\n if (templates.length === 0) return undefined;\n\n const time = parseTimeDimension(layerEl);\n const resourceTemplate =\n (time ? templates.find((t) => t.includes(\"{Time}\")) : undefined) ??\n templates.find((t) => !t.includes(\"{Time}\")) ??\n templates[0];\n\n const extMatch = /\\.([a-z0-9]+)$/i.exec(resourceTemplate);\n const fileExtension = extMatch\n ? extMatch[1]\n : format === \"jpeg\"\n ? \"jpg\"\n : format;\n\n return {\n id,\n title,\n // Platform/instrument prefix of the identifier, used for grouping\n category: id.split(\"_\")[0],\n format,\n fileExtension,\n tileMatrixSet,\n maxZoom,\n bbox: parseBbox(layerEl),\n resourceTemplate,\n time,\n legendUrl: parseLegendUrl(layerEl),\n };\n}\n\n/**\n * Parses a WMTS GetCapabilities XML document into a GibsCapabilities object.\n *\n * Layers without a usable tile resource template are skipped. Vector-tile\n * (MVT) layers are skipped unless `options.includeVector` is true.\n *\n * @param xml - The raw WMTSCapabilities.xml document text\n * @param options - Parse options\n * @returns Parsed capabilities with layers sorted by title\n */\nexport function parseCapabilities(\n xml: string,\n options?: ParseOptions,\n): GibsCapabilities {\n const includeVector = options?.includeVector ?? false;\n\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const parserError = doc.getElementsByTagName(\"parsererror\")[0];\n if (parserError) {\n throw new Error(\n `Failed to parse capabilities XML: ${parserError.textContent ?? \"unknown error\"}`,\n );\n }\n\n const root = doc.documentElement;\n const contents = childrenByLocalName(root, \"Contents\")[0];\n if (!contents) {\n throw new Error(\"Invalid capabilities document: missing Contents element\");\n }\n\n const layers: GibsLayer[] = [];\n for (const layerEl of childrenByLocalName(contents, \"Layer\")) {\n const layer = parseLayer(layerEl, includeVector);\n if (layer) layers.push(layer);\n }\n\n layers.sort((a, b) => a.title.localeCompare(b.title));\n\n return { layers, fetchedAt: Date.now() };\n}\n","import type { GibsLayer } from \"./types\";\n\n/**\n * Filters GIBS layers by a free-text query.\n *\n * Performs a case-insensitive substring match against the layer title and\n * identifier. An empty or whitespace-only query returns all layers.\n *\n * @param layers - The layers to search\n * @param query - The search query\n * @returns Layers whose title or identifier contains the query\n */\nexport function searchLayers(layers: GibsLayer[], query: string): GibsLayer[] {\n const q = query.trim().toLowerCase();\n if (!q) return layers;\n\n return layers.filter(\n (layer) =>\n layer.title.toLowerCase().includes(q) ||\n layer.id.toLowerCase().includes(q),\n );\n}\n","import { parseCapabilities } from \"./parseCapabilities\";\nimport { searchLayers } from \"./search\";\nimport type { GibsCapabilities, GibsLayer } from \"./types\";\n\n/**\n * Default URL of the NASA GIBS WMTS capabilities document (EPSG:3857, \"best\" imagery).\n */\nexport const DEFAULT_CAPABILITIES_URL =\n \"https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/1.0.0/WMTSCapabilities.xml\";\n\n/**\n * Options for creating a GibsClient.\n */\nexport interface GibsClientOptions {\n /**\n * URL of the WMTS capabilities document.\n * @default DEFAULT_CAPABILITIES_URL\n */\n url?: string;\n\n /**\n * Whether to include vector-tile (MVT) layers.\n * @default false\n */\n includeVector?: boolean;\n}\n\n/**\n * Fetches, parses, and caches the NASA GIBS WMTS capabilities document.\n *\n * Concurrent calls to {@link GibsClient.getCapabilities} are deduplicated:\n * the document is fetched and parsed at most once unless `force` is passed.\n *\n * @example\n * ```typescript\n * const client = new GibsClient();\n * const { layers } = await client.getCapabilities();\n * const matches = client.search('temperature');\n * ```\n */\nexport class GibsClient {\n private _url: string;\n private _includeVector: boolean;\n private _capabilities?: GibsCapabilities;\n private _pending?: Promise<GibsCapabilities>;\n\n /**\n * Creates a new GibsClient.\n *\n * @param options - Client options\n */\n constructor(options?: GibsClientOptions) {\n this._url = options?.url ?? DEFAULT_CAPABILITIES_URL;\n this._includeVector = options?.includeVector ?? false;\n }\n\n /**\n * Fetches and parses the capabilities document, caching the result.\n *\n * @param force - If true, refetch even if a cached result exists\n * @returns The parsed capabilities\n */\n async getCapabilities(force = false): Promise<GibsCapabilities> {\n if (this._capabilities && !force) return this._capabilities;\n if (this._pending && !force) return this._pending;\n\n this._pending = (async () => {\n const response = await fetch(this._url);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch capabilities: ${response.status} ${response.statusText}`,\n );\n }\n const xml = await response.text();\n const capabilities = parseCapabilities(xml, {\n includeVector: this._includeVector,\n });\n this._capabilities = capabilities;\n return capabilities;\n })();\n\n try {\n return await this._pending;\n } finally {\n this._pending = undefined;\n }\n }\n\n /**\n * Returns the cached capabilities, if already loaded.\n */\n getCachedCapabilities(): GibsCapabilities | undefined {\n return this._capabilities;\n }\n\n /**\n * Searches the cached layers by title or identifier.\n * Returns an empty array if capabilities have not been loaded yet.\n *\n * @param query - The search query\n */\n search(query: string): GibsLayer[] {\n return searchLayers(this._capabilities?.layers ?? [], query);\n }\n\n /**\n * Looks up a cached layer by identifier.\n *\n * @param id - The layer identifier\n */\n getLayer(id: string): GibsLayer | undefined {\n return this._capabilities?.layers.find((layer) => layer.id === id);\n }\n}\n","import type { GibsLayer } from \"./types\";\n\n/**\n * Builds a MapLibre-compatible XYZ tile URL template from a GIBS layer.\n *\n * Substitutes {Time} with the given date (or the layer's default date),\n * {TileMatrixSet} with the layer's tile matrix set, and maps the WMTS\n * placeholders {TileMatrix}/{TileRow}/{TileCol} to {z}/{y}/{x}.\n *\n * @param layer - The GIBS layer to build a URL for\n * @param time - Optional ISO 8601 date overriding the layer's default date\n * @returns A tile URL template usable in a MapLibre raster source\n */\nexport function buildTileUrl(layer: GibsLayer, time?: string): string {\n return layer.resourceTemplate\n .replace(\"{Time}\", time ?? layer.time?.default ?? \"default\")\n .replace(\"{TileMatrixSet}\", layer.tileMatrixSet)\n .replace(\"{TileMatrix}\", \"{z}\")\n .replace(\"{TileRow}\", \"{y}\")\n .replace(\"{TileCol}\", \"{x}\");\n}\n","/**\n * Clamps a value between a minimum and maximum.\n *\n * @param value - The value to clamp\n * @param min - The minimum allowed value\n * @param max - The maximum allowed value\n * @returns The clamped value\n *\n * @example\n * ```typescript\n * clamp(5, 0, 10); // returns 5\n * clamp(-5, 0, 10); // returns 0\n * clamp(15, 0, 10); // returns 10\n * ```\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Formats a numeric value with appropriate decimal places based on step size.\n *\n * @param value - The value to format\n * @param step - The step size to determine decimal places\n * @returns The formatted value as a string\n *\n * @example\n * ```typescript\n * formatNumericValue(5, 1); // returns \"5\"\n * formatNumericValue(0.5, 0.1); // returns \"0.5\"\n * formatNumericValue(0.55, 0.01); // returns \"0.55\"\n * ```\n */\nexport function formatNumericValue(value: number, step: number): string {\n if (step === 0) return value.toString();\n const decimals = Math.max(0, -Math.floor(Math.log10(step)));\n return value.toFixed(decimals);\n}\n\n/**\n * Generates a unique ID string.\n *\n * @param prefix - Optional prefix for the ID\n * @returns A unique ID string\n *\n * @example\n * ```typescript\n * generateId('control'); // returns \"control-abc123\"\n * generateId(); // returns \"abc123\"\n * ```\n */\nexport function generateId(prefix?: string): string {\n const id = Math.random().toString(36).substring(2, 9);\n return prefix ? `${prefix}-${id}` : id;\n}\n\n/**\n * Debounces a function call.\n *\n * @param fn - The function to debounce\n * @param delay - The delay in milliseconds\n * @returns A debounced version of the function\n *\n * @example\n * ```typescript\n * const debouncedUpdate = debounce(() => updateMap(), 100);\n * window.addEventListener('resize', debouncedUpdate);\n * ```\n */\nexport function debounce<T extends (...args: unknown[]) => void>(\n fn: T,\n delay: number,\n): (...args: Parameters<T>) => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n return (...args: Parameters<T>) => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n timeoutId = setTimeout(() => {\n fn(...args);\n timeoutId = null;\n }, delay);\n };\n}\n\n/**\n * Throttles a function call.\n *\n * @param fn - The function to throttle\n * @param limit - The minimum time between calls in milliseconds\n * @returns A throttled version of the function\n *\n * @example\n * ```typescript\n * const throttledScroll = throttle(() => handleScroll(), 100);\n * window.addEventListener('scroll', throttledScroll);\n * ```\n */\nexport function throttle<T extends (...args: unknown[]) => void>(\n fn: T,\n limit: number,\n): (...args: Parameters<T>) => void {\n let inThrottle = false;\n\n return (...args: Parameters<T>) => {\n if (!inThrottle) {\n fn(...args);\n inThrottle = true;\n setTimeout(() => {\n inThrottle = false;\n }, limit);\n }\n };\n}\n\n/**\n * Creates a CSS class string from an object of class names.\n *\n * @param classes - Object with class names as keys and boolean values\n * @returns A space-separated string of class names\n *\n * @example\n * ```typescript\n * classNames({ active: true, disabled: false, visible: true });\n * // returns \"active visible\"\n * ```\n */\nexport function classNames(classes: Record<string, boolean>): string {\n return Object.entries(classes)\n .filter(([, value]) => value)\n .map(([key]) => key)\n .join(\" \");\n}\n","import type {\n IControl,\n Map as MapLibreMap,\n RasterTileSource,\n} from \"maplibre-gl\";\nimport { GibsClient, DEFAULT_CAPABILITIES_URL } from \"../gibs/GibsClient\";\nimport { buildTileUrl } from \"../gibs/tileUrl\";\nimport { searchLayers } from \"../gibs/search\";\nimport type { GibsCapabilities, GibsLayer } from \"../gibs/types\";\nimport { clamp, debounce } from \"../utils\";\nimport type {\n NasaEarthdataControlOptions,\n NasaEarthdataState,\n NasaEarthdataEvent,\n NasaEarthdataEventHandler,\n NasaEarthdataEventPayload,\n AddedLayerState,\n} from \"./types\";\n\n/**\n * Default options for the NasaEarthdataControl\n */\nconst DEFAULT_OPTIONS: Required<NasaEarthdataControlOptions> = {\n collapsed: true,\n position: \"top-right\",\n title: \"NASA Earthdata\",\n panelWidth: 320,\n className: \"\",\n capabilitiesUrl: DEFAULT_CAPABILITIES_URL,\n includeVector: false,\n showOpacity: true,\n attribution:\n '<a href=\"https://earthdata.nasa.gov/gibs\" target=\"_blank\">NASA EOSDIS GIBS</a>',\n theme: \"auto\",\n};\n\n/**\n * Prefix used for source and layer ids added to the map\n */\nconst LAYER_ID_PREFIX = \"nasa-gibs-\";\n\n/**\n * Event handlers map type\n */\ntype EventHandlersMap = globalThis.Map<\n NasaEarthdataEvent,\n Set<NasaEarthdataEventHandler>\n>;\n\n/**\n * A MapLibre GL control for searching and adding NASA GIBS (Global Imagery\n * Browse Services) WMTS layers to the map.\n *\n * The control renders a collapsible button. When expanded, it fetches the\n * GIBS capabilities document, lets the user search the layer catalog, and\n * add/remove raster layers with per-layer date and opacity controls.\n *\n * @example\n * ```typescript\n * const control = new NasaEarthdataControl({\n * title: 'NASA Earthdata',\n * collapsed: false,\n * });\n * map.addControl(control, 'top-right');\n * control.on('layeradd', (e) => console.log('Added', e.layer?.id));\n * ```\n */\nexport class NasaEarthdataControl implements IControl {\n private _map?: MapLibreMap;\n private _mapContainer?: HTMLElement;\n private _container?: HTMLElement;\n private _panel?: HTMLElement;\n private _options: Required<NasaEarthdataControlOptions>;\n private _state: NasaEarthdataState;\n private _eventHandlers: EventHandlersMap = new globalThis.Map();\n\n private _client: GibsClient;\n private _capabilities?: GibsCapabilities;\n private _loading = false;\n private _addedLayers: globalThis.Map<string, AddedLayerState> =\n new globalThis.Map();\n\n // Panel content elements\n private _searchInput?: HTMLInputElement;\n private _metaEl?: HTMLElement;\n private _resultsEl?: HTMLElement;\n private _addedEl?: HTMLElement;\n private _insertSelect?: HTMLSelectElement;\n\n // UI state: expanded category groups, open legends, insertion position\n private _expandedCategories = new Set<string>();\n private _openLegends = new Set<string>();\n private _insertBefore = \"\";\n\n // Panel positioning handlers\n private _resizeHandler: (() => void) | null = null;\n private _mapResizeHandler: (() => void) | null = null;\n private _clickOutsideHandler: ((e: MouseEvent) => void) | null = null;\n\n /**\n * Creates a new NasaEarthdataControl instance.\n *\n * @param options - Configuration options for the control\n */\n constructor(options?: Partial<NasaEarthdataControlOptions>) {\n this._options = { ...DEFAULT_OPTIONS, ...options };\n this._state = {\n collapsed: this._options.collapsed,\n panelWidth: this._options.panelWidth,\n query: \"\",\n addedLayers: [],\n };\n this._client = new GibsClient({\n url: this._options.capabilitiesUrl,\n includeVector: this._options.includeVector,\n });\n }\n\n /**\n * Called when the control is added to the map.\n * Implements the IControl interface.\n *\n * @param map - The MapLibre GL map instance\n * @returns The control's container element\n */\n onAdd(map: MapLibreMap): HTMLElement {\n this._map = map;\n this._mapContainer = map.getContainer();\n this._container = this._createContainer();\n this._panel = this._createPanel();\n\n // Append panel to map container for independent positioning (avoids overlap with other controls)\n this._mapContainer.appendChild(this._panel);\n\n // Setup event listeners for panel positioning and click-outside\n this._setupEventListeners();\n\n // Set initial panel state\n if (!this._state.collapsed) {\n this._panel.classList.add(\"expanded\");\n this._loadCapabilities();\n // Update position after control is added to DOM\n requestAnimationFrame(() => {\n this._updatePanelPosition();\n });\n }\n\n return this._container;\n }\n\n /**\n * Called when the control is removed from the map.\n * Implements the IControl interface.\n */\n onRemove(): void {\n // Remove all added GIBS layers and sources from the map\n for (const layerId of Array.from(this._addedLayers.keys())) {\n this._removeMapLayer(layerId);\n }\n this._addedLayers.clear();\n this._state.addedLayers = [];\n\n // Remove event listeners\n if (this._resizeHandler) {\n window.removeEventListener(\"resize\", this._resizeHandler);\n this._resizeHandler = null;\n }\n if (this._mapResizeHandler && this._map) {\n this._map.off(\"resize\", this._mapResizeHandler);\n this._mapResizeHandler = null;\n }\n if (this._clickOutsideHandler) {\n document.removeEventListener(\"click\", this._clickOutsideHandler);\n this._clickOutsideHandler = null;\n }\n\n // Remove panel from map container\n this._panel?.parentNode?.removeChild(this._panel);\n\n // Remove button container from control stack\n this._container?.parentNode?.removeChild(this._container);\n\n this._map = undefined;\n this._mapContainer = undefined;\n this._container = undefined;\n this._panel = undefined;\n this._searchInput = undefined;\n this._metaEl = undefined;\n this._resultsEl = undefined;\n this._addedEl = undefined;\n this._insertSelect = undefined;\n this._openLegends.clear();\n this._eventHandlers.clear();\n }\n\n /**\n * Gets the current state of the control.\n *\n * @returns The current control state\n */\n getState(): NasaEarthdataState {\n return {\n ...this._state,\n addedLayers: this._state.addedLayers.map((l) => ({ ...l })),\n };\n }\n\n /**\n * Updates the control state. Changes to `addedLayers` are reconciled\n * against the map: missing layers are added, extra layers are removed,\n * and date/opacity changes are applied.\n *\n * @param newState - Partial state to merge with current state\n */\n setState(newState: Partial<NasaEarthdataState>): void {\n if (\n newState.collapsed !== undefined &&\n newState.collapsed !== this._state.collapsed\n ) {\n if (newState.collapsed) {\n this.collapse();\n } else {\n this.expand();\n }\n }\n\n if (newState.panelWidth !== undefined) {\n this._state.panelWidth = newState.panelWidth;\n if (this._panel) {\n this._panel.style.width = `${newState.panelWidth}px`;\n }\n }\n\n if (newState.query !== undefined && newState.query !== this._state.query) {\n this._state.query = newState.query;\n if (this._searchInput) {\n this._searchInput.value = newState.query;\n }\n this._renderResults();\n }\n\n let deferStateEmit = false;\n if (newState.addedLayers) {\n // Reconciliation needs the layer catalog; defer it until the\n // capabilities are loaded so state restoration works before the\n // panel is first expanded. An empty list needs no catalog data.\n const desired = newState.addedLayers.map((l) => ({ ...l }));\n if (desired.length === 0 || this._capabilities) {\n this._reconcileAddedLayers(desired);\n } else {\n // Emit statechange only after reconciliation so listeners never\n // see a stale addedLayers snapshot.\n deferStateEmit = true;\n void this.getCapabilities()\n .then(() => {\n this._reconcileAddedLayers(desired);\n this._renderResults();\n this._renderAddedSection();\n this._emit(\"statechange\");\n })\n .catch((error: unknown) => {\n const err =\n error instanceof Error ? error : new Error(String(error));\n this._emitError(err);\n });\n }\n }\n\n if (!deferStateEmit) {\n this._emit(\"statechange\");\n }\n }\n\n /**\n * Toggles the collapsed state of the control panel.\n */\n toggle(): void {\n this._state.collapsed = !this._state.collapsed;\n\n if (this._panel) {\n if (this._state.collapsed) {\n this._panel.classList.remove(\"expanded\");\n this._emit(\"collapse\");\n } else {\n this._panel.classList.add(\"expanded\");\n this._updatePanelPosition();\n this._loadCapabilities();\n this._refreshInsertOptions();\n this._emit(\"expand\");\n }\n }\n\n this._emit(\"statechange\");\n }\n\n /**\n * Expands the control panel.\n */\n expand(): void {\n if (this._state.collapsed) {\n this.toggle();\n }\n }\n\n /**\n * Collapses the control panel.\n */\n collapse(): void {\n if (!this._state.collapsed) {\n this.toggle();\n }\n }\n\n /**\n * Registers an event handler.\n *\n * @param event - The event type to listen for\n * @param handler - The callback function\n */\n on(event: NasaEarthdataEvent, handler: NasaEarthdataEventHandler): void {\n if (!this._eventHandlers.has(event)) {\n this._eventHandlers.set(event, new Set());\n }\n this._eventHandlers.get(event)!.add(handler);\n }\n\n /**\n * Removes an event handler.\n *\n * @param event - The event type\n * @param handler - The callback function to remove\n */\n off(event: NasaEarthdataEvent, handler: NasaEarthdataEventHandler): void {\n this._eventHandlers.get(event)?.delete(handler);\n }\n\n /**\n * Gets the map instance.\n *\n * @returns The MapLibre GL map instance or undefined if not added to a map\n */\n getMap(): MapLibreMap | undefined {\n return this._map;\n }\n\n /**\n * Gets the control container element.\n *\n * @returns The container element or undefined if not added to a map\n */\n getContainer(): HTMLElement | undefined {\n return this._container;\n }\n\n /**\n * Fetches and caches the GIBS capabilities document.\n *\n * @param force - If true, refetch even if cached\n * @returns The parsed capabilities\n */\n async getCapabilities(force = false): Promise<GibsCapabilities> {\n const capabilities = await this._client.getCapabilities(force);\n this._capabilities = capabilities;\n return capabilities;\n }\n\n /**\n * Searches the loaded GIBS layers by title or identifier.\n * Returns an empty array if the capabilities have not been loaded yet.\n *\n * @param query - The search query\n * @returns Matching layers\n */\n search(query: string): GibsLayer[] {\n return searchLayers(this._capabilities?.layers ?? [], query);\n }\n\n /**\n * Gets the state of all layers currently added to the map.\n *\n * @returns Added layer states\n */\n getAddedLayers(): AddedLayerState[] {\n return Array.from(this._addedLayers.values()).map((l) => ({ ...l }));\n }\n\n /**\n * Adds a GIBS layer to the map as a raster source and layer.\n * Requires the capabilities to be loaded (call getCapabilities() first\n * when using the control programmatically).\n *\n * @param layerId - The GIBS layer identifier\n * @param options - Optional date, opacity, visibility, and insertion position\n */\n addLayer(\n layerId: string,\n options?: {\n date?: string;\n opacity?: number;\n visible?: boolean;\n before?: string;\n key?: string;\n },\n ): void {\n if (!this._map) return;\n\n const layer = this._client.getLayer(layerId);\n if (!layer) {\n this._emitError(\n new Error(`Unknown GIBS layer: ${layerId} (capabilities not loaded?)`),\n );\n return;\n }\n\n const date = options?.date ?? layer.time?.default;\n const opacity = clamp(options?.opacity ?? 1, 0, 1);\n const visible = options?.visible ?? true;\n const before = options?.before ?? this._insertBefore;\n\n // Restoring a known instance key is a no-op if it is already on the map\n if (options?.key && this._addedLayers.has(options.key)) return;\n const key = options?.key ?? this._instanceKey(layer, date);\n const mapId = LAYER_ID_PREFIX + key;\n\n // Host applications that persist and restore layers may have already\n // recreated the native source/layer from saved state. Reuse them when\n // present instead of re-adding (which would throw on a duplicate id),\n // and apply the requested opacity/visibility so control state and map\n // agree.\n if (!this._map.getSource(mapId)) {\n this._map.addSource(mapId, {\n type: \"raster\",\n tiles: [buildTileUrl(layer, date)],\n tileSize: 256,\n maxzoom: layer.maxZoom,\n attribution: this._options.attribution,\n });\n }\n if (!this._map.getLayer(mapId)) {\n this._map.addLayer(\n {\n id: mapId,\n type: \"raster\",\n source: mapId,\n paint: { \"raster-opacity\": opacity },\n layout: { visibility: visible ? \"visible\" : \"none\" },\n },\n before && this._map.getLayer(before) ? before : undefined,\n );\n } else {\n this._map.setPaintProperty(mapId, \"raster-opacity\", opacity);\n this._map.setLayoutProperty(\n mapId,\n \"visibility\",\n visible ? \"visible\" : \"none\",\n );\n }\n\n const added: AddedLayerState = { key, id: layerId, date, opacity, visible };\n this._addedLayers.set(key, added);\n this._syncAddedLayersState();\n this._renderResults();\n this._renderAddedSection();\n this._refreshInsertOptions();\n this._emit(\"layeradd\", { layer });\n this._emit(\"statechange\");\n }\n\n /**\n * Builds a unique instance key for a layer/date pair. Non-time layers can\n * only be added once; time-enabled layers get one instance per addition,\n * so a numeric suffix disambiguates repeated adds at the same date.\n */\n private _instanceKey(layer: GibsLayer, date?: string): string {\n if (!layer.time) return layer.id;\n\n const base = `${layer.id}@${date ?? layer.time.default}`;\n if (!this._addedLayers.has(base)) return base;\n let n = 2;\n while (this._addedLayers.has(`${base}#${n}`)) n++;\n return `${base}#${n}`;\n }\n\n /**\n * Resolves an instance key or a GIBS layer identifier to instances.\n * An exact key match wins; otherwise all instances of the layer match.\n */\n private _resolveInstances(keyOrId: string): AddedLayerState[] {\n const exact = this._addedLayers.get(keyOrId);\n if (exact) return [exact];\n return Array.from(this._addedLayers.values()).filter(\n (l) => l.id === keyOrId,\n );\n }\n\n /**\n * Removes added GIBS layer instances from the map. Pass an instance key\n * to remove a single instance, or a GIBS layer identifier to remove all\n * instances of that layer.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n */\n removeLayer(keyOrId: string): void {\n const instances = this._resolveInstances(keyOrId);\n if (instances.length === 0) return;\n\n for (const instance of instances) {\n this._removeMapLayer(instance.key);\n this._addedLayers.delete(instance.key);\n this._openLegends.delete(instance.key);\n this._emit(\"layerremove\", { layer: this._client.getLayer(instance.id) });\n }\n this._syncAddedLayersState();\n this._renderResults();\n this._renderAddedSection();\n this._refreshInsertOptions();\n this._emit(\"statechange\");\n }\n\n /**\n * Changes the date of an added time-enabled layer instance.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n * @param date - The new ISO 8601 date\n */\n setLayerDate(keyOrId: string, date: string): void {\n const added = this._resolveInstances(keyOrId)[0];\n const layer = added ? this._client.getLayer(added.id) : undefined;\n if (!this._map || !added || !layer) return;\n\n added.date = date;\n const mapId = LAYER_ID_PREFIX + added.key;\n const source = this._map.getSource(mapId) as RasterTileSource | undefined;\n const tiles = [buildTileUrl(layer, date)];\n\n if (source && typeof source.setTiles === \"function\") {\n source.setTiles(tiles);\n } else {\n // Fallback: re-create the source and layer\n this._removeMapLayer(added.key);\n this._addedLayers.delete(added.key);\n this.addLayer(added.id, {\n date,\n opacity: added.opacity,\n visible: added.visible,\n key: added.key,\n });\n return;\n }\n\n this._syncAddedLayersState();\n this._emit(\"statechange\");\n }\n\n /**\n * Changes the opacity of an added layer instance.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n * @param opacity - The new opacity (0 to 1)\n */\n setLayerOpacity(keyOrId: string, opacity: number): void {\n const added = this._resolveInstances(keyOrId)[0];\n if (!this._map || !added) return;\n\n added.opacity = clamp(opacity, 0, 1);\n this._map.setPaintProperty(\n LAYER_ID_PREFIX + added.key,\n \"raster-opacity\",\n added.opacity,\n );\n this._syncAddedLayersState();\n this._emit(\"statechange\");\n }\n\n /**\n * Toggles the visibility of an added layer instance on the map.\n *\n * @param keyOrId - An instance key or a GIBS layer identifier\n * @param visible - Whether the layer should be visible\n */\n setLayerVisibility(keyOrId: string, visible: boolean): void {\n const added = this._resolveInstances(keyOrId)[0];\n if (!this._map || !added) return;\n\n added.visible = visible;\n this._map.setLayoutProperty(\n LAYER_ID_PREFIX + added.key,\n \"visibility\",\n visible ? \"visible\" : \"none\",\n );\n this._syncAddedLayersState();\n this._emit(\"statechange\");\n }\n\n /**\n * Removes the map source and layer for an instance key, if present.\n */\n private _removeMapLayer(key: string): void {\n if (!this._map) return;\n const mapId = LAYER_ID_PREFIX + key;\n if (this._map.getLayer(mapId)) {\n this._map.removeLayer(mapId);\n }\n if (this._map.getSource(mapId)) {\n this._map.removeSource(mapId);\n }\n }\n\n /**\n * Mirrors the internal added-layers map into the serializable state.\n */\n private _syncAddedLayersState(): void {\n this._state.addedLayers = this.getAddedLayers();\n }\n\n /**\n * Reconciles a desired added-layers list against the map:\n * adds missing layers, removes extras, and applies date/opacity changes.\n */\n private _reconcileAddedLayers(desired: AddedLayerState[]): void {\n const desiredKeys = new Set(desired.map((l) => l.key));\n\n for (const existingKey of Array.from(this._addedLayers.keys())) {\n if (!desiredKeys.has(existingKey)) {\n this.removeLayer(existingKey);\n }\n }\n\n for (const target of desired) {\n const existing = this._addedLayers.get(target.key);\n if (!existing) {\n this.addLayer(target.id, {\n date: target.date,\n opacity: target.opacity,\n visible: target.visible,\n key: target.key,\n });\n } else {\n if (target.date && target.date !== existing.date) {\n this.setLayerDate(target.key, target.date);\n }\n if (\n target.opacity !== undefined &&\n target.opacity !== existing.opacity\n ) {\n this.setLayerOpacity(target.key, target.opacity);\n }\n if (\n target.visible !== undefined &&\n target.visible !== existing.visible\n ) {\n this.setLayerVisibility(target.key, target.visible);\n }\n }\n }\n }\n\n /**\n * Loads the capabilities document (once) and renders the results list.\n */\n private _loadCapabilities(): void {\n if (this._capabilities || this._loading) return;\n\n this._loading = true;\n this._renderStatus(\"Loading NASA GIBS layer catalog…\");\n\n this.getCapabilities()\n .then(() => {\n this._loading = false;\n this._renderResults();\n this._renderAddedSection();\n this._refreshInsertOptions();\n this._emit(\"capabilitiesload\");\n })\n .catch((error: unknown) => {\n this._loading = false;\n const err = error instanceof Error ? error : new Error(String(error));\n this._renderStatus(\n `Failed to load layer catalog: ${err.message}`,\n true,\n );\n this._emitError(err);\n });\n }\n\n /**\n * Emits an event to all registered handlers.\n *\n * @param event - The event type to emit\n * @param extra - Extra payload fields (layer, error)\n */\n private _emit(\n event: NasaEarthdataEvent,\n extra?: Partial<NasaEarthdataEventPayload>,\n ): void {\n const handlers = this._eventHandlers.get(event);\n if (handlers) {\n const eventData: NasaEarthdataEventPayload = {\n type: event,\n state: this.getState(),\n ...extra,\n };\n handlers.forEach((handler) => handler(eventData));\n }\n }\n\n /**\n * Emits an 'error' event.\n */\n private _emitError(error: Error): void {\n this._emit(\"error\", { error });\n }\n\n /**\n * Returns the theme class for the configured theme, if explicit.\n */\n private _themeClass(): string {\n if (this._options.theme === \"dark\") return \" ne-theme-dark\";\n if (this._options.theme === \"light\") return \" ne-theme-light\";\n return \"\";\n }\n\n /**\n * Creates the main container element for the control.\n * Contains a toggle button (29x29) matching navigation control size.\n *\n * @returns The container element\n */\n private _createContainer(): HTMLElement {\n const container = document.createElement(\"div\");\n container.className = `maplibregl-ctrl maplibregl-ctrl-group plugin-control maplibre-gl-nasa-earthdata${this._themeClass()}${\n this._options.className ? ` ${this._options.className}` : \"\"\n }`;\n\n // Create toggle button (29x29 to match navigation control)\n const toggleBtn = document.createElement(\"button\");\n toggleBtn.className = \"plugin-control-toggle\";\n toggleBtn.type = \"button\";\n toggleBtn.setAttribute(\"aria-label\", this._options.title);\n // Globe icon\n toggleBtn.innerHTML = `\n <span class=\"plugin-control-icon\">\n <svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"9\"/>\n <path d=\"M3 12h18\"/>\n <path d=\"M12 3a13.5 13.5 0 0 1 0 18a13.5 13.5 0 0 1 0-18\"/>\n </svg>\n </span>\n `;\n toggleBtn.addEventListener(\"click\", () => this.toggle());\n\n container.appendChild(toggleBtn);\n\n return container;\n }\n\n /**\n * Creates the panel element with header, search box, and results list.\n * Panel is positioned as a dropdown below the toggle button.\n *\n * @returns The panel element\n */\n private _createPanel(): HTMLElement {\n const panel = document.createElement(\"div\");\n panel.className = `plugin-control-panel maplibre-gl-nasa-earthdata${this._themeClass()}`;\n panel.style.width = `${this._options.panelWidth}px`;\n\n // Create header with title and close button\n const header = document.createElement(\"div\");\n header.className = \"plugin-control-header\";\n\n const title = document.createElement(\"span\");\n title.className = \"plugin-control-title\";\n title.textContent = this._options.title;\n\n const closeBtn = document.createElement(\"button\");\n closeBtn.className = \"plugin-control-close\";\n closeBtn.type = \"button\";\n closeBtn.setAttribute(\"aria-label\", \"Close panel\");\n closeBtn.innerHTML = \"×\";\n closeBtn.addEventListener(\"click\", () => this.collapse());\n\n header.appendChild(title);\n header.appendChild(closeBtn);\n\n // Create content area: search input, insert-before row, category list,\n // and the added-layers management section\n const content = document.createElement(\"div\");\n content.className = \"plugin-control-content nasa-content\";\n\n const search = document.createElement(\"input\");\n search.className = \"plugin-control-input nasa-search\";\n search.type = \"search\";\n search.placeholder = \"Search layers (e.g. temperature)\";\n search.setAttribute(\"aria-label\", \"Search NASA GIBS layers\");\n search.value = this._state.query;\n const onSearch = debounce(() => {\n this._state.query = search.value;\n this._renderResults();\n this._emit(\"statechange\");\n }, 250);\n search.addEventListener(\"input\", onSearch);\n this._searchInput = search;\n\n // \"Insert before\" row: choose where new layers are inserted in the\n // map's layer stack\n const insertRow = document.createElement(\"div\");\n insertRow.className = \"nasa-insert-row\";\n\n const insertLabel = document.createElement(\"span\");\n insertLabel.className = \"nasa-insert-label\";\n insertLabel.textContent = \"Insert before\";\n\n const insertSelect = document.createElement(\"select\");\n insertSelect.className = \"nasa-insert-select\";\n insertSelect.setAttribute(\"aria-label\", \"Insert new layers before\");\n insertSelect.addEventListener(\"change\", () => {\n this._insertBefore = insertSelect.value;\n });\n this._insertSelect = insertSelect;\n\n insertRow.appendChild(insertLabel);\n insertRow.appendChild(insertSelect);\n\n const meta = document.createElement(\"div\");\n meta.className = \"nasa-meta\";\n this._metaEl = meta;\n\n // Scrollable body holding the category groups and the added layers\n const body = document.createElement(\"div\");\n body.className = \"nasa-body\";\n\n const results = document.createElement(\"div\");\n results.className = \"nasa-results\";\n this._resultsEl = results;\n\n const added = document.createElement(\"div\");\n added.className = \"nasa-added-section\";\n this._addedEl = added;\n\n body.appendChild(results);\n body.appendChild(added);\n\n content.appendChild(search);\n content.appendChild(insertRow);\n content.appendChild(meta);\n content.appendChild(body);\n\n panel.appendChild(header);\n panel.appendChild(content);\n\n // Resize handle on the panel's outer edge for adjusting the width by\n // dragging. _updatePanelPosition() moves it to the correct side for the\n // control corner (left edge when right-anchored, right edge when\n // left-anchored).\n const resizeHandle = document.createElement(\"div\");\n resizeHandle.className = \"nasa-resize-handle\";\n resizeHandle.setAttribute(\"aria-hidden\", \"true\");\n resizeHandle.addEventListener(\"pointerdown\", (e) =>\n this._startResize(e, resizeHandle),\n );\n panel.appendChild(resizeHandle);\n\n return panel;\n }\n\n /**\n * Returns the maximum panel width that fits the map container.\n */\n private _maxPanelWidth(): number {\n return Math.max(\n 240,\n (this._mapContainer?.getBoundingClientRect().width ?? window.innerWidth) -\n 20,\n );\n }\n\n /**\n * Clamps the panel width to the map container. Applied whenever the panel\n * is (re)positioned so a saved width survives map shrinking.\n */\n private _applyPanelWidthBounds(): void {\n if (!this._panel) return;\n const width = Math.round(\n clamp(this._state.panelWidth, 240, this._maxPanelWidth()),\n );\n this._state.panelWidth = width;\n this._panel.style.width = `${width}px`;\n }\n\n /**\n * Starts a panel width drag-resize. The drag direction is derived from\n * the control corner so resizing works whether the panel is anchored to\n * the left or the right edge of the map.\n */\n private _startResize(e: PointerEvent, handle: HTMLElement): void {\n if (!this._panel) return;\n e.preventDefault();\n\n const startX = e.clientX;\n const startWidth = this._panel.getBoundingClientRect().width;\n // Right-anchored panels grow leftward, so invert the pointer delta\n const anchoredRight = !this._getControlPosition().endsWith(\"left\");\n const maxWidth = this._maxPanelWidth();\n\n const onMove = (ev: PointerEvent) => {\n const delta = anchoredRight ? startX - ev.clientX : ev.clientX - startX;\n const width = Math.round(clamp(startWidth + delta, 240, maxWidth));\n this._state.panelWidth = width;\n if (this._panel) {\n this._panel.style.width = `${width}px`;\n }\n };\n const onEnd = (ev: PointerEvent) => {\n handle.removeEventListener(\"pointermove\", onMove);\n handle.removeEventListener(\"pointerup\", onEnd);\n handle.removeEventListener(\"pointercancel\", onEnd);\n if (handle.hasPointerCapture(ev.pointerId)) {\n handle.releasePointerCapture(ev.pointerId);\n }\n this._panel?.classList.remove(\"nasa-resizing\");\n this._emit(\"statechange\");\n };\n\n handle.setPointerCapture(e.pointerId);\n handle.addEventListener(\"pointermove\", onMove);\n handle.addEventListener(\"pointerup\", onEnd);\n handle.addEventListener(\"pointercancel\", onEnd);\n this._panel.classList.add(\"nasa-resizing\");\n }\n\n /**\n * Shows a status message (loading or error) in the results area.\n */\n private _renderStatus(message: string, isError = false): void {\n if (!this._resultsEl || !this._metaEl) return;\n this._metaEl.textContent = \"\";\n this._resultsEl.innerHTML = \"\";\n const status = document.createElement(\"div\");\n status.className = `nasa-status${isError ? \" nasa-status-error\" : \"\"}`;\n status.textContent = message;\n this._resultsEl.appendChild(status);\n }\n\n /**\n * Groups layers by category, merging categories with fewer layers than\n * the threshold into \"Other\". Returns entries sorted alphabetically\n * with \"Other\" last.\n */\n private _groupByCategory(layers: GibsLayer[]): [string, GibsLayer[]][] {\n const MIN_CATEGORY_SIZE = 3;\n const all = this._capabilities?.layers ?? [];\n\n // Count category sizes over the FULL catalog so a category does not\n // flip into \"Other\" while filtering\n const totals = new globalThis.Map<string, number>();\n for (const layer of all) {\n totals.set(layer.category, (totals.get(layer.category) ?? 0) + 1);\n }\n\n const groups = new globalThis.Map<string, GibsLayer[]>();\n for (const layer of layers) {\n const total = totals.get(layer.category) ?? 0;\n const key = total >= MIN_CATEGORY_SIZE ? layer.category : \"Other\";\n const group = groups.get(key);\n if (group) {\n group.push(layer);\n } else {\n groups.set(key, [layer]);\n }\n }\n\n return Array.from(groups.entries()).sort(([a], [b]) => {\n if (a === \"Other\") return 1;\n if (b === \"Other\") return -1;\n return a.localeCompare(b);\n });\n }\n\n /**\n * Renders the (filtered) layer catalog grouped by category.\n */\n private _renderResults(): void {\n if (!this._resultsEl || !this._metaEl) return;\n if (!this._capabilities) {\n if (!this._loading) {\n this._renderStatus(\"Expand the panel to load the layer catalog.\");\n }\n return;\n }\n\n const query = this._state.query.trim();\n const matches = searchLayers(this._capabilities.layers, query);\n const groups = this._groupByCategory(matches);\n\n const layerWord = matches.length === 1 ? \"layer\" : \"layers\";\n const categoryWord = groups.length === 1 ? \"category\" : \"categories\";\n this._metaEl.textContent = `${matches.length} ${layerWord} in ${groups.length} ${categoryWord}`;\n\n this._resultsEl.innerHTML = \"\";\n if (matches.length === 0) {\n const status = document.createElement(\"div\");\n status.className = \"nasa-status\";\n status.textContent = \"No layers match your search.\";\n this._resultsEl.appendChild(status);\n return;\n }\n\n for (const [category, layers] of groups) {\n // While searching, auto-expand the matching categories\n const expanded = query ? true : this._expandedCategories.has(category);\n this._resultsEl.appendChild(\n this._createCategoryGroup(category, layers, expanded),\n );\n }\n }\n\n /**\n * Creates a collapsible category group with a count badge.\n */\n private _createCategoryGroup(\n category: string,\n layers: GibsLayer[],\n expanded: boolean,\n ): HTMLElement {\n const group = document.createElement(\"div\");\n group.className = \"nasa-category\";\n\n const header = document.createElement(\"button\");\n header.type = \"button\";\n header.className = `nasa-category-header${expanded ? \" expanded\" : \"\"}`;\n header.setAttribute(\"aria-expanded\", String(expanded));\n\n const chevron = document.createElement(\"span\");\n chevron.className = \"nasa-category-chevron\";\n chevron.innerHTML = `\n <svg viewBox=\"0 0 24 24\" width=\"12\" height=\"12\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9 6l6 6-6 6\"/>\n </svg>\n `;\n\n const name = document.createElement(\"span\");\n name.className = \"nasa-category-name\";\n name.textContent = category;\n\n const count = document.createElement(\"span\");\n count.className = \"nasa-category-count\";\n count.textContent = String(layers.length);\n\n header.appendChild(chevron);\n header.appendChild(name);\n header.appendChild(count);\n header.addEventListener(\"click\", () => {\n if (this._expandedCategories.has(category)) {\n this._expandedCategories.delete(category);\n } else {\n this._expandedCategories.add(category);\n }\n this._renderResults();\n });\n\n group.appendChild(header);\n\n if (expanded) {\n const list = document.createElement(\"div\");\n list.className = \"nasa-category-layers\";\n list.setAttribute(\"role\", \"list\");\n\n for (const layer of layers) {\n list.appendChild(this._createLayerRow(layer));\n }\n\n group.appendChild(list);\n }\n\n return group;\n }\n\n /**\n * Creates a single catalog layer row with title, badges, and an action\n * button. Non-time layers toggle add/remove; time-enabled layers always\n * offer \"Add\" so additional instances with different dates can be added.\n * Per-layer controls live in the added-layers section.\n */\n private _createLayerRow(layer: GibsLayer): HTMLElement {\n const instanceCount = Array.from(this._addedLayers.values()).filter(\n (l) => l.id === layer.id,\n ).length;\n const added = instanceCount > 0;\n\n const row = document.createElement(\"div\");\n row.className = `nasa-layer-row${added ? \" nasa-layer-row-added\" : \"\"}`;\n row.setAttribute(\"role\", \"listitem\");\n\n const main = document.createElement(\"div\");\n main.className = \"nasa-layer-main\";\n\n const info = document.createElement(\"div\");\n info.className = \"nasa-layer-info\";\n\n const titleEl = document.createElement(\"div\");\n titleEl.className = \"nasa-layer-title\";\n titleEl.textContent = layer.title;\n titleEl.title = layer.id;\n\n const badges = document.createElement(\"div\");\n badges.className = \"nasa-layer-badges\";\n const formatBadge = document.createElement(\"span\");\n formatBadge.className = \"nasa-badge\";\n formatBadge.textContent = layer.format;\n badges.appendChild(formatBadge);\n if (layer.time) {\n const timeBadge = document.createElement(\"span\");\n timeBadge.className = \"nasa-badge nasa-badge-time\";\n timeBadge.textContent = \"time\";\n timeBadge.title = `Default date: ${layer.time.default}`;\n badges.appendChild(timeBadge);\n }\n if (instanceCount > 1) {\n const countBadge = document.createElement(\"span\");\n countBadge.className = \"nasa-badge nasa-badge-time\";\n countBadge.textContent = `${instanceCount} added`;\n badges.appendChild(countBadge);\n }\n\n info.appendChild(titleEl);\n info.appendChild(badges);\n\n // Time-enabled layers can be added repeatedly (one instance per date),\n // so their button always reads \"Add\"\n const isRemove = added && !layer.time;\n const actionBtn = document.createElement(\"button\");\n actionBtn.type = \"button\";\n actionBtn.className = `nasa-layer-action${isRemove ? \" nasa-layer-action-remove\" : \"\"}`;\n actionBtn.textContent = isRemove ? \"Remove\" : \"Add\";\n actionBtn.setAttribute(\n \"aria-label\",\n isRemove\n ? `Remove layer ${layer.title}`\n : added\n ? `Add layer ${layer.title} for another date`\n : `Add layer ${layer.title}`,\n );\n actionBtn.addEventListener(\"click\", () => {\n if (!layer.time && this._resolveInstances(layer.id).length > 0) {\n this.removeLayer(layer.id);\n } else {\n this.addLayer(layer.id);\n }\n });\n\n main.appendChild(info);\n main.appendChild(actionBtn);\n row.appendChild(main);\n\n return row;\n }\n\n /**\n * Renders the \"Added layers\" management section: visibility checkbox,\n * legend toggle, remove button, opacity slider, and date picker.\n */\n private _renderAddedSection(): void {\n if (!this._addedEl) return;\n\n this._addedEl.innerHTML = \"\";\n if (this._addedLayers.size === 0) return;\n\n const heading = document.createElement(\"div\");\n heading.className = \"nasa-added-heading\";\n heading.textContent = \"Added layers\";\n this._addedEl.appendChild(heading);\n\n for (const added of this._addedLayers.values()) {\n const layer = this._client.getLayer(added.id);\n if (layer) {\n this._addedEl.appendChild(this._createAddedRow(layer, added));\n }\n }\n }\n\n /**\n * Creates a management card for one added layer.\n */\n private _createAddedRow(\n layer: GibsLayer,\n added: AddedLayerState,\n ): HTMLElement {\n const card = document.createElement(\"div\");\n card.className = \"nasa-added-card\";\n\n // Header row: visibility checkbox, title, legend toggle, remove\n const head = document.createElement(\"div\");\n head.className = \"nasa-added-head\";\n\n const visLabel = document.createElement(\"label\");\n visLabel.className = \"nasa-added-vis\";\n visLabel.title = \"Toggle layer visibility\";\n\n const visCheckbox = document.createElement(\"input\");\n visCheckbox.type = \"checkbox\";\n visCheckbox.className = \"nasa-added-checkbox\";\n visCheckbox.checked = added.visible;\n visCheckbox.setAttribute(\n \"aria-label\",\n `Toggle visibility of ${layer.title}`,\n );\n visCheckbox.addEventListener(\"change\", () => {\n this.setLayerVisibility(added.key, visCheckbox.checked);\n });\n\n const titleEl = document.createElement(\"span\");\n titleEl.className = \"nasa-added-title\";\n titleEl.textContent = layer.title;\n titleEl.title = added.key;\n\n visLabel.appendChild(visCheckbox);\n visLabel.appendChild(titleEl);\n\n // Date chip distinguishes multiple instances of the same time layer\n let dateChip: HTMLElement | undefined;\n if (layer.time && added.date) {\n dateChip = document.createElement(\"span\");\n dateChip.className = \"nasa-badge nasa-badge-time\";\n dateChip.textContent = added.date.slice(0, 10);\n visLabel.appendChild(dateChip);\n }\n\n const actions = document.createElement(\"div\");\n actions.className = \"nasa-added-actions\";\n\n if (layer.legendUrl) {\n const legendBtn = document.createElement(\"button\");\n legendBtn.type = \"button\";\n legendBtn.className = `nasa-icon-button${\n this._openLegends.has(added.key) ? \" active\" : \"\"\n }`;\n legendBtn.title = \"Toggle legend\";\n legendBtn.setAttribute(\"aria-label\", `Toggle legend for ${layer.title}`);\n legendBtn.innerHTML = `\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M8 6h13M8 12h13M8 18h13\"/>\n <rect x=\"3\" y=\"4.5\" width=\"3\" height=\"3\" rx=\"0.5\"/>\n <rect x=\"3\" y=\"10.5\" width=\"3\" height=\"3\" rx=\"0.5\"/>\n <rect x=\"3\" y=\"16.5\" width=\"3\" height=\"3\" rx=\"0.5\"/>\n </svg>\n `;\n legendBtn.addEventListener(\"click\", () => {\n if (this._openLegends.has(added.key)) {\n this._openLegends.delete(added.key);\n } else {\n this._openLegends.add(added.key);\n }\n this._renderAddedSection();\n });\n actions.appendChild(legendBtn);\n }\n\n const removeBtn = document.createElement(\"button\");\n removeBtn.type = \"button\";\n removeBtn.className = \"nasa-icon-button nasa-icon-button-danger\";\n removeBtn.title = \"Remove layer\";\n removeBtn.setAttribute(\"aria-label\", `Remove layer ${layer.title}`);\n removeBtn.innerHTML = `\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\"/>\n <path d=\"M10 11v6M14 11v6\"/>\n </svg>\n `;\n removeBtn.addEventListener(\"click\", () => {\n this.removeLayer(added.key);\n });\n actions.appendChild(removeBtn);\n\n head.appendChild(visLabel);\n head.appendChild(actions);\n card.appendChild(head);\n\n // Opacity slider with live percentage readout\n if (this._options.showOpacity) {\n const opacityRow = document.createElement(\"label\");\n opacityRow.className = \"nasa-control-label\";\n opacityRow.textContent = \"Opacity\";\n\n const opacityInput = document.createElement(\"input\");\n opacityInput.type = \"range\";\n opacityInput.className = \"nasa-opacity\";\n opacityInput.min = \"0\";\n opacityInput.max = \"1\";\n opacityInput.step = \"0.05\";\n opacityInput.value = String(added.opacity);\n opacityInput.setAttribute(\"aria-label\", `Opacity for ${layer.title}`);\n\n const opacityValue = document.createElement(\"span\");\n opacityValue.className = \"nasa-opacity-value\";\n opacityValue.textContent = `${Math.round(added.opacity * 100)}%`;\n\n opacityInput.addEventListener(\"input\", () => {\n const value = Number(opacityInput.value);\n this.setLayerOpacity(added.key, value);\n opacityValue.textContent = `${Math.round(value * 100)}%`;\n });\n\n opacityRow.appendChild(opacityInput);\n opacityRow.appendChild(opacityValue);\n card.appendChild(opacityRow);\n }\n\n // Date picker for time-enabled layers\n if (layer.time) {\n const dateLabel = document.createElement(\"label\");\n dateLabel.className = \"nasa-control-label\";\n dateLabel.textContent = \"Date\";\n\n const dateInput = document.createElement(\"input\");\n dateInput.type = \"date\";\n dateInput.className = \"nasa-date\";\n dateInput.value = (added.date ?? layer.time.default).slice(0, 10);\n const range = this._timeRange(layer);\n if (range.min) dateInput.min = range.min;\n if (range.max) dateInput.max = range.max;\n dateInput.addEventListener(\"change\", () => {\n if (dateInput.value) {\n this.setLayerDate(added.key, dateInput.value);\n if (dateChip) {\n dateChip.textContent = dateInput.value;\n }\n }\n });\n\n dateLabel.appendChild(dateInput);\n card.appendChild(dateLabel);\n }\n\n // Legend image (GIBS horizontal legend SVG)\n if (layer.legendUrl && this._openLegends.has(added.key)) {\n const legend = document.createElement(\"img\");\n legend.className = \"nasa-legend-image\";\n legend.src = layer.legendUrl;\n legend.alt = `Legend for ${layer.title}`;\n legend.loading = \"lazy\";\n card.appendChild(legend);\n }\n\n return card;\n }\n\n /**\n * Refreshes the \"Insert before\" dropdown with the map's current layers.\n */\n private _refreshInsertOptions(): void {\n if (!this._insertSelect || !this._map) return;\n\n const layers = this._map.getStyle()?.layers ?? [];\n const select = this._insertSelect;\n select.innerHTML = \"\";\n\n const topOption = document.createElement(\"option\");\n topOption.value = \"\";\n topOption.textContent = \"Top of map\";\n select.appendChild(topOption);\n\n for (const layer of layers) {\n const option = document.createElement(\"option\");\n option.value = layer.id;\n option.textContent = layer.id;\n select.appendChild(option);\n }\n\n // Restore the previous selection if the layer still exists\n if (this._insertBefore && layers.some((l) => l.id === this._insertBefore)) {\n select.value = this._insertBefore;\n } else {\n this._insertBefore = \"\";\n select.value = \"\";\n }\n }\n\n /**\n * Derives the min/max selectable dates from a layer's time domain values.\n */\n private _timeRange(layer: GibsLayer): { min?: string; max?: string } {\n const time = layer.time;\n if (!time) return {};\n\n let min: string | undefined;\n let max: string | undefined;\n for (const value of time.values) {\n const [start, end] = value.split(\"/\");\n const startDate = start?.slice(0, 10);\n const endDate = (end ?? start)?.slice(0, 10);\n if (startDate && (!min || startDate < min)) min = startDate;\n if (endDate && (!max || endDate > max)) max = endDate;\n }\n const defaultDate = time.default.slice(0, 10);\n if (!max || defaultDate > max) max = defaultDate;\n return { min, max };\n }\n\n /**\n * Setup event listeners for panel positioning and click-outside behavior.\n */\n private _setupEventListeners(): void {\n // Click outside to close (check both container and panel since they're now separate).\n // Ignore targets that are no longer connected to the DOM: clicking an\n // Add/Remove button re-renders the results list, detaching the button\n // before this document-level handler runs.\n this._clickOutsideHandler = (e: MouseEvent) => {\n const target = e.target as Node;\n if (\n this._container &&\n this._panel &&\n target.isConnected &&\n !this._container.contains(target) &&\n !this._panel.contains(target)\n ) {\n this.collapse();\n }\n };\n document.addEventListener(\"click\", this._clickOutsideHandler);\n\n // Update panel position on window resize\n this._resizeHandler = () => {\n if (!this._state.collapsed) {\n this._updatePanelPosition();\n }\n };\n window.addEventListener(\"resize\", this._resizeHandler);\n\n // Update panel position on map resize (e.g., sidebar toggle)\n this._mapResizeHandler = () => {\n if (!this._state.collapsed) {\n this._updatePanelPosition();\n }\n };\n this._map?.on(\"resize\", this._mapResizeHandler);\n }\n\n /**\n * Detect which corner the control is positioned in.\n *\n * @returns The position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n */\n private _getControlPosition():\n | \"top-left\"\n | \"top-right\"\n | \"bottom-left\"\n | \"bottom-right\" {\n const parent = this._container?.parentElement;\n if (!parent) return \"top-right\"; // Default\n\n if (parent.classList.contains(\"maplibregl-ctrl-top-left\"))\n return \"top-left\";\n if (parent.classList.contains(\"maplibregl-ctrl-top-right\"))\n return \"top-right\";\n if (parent.classList.contains(\"maplibregl-ctrl-bottom-left\"))\n return \"bottom-left\";\n if (parent.classList.contains(\"maplibregl-ctrl-bottom-right\"))\n return \"bottom-right\";\n\n return \"top-right\"; // Default\n }\n\n /**\n * Update the panel position based on button location and control corner.\n * Positions the panel next to the button, expanding in the appropriate direction.\n */\n private _updatePanelPosition(): void {\n if (!this._container || !this._panel || !this._mapContainer) return;\n\n // Get the toggle button (first child of container)\n const button = this._container.querySelector(\".plugin-control-toggle\");\n if (!button) return;\n\n // Keep the panel within the map even if it shrank since the width was set\n this._applyPanelWidthBounds();\n\n const buttonRect = button.getBoundingClientRect();\n const mapRect = this._mapContainer.getBoundingClientRect();\n const position = this._getControlPosition();\n\n // Calculate button position relative to map container\n const buttonTop = buttonRect.top - mapRect.top;\n const buttonBottom = mapRect.bottom - buttonRect.bottom;\n const buttonLeft = buttonRect.left - mapRect.left;\n const buttonRight = mapRect.right - buttonRect.right;\n\n const panelGap = 5; // Gap between button and panel\n\n // Reset all positioning\n this._panel.style.top = \"\";\n this._panel.style.bottom = \"\";\n this._panel.style.left = \"\";\n this._panel.style.right = \"\";\n\n // Keep the resize handle on the panel's outer (growing) edge:\n // right edge for left-anchored panels, left edge for right-anchored ones\n this._panel\n .querySelector(\".nasa-resize-handle\")\n ?.classList.toggle(\"nasa-resize-handle-right\", position.endsWith(\"left\"));\n\n switch (position) {\n case \"top-left\":\n // Panel expands down and to the right\n this._panel.style.top = `${buttonTop + buttonRect.height + panelGap}px`;\n this._panel.style.left = `${buttonLeft}px`;\n break;\n\n case \"top-right\":\n // Panel expands down and to the left\n this._panel.style.top = `${buttonTop + buttonRect.height + panelGap}px`;\n this._panel.style.right = `${buttonRight}px`;\n break;\n\n case \"bottom-left\":\n // Panel expands up and to the right\n this._panel.style.bottom = `${buttonBottom + buttonRect.height + panelGap}px`;\n this._panel.style.left = `${buttonLeft}px`;\n break;\n\n case \"bottom-right\":\n // Panel expands up and to the left\n this._panel.style.bottom = `${buttonBottom + buttonRect.height + panelGap}px`;\n this._panel.style.right = `${buttonRight}px`;\n break;\n }\n }\n}\n"],"mappings":"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"}
|