🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

river-data-widget

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

river-data-widget - npm Package Compare versions

Comparing version
1.0.0
to
1.0.1
+13
-9
dist/index.js

@@ -1,2 +0,2 @@

/*! RiverDataWidget v1.0.0 2023-05-17 08:04:47
/*! RiverDataWidget v1.0.1 2023-05-17 11:39:50
*! https://github.com/pb-uk/river-data-widget#readme

@@ -7,3 +7,3 @@ *! Copyright (C) 2023 pbuk (https://github.com/pb-uk).

var version = "1.0.0";
var version = "1.0.1";

@@ -267,3 +267,3 @@ class RiverDataWidgetError extends Error {

const [time, value] = data[data.length - 1];
const { minTime, timeScale, minValue, maxValue, valueScale } = this.getLimits();
const { minTime, timeScale, minValue, valueScale } = this.getLimits();
const v = formatter == null ? value : formatter(value);

@@ -426,3 +426,4 @@ const xOffset = this.strokeWidth / 2;

const THROTTLE_MS = 15 * MINUTE_MS;
// Throttle requests to five minutes.
const THROTTLE_MS = 5 * MINUTE_MS;
/**

@@ -460,3 +461,3 @@ * Fetch the readings for a measure.

const store = useStore();
const { data, lastCheck, storedSince } = store.get(key) || {
const stored = store.get(key) || {
data: [],

@@ -466,8 +467,11 @@ lastCheck: 0,

};
const discardBefore = startOfDay(null, -30, true).valueOf() / 1000;
const { data, lastCheck } = stored;
let { storedSince } = stored;
const discardBefore = startOfDay(null, -8, true).valueOf() / 1000;
// Discard any older than 30 days.
while (data.length && data[0][0] < discardBefore) {
[storedSince] = data[0];
data.shift();
}
// If we have data early enough Throttle at 15 mins.
// If we have data early enough apply throttle.
const lastStored = data.length ? data[data.length - 1][0] : 0;

@@ -483,4 +487,4 @@ const requestedSince = (options.since && options.since.valueOf() / 1000) || 0;

mergeReadings(data, newData);
const newSince = Math.min(requestedSince, storedSince);
store.set(key, { lastCheck: Date.now() / 1000, data, storedSince: newSince });
storedSince = Math.min(requestedSince, storedSince);
store.set(key, { lastCheck: Date.now() / 1000, data, storedSince });
return filterSince(data, requestedSince);

@@ -487,0 +491,0 @@ };

@@ -1,1 +0,1 @@

{"version":3,"file":"index.js","sources":["../src/error.ts","../src/helpers/format.ts","../src/flood-monitoring-api/error.ts","../src/flood-monitoring-api/measure.ts","../src/helpers/dom.ts","../src/helpers/time.ts","../src/widget/chart.ts","../src/flood-monitoring-api/api.ts","../src/flood-monitoring-api/store.ts","../src/flood-monitoring-api/reading.ts","../src/widget/render.ts","../src/autoload.ts"],"sourcesContent":["export type RiverDataWidgetErrorInfo = Record<string, unknown>;\n\nexport class RiverDataWidgetError extends Error {\n public info: RiverDataWidgetErrorInfo;\n\n constructor(msg: string, info: RiverDataWidgetErrorInfo = {}) {\n super(msg);\n this.name = 'RiverDataWidgetError';\n this.info = info;\n }\n}\n","export const FONT_STACK =\n '-apple-system,BlinkMacSystemFont,\"Segoe UI\",\"Roboto\",\"Helvetica Neue\",Arial,sans-serif';\n\nexport const round3 = (value: number) =>\n value < 100 ? value.toPrecision(3) : Math.round(value).toString();\n","export class FloodMonitoringApiError extends Error {\n public info: Record<string, unknown>;\n\n constructor(msg: string, info: Record<string, unknown> = {}) {\n super(msg);\n this.name = 'FloodMonitoringApiError';\n this.info = info;\n }\n}\n","import { FloodMonitoringApiError } from './error';\n\nexport { parseMeasureId };\n\nconst parseMeasureId = (measureId: string) => {\n // ............base/ stat-paramet-qualifi- type -interva-unit\n const regExp = /(.*)-([^-]*)-([^-]*)-([^-]*)-([^-]*)-([^-]*)$/;\n const matches = measureId.match(regExp);\n if (matches === null) {\n throw new FloodMonitoringApiError('Cannot parse measure id', { measureId });\n }\n const [unit, interval, type, qualifier, parameter, stationId] =\n matches.reverse();\n const qualifiedParameter = qualifier.length\n ? `${parameter}-${qualifier}`\n : parameter;\n return {\n stationId,\n parameter,\n qualifier,\n type,\n interval,\n unit,\n qualifiedParameter,\n };\n};\n\nconst measureTranslations: Record<string, Record<string, string>> = {\n unit: {\n m3_s: 'm³/s',\n mAOD: 'm',\n mASD: 'm',\n },\n qualifiedParameter: {\n 'level-stage': 'level',\n 'level-downstage': 'downstream level',\n },\n};\n\nexport const translateMeasureProperties = (measure: Record<string, string>) => {\n const translated: Record<string, string> = {};\n for (const prop in measure) {\n const value = measure[prop];\n if (measureTranslations[prop] && measureTranslations[prop][value]) {\n translated[prop] = measureTranslations[prop][value];\n } else {\n translated[prop] = value;\n }\n }\n return translated;\n};\n","/**\n * RiverDataWidget https://github.com/pb-uk/river-data-widget.\n *\n * @copyright Copyright (C) 2022 pbuk https://github.com/pb-uk.\n * @license AGPL-3.0-or-later see LICENSE.md.\n */\n\nexport { createElement, createSvgElement, setAttributes, setStyles };\n\ntype AttributeList = Record<string, string | number>;\n\nconst setAttributes = <T extends HTMLElement | SVGElement>(\n el: T,\n attributes: AttributeList\n): T => {\n Object.entries(attributes).forEach(([key, value]) => {\n el.setAttribute(key, `${value}`);\n });\n return el;\n};\n\nconst setStyles = <T extends HTMLElement | SVGElement>(\n el: T,\n styles: AttributeList\n): T => {\n Object.entries(styles).forEach(([key, value]) => {\n // Workaround (el.style.setProperty uses kebab-case keys).\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (<any>el.style)[key] = value;\n });\n return el;\n};\n\nconst createElement = (\n name = 'div',\n attributes: AttributeList = {},\n styles: AttributeList = {},\n innerHTML: string | false = false\n): HTMLElement => {\n const el = document.createElement(name);\n if (innerHTML !== false) {\n el.innerHTML = innerHTML;\n }\n return setStyles(setAttributes(el, attributes), styles);\n};\n\nconst createSvgElement = (\n name = 'svg',\n attributes: AttributeList = {},\n styles: AttributeList = {},\n innerHTML: string | false = false\n) => {\n const el = document.createElementNS('http://www.w3.org/2000/svg', name);\n if (innerHTML !== false) {\n el.innerHTML = innerHTML;\n }\n return setStyles(setAttributes(el, attributes), styles);\n};\n","export const MINUTE_MS = 60000;\n// const HOUR_MS = 3600000;\nexport const DAY_MS = 86400000;\n\n/**\n * Get the Date at the start of a day in UTC or local time.\n *\n * @param offset\n * @param timeZone The time zone offset in minutes, or set to `true` to use the\n * local time zone (`false`, the default, uses UTC).\n * @returns The reqested date.\n */\nexport const startOfDay = (\n date: Date | null = null,\n offset = 0,\n timeZone: boolean | number = false\n): Date => {\n if (timeZone === false) {\n // Use UTC.\n const base = date === null ? Date.now() : date.valueOf();\n return new Date(Math.floor(base / DAY_MS + offset) * DAY_MS);\n }\n\n const now = new Date();\n const tz = timeZone === true ? now.getTimezoneOffset() : timeZone;\n const local = now.valueOf() + tz * MINUTE_MS;\n return new Date(Math.floor(local / DAY_MS + offset) * DAY_MS);\n};\n\n/**\n * | | long |short|narrow|numeric|2-digit|\n * |:-------:|:-----------:|:---:|:----:|:-----:|:-----:|\n * | weekday | Monday | Mon | M | | |\n * | era | Anno Domini | AD | A | | |\n * | year | | | | 2012 | 12 |\n * | month | March | Mar | M | 3 | 03 |\n * | day | | | | 1 | 01 |\n * | hour | | | | 1 | 01 |\n * | minute | | | | 1 | 01 |\n * | second | | | | 1 | 01 |\n *\n * * fractionalSecondDigits: 1, 2 or 3 for number of digits.\n * * timeZoneName: long (Pacific Standard Time), short (PST),\n * longOffset (GMT-0800), shortOffset (GMT-8), longGeneric (Pacific Time),\n * shortGeneric (PT).\n */\n\nexport const dateFormatter = new Intl.DateTimeFormat('en-GB', {\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n // year: 'numeric',\n});\n\nexport const timeFormatter = new Intl.DateTimeFormat('en-GB', {\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n timeZoneName: 'short',\n});\n\nexport const dddFormatter = new Intl.DateTimeFormat('en-GB', {\n weekday: 'short',\n});\n\nexport const dMmmFormatter = new Intl.DateTimeFormat('en-GB', {\n day: 'numeric',\n month: 'short',\n});\n","import { createSvgElement } from '../helpers/dom';\nimport { timeFormatter, dddFormatter, dMmmFormatter } from '../helpers/time';\nimport { FloodMonitoringApiError } from '../flood-monitoring-api/error';\nimport { FONT_STACK } from '../helpers/format';\n\nconst PLOT_COLOR = '#77C';\n\nexport interface ChartOptions {\n minTime?: number;\n maxTime?: number;\n attribution?: string;\n}\n\nexport interface ChartScaleLimits {\n minTime: number;\n maxTime: number;\n timeScale: number;\n minValue: number;\n maxValue: number;\n valueScale: number;\n}\n\nexport interface ChartSeries {\n data: TimeSeriesValue[];\n min?: number;\n max?: number;\n unit?: string;\n formatter?: (value: number) => string;\n}\n\nexport type TimeSeriesValue = [\n ts: number, // Unix time stamp (seconds).\n v: number // Value.\n];\n\nexport class Chart {\n protected fontSizePx = 14;\n\n protected el: SVGElement;\n protected series: ChartSeries[];\n protected options: ChartOptions;\n protected width = 480; // 400;\n protected height = 270; // 225;\n protected plotHeight = this.height - this.fontSizePx * 4.5;\n protected strokeWidth = 2;\n protected limits?: ChartScaleLimits;\n\n protected attribution =\n 'Uses Environment Agency data from the real-time API (Beta)';\n\n // CSS settings.\n // Just readable at 320x180.\n // Good from 400x225.\n // Perfect at 480x270 (font is 12px);\n protected styles = {\n 'font-family': FONT_STACK,\n 'font-size': `${this.fontSizePx}px`,\n display: 'block',\n margin: 'auto',\n 'max-width': '150vh',\n };\n\n constructor(\n el: HTMLElement,\n series: ChartSeries[],\n options: ChartOptions = {}\n ) {\n this.series = series;\n this.options = options;\n const viewBox = `0 0 ${this.width} ${this.height}`;\n this.attribution = options.attribution ?? this.attribution;\n this.el = createSvgElement('svg', { viewBox }, this.styles);\n el.append(this.el);\n }\n\n getLimits(): ChartScaleLimits {\n if (this.limits == null) {\n throw new FloodMonitoringApiError('Chart axis limits have not been set');\n }\n return this.limits;\n }\n\n getHorizontalGridlines(): SVGElement[] {\n const { minTime, maxTime, timeScale, minValue, maxValue, valueScale } =\n this.getLimits();\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight;\n const x1 = xOffset;\n const x2 = xOffset + (maxTime - minTime) * timeScale;\n // Horizontal grid lines.\n const stroke = '#ddd';\n const lines = createSvgElement('g', { stroke });\n const labels = createSvgElement('g');\n const valueRange = maxValue - minValue;\n // Horizontal grid interval.\n const [interval, exponent] = getInterval(valueRange, 9);\n const factor = 10 ** -exponent;\n const base = Math.ceil((minValue * factor) / interval + 1) * interval;\n let i = 0;\n let current = base / factor;\n while (current < maxValue) {\n const y1 = yOffset - (current - minValue) * valueScale;\n lines.append(createSvgElement('line', { x1, y1, x2, y2: y1 }));\n labels.append(\n createSvgElement('text', { x: x1 + 4, y: y1 + 4 }, {}, `${current}`)\n );\n ++i;\n current = (base + i * interval) / factor;\n }\n const timeAxisLine = createSvgElement(\n 'line',\n { x1, y1: yOffset, x2, y2: yOffset },\n { stroke: '#777' }\n );\n\n return [lines, labels, timeAxisLine];\n }\n\n getTimeScale(): SVGElement[] {\n const { minTime, maxTime, timeScale } = this.getLimits();\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight + this.strokeWidth / 2;\n const y1 = yOffset + this.fontSizePx * 3;\n const y2 = yOffset - this.plotHeight;\n // Vertical grid lines.\n const stroke = '#ddd';\n const lines = createSvgElement('g', { stroke });\n const labels = createSvgElement('g');\n // Vertical grid interval.\n const base = minTime;\n const interval = 86400;\n let i = 0;\n let current = base;\n const labelOffset = 43200 * timeScale;\n const fill = '#444';\n while (current <= maxTime) {\n const x1 = xOffset + (current - minTime) * timeScale;\n const d = new Date(current * 1000);\n // lines.append(createSvgElement('line', { x1, y1, x2, y2: y1 }));\n lines.append(createSvgElement('line', { x1, y1, x2: x1, y2 }));\n labels.append(\n createSvgElement(\n 'text',\n {\n x: x1 + labelOffset,\n y: y1 - this.fontSizePx * 1.8,\n 'text-anchor': 'middle',\n },\n { fill },\n `${dddFormatter.format(d)}`\n ),\n createSvgElement(\n 'text',\n {\n x: x1 + labelOffset,\n y: y1 - this.fontSizePx * 0.5,\n 'text-anchor': 'middle',\n },\n { fill },\n `${dMmmFormatter.format(d)}`\n )\n );\n ++i;\n current = base + i * interval;\n }\n return [lines, labels];\n }\n\n render() {\n // Calculate axis scales.\n const limits = getLimits(this.series[0].data);\n limits.minValue = this.series[0].min ?? limits.minValue;\n limits.maxValue = this.series[0].max ?? limits.maxValue;\n limits.minTime = this.options.minTime ?? limits.minTime;\n limits.maxTime = this.options.maxTime ?? limits.maxTime;\n\n this.limits = {\n ...limits,\n valueScale:\n (this.plotHeight - this.strokeWidth) /\n (limits.maxValue - limits.minValue),\n timeScale:\n (this.width - this.strokeWidth) / (limits.maxTime - limits.minTime),\n };\n\n // Time axis.\n const [timeLines, timeLabels] = this.getTimeScale();\n this.el.append(timeLines);\n\n // Value axis.\n const [valueLines, valueLabels, timeAxisLine] =\n this.getHorizontalGridlines();\n this.el.append(valueLines);\n this.el.append(timeAxisLine);\n\n this.plotData();\n\n // Plot labels on top of the line.\n this.el.append(timeLabels);\n this.el.append(valueLabels);\n\n this.el.append(\n createSvgElement(\n 'text',\n {\n x: this.width / 2,\n 'text-anchor': 'middle',\n y: this.height - this.fontSizePx * 0.5,\n },\n { fill: '#595959' },\n this.attribution\n )\n );\n\n this.plotLastValue();\n }\n\n plotLastValue() {\n const { data, unit, formatter } = this.series[0];\n const [time, value] = data[data.length - 1];\n const { minTime, timeScale, minValue, maxValue, valueScale } =\n this.getLimits();\n\n const v = formatter == null ? value : formatter(value);\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight - this.strokeWidth / 2;\n const x = xOffset + (time - minTime) * timeScale;\n const highLabel = (value - minValue) / valueScale >= 0.5;\n const y =\n yOffset - this.plotHeight * (highLabel ? 1 : 0.5) + this.fontSizePx * 2;\n\n this.el.append(\n createSvgElement(\n 'text',\n { x, y, 'text-anchor': 'end' },\n { fill: PLOT_COLOR, 'font-size': '1.5em', 'font-weight': 'bold' },\n `${v} ${unit}`\n )\n );\n this.el.append(\n createSvgElement(\n 'text',\n { x, y: y + this.fontSizePx * 1.5, 'text-anchor': 'end' },\n { fill: PLOT_COLOR },\n `${timeFormatter.format(new Date(time * 1000))}`\n )\n );\n }\n\n plotData() {\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight - this.strokeWidth / 2;\n const { data } = this.series[0];\n const { minTime, timeScale, minValue, valueScale } = this.getLimits();\n // First data point.\n const x = xOffset + (data[0][0] - minTime) * timeScale;\n const y = yOffset - (data[0][1] - minValue) * valueScale;\n const points = [`M${x},${y}`];\n // Remaining data points.\n for (let i = 1; i < data.length; ++i) {\n const x = xOffset + (data[i][0] - minTime) * timeScale;\n const y = yOffset - (data[i][1] - minValue) * valueScale;\n points.push(`L${x},${y}`);\n }\n // Plot the data.\n const path = createSvgElement('path', {\n d: points.join(''),\n stroke: PLOT_COLOR,\n 'stroke-width': this.strokeWidth,\n fill: 'none',\n });\n this.el.append(path);\n }\n}\n\nexport const getLimits = (data: TimeSeriesValue[]) => {\n if (data.length < 1) {\n throw new Error('Readings must not be empty');\n }\n const minTime = data[0][0];\n const maxTime = data[data.length - 1][0];\n let minValue = Infinity;\n let maxValue = -minValue;\n data.forEach(([, value]) => {\n minValue = Math.min(minValue, value);\n maxValue = Math.max(maxValue, value);\n });\n return { minTime, maxTime, minValue, maxValue };\n};\n\nexport const getInterval = (range: number, maxDivisions: number) => {\n const exponent = Math.floor(Math.log10(range)) - 1;\n const k = range / (maxDivisions * 10 ** exponent);\n const mantissa = k <= 2 ? 2 : k <= 5 ? 5 : 10;\n return [mantissa, exponent];\n};\n","// There is no need to be secure about this!\nconst baseUrl = 'http://environment.data.gov.uk/flood-monitoring';\n\nexport interface ApiResponse<T> {\n data: {\n items: T;\n };\n response: Response;\n}\n\nexport interface ApiParameters {\n since?: string; // Time from.\n _sorted?: ''; // Flag for sorting.\n}\n\nexport const apiFetch = async (\n path: string,\n query = {}\n): Promise<ApiResponse<unknown>> => {\n const queryString = new URLSearchParams(query).toString();\n const uri = queryString\n ? `${baseUrl}${path}?${queryString}`\n : `${baseUrl}${path}`;\n const response = await fetch(uri);\n return { data: await response.json(), response };\n};\n\n/**\n * Convert a Date to a format recognized by the EA API for a query parameter.\n *\n * @param date Convert from.\n * @returns A string in the EA API query parameter format.\n */\nexport const toTimeParameter = (date: Date): string => {\n return date.toISOString().substring(0, 19) + 'Z';\n};\n\n/*\nUseful response headers\n Date: 'Sat, 13 May 2023 09:14:07 GMT',\n last-modified: Sat, 13 May 2023 09:03:13 GMT,\nResponse meta:\n publisher: 'Environment Agency',\n license: 'http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/',\n documentation: 'http://environment.data.gov.uk/flood-monitoring/doc/reference',\n version: '0.9',\n comment: 'Status: Beta service',\n hasFormat: [\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.csv?_sorted=&since=2023-05-12T08%3A00%3A00Z\",\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.rdf?_sorted=&since=2023-05-12T08%3A00%3A00Z\",\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.ttl?_sorted=&since=2023-05-12T08%3A00%3A00Z\",\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.html?_sorted=&since=2023-05-12T08%3A00%3A00Z\"\n ],\n*/\n","const prefix = 'riverDataWidget';\n\nconst addPrefix = (key: string): string => `${prefix}|${key}`;\n\nlet instance: Store;\n\nclass Store {\n clear(destroy = false) {\n for (const key of this.keys()) {\n localStorage.removeItem(addPrefix(key));\n }\n if (destroy) {\n localStorage.removeItem(prefix);\n return;\n }\n localStorage.setItem(prefix, JSON.stringify([]));\n }\n\n get(key: string) {\n const value = localStorage.getItem(addPrefix(key));\n return value === null ? null : JSON.parse(value);\n }\n\n has(key: string): boolean {\n return this.keys().includes(key);\n }\n\n /**\n * Detect active localStorage.\n *\n * @returns true iff localStorage for the widget is active.\n */\n isActive() {\n return localStorage.getItem(prefix) !== null;\n }\n\n keys(): string[] {\n const storedKeys = localStorage.getItem(prefix);\n return storedKeys === null ? [] : JSON.parse(storedKeys);\n }\n\n set(key: string, value: unknown) {\n const json = JSON.stringify(value);\n const storedKeys = localStorage.getItem(prefix);\n const keys: string[] = storedKeys === null ? [] : JSON.parse(storedKeys);\n if (!keys.includes(key)) {\n keys.push(key);\n localStorage.setItem(prefix, JSON.stringify(keys));\n }\n localStorage.setItem(addPrefix(key), json);\n }\n\n unset(key: string): boolean {\n // Remove it before we do anything else.\n localStorage.removeItem(addPrefix(key));\n\n // Then remove it from the list of keys.\n const storedKeys = localStorage.getItem(prefix);\n const keys: string[] = storedKeys === null ? [] : JSON.parse(storedKeys);\n const index = keys.indexOf(key);\n\n // If it doesn't exist we don't have to remove it.\n if (index === -1) return false;\n\n keys.splice(index, 1);\n localStorage.setItem(prefix, JSON.stringify(keys));\n return true;\n }\n}\n\nexport const useStore = (): Store => {\n if (!instance) {\n instance = new Store();\n }\n return instance;\n};\n","import { apiFetch, toTimeParameter } from './api';\nimport { useStore } from './store';\nimport { MINUTE_MS, startOfDay } from '../helpers/time';\n\nimport type { ApiParameters, ApiResponse } from './api';\n\nconst THROTTLE_MS = 15 * MINUTE_MS;\n\n/**\n * Internal format for readings.\n */\nexport type Reading = [\n timestamp: number, // Unix epoch timestamp (seconds).\n value: number // Value.\n];\n\n/**\n * Internal format for readings.\n */\nexport interface ReadingOptions {\n since?: Date; // Time from.\n}\n\n/**\n * Internal format for readings.\n */\ntype ReadingResponse = [a: Reading[], b: ApiResponse<ReadingDTO[]>];\n\n/**\n * Data transfer object for readings provided by the API.\n */\ninterface ReadingDTO {\n '@id': string; // The URL of this reading.\n dateTime: string; // e.g. '2023-05-13T09:00:00Z'.\n measure: string; // The URL of the measure.\n value: number; // The value in the appropriate units.\n}\n\ninterface StoredReadings {\n storedSince: number;\n lastCheck: number;\n data: Reading[];\n}\n\n/**\n * Fetch the readings for a measure.\n *\n * @param id The EA measure id.\n * @returns A promise for an array of readings for the measure.\n */\nconst fetchMeasureReadings = async (\n id: string,\n options: ReadingOptions = {}\n): Promise<ReadingResponse> => {\n // Set the parameters for the request.\n const params: ApiParameters = { _sorted: '' };\n if (options.since) {\n params.since = toTimeParameter(options.since);\n }\n // Get the response, casting the items to ReadingDTOs.\n const response = <ApiResponse<ReadingDTO[]>>(\n await apiFetch(`/id/measures/${id}/readings`, params)\n );\n return [parseReadings(response.data.items)[id] || [], response];\n};\n\nexport const filterSince = (data: Reading[], since: number) => {\n const position = data.findIndex((reading) => reading[0] >= since);\n return position < 0 ? [] : data.slice(position);\n};\n\n/**\n * Get the readings for a measure.\n *\n * @todo Caching and throttling.\n *\n * @param id The EA measure id.\n * @returns A promise for an array of readings for the measure.\n */\nexport const getMeasureReadings = async (\n id: string,\n options: ReadingOptions = {}\n): Promise<Reading[]> => {\n // Get the saved readings.\n const key = `readings|${id}`;\n const store = useStore();\n\n const { data, lastCheck, storedSince }: StoredReadings = store.get(key) || {\n data: [],\n lastCheck: 0,\n storedSince: Infinity,\n };\n const discardBefore = startOfDay(null, -30, true).valueOf() / 1000;\n\n // Discard any older than 30 days.\n while (data.length && data[0][0] < discardBefore) {\n data.shift();\n }\n\n // If we have data early enough Throttle at 15 mins.\n const lastStored = data.length ? data[data.length - 1][0] : 0;\n const requestedSince = (options.since && options.since.valueOf() / 1000) || 0;\n\n if (\n storedSince <= requestedSince &&\n Date.now() < lastCheck * 1000 + THROTTLE_MS\n ) {\n // Throttled.\n return filterSince(data, requestedSince);\n }\n\n const fetchOptions: ReadingOptions = {\n ...options,\n since: new Date(Math.max(requestedSince, lastStored) * 1000),\n };\n\n const [newData] = await fetchMeasureReadings(id, fetchOptions);\n mergeReadings(data, newData);\n const newSince = Math.min(requestedSince, storedSince);\n store.set(key, { lastCheck: Date.now() / 1000, data, storedSince: newSince });\n return filterSince(data, requestedSince);\n};\n\nexport const mergeReadings = (first: Reading[], second: Reading[]): void => {\n if (!second.length) return;\n\n let firstPos = first.length - 1;\n while (firstPos >= 0 && first[firstPos][0] >= second[0][0]) {\n --firstPos;\n }\n first.splice(firstPos + 1, Infinity, ...second);\n};\n\nconst parseReadings = (items: ReadingDTO[]): Record<string, Reading[]> => {\n const ranges: Record<string, Reading[]> = {};\n items.forEach(({ measure, dateTime, value }) => {\n if (ranges[measure] == null) {\n ranges[measure] = [];\n }\n ranges[measure].unshift([new Date(dateTime).valueOf() / 1000, value]);\n });\n\n const rangesById: Record<string, Reading[]> = {};\n Object.entries(ranges).forEach(([key, range]) => {\n rangesById[key.substring(key.lastIndexOf('/') + 1)] = range;\n });\n\n return rangesById;\n};\n","import { RiverDataWidgetError } from '../error';\nimport { round3 } from '../helpers/format';\nimport {\n parseMeasureId,\n translateMeasureProperties,\n} from '../flood-monitoring-api/measure';\nimport { Chart } from './chart';\nimport { getMeasureReadings } from '../flood-monitoring-api';\nimport { startOfDay } from '../helpers/time';\n\nimport type { ChartSeries } from './chart';\n\nconst drawMeasureWidget = async (\n parentEl: HTMLElement,\n measureId: string,\n options: Record<string, unknown> = {}\n) => {\n // Get readings for the last 7 days in local time.\n const since = startOfDay(null, -7, true);\n\n const data = await getMeasureReadings(measureId, { since });\n\n parentEl.replaceChildren();\n\n const measure = parseMeasureId(measureId);\n const { unit } = translateMeasureProperties(measure);\n\n // const [time, value] = data[data.length - 1];\n // const v = round3(value);\n // const param = m.qualifiedParameter;\n // const station = measure.stationId;\n // const unit = m.unit;\n\n // let textEl = createElement('div');\n // const d = dateFormatter.format(new Date(time * 1000));\n // const t = timeFormatter.format(new Date(time * 1000));\n // textEl.innerHTML = `The most recent ${param} reading for station ${station} was ${v} ${unit} at ${t} on ${d}.`;\n // widgetEl.append(textEl);\n\n const series1: ChartSeries = { data, unit, formatter: round3 };\n if (options.riverDataWidgetMinValue != null) {\n series1.min = parseFloat(<string>options.riverDataWidgetMinValue);\n }\n const minTime = startOfDay(new Date(data[0][0] * 1000)).valueOf() / 1000;\n const maxTime =\n startOfDay(new Date(data[data.length - 1][0] * 1000), 1).valueOf() / 1000;\n const chartOptions = {\n minTime,\n maxTime,\n // attribution: `www.riverdata.co.uk/station/${measure.stationId}`,\n };\n\n const chart = new Chart(parentEl, [series1], chartOptions);\n chart.render();\n};\n\n/**\n * Load a widget specified by a DOM element.\n */\nexport const loadWidget = (el: HTMLElement | string) => {\n // Get the target element from a query selector if necessary and check it\n // exists.\n const targetEl =\n typeof el === 'string' ? <HTMLElement>document.querySelector(el) : el;\n if (targetEl === null) {\n throw new Error('Target element not found');\n }\n\n // Parse element for widget type and options.\n const widgetIdParts = targetEl.dataset.riverDataWidget?.split(':') ?? [];\n const type = widgetIdParts.shift();\n const id = widgetIdParts.join(':');\n const options = targetEl.dataset;\n\n switch (type) {\n case 'measure':\n drawMeasureWidget(targetEl, id, options);\n break;\n\n // The 'station' widget is experimental in v1.0 and should not be used.\n // case 'station':\n // break;\n\n default:\n throw new RiverDataWidgetError('Unknown widget definition', { type, id });\n }\n};\n","import { loadWidget } from './widget/render';\n\nconst autoload = async () => {\n document.querySelectorAll('[data-river-data-widget]').forEach((el) => {\n try {\n loadWidget(<HTMLElement>el);\n } catch (error) {\n console.error(error, { error });\n }\n });\n};\n\nif (document.readyState === 'loading') {\n // Loading hasn't finished yet.\n document.addEventListener('DOMContentLoaded', autoload);\n} else {\n // `DOMContentLoaded` has already fired.\n autoload();\n}\n"],"names":[],"mappings":"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"}
{"version":3,"file":"index.js","sources":["../src/error.ts","../src/helpers/format.ts","../src/flood-monitoring-api/error.ts","../src/flood-monitoring-api/measure.ts","../src/helpers/dom.ts","../src/helpers/time.ts","../src/widget/chart.ts","../src/flood-monitoring-api/api.ts","../src/flood-monitoring-api/store.ts","../src/flood-monitoring-api/reading.ts","../src/widget/render.ts","../src/autoload.ts"],"sourcesContent":["export type RiverDataWidgetErrorInfo = Record<string, unknown>;\n\nexport class RiverDataWidgetError extends Error {\n public info: RiverDataWidgetErrorInfo;\n\n constructor(msg: string, info: RiverDataWidgetErrorInfo = {}) {\n super(msg);\n this.name = 'RiverDataWidgetError';\n this.info = info;\n }\n}\n","export const FONT_STACK =\n '-apple-system,BlinkMacSystemFont,\"Segoe UI\",\"Roboto\",\"Helvetica Neue\",Arial,sans-serif';\n\nexport const round3 = (value: number) =>\n value < 100 ? value.toPrecision(3) : Math.round(value).toString();\n","export class FloodMonitoringApiError extends Error {\n public info: Record<string, unknown>;\n\n constructor(msg: string, info: Record<string, unknown> = {}) {\n super(msg);\n this.name = 'FloodMonitoringApiError';\n this.info = info;\n }\n}\n","import { FloodMonitoringApiError } from './error';\n\nexport { parseMeasureId };\n\nconst parseMeasureId = (measureId: string) => {\n // ............base/ stat-paramet-qualifi- type -interva-unit\n const regExp = /(.*)-([^-]*)-([^-]*)-([^-]*)-([^-]*)-([^-]*)$/;\n const matches = measureId.match(regExp);\n if (matches === null) {\n throw new FloodMonitoringApiError('Cannot parse measure id', { measureId });\n }\n const [unit, interval, type, qualifier, parameter, stationId] =\n matches.reverse();\n const qualifiedParameter = qualifier.length\n ? `${parameter}-${qualifier}`\n : parameter;\n return {\n stationId,\n parameter,\n qualifier,\n type,\n interval,\n unit,\n qualifiedParameter,\n };\n};\n\nconst measureTranslations: Record<string, Record<string, string>> = {\n unit: {\n m3_s: 'm³/s',\n mAOD: 'm',\n mASD: 'm',\n },\n qualifiedParameter: {\n 'level-stage': 'level',\n 'level-downstage': 'downstream level',\n },\n};\n\nexport const translateMeasureProperties = (measure: Record<string, string>) => {\n const translated: Record<string, string> = {};\n for (const prop in measure) {\n const value = measure[prop];\n if (measureTranslations[prop] && measureTranslations[prop][value]) {\n translated[prop] = measureTranslations[prop][value];\n } else {\n translated[prop] = value;\n }\n }\n return translated;\n};\n","/**\n * RiverDataWidget https://github.com/pb-uk/river-data-widget.\n *\n * @copyright Copyright (C) 2022 pbuk https://github.com/pb-uk.\n * @license AGPL-3.0-or-later see LICENSE.md.\n */\n\nexport { createElement, createSvgElement, setAttributes, setStyles };\n\ntype AttributeList = Record<string, string | number>;\n\nconst setAttributes = <T extends HTMLElement | SVGElement>(\n el: T,\n attributes: AttributeList\n): T => {\n Object.entries(attributes).forEach(([key, value]) => {\n el.setAttribute(key, `${value}`);\n });\n return el;\n};\n\nconst setStyles = <T extends HTMLElement | SVGElement>(\n el: T,\n styles: AttributeList\n): T => {\n Object.entries(styles).forEach(([key, value]) => {\n // Workaround (el.style.setProperty uses kebab-case keys).\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (<any>el.style)[key] = value;\n });\n return el;\n};\n\nconst createElement = (\n name = 'div',\n attributes: AttributeList = {},\n styles: AttributeList = {},\n innerHTML: string | false = false\n): HTMLElement => {\n const el = document.createElement(name);\n if (innerHTML !== false) {\n el.innerHTML = innerHTML;\n }\n return setStyles(setAttributes(el, attributes), styles);\n};\n\nconst createSvgElement = (\n name = 'svg',\n attributes: AttributeList = {},\n styles: AttributeList = {},\n innerHTML: string | false = false\n) => {\n const el = document.createElementNS('http://www.w3.org/2000/svg', name);\n if (innerHTML !== false) {\n el.innerHTML = innerHTML;\n }\n return setStyles(setAttributes(el, attributes), styles);\n};\n","export const MINUTE_MS = 60000;\n// const HOUR_MS = 3600000;\nexport const DAY_MS = 86400000;\n\n/**\n * Get the Date at the start of a day in UTC or local time.\n *\n * @param offset\n * @param timeZone The time zone offset in minutes, or set to `true` to use the\n * local time zone (`false`, the default, uses UTC).\n * @returns The reqested date.\n */\nexport const startOfDay = (\n date: Date | null = null,\n offset = 0,\n timeZone: boolean | number = false\n): Date => {\n if (timeZone === false) {\n // Use UTC.\n const base = date === null ? Date.now() : date.valueOf();\n return new Date(Math.floor(base / DAY_MS + offset) * DAY_MS);\n }\n\n const now = new Date();\n const tz = timeZone === true ? now.getTimezoneOffset() : timeZone;\n const local = now.valueOf() + tz * MINUTE_MS;\n return new Date(Math.floor(local / DAY_MS + offset) * DAY_MS);\n};\n\n/**\n * | | long |short|narrow|numeric|2-digit|\n * |:-------:|:-----------:|:---:|:----:|:-----:|:-----:|\n * | weekday | Monday | Mon | M | | |\n * | era | Anno Domini | AD | A | | |\n * | year | | | | 2012 | 12 |\n * | month | March | Mar | M | 3 | 03 |\n * | day | | | | 1 | 01 |\n * | hour | | | | 1 | 01 |\n * | minute | | | | 1 | 01 |\n * | second | | | | 1 | 01 |\n *\n * * fractionalSecondDigits: 1, 2 or 3 for number of digits.\n * * timeZoneName: long (Pacific Standard Time), short (PST),\n * longOffset (GMT-0800), shortOffset (GMT-8), longGeneric (Pacific Time),\n * shortGeneric (PT).\n */\n\nexport const dateFormatter = new Intl.DateTimeFormat('en-GB', {\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n // year: 'numeric',\n});\n\nexport const timeFormatter = new Intl.DateTimeFormat('en-GB', {\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n timeZoneName: 'short',\n});\n\nexport const dddFormatter = new Intl.DateTimeFormat('en-GB', {\n weekday: 'short',\n});\n\nexport const dMmmFormatter = new Intl.DateTimeFormat('en-GB', {\n day: 'numeric',\n month: 'short',\n});\n","import { createSvgElement } from '../helpers/dom';\nimport { timeFormatter, dddFormatter, dMmmFormatter } from '../helpers/time';\nimport { FloodMonitoringApiError } from '../flood-monitoring-api/error';\nimport { FONT_STACK } from '../helpers/format';\n\nconst PLOT_COLOR = '#77C';\n\nexport interface ChartOptions {\n minTime?: number;\n maxTime?: number;\n attribution?: string;\n}\n\nexport interface ChartScaleLimits {\n minTime: number;\n maxTime: number;\n timeScale: number;\n minValue: number;\n maxValue: number;\n valueScale: number;\n}\n\nexport interface ChartSeries {\n data: TimeSeriesValue[];\n min?: number;\n max?: number;\n unit?: string;\n formatter?: (value: number) => string;\n}\n\nexport type TimeSeriesValue = [\n ts: number, // Unix time stamp (seconds).\n v: number // Value.\n];\n\nexport class Chart {\n protected fontSizePx = 14;\n\n protected el: SVGElement;\n protected series: ChartSeries[];\n protected options: ChartOptions;\n protected width = 480; // 400;\n protected height = 270; // 225;\n protected plotHeight = this.height - this.fontSizePx * 4.5;\n protected strokeWidth = 2;\n protected limits?: ChartScaleLimits;\n\n protected attribution =\n 'Uses Environment Agency data from the real-time API (Beta)';\n\n // CSS settings.\n // Just readable at 320x180.\n // Good from 400x225.\n // Perfect at 480x270 (font is 12px);\n protected styles = {\n 'font-family': FONT_STACK,\n 'font-size': `${this.fontSizePx}px`,\n display: 'block',\n margin: 'auto',\n 'max-width': '150vh',\n };\n\n constructor(\n el: HTMLElement,\n series: ChartSeries[],\n options: ChartOptions = {}\n ) {\n this.series = series;\n this.options = options;\n const viewBox = `0 0 ${this.width} ${this.height}`;\n this.attribution = options.attribution ?? this.attribution;\n this.el = createSvgElement('svg', { viewBox }, this.styles);\n el.append(this.el);\n }\n\n getLimits(): ChartScaleLimits {\n if (this.limits == null) {\n throw new FloodMonitoringApiError('Chart axis limits have not been set');\n }\n return this.limits;\n }\n\n getHorizontalGridlines(): SVGElement[] {\n const { minTime, maxTime, timeScale, minValue, maxValue, valueScale } =\n this.getLimits();\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight;\n const x1 = xOffset;\n const x2 = xOffset + (maxTime - minTime) * timeScale;\n // Horizontal grid lines.\n const stroke = '#ddd';\n const lines = createSvgElement('g', { stroke });\n const labels = createSvgElement('g');\n const valueRange = maxValue - minValue;\n // Horizontal grid interval.\n const [interval, exponent] = getInterval(valueRange, 9);\n const factor = 10 ** -exponent;\n const base = Math.ceil((minValue * factor) / interval + 1) * interval;\n let i = 0;\n let current = base / factor;\n while (current < maxValue) {\n const y1 = yOffset - (current - minValue) * valueScale;\n lines.append(createSvgElement('line', { x1, y1, x2, y2: y1 }));\n labels.append(\n createSvgElement('text', { x: x1 + 4, y: y1 + 4 }, {}, `${current}`)\n );\n ++i;\n current = (base + i * interval) / factor;\n }\n const timeAxisLine = createSvgElement(\n 'line',\n { x1, y1: yOffset, x2, y2: yOffset },\n { stroke: '#777' }\n );\n\n return [lines, labels, timeAxisLine];\n }\n\n getTimeScale(): SVGElement[] {\n const { minTime, maxTime, timeScale } = this.getLimits();\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight + this.strokeWidth / 2;\n const y1 = yOffset + this.fontSizePx * 3;\n const y2 = yOffset - this.plotHeight;\n // Vertical grid lines.\n const stroke = '#ddd';\n const lines = createSvgElement('g', { stroke });\n const labels = createSvgElement('g');\n // Vertical grid interval.\n const base = minTime;\n const interval = 86400;\n let i = 0;\n let current = base;\n const labelOffset = 43200 * timeScale;\n const fill = '#444';\n while (current <= maxTime) {\n const x1 = xOffset + (current - minTime) * timeScale;\n const d = new Date(current * 1000);\n // lines.append(createSvgElement('line', { x1, y1, x2, y2: y1 }));\n lines.append(createSvgElement('line', { x1, y1, x2: x1, y2 }));\n labels.append(\n createSvgElement(\n 'text',\n {\n x: x1 + labelOffset,\n y: y1 - this.fontSizePx * 1.8,\n 'text-anchor': 'middle',\n },\n { fill },\n `${dddFormatter.format(d)}`\n ),\n createSvgElement(\n 'text',\n {\n x: x1 + labelOffset,\n y: y1 - this.fontSizePx * 0.5,\n 'text-anchor': 'middle',\n },\n { fill },\n `${dMmmFormatter.format(d)}`\n )\n );\n ++i;\n current = base + i * interval;\n }\n return [lines, labels];\n }\n\n render() {\n // Calculate axis scales.\n const limits = getLimits(this.series[0].data);\n limits.minValue = this.series[0].min ?? limits.minValue;\n limits.maxValue = this.series[0].max ?? limits.maxValue;\n limits.minTime = this.options.minTime ?? limits.minTime;\n limits.maxTime = this.options.maxTime ?? limits.maxTime;\n\n this.limits = {\n ...limits,\n valueScale:\n (this.plotHeight - this.strokeWidth) /\n (limits.maxValue - limits.minValue),\n timeScale:\n (this.width - this.strokeWidth) / (limits.maxTime - limits.minTime),\n };\n\n // Time axis.\n const [timeLines, timeLabels] = this.getTimeScale();\n this.el.append(timeLines);\n\n // Value axis.\n const [valueLines, valueLabels, timeAxisLine] =\n this.getHorizontalGridlines();\n this.el.append(valueLines);\n this.el.append(timeAxisLine);\n\n this.plotData();\n\n // Plot labels on top of the line.\n this.el.append(timeLabels);\n this.el.append(valueLabels);\n\n this.el.append(\n createSvgElement(\n 'text',\n {\n x: this.width / 2,\n 'text-anchor': 'middle',\n y: this.height - this.fontSizePx * 0.5,\n },\n { fill: '#595959' },\n this.attribution\n )\n );\n\n this.plotLastValue();\n }\n\n plotLastValue() {\n const { data, unit, formatter } = this.series[0];\n const [time, value] = data[data.length - 1];\n const { minTime, timeScale, minValue, valueScale } = this.getLimits();\n\n const v = formatter == null ? value : formatter(value);\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight - this.strokeWidth / 2;\n const x = xOffset + (time - minTime) * timeScale;\n const highLabel = (value - minValue) / valueScale >= 0.5;\n const y =\n yOffset - this.plotHeight * (highLabel ? 1 : 0.5) + this.fontSizePx * 2;\n\n this.el.append(\n createSvgElement(\n 'text',\n { x, y, 'text-anchor': 'end' },\n { fill: PLOT_COLOR, 'font-size': '1.5em', 'font-weight': 'bold' },\n `${v} ${unit}`\n )\n );\n this.el.append(\n createSvgElement(\n 'text',\n { x, y: y + this.fontSizePx * 1.5, 'text-anchor': 'end' },\n { fill: PLOT_COLOR },\n `${timeFormatter.format(new Date(time * 1000))}`\n )\n );\n }\n\n plotData() {\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight - this.strokeWidth / 2;\n const { data } = this.series[0];\n const { minTime, timeScale, minValue, valueScale } = this.getLimits();\n // First data point.\n const x = xOffset + (data[0][0] - minTime) * timeScale;\n const y = yOffset - (data[0][1] - minValue) * valueScale;\n const points = [`M${x},${y}`];\n // Remaining data points.\n for (let i = 1; i < data.length; ++i) {\n const x = xOffset + (data[i][0] - minTime) * timeScale;\n const y = yOffset - (data[i][1] - minValue) * valueScale;\n points.push(`L${x},${y}`);\n }\n // Plot the data.\n const path = createSvgElement('path', {\n d: points.join(''),\n stroke: PLOT_COLOR,\n 'stroke-width': this.strokeWidth,\n fill: 'none',\n });\n this.el.append(path);\n }\n}\n\nexport const getLimits = (data: TimeSeriesValue[]) => {\n if (data.length < 1) {\n throw new Error('Readings must not be empty');\n }\n const minTime = data[0][0];\n const maxTime = data[data.length - 1][0];\n let minValue = Infinity;\n let maxValue = -minValue;\n data.forEach(([, value]) => {\n minValue = Math.min(minValue, value);\n maxValue = Math.max(maxValue, value);\n });\n return { minTime, maxTime, minValue, maxValue };\n};\n\nexport const getInterval = (range: number, maxDivisions: number) => {\n const exponent = Math.floor(Math.log10(range)) - 1;\n const k = range / (maxDivisions * 10 ** exponent);\n const mantissa = k <= 2 ? 2 : k <= 5 ? 5 : 10;\n return [mantissa, exponent];\n};\n","// There is no need to be secure about this!\nconst baseUrl = 'http://environment.data.gov.uk/flood-monitoring';\n\nexport interface ApiResponse<T> {\n data: {\n items: T;\n };\n response: Response;\n}\n\nexport interface ApiParameters {\n since?: string; // Time from.\n _sorted?: ''; // Flag for sorting.\n}\n\nexport const apiFetch = async (\n path: string,\n query = {}\n): Promise<ApiResponse<unknown>> => {\n const queryString = new URLSearchParams(query).toString();\n const uri = queryString\n ? `${baseUrl}${path}?${queryString}`\n : `${baseUrl}${path}`;\n const response = await fetch(uri);\n return { data: await response.json(), response };\n};\n\n/**\n * Convert a Date to a format recognized by the EA API for a query parameter.\n *\n * @param date Convert from.\n * @returns A string in the EA API query parameter format.\n */\nexport const toTimeParameter = (date: Date): string => {\n return date.toISOString().substring(0, 19) + 'Z';\n};\n\n/*\nUseful response headers\n Date: 'Sat, 13 May 2023 09:14:07 GMT',\n last-modified: Sat, 13 May 2023 09:03:13 GMT,\nResponse meta:\n publisher: 'Environment Agency',\n license: 'http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/',\n documentation: 'http://environment.data.gov.uk/flood-monitoring/doc/reference',\n version: '0.9',\n comment: 'Status: Beta service',\n hasFormat: [\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.csv?_sorted=&since=2023-05-12T08%3A00%3A00Z\",\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.rdf?_sorted=&since=2023-05-12T08%3A00%3A00Z\",\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.ttl?_sorted=&since=2023-05-12T08%3A00%3A00Z\",\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.html?_sorted=&since=2023-05-12T08%3A00%3A00Z\"\n ],\n*/\n","const prefix = 'riverDataWidget';\n\nconst addPrefix = (key: string): string => `${prefix}|${key}`;\n\nlet instance: Store;\n\nclass Store {\n clear(destroy = false) {\n for (const key of this.keys()) {\n localStorage.removeItem(addPrefix(key));\n }\n if (destroy) {\n localStorage.removeItem(prefix);\n return;\n }\n localStorage.setItem(prefix, JSON.stringify([]));\n }\n\n get(key: string) {\n const value = localStorage.getItem(addPrefix(key));\n return value === null ? null : JSON.parse(value);\n }\n\n has(key: string): boolean {\n return this.keys().includes(key);\n }\n\n /**\n * Detect active localStorage.\n *\n * @returns true iff localStorage for the widget is active.\n */\n isActive() {\n return localStorage.getItem(prefix) !== null;\n }\n\n keys(): string[] {\n const storedKeys = localStorage.getItem(prefix);\n return storedKeys === null ? [] : JSON.parse(storedKeys);\n }\n\n set(key: string, value: unknown) {\n const json = JSON.stringify(value);\n const storedKeys = localStorage.getItem(prefix);\n const keys: string[] = storedKeys === null ? [] : JSON.parse(storedKeys);\n if (!keys.includes(key)) {\n keys.push(key);\n localStorage.setItem(prefix, JSON.stringify(keys));\n }\n localStorage.setItem(addPrefix(key), json);\n }\n\n unset(key: string): boolean {\n // Remove it before we do anything else.\n localStorage.removeItem(addPrefix(key));\n\n // Then remove it from the list of keys.\n const storedKeys = localStorage.getItem(prefix);\n const keys: string[] = storedKeys === null ? [] : JSON.parse(storedKeys);\n const index = keys.indexOf(key);\n\n // If it doesn't exist we don't have to remove it.\n if (index === -1) return false;\n\n keys.splice(index, 1);\n localStorage.setItem(prefix, JSON.stringify(keys));\n return true;\n }\n}\n\nexport const useStore = (): Store => {\n if (!instance) {\n instance = new Store();\n }\n return instance;\n};\n","import { apiFetch, toTimeParameter } from './api';\nimport { useStore } from './store';\nimport { MINUTE_MS, startOfDay } from '../helpers/time';\n\nimport type { ApiParameters, ApiResponse } from './api';\n\n// Throttle requests to five minutes.\nconst THROTTLE_MS = 5 * MINUTE_MS;\n\n/**\n * Internal format for readings.\n */\nexport type Reading = [\n timestamp: number, // Unix epoch timestamp (seconds).\n value: number // Value.\n];\n\n/**\n * Internal format for readings.\n */\nexport interface ReadingOptions {\n since?: Date; // Time from.\n}\n\n/**\n * Internal format for readings.\n */\ntype ReadingResponse = [a: Reading[], b: ApiResponse<ReadingDTO[]>];\n\n/**\n * Data transfer object for readings provided by the API.\n */\ninterface ReadingDTO {\n '@id': string; // The URL of this reading.\n dateTime: string; // e.g. '2023-05-13T09:00:00Z'.\n measure: string; // The URL of the measure.\n value: number; // The value in the appropriate units.\n}\n\ninterface StoredReadings {\n storedSince: number;\n lastCheck: number;\n data: Reading[];\n}\n\n/**\n * Fetch the readings for a measure.\n *\n * @param id The EA measure id.\n * @returns A promise for an array of readings for the measure.\n */\nconst fetchMeasureReadings = async (\n id: string,\n options: ReadingOptions = {}\n): Promise<ReadingResponse> => {\n // Set the parameters for the request.\n const params: ApiParameters = { _sorted: '' };\n if (options.since) {\n params.since = toTimeParameter(options.since);\n }\n // Get the response, casting the items to ReadingDTOs.\n const response = <ApiResponse<ReadingDTO[]>>(\n await apiFetch(`/id/measures/${id}/readings`, params)\n );\n return [parseReadings(response.data.items)[id] || [], response];\n};\n\nexport const filterSince = (data: Reading[], since: number) => {\n const position = data.findIndex((reading) => reading[0] >= since);\n return position < 0 ? [] : data.slice(position);\n};\n\n/**\n * Get the readings for a measure.\n *\n * @todo Caching and throttling.\n *\n * @param id The EA measure id.\n * @returns A promise for an array of readings for the measure.\n */\nexport const getMeasureReadings = async (\n id: string,\n options: ReadingOptions = {}\n): Promise<Reading[]> => {\n // Get the saved readings.\n const key = `readings|${id}`;\n const store = useStore();\n\n const stored: StoredReadings = store.get(key) || {\n data: [],\n lastCheck: 0,\n storedSince: Infinity,\n };\n const { data, lastCheck } = stored;\n let { storedSince } = stored;\n\n const discardBefore = startOfDay(null, -8, true).valueOf() / 1000;\n\n // Discard any older than 30 days.\n while (data.length && data[0][0] < discardBefore) {\n [storedSince] = data[0];\n data.shift();\n }\n\n // If we have data early enough apply throttle.\n const lastStored = data.length ? data[data.length - 1][0] : 0;\n const requestedSince = (options.since && options.since.valueOf() / 1000) || 0;\n if (\n storedSince <= requestedSince &&\n Date.now() < lastCheck * 1000 + THROTTLE_MS\n ) {\n // Throttled.\n return filterSince(data, requestedSince);\n }\n\n const fetchOptions: ReadingOptions = {\n ...options,\n since: new Date(Math.max(requestedSince, lastStored) * 1000),\n };\n\n const [newData] = await fetchMeasureReadings(id, fetchOptions);\n mergeReadings(data, newData);\n storedSince = Math.min(requestedSince, storedSince);\n store.set(key, { lastCheck: Date.now() / 1000, data, storedSince });\n return filterSince(data, requestedSince);\n};\n\nexport const mergeReadings = (first: Reading[], second: Reading[]): void => {\n if (!second.length) return;\n\n let firstPos = first.length - 1;\n while (firstPos >= 0 && first[firstPos][0] >= second[0][0]) {\n --firstPos;\n }\n first.splice(firstPos + 1, Infinity, ...second);\n};\n\nconst parseReadings = (items: ReadingDTO[]): Record<string, Reading[]> => {\n const ranges: Record<string, Reading[]> = {};\n items.forEach(({ measure, dateTime, value }) => {\n if (ranges[measure] == null) {\n ranges[measure] = [];\n }\n ranges[measure].unshift([new Date(dateTime).valueOf() / 1000, value]);\n });\n\n const rangesById: Record<string, Reading[]> = {};\n Object.entries(ranges).forEach(([key, range]) => {\n rangesById[key.substring(key.lastIndexOf('/') + 1)] = range;\n });\n\n return rangesById;\n};\n","import { RiverDataWidgetError } from '../error';\nimport { round3 } from '../helpers/format';\nimport {\n parseMeasureId,\n translateMeasureProperties,\n} from '../flood-monitoring-api/measure';\nimport { Chart } from './chart';\nimport { getMeasureReadings } from '../flood-monitoring-api';\nimport { startOfDay } from '../helpers/time';\n\nimport type { ChartSeries } from './chart';\n\nconst drawMeasureWidget = async (\n parentEl: HTMLElement,\n measureId: string,\n options: Record<string, unknown> = {}\n) => {\n // Get readings for the last 7 days in local time.\n const since = startOfDay(null, -7, true);\n\n const data = await getMeasureReadings(measureId, { since });\n\n parentEl.replaceChildren();\n\n const measure = parseMeasureId(measureId);\n const { unit } = translateMeasureProperties(measure);\n\n // const [time, value] = data[data.length - 1];\n // const v = round3(value);\n // const param = m.qualifiedParameter;\n // const station = measure.stationId;\n // const unit = m.unit;\n\n // let textEl = createElement('div');\n // const d = dateFormatter.format(new Date(time * 1000));\n // const t = timeFormatter.format(new Date(time * 1000));\n // textEl.innerHTML = `The most recent ${param} reading for station ${station} was ${v} ${unit} at ${t} on ${d}.`;\n // widgetEl.append(textEl);\n\n const series1: ChartSeries = { data, unit, formatter: round3 };\n if (options.riverDataWidgetMinValue != null) {\n series1.min = parseFloat(<string>options.riverDataWidgetMinValue);\n }\n const minTime = startOfDay(new Date(data[0][0] * 1000)).valueOf() / 1000;\n const maxTime =\n startOfDay(new Date(data[data.length - 1][0] * 1000), 1).valueOf() / 1000;\n const chartOptions = {\n minTime,\n maxTime,\n // attribution: `www.riverdata.co.uk/station/${measure.stationId}`,\n };\n\n const chart = new Chart(parentEl, [series1], chartOptions);\n chart.render();\n};\n\n/**\n * Load a widget specified by a DOM element.\n */\nexport const loadWidget = (el: HTMLElement | string) => {\n // Get the target element from a query selector if necessary and check it\n // exists.\n const targetEl =\n typeof el === 'string' ? <HTMLElement>document.querySelector(el) : el;\n if (targetEl === null) {\n throw new Error('Target element not found');\n }\n\n // Parse element for widget type and options.\n const widgetIdParts = targetEl.dataset.riverDataWidget?.split(':') ?? [];\n const type = widgetIdParts.shift();\n const id = widgetIdParts.join(':');\n const options = targetEl.dataset;\n\n switch (type) {\n case 'measure':\n drawMeasureWidget(targetEl, id, options);\n break;\n\n // The 'station' widget is experimental in v1.0 and should not be used.\n // case 'station':\n // break;\n\n default:\n throw new RiverDataWidgetError('Unknown widget definition', { type, id });\n }\n};\n","import { loadWidget } from './widget/render';\n\nconst autoload = async () => {\n document.querySelectorAll('[data-river-data-widget]').forEach((el) => {\n try {\n loadWidget(<HTMLElement>el);\n } catch (error) {\n console.error(error, { error });\n }\n });\n};\n\nif (document.readyState === 'loading') {\n // Loading hasn't finished yet.\n document.addEventListener('DOMContentLoaded', autoload);\n} else {\n // `DOMContentLoaded` has already fired.\n autoload();\n}\n"],"names":[],"mappings":"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"}

@@ -1,2 +0,2 @@

/*! RiverDataWidget v1.0.0 2023-05-17 08:04:47
/*! RiverDataWidget v1.0.1 2023-05-17 11:39:50
*! https://github.com/pb-uk/river-data-widget#readme

@@ -6,3 +6,3 @@ *! Copyright (C) 2023 pbuk (https://github.com/pb-uk).

*/
var RiverDataWidget=function(t){"use strict";class e extends Error{constructor(t,e={}){super(t),this.name="RiverDataWidgetError",this.info=e}}const i=t=>t<100?t.toPrecision(3):Math.round(t).toString();class n extends Error{constructor(t,e={}){super(t),this.name="FloodMonitoringApiError",this.info=e}}const s={unit:{m3_s:"m³/s",mAOD:"m",mASD:"m"},qualifiedParameter:{"level-stage":"level","level-downstage":"downstream level"}},a=(t="svg",e={},i={},n=!1)=>{const s=document.createElementNS("http://www.w3.org/2000/svg",t);return!1!==n&&(s.innerHTML=n),((t,e)=>(Object.entries(e).forEach((([e,i])=>{t.style[e]=i})),t))(((t,e)=>(Object.entries(e).forEach((([e,i])=>{t.setAttribute(e,`${i}`)})),t))(s,e),i)},o=864e5,r=(t=null,e=0,i=!1)=>{if(!1===i){const i=null===t?Date.now():t.valueOf();return new Date(Math.floor(i/o+e)*o)}const n=new Date,s=!0===i?n.getTimezoneOffset():i,a=n.valueOf()+6e4*s;return new Date(Math.floor(a/o+e)*o)},l=new Intl.DateTimeFormat("en-GB",{hour:"2-digit",minute:"2-digit",hour12:!1,timeZoneName:"short"}),h=new Intl.DateTimeFormat("en-GB",{weekday:"short"}),m=new Intl.DateTimeFormat("en-GB",{day:"numeric",month:"short"}),c="#77C";class d{constructor(t,e,i={}){var n;this.fontSizePx=14,this.width=480,this.height=270,this.plotHeight=this.height-4.5*this.fontSizePx,this.strokeWidth=2,this.attribution="Uses Environment Agency data from the real-time API (Beta)",this.styles={"font-family":'-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Helvetica Neue",Arial,sans-serif',"font-size":`${this.fontSizePx}px`,display:"block",margin:"auto","max-width":"150vh"},this.series=e,this.options=i;const s=`0 0 ${this.width} ${this.height}`;this.attribution=null!==(n=i.attribution)&&void 0!==n?n:this.attribution,this.el=a("svg",{viewBox:s},this.styles),t.append(this.el)}getLimits(){if(null==this.limits)throw new n("Chart axis limits have not been set");return this.limits}getHorizontalGridlines(){const{minTime:t,maxTime:e,timeScale:i,minValue:n,maxValue:s,valueScale:o}=this.getLimits(),r=this.strokeWidth/2,l=this.plotHeight,h=r,m=r+(e-t)*i,c=a("g",{stroke:"#ddd"}),d=a("g"),u=s-n,[f,p]=g(u,9),x=10**-p,S=Math.ceil(n*x/f+1)*f;let v=0,w=S/x;for(;w<s;){const t=l-(w-n)*o;c.append(a("line",{x1:h,y1:t,x2:m,y2:t})),d.append(a("text",{x:h+4,y:t+4},{},`${w}`)),++v,w=(S+v*f)/x}return[c,d,a("line",{x1:h,y1:l,x2:m,y2:l},{stroke:"#777"})]}getTimeScale(){const{minTime:t,maxTime:e,timeScale:i}=this.getLimits(),n=this.strokeWidth/2,s=this.plotHeight+this.strokeWidth/2,o=s+3*this.fontSizePx,r=s-this.plotHeight,l=a("g",{stroke:"#ddd"}),c=a("g"),d=t;let u=0,g=d;const f=43200*i,p="#444";for(;g<=e;){const e=n+(g-t)*i,s=new Date(1e3*g);l.append(a("line",{x1:e,y1:o,x2:e,y2:r})),c.append(a("text",{x:e+f,y:o-1.8*this.fontSizePx,"text-anchor":"middle"},{fill:p},`${h.format(s)}`),a("text",{x:e+f,y:o-.5*this.fontSizePx,"text-anchor":"middle"},{fill:p},`${m.format(s)}`)),++u,g=d+86400*u}return[l,c]}render(){var t,e,i,n;const s=u(this.series[0].data);s.minValue=null!==(t=this.series[0].min)&&void 0!==t?t:s.minValue,s.maxValue=null!==(e=this.series[0].max)&&void 0!==e?e:s.maxValue,s.minTime=null!==(i=this.options.minTime)&&void 0!==i?i:s.minTime,s.maxTime=null!==(n=this.options.maxTime)&&void 0!==n?n:s.maxTime,this.limits=Object.assign(Object.assign({},s),{valueScale:(this.plotHeight-this.strokeWidth)/(s.maxValue-s.minValue),timeScale:(this.width-this.strokeWidth)/(s.maxTime-s.minTime)});const[o,r]=this.getTimeScale();this.el.append(o);const[l,h,m]=this.getHorizontalGridlines();this.el.append(l),this.el.append(m),this.plotData(),this.el.append(r),this.el.append(h),this.el.append(a("text",{x:this.width/2,"text-anchor":"middle",y:this.height-.5*this.fontSizePx},{fill:"#595959"},this.attribution)),this.plotLastValue()}plotLastValue(){const{data:t,unit:e,formatter:i}=this.series[0],[n,s]=t[t.length-1],{minTime:o,timeScale:r,minValue:h,maxValue:m,valueScale:d}=this.getLimits(),u=null==i?s:i(s),g=this.strokeWidth/2+(n-o)*r,f=(s-h)/d>=.5,p=this.plotHeight-this.strokeWidth/2-this.plotHeight*(f?1:.5)+2*this.fontSizePx;this.el.append(a("text",{x:g,y:p,"text-anchor":"end"},{fill:c,"font-size":"1.5em","font-weight":"bold"},`${u} ${e}`)),this.el.append(a("text",{x:g,y:p+1.5*this.fontSizePx,"text-anchor":"end"},{fill:c},`${l.format(new Date(1e3*n))}`))}plotData(){const t=this.strokeWidth/2,e=this.plotHeight-this.strokeWidth/2,{data:i}=this.series[0],{minTime:n,timeScale:s,minValue:o,valueScale:r}=this.getLimits(),l=[`M${t+(i[0][0]-n)*s},${e-(i[0][1]-o)*r}`];for(let a=1;a<i.length;++a){const h=t+(i[a][0]-n)*s,m=e-(i[a][1]-o)*r;l.push(`L${h},${m}`)}const h=a("path",{d:l.join(""),stroke:c,"stroke-width":this.strokeWidth,fill:"none"});this.el.append(h)}}const u=t=>{if(t.length<1)throw new Error("Readings must not be empty");const e=t[0][0],i=t[t.length-1][0];let n=1/0,s=-n;return t.forEach((([,t])=>{n=Math.min(n,t),s=Math.max(s,t)})),{minTime:e,maxTime:i,minValue:n,maxValue:s}},g=(t,e)=>{const i=Math.floor(Math.log10(t))-1,n=t/(e*10**i);return[n<=2?2:n<=5?5:10,i]},f="http://environment.data.gov.uk/flood-monitoring",p="riverDataWidget",x=t=>`${p}|${t}`;let S;class v{clear(t=!1){for(const t of this.keys())localStorage.removeItem(x(t));t?localStorage.removeItem(p):localStorage.setItem(p,JSON.stringify([]))}get(t){const e=localStorage.getItem(x(t));return null===e?null:JSON.parse(e)}has(t){return this.keys().includes(t)}isActive(){return null!==localStorage.getItem(p)}keys(){const t=localStorage.getItem(p);return null===t?[]:JSON.parse(t)}set(t,e){const i=JSON.stringify(e),n=localStorage.getItem(p),s=null===n?[]:JSON.parse(n);s.includes(t)||(s.push(t),localStorage.setItem(p,JSON.stringify(s))),localStorage.setItem(x(t),i)}unset(t){localStorage.removeItem(x(t));const e=localStorage.getItem(p),i=null===e?[]:JSON.parse(e),n=i.indexOf(t);return-1!==n&&(i.splice(n,1),localStorage.setItem(p,JSON.stringify(i)),!0)}}const w=async(t,e={})=>{const i={_sorted:""};e.since&&(i.since=e.since.toISOString().substring(0,19)+"Z");const n=await(async(t,e={})=>{const i=new URLSearchParams(e).toString(),n=i?`${f}${t}?${i}`:`${f}${t}`,s=await fetch(n);return{data:await s.json(),response:s}})(`/id/measures/${t}/readings`,i);return[T(n.data.items)[t]||[],n]},y=(t,e)=>{const i=t.findIndex((t=>t[0]>=e));return i<0?[]:t.slice(i)},O=async(t,e={})=>{const i=`readings|${t}`,n=(S||(S=new v),S),{data:s,lastCheck:a,storedSince:o}=n.get(i)||{data:[],lastCheck:0,storedSince:1/0},l=r(null,-30,!0).valueOf()/1e3;for(;s.length&&s[0][0]<l;)s.shift();const h=s.length?s[s.length-1][0]:0,m=e.since&&e.since.valueOf()/1e3||0;if(o<=m&&Date.now()<1e3*a+9e5)return y(s,m);const c=Object.assign(Object.assign({},e),{since:new Date(1e3*Math.max(m,h))}),[d]=await w(t,c);k(s,d);const u=Math.min(m,o);return n.set(i,{lastCheck:Date.now()/1e3,data:s,storedSince:u}),y(s,m)},k=(t,e)=>{if(!e.length)return;let i=t.length-1;for(;i>=0&&t[i][0]>=e[0][0];)--i;t.splice(i+1,1/0,...e)},T=t=>{const e={};t.forEach((({measure:t,dateTime:i,value:n})=>{null==e[t]&&(e[t]=[]),e[t].unshift([new Date(i).valueOf()/1e3,n])}));const i={};return Object.entries(e).forEach((([t,e])=>{i[t.substring(t.lastIndexOf("/")+1)]=e})),i},D=async(t,e,a={})=>{const o=r(null,-7,!0),l=await O(e,{since:o});t.replaceChildren();const h=(t=>{const e=t.match(/(.*)-([^-]*)-([^-]*)-([^-]*)-([^-]*)-([^-]*)$/);if(null===e)throw new n("Cannot parse measure id",{measureId:t});const[i,s,a,o,r,l]=e.reverse();return{stationId:l,parameter:r,qualifier:o,type:a,interval:s,unit:i,qualifiedParameter:o.length?`${r}-${o}`:r}})(e),{unit:m}=(t=>{const e={};for(const i in t){const n=t[i];s[i]&&s[i][n]?e[i]=s[i][n]:e[i]=n}return e})(h),c={data:l,unit:m,formatter:i};null!=a.riverDataWidgetMinValue&&(c.min=parseFloat(a.riverDataWidgetMinValue));const u=r(new Date(1e3*l[0][0])).valueOf()/1e3,g=r(new Date(1e3*l[l.length-1][0]),1).valueOf()/1e3;new d(t,[c],{minTime:u,maxTime:g}).render()},$=async()=>{document.querySelectorAll("[data-river-data-widget]").forEach((t=>{try{(t=>{var i,n;const s="string"==typeof t?document.querySelector(t):t;if(null===s)throw new Error("Target element not found");const a=null!==(n=null===(i=s.dataset.riverDataWidget)||void 0===i?void 0:i.split(":"))&&void 0!==n?n:[],o=a.shift(),r=a.join(":"),l=s.dataset;if("measure"!==o)throw new e("Unknown widget definition",{type:o,id:r});D(s,r,l)})(t)}catch(t){console.error(t,{error:t})}}))};return"loading"===document.readyState?document.addEventListener("DOMContentLoaded",$):$(),t.version="1.0.0",t}({});
var RiverDataWidget=function(t){"use strict";class e extends Error{constructor(t,e={}){super(t),this.name="RiverDataWidgetError",this.info=e}}const i=t=>t<100?t.toPrecision(3):Math.round(t).toString();class n extends Error{constructor(t,e={}){super(t),this.name="FloodMonitoringApiError",this.info=e}}const s={unit:{m3_s:"m³/s",mAOD:"m",mASD:"m"},qualifiedParameter:{"level-stage":"level","level-downstage":"downstream level"}},a=(t="svg",e={},i={},n=!1)=>{const s=document.createElementNS("http://www.w3.org/2000/svg",t);return!1!==n&&(s.innerHTML=n),((t,e)=>(Object.entries(e).forEach((([e,i])=>{t.style[e]=i})),t))(((t,e)=>(Object.entries(e).forEach((([e,i])=>{t.setAttribute(e,`${i}`)})),t))(s,e),i)},o=864e5,r=(t=null,e=0,i=!1)=>{if(!1===i){const i=null===t?Date.now():t.valueOf();return new Date(Math.floor(i/o+e)*o)}const n=new Date,s=!0===i?n.getTimezoneOffset():i,a=n.valueOf()+6e4*s;return new Date(Math.floor(a/o+e)*o)},l=new Intl.DateTimeFormat("en-GB",{hour:"2-digit",minute:"2-digit",hour12:!1,timeZoneName:"short"}),h=new Intl.DateTimeFormat("en-GB",{weekday:"short"}),c=new Intl.DateTimeFormat("en-GB",{day:"numeric",month:"short"}),m="#77C";class d{constructor(t,e,i={}){var n;this.fontSizePx=14,this.width=480,this.height=270,this.plotHeight=this.height-4.5*this.fontSizePx,this.strokeWidth=2,this.attribution="Uses Environment Agency data from the real-time API (Beta)",this.styles={"font-family":'-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Helvetica Neue",Arial,sans-serif',"font-size":`${this.fontSizePx}px`,display:"block",margin:"auto","max-width":"150vh"},this.series=e,this.options=i;const s=`0 0 ${this.width} ${this.height}`;this.attribution=null!==(n=i.attribution)&&void 0!==n?n:this.attribution,this.el=a("svg",{viewBox:s},this.styles),t.append(this.el)}getLimits(){if(null==this.limits)throw new n("Chart axis limits have not been set");return this.limits}getHorizontalGridlines(){const{minTime:t,maxTime:e,timeScale:i,minValue:n,maxValue:s,valueScale:o}=this.getLimits(),r=this.strokeWidth/2,l=this.plotHeight,h=r,c=r+(e-t)*i,m=a("g",{stroke:"#ddd"}),d=a("g"),u=s-n,[f,p]=g(u,9),x=10**-p,S=Math.ceil(n*x/f+1)*f;let v=0,w=S/x;for(;w<s;){const t=l-(w-n)*o;m.append(a("line",{x1:h,y1:t,x2:c,y2:t})),d.append(a("text",{x:h+4,y:t+4},{},`${w}`)),++v,w=(S+v*f)/x}return[m,d,a("line",{x1:h,y1:l,x2:c,y2:l},{stroke:"#777"})]}getTimeScale(){const{minTime:t,maxTime:e,timeScale:i}=this.getLimits(),n=this.strokeWidth/2,s=this.plotHeight+this.strokeWidth/2,o=s+3*this.fontSizePx,r=s-this.plotHeight,l=a("g",{stroke:"#ddd"}),m=a("g"),d=t;let u=0,g=d;const f=43200*i,p="#444";for(;g<=e;){const e=n+(g-t)*i,s=new Date(1e3*g);l.append(a("line",{x1:e,y1:o,x2:e,y2:r})),m.append(a("text",{x:e+f,y:o-1.8*this.fontSizePx,"text-anchor":"middle"},{fill:p},`${h.format(s)}`),a("text",{x:e+f,y:o-.5*this.fontSizePx,"text-anchor":"middle"},{fill:p},`${c.format(s)}`)),++u,g=d+86400*u}return[l,m]}render(){var t,e,i,n;const s=u(this.series[0].data);s.minValue=null!==(t=this.series[0].min)&&void 0!==t?t:s.minValue,s.maxValue=null!==(e=this.series[0].max)&&void 0!==e?e:s.maxValue,s.minTime=null!==(i=this.options.minTime)&&void 0!==i?i:s.minTime,s.maxTime=null!==(n=this.options.maxTime)&&void 0!==n?n:s.maxTime,this.limits=Object.assign(Object.assign({},s),{valueScale:(this.plotHeight-this.strokeWidth)/(s.maxValue-s.minValue),timeScale:(this.width-this.strokeWidth)/(s.maxTime-s.minTime)});const[o,r]=this.getTimeScale();this.el.append(o);const[l,h,c]=this.getHorizontalGridlines();this.el.append(l),this.el.append(c),this.plotData(),this.el.append(r),this.el.append(h),this.el.append(a("text",{x:this.width/2,"text-anchor":"middle",y:this.height-.5*this.fontSizePx},{fill:"#595959"},this.attribution)),this.plotLastValue()}plotLastValue(){const{data:t,unit:e,formatter:i}=this.series[0],[n,s]=t[t.length-1],{minTime:o,timeScale:r,minValue:h,valueScale:c}=this.getLimits(),d=null==i?s:i(s),u=this.strokeWidth/2+(n-o)*r,g=(s-h)/c>=.5,f=this.plotHeight-this.strokeWidth/2-this.plotHeight*(g?1:.5)+2*this.fontSizePx;this.el.append(a("text",{x:u,y:f,"text-anchor":"end"},{fill:m,"font-size":"1.5em","font-weight":"bold"},`${d} ${e}`)),this.el.append(a("text",{x:u,y:f+1.5*this.fontSizePx,"text-anchor":"end"},{fill:m},`${l.format(new Date(1e3*n))}`))}plotData(){const t=this.strokeWidth/2,e=this.plotHeight-this.strokeWidth/2,{data:i}=this.series[0],{minTime:n,timeScale:s,minValue:o,valueScale:r}=this.getLimits(),l=[`M${t+(i[0][0]-n)*s},${e-(i[0][1]-o)*r}`];for(let a=1;a<i.length;++a){const h=t+(i[a][0]-n)*s,c=e-(i[a][1]-o)*r;l.push(`L${h},${c}`)}const h=a("path",{d:l.join(""),stroke:m,"stroke-width":this.strokeWidth,fill:"none"});this.el.append(h)}}const u=t=>{if(t.length<1)throw new Error("Readings must not be empty");const e=t[0][0],i=t[t.length-1][0];let n=1/0,s=-n;return t.forEach((([,t])=>{n=Math.min(n,t),s=Math.max(s,t)})),{minTime:e,maxTime:i,minValue:n,maxValue:s}},g=(t,e)=>{const i=Math.floor(Math.log10(t))-1,n=t/(e*10**i);return[n<=2?2:n<=5?5:10,i]},f="http://environment.data.gov.uk/flood-monitoring",p="riverDataWidget",x=t=>`${p}|${t}`;let S;class v{clear(t=!1){for(const t of this.keys())localStorage.removeItem(x(t));t?localStorage.removeItem(p):localStorage.setItem(p,JSON.stringify([]))}get(t){const e=localStorage.getItem(x(t));return null===e?null:JSON.parse(e)}has(t){return this.keys().includes(t)}isActive(){return null!==localStorage.getItem(p)}keys(){const t=localStorage.getItem(p);return null===t?[]:JSON.parse(t)}set(t,e){const i=JSON.stringify(e),n=localStorage.getItem(p),s=null===n?[]:JSON.parse(n);s.includes(t)||(s.push(t),localStorage.setItem(p,JSON.stringify(s))),localStorage.setItem(x(t),i)}unset(t){localStorage.removeItem(x(t));const e=localStorage.getItem(p),i=null===e?[]:JSON.parse(e),n=i.indexOf(t);return-1!==n&&(i.splice(n,1),localStorage.setItem(p,JSON.stringify(i)),!0)}}const w=async(t,e={})=>{const i={_sorted:""};e.since&&(i.since=e.since.toISOString().substring(0,19)+"Z");const n=await(async(t,e={})=>{const i=new URLSearchParams(e).toString(),n=i?`${f}${t}?${i}`:`${f}${t}`,s=await fetch(n);return{data:await s.json(),response:s}})(`/id/measures/${t}/readings`,i);return[T(n.data.items)[t]||[],n]},y=(t,e)=>{const i=t.findIndex((t=>t[0]>=e));return i<0?[]:t.slice(i)},O=async(t,e={})=>{const i=`readings|${t}`,n=(S||(S=new v),S),s=n.get(i)||{data:[],lastCheck:0,storedSince:1/0},{data:a,lastCheck:o}=s;let{storedSince:l}=s;const h=r(null,-8,!0).valueOf()/1e3;for(;a.length&&a[0][0]<h;)[l]=a[0],a.shift();const c=a.length?a[a.length-1][0]:0,m=e.since&&e.since.valueOf()/1e3||0;if(l<=m&&Date.now()<1e3*o+3e5)return y(a,m);const d=Object.assign(Object.assign({},e),{since:new Date(1e3*Math.max(m,c))}),[u]=await w(t,d);return k(a,u),l=Math.min(m,l),n.set(i,{lastCheck:Date.now()/1e3,data:a,storedSince:l}),y(a,m)},k=(t,e)=>{if(!e.length)return;let i=t.length-1;for(;i>=0&&t[i][0]>=e[0][0];)--i;t.splice(i+1,1/0,...e)},T=t=>{const e={};t.forEach((({measure:t,dateTime:i,value:n})=>{null==e[t]&&(e[t]=[]),e[t].unshift([new Date(i).valueOf()/1e3,n])}));const i={};return Object.entries(e).forEach((([t,e])=>{i[t.substring(t.lastIndexOf("/")+1)]=e})),i},D=async(t,e,a={})=>{const o=r(null,-7,!0),l=await O(e,{since:o});t.replaceChildren();const h=(t=>{const e=t.match(/(.*)-([^-]*)-([^-]*)-([^-]*)-([^-]*)-([^-]*)$/);if(null===e)throw new n("Cannot parse measure id",{measureId:t});const[i,s,a,o,r,l]=e.reverse();return{stationId:l,parameter:r,qualifier:o,type:a,interval:s,unit:i,qualifiedParameter:o.length?`${r}-${o}`:r}})(e),{unit:c}=(t=>{const e={};for(const i in t){const n=t[i];s[i]&&s[i][n]?e[i]=s[i][n]:e[i]=n}return e})(h),m={data:l,unit:c,formatter:i};null!=a.riverDataWidgetMinValue&&(m.min=parseFloat(a.riverDataWidgetMinValue));const u=r(new Date(1e3*l[0][0])).valueOf()/1e3,g=r(new Date(1e3*l[l.length-1][0]),1).valueOf()/1e3;new d(t,[m],{minTime:u,maxTime:g}).render()},$=async()=>{document.querySelectorAll("[data-river-data-widget]").forEach((t=>{try{(t=>{var i,n;const s="string"==typeof t?document.querySelector(t):t;if(null===s)throw new Error("Target element not found");const a=null!==(n=null===(i=s.dataset.riverDataWidget)||void 0===i?void 0:i.split(":"))&&void 0!==n?n:[],o=a.shift(),r=a.join(":"),l=s.dataset;if("measure"!==o)throw new e("Unknown widget definition",{type:o,id:r});D(s,r,l)})(t)}catch(t){console.error(t,{error:t})}}))};return"loading"===document.readyState?document.addEventListener("DOMContentLoaded",$):$(),t.version="1.0.1",t}({});
//# sourceMappingURL=river-data-widget.min.js.map

@@ -1,1 +0,1 @@

{"version":3,"file":"river-data-widget.min.js","sources":["../src/error.ts","../src/helpers/format.ts","../src/flood-monitoring-api/error.ts","../src/flood-monitoring-api/measure.ts","../src/helpers/dom.ts","../src/helpers/time.ts","../src/widget/chart.ts","../src/flood-monitoring-api/api.ts","../src/flood-monitoring-api/store.ts","../src/flood-monitoring-api/reading.ts","../src/widget/render.ts","../src/autoload.ts"],"sourcesContent":["export type RiverDataWidgetErrorInfo = Record<string, unknown>;\n\nexport class RiverDataWidgetError extends Error {\n public info: RiverDataWidgetErrorInfo;\n\n constructor(msg: string, info: RiverDataWidgetErrorInfo = {}) {\n super(msg);\n this.name = 'RiverDataWidgetError';\n this.info = info;\n }\n}\n","export const FONT_STACK =\n '-apple-system,BlinkMacSystemFont,\"Segoe UI\",\"Roboto\",\"Helvetica Neue\",Arial,sans-serif';\n\nexport const round3 = (value: number) =>\n value < 100 ? value.toPrecision(3) : Math.round(value).toString();\n","export class FloodMonitoringApiError extends Error {\n public info: Record<string, unknown>;\n\n constructor(msg: string, info: Record<string, unknown> = {}) {\n super(msg);\n this.name = 'FloodMonitoringApiError';\n this.info = info;\n }\n}\n","import { FloodMonitoringApiError } from './error';\n\nexport { parseMeasureId };\n\nconst parseMeasureId = (measureId: string) => {\n // ............base/ stat-paramet-qualifi- type -interva-unit\n const regExp = /(.*)-([^-]*)-([^-]*)-([^-]*)-([^-]*)-([^-]*)$/;\n const matches = measureId.match(regExp);\n if (matches === null) {\n throw new FloodMonitoringApiError('Cannot parse measure id', { measureId });\n }\n const [unit, interval, type, qualifier, parameter, stationId] =\n matches.reverse();\n const qualifiedParameter = qualifier.length\n ? `${parameter}-${qualifier}`\n : parameter;\n return {\n stationId,\n parameter,\n qualifier,\n type,\n interval,\n unit,\n qualifiedParameter,\n };\n};\n\nconst measureTranslations: Record<string, Record<string, string>> = {\n unit: {\n m3_s: 'm³/s',\n mAOD: 'm',\n mASD: 'm',\n },\n qualifiedParameter: {\n 'level-stage': 'level',\n 'level-downstage': 'downstream level',\n },\n};\n\nexport const translateMeasureProperties = (measure: Record<string, string>) => {\n const translated: Record<string, string> = {};\n for (const prop in measure) {\n const value = measure[prop];\n if (measureTranslations[prop] && measureTranslations[prop][value]) {\n translated[prop] = measureTranslations[prop][value];\n } else {\n translated[prop] = value;\n }\n }\n return translated;\n};\n","/**\n * RiverDataWidget https://github.com/pb-uk/river-data-widget.\n *\n * @copyright Copyright (C) 2022 pbuk https://github.com/pb-uk.\n * @license AGPL-3.0-or-later see LICENSE.md.\n */\n\nexport { createElement, createSvgElement, setAttributes, setStyles };\n\ntype AttributeList = Record<string, string | number>;\n\nconst setAttributes = <T extends HTMLElement | SVGElement>(\n el: T,\n attributes: AttributeList\n): T => {\n Object.entries(attributes).forEach(([key, value]) => {\n el.setAttribute(key, `${value}`);\n });\n return el;\n};\n\nconst setStyles = <T extends HTMLElement | SVGElement>(\n el: T,\n styles: AttributeList\n): T => {\n Object.entries(styles).forEach(([key, value]) => {\n // Workaround (el.style.setProperty uses kebab-case keys).\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (<any>el.style)[key] = value;\n });\n return el;\n};\n\nconst createElement = (\n name = 'div',\n attributes: AttributeList = {},\n styles: AttributeList = {},\n innerHTML: string | false = false\n): HTMLElement => {\n const el = document.createElement(name);\n if (innerHTML !== false) {\n el.innerHTML = innerHTML;\n }\n return setStyles(setAttributes(el, attributes), styles);\n};\n\nconst createSvgElement = (\n name = 'svg',\n attributes: AttributeList = {},\n styles: AttributeList = {},\n innerHTML: string | false = false\n) => {\n const el = document.createElementNS('http://www.w3.org/2000/svg', name);\n if (innerHTML !== false) {\n el.innerHTML = innerHTML;\n }\n return setStyles(setAttributes(el, attributes), styles);\n};\n","export const MINUTE_MS = 60000;\n// const HOUR_MS = 3600000;\nexport const DAY_MS = 86400000;\n\n/**\n * Get the Date at the start of a day in UTC or local time.\n *\n * @param offset\n * @param timeZone The time zone offset in minutes, or set to `true` to use the\n * local time zone (`false`, the default, uses UTC).\n * @returns The reqested date.\n */\nexport const startOfDay = (\n date: Date | null = null,\n offset = 0,\n timeZone: boolean | number = false\n): Date => {\n if (timeZone === false) {\n // Use UTC.\n const base = date === null ? Date.now() : date.valueOf();\n return new Date(Math.floor(base / DAY_MS + offset) * DAY_MS);\n }\n\n const now = new Date();\n const tz = timeZone === true ? now.getTimezoneOffset() : timeZone;\n const local = now.valueOf() + tz * MINUTE_MS;\n return new Date(Math.floor(local / DAY_MS + offset) * DAY_MS);\n};\n\n/**\n * | | long |short|narrow|numeric|2-digit|\n * |:-------:|:-----------:|:---:|:----:|:-----:|:-----:|\n * | weekday | Monday | Mon | M | | |\n * | era | Anno Domini | AD | A | | |\n * | year | | | | 2012 | 12 |\n * | month | March | Mar | M | 3 | 03 |\n * | day | | | | 1 | 01 |\n * | hour | | | | 1 | 01 |\n * | minute | | | | 1 | 01 |\n * | second | | | | 1 | 01 |\n *\n * * fractionalSecondDigits: 1, 2 or 3 for number of digits.\n * * timeZoneName: long (Pacific Standard Time), short (PST),\n * longOffset (GMT-0800), shortOffset (GMT-8), longGeneric (Pacific Time),\n * shortGeneric (PT).\n */\n\nexport const dateFormatter = new Intl.DateTimeFormat('en-GB', {\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n // year: 'numeric',\n});\n\nexport const timeFormatter = new Intl.DateTimeFormat('en-GB', {\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n timeZoneName: 'short',\n});\n\nexport const dddFormatter = new Intl.DateTimeFormat('en-GB', {\n weekday: 'short',\n});\n\nexport const dMmmFormatter = new Intl.DateTimeFormat('en-GB', {\n day: 'numeric',\n month: 'short',\n});\n","import { createSvgElement } from '../helpers/dom';\nimport { timeFormatter, dddFormatter, dMmmFormatter } from '../helpers/time';\nimport { FloodMonitoringApiError } from '../flood-monitoring-api/error';\nimport { FONT_STACK } from '../helpers/format';\n\nconst PLOT_COLOR = '#77C';\n\nexport interface ChartOptions {\n minTime?: number;\n maxTime?: number;\n attribution?: string;\n}\n\nexport interface ChartScaleLimits {\n minTime: number;\n maxTime: number;\n timeScale: number;\n minValue: number;\n maxValue: number;\n valueScale: number;\n}\n\nexport interface ChartSeries {\n data: TimeSeriesValue[];\n min?: number;\n max?: number;\n unit?: string;\n formatter?: (value: number) => string;\n}\n\nexport type TimeSeriesValue = [\n ts: number, // Unix time stamp (seconds).\n v: number // Value.\n];\n\nexport class Chart {\n protected fontSizePx = 14;\n\n protected el: SVGElement;\n protected series: ChartSeries[];\n protected options: ChartOptions;\n protected width = 480; // 400;\n protected height = 270; // 225;\n protected plotHeight = this.height - this.fontSizePx * 4.5;\n protected strokeWidth = 2;\n protected limits?: ChartScaleLimits;\n\n protected attribution =\n 'Uses Environment Agency data from the real-time API (Beta)';\n\n // CSS settings.\n // Just readable at 320x180.\n // Good from 400x225.\n // Perfect at 480x270 (font is 12px);\n protected styles = {\n 'font-family': FONT_STACK,\n 'font-size': `${this.fontSizePx}px`,\n display: 'block',\n margin: 'auto',\n 'max-width': '150vh',\n };\n\n constructor(\n el: HTMLElement,\n series: ChartSeries[],\n options: ChartOptions = {}\n ) {\n this.series = series;\n this.options = options;\n const viewBox = `0 0 ${this.width} ${this.height}`;\n this.attribution = options.attribution ?? this.attribution;\n this.el = createSvgElement('svg', { viewBox }, this.styles);\n el.append(this.el);\n }\n\n getLimits(): ChartScaleLimits {\n if (this.limits == null) {\n throw new FloodMonitoringApiError('Chart axis limits have not been set');\n }\n return this.limits;\n }\n\n getHorizontalGridlines(): SVGElement[] {\n const { minTime, maxTime, timeScale, minValue, maxValue, valueScale } =\n this.getLimits();\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight;\n const x1 = xOffset;\n const x2 = xOffset + (maxTime - minTime) * timeScale;\n // Horizontal grid lines.\n const stroke = '#ddd';\n const lines = createSvgElement('g', { stroke });\n const labels = createSvgElement('g');\n const valueRange = maxValue - minValue;\n // Horizontal grid interval.\n const [interval, exponent] = getInterval(valueRange, 9);\n const factor = 10 ** -exponent;\n const base = Math.ceil((minValue * factor) / interval + 1) * interval;\n let i = 0;\n let current = base / factor;\n while (current < maxValue) {\n const y1 = yOffset - (current - minValue) * valueScale;\n lines.append(createSvgElement('line', { x1, y1, x2, y2: y1 }));\n labels.append(\n createSvgElement('text', { x: x1 + 4, y: y1 + 4 }, {}, `${current}`)\n );\n ++i;\n current = (base + i * interval) / factor;\n }\n const timeAxisLine = createSvgElement(\n 'line',\n { x1, y1: yOffset, x2, y2: yOffset },\n { stroke: '#777' }\n );\n\n return [lines, labels, timeAxisLine];\n }\n\n getTimeScale(): SVGElement[] {\n const { minTime, maxTime, timeScale } = this.getLimits();\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight + this.strokeWidth / 2;\n const y1 = yOffset + this.fontSizePx * 3;\n const y2 = yOffset - this.plotHeight;\n // Vertical grid lines.\n const stroke = '#ddd';\n const lines = createSvgElement('g', { stroke });\n const labels = createSvgElement('g');\n // Vertical grid interval.\n const base = minTime;\n const interval = 86400;\n let i = 0;\n let current = base;\n const labelOffset = 43200 * timeScale;\n const fill = '#444';\n while (current <= maxTime) {\n const x1 = xOffset + (current - minTime) * timeScale;\n const d = new Date(current * 1000);\n // lines.append(createSvgElement('line', { x1, y1, x2, y2: y1 }));\n lines.append(createSvgElement('line', { x1, y1, x2: x1, y2 }));\n labels.append(\n createSvgElement(\n 'text',\n {\n x: x1 + labelOffset,\n y: y1 - this.fontSizePx * 1.8,\n 'text-anchor': 'middle',\n },\n { fill },\n `${dddFormatter.format(d)}`\n ),\n createSvgElement(\n 'text',\n {\n x: x1 + labelOffset,\n y: y1 - this.fontSizePx * 0.5,\n 'text-anchor': 'middle',\n },\n { fill },\n `${dMmmFormatter.format(d)}`\n )\n );\n ++i;\n current = base + i * interval;\n }\n return [lines, labels];\n }\n\n render() {\n // Calculate axis scales.\n const limits = getLimits(this.series[0].data);\n limits.minValue = this.series[0].min ?? limits.minValue;\n limits.maxValue = this.series[0].max ?? limits.maxValue;\n limits.minTime = this.options.minTime ?? limits.minTime;\n limits.maxTime = this.options.maxTime ?? limits.maxTime;\n\n this.limits = {\n ...limits,\n valueScale:\n (this.plotHeight - this.strokeWidth) /\n (limits.maxValue - limits.minValue),\n timeScale:\n (this.width - this.strokeWidth) / (limits.maxTime - limits.minTime),\n };\n\n // Time axis.\n const [timeLines, timeLabels] = this.getTimeScale();\n this.el.append(timeLines);\n\n // Value axis.\n const [valueLines, valueLabels, timeAxisLine] =\n this.getHorizontalGridlines();\n this.el.append(valueLines);\n this.el.append(timeAxisLine);\n\n this.plotData();\n\n // Plot labels on top of the line.\n this.el.append(timeLabels);\n this.el.append(valueLabels);\n\n this.el.append(\n createSvgElement(\n 'text',\n {\n x: this.width / 2,\n 'text-anchor': 'middle',\n y: this.height - this.fontSizePx * 0.5,\n },\n { fill: '#595959' },\n this.attribution\n )\n );\n\n this.plotLastValue();\n }\n\n plotLastValue() {\n const { data, unit, formatter } = this.series[0];\n const [time, value] = data[data.length - 1];\n const { minTime, timeScale, minValue, maxValue, valueScale } =\n this.getLimits();\n\n const v = formatter == null ? value : formatter(value);\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight - this.strokeWidth / 2;\n const x = xOffset + (time - minTime) * timeScale;\n const highLabel = (value - minValue) / valueScale >= 0.5;\n const y =\n yOffset - this.plotHeight * (highLabel ? 1 : 0.5) + this.fontSizePx * 2;\n\n this.el.append(\n createSvgElement(\n 'text',\n { x, y, 'text-anchor': 'end' },\n { fill: PLOT_COLOR, 'font-size': '1.5em', 'font-weight': 'bold' },\n `${v} ${unit}`\n )\n );\n this.el.append(\n createSvgElement(\n 'text',\n { x, y: y + this.fontSizePx * 1.5, 'text-anchor': 'end' },\n { fill: PLOT_COLOR },\n `${timeFormatter.format(new Date(time * 1000))}`\n )\n );\n }\n\n plotData() {\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight - this.strokeWidth / 2;\n const { data } = this.series[0];\n const { minTime, timeScale, minValue, valueScale } = this.getLimits();\n // First data point.\n const x = xOffset + (data[0][0] - minTime) * timeScale;\n const y = yOffset - (data[0][1] - minValue) * valueScale;\n const points = [`M${x},${y}`];\n // Remaining data points.\n for (let i = 1; i < data.length; ++i) {\n const x = xOffset + (data[i][0] - minTime) * timeScale;\n const y = yOffset - (data[i][1] - minValue) * valueScale;\n points.push(`L${x},${y}`);\n }\n // Plot the data.\n const path = createSvgElement('path', {\n d: points.join(''),\n stroke: PLOT_COLOR,\n 'stroke-width': this.strokeWidth,\n fill: 'none',\n });\n this.el.append(path);\n }\n}\n\nexport const getLimits = (data: TimeSeriesValue[]) => {\n if (data.length < 1) {\n throw new Error('Readings must not be empty');\n }\n const minTime = data[0][0];\n const maxTime = data[data.length - 1][0];\n let minValue = Infinity;\n let maxValue = -minValue;\n data.forEach(([, value]) => {\n minValue = Math.min(minValue, value);\n maxValue = Math.max(maxValue, value);\n });\n return { minTime, maxTime, minValue, maxValue };\n};\n\nexport const getInterval = (range: number, maxDivisions: number) => {\n const exponent = Math.floor(Math.log10(range)) - 1;\n const k = range / (maxDivisions * 10 ** exponent);\n const mantissa = k <= 2 ? 2 : k <= 5 ? 5 : 10;\n return [mantissa, exponent];\n};\n","// There is no need to be secure about this!\nconst baseUrl = 'http://environment.data.gov.uk/flood-monitoring';\n\nexport interface ApiResponse<T> {\n data: {\n items: T;\n };\n response: Response;\n}\n\nexport interface ApiParameters {\n since?: string; // Time from.\n _sorted?: ''; // Flag for sorting.\n}\n\nexport const apiFetch = async (\n path: string,\n query = {}\n): Promise<ApiResponse<unknown>> => {\n const queryString = new URLSearchParams(query).toString();\n const uri = queryString\n ? `${baseUrl}${path}?${queryString}`\n : `${baseUrl}${path}`;\n const response = await fetch(uri);\n return { data: await response.json(), response };\n};\n\n/**\n * Convert a Date to a format recognized by the EA API for a query parameter.\n *\n * @param date Convert from.\n * @returns A string in the EA API query parameter format.\n */\nexport const toTimeParameter = (date: Date): string => {\n return date.toISOString().substring(0, 19) + 'Z';\n};\n\n/*\nUseful response headers\n Date: 'Sat, 13 May 2023 09:14:07 GMT',\n last-modified: Sat, 13 May 2023 09:03:13 GMT,\nResponse meta:\n publisher: 'Environment Agency',\n license: 'http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/',\n documentation: 'http://environment.data.gov.uk/flood-monitoring/doc/reference',\n version: '0.9',\n comment: 'Status: Beta service',\n hasFormat: [\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.csv?_sorted=&since=2023-05-12T08%3A00%3A00Z\",\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.rdf?_sorted=&since=2023-05-12T08%3A00%3A00Z\",\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.ttl?_sorted=&since=2023-05-12T08%3A00%3A00Z\",\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.html?_sorted=&since=2023-05-12T08%3A00%3A00Z\"\n ],\n*/\n","const prefix = 'riverDataWidget';\n\nconst addPrefix = (key: string): string => `${prefix}|${key}`;\n\nlet instance: Store;\n\nclass Store {\n clear(destroy = false) {\n for (const key of this.keys()) {\n localStorage.removeItem(addPrefix(key));\n }\n if (destroy) {\n localStorage.removeItem(prefix);\n return;\n }\n localStorage.setItem(prefix, JSON.stringify([]));\n }\n\n get(key: string) {\n const value = localStorage.getItem(addPrefix(key));\n return value === null ? null : JSON.parse(value);\n }\n\n has(key: string): boolean {\n return this.keys().includes(key);\n }\n\n /**\n * Detect active localStorage.\n *\n * @returns true iff localStorage for the widget is active.\n */\n isActive() {\n return localStorage.getItem(prefix) !== null;\n }\n\n keys(): string[] {\n const storedKeys = localStorage.getItem(prefix);\n return storedKeys === null ? [] : JSON.parse(storedKeys);\n }\n\n set(key: string, value: unknown) {\n const json = JSON.stringify(value);\n const storedKeys = localStorage.getItem(prefix);\n const keys: string[] = storedKeys === null ? [] : JSON.parse(storedKeys);\n if (!keys.includes(key)) {\n keys.push(key);\n localStorage.setItem(prefix, JSON.stringify(keys));\n }\n localStorage.setItem(addPrefix(key), json);\n }\n\n unset(key: string): boolean {\n // Remove it before we do anything else.\n localStorage.removeItem(addPrefix(key));\n\n // Then remove it from the list of keys.\n const storedKeys = localStorage.getItem(prefix);\n const keys: string[] = storedKeys === null ? [] : JSON.parse(storedKeys);\n const index = keys.indexOf(key);\n\n // If it doesn't exist we don't have to remove it.\n if (index === -1) return false;\n\n keys.splice(index, 1);\n localStorage.setItem(prefix, JSON.stringify(keys));\n return true;\n }\n}\n\nexport const useStore = (): Store => {\n if (!instance) {\n instance = new Store();\n }\n return instance;\n};\n","import { apiFetch, toTimeParameter } from './api';\nimport { useStore } from './store';\nimport { MINUTE_MS, startOfDay } from '../helpers/time';\n\nimport type { ApiParameters, ApiResponse } from './api';\n\nconst THROTTLE_MS = 15 * MINUTE_MS;\n\n/**\n * Internal format for readings.\n */\nexport type Reading = [\n timestamp: number, // Unix epoch timestamp (seconds).\n value: number // Value.\n];\n\n/**\n * Internal format for readings.\n */\nexport interface ReadingOptions {\n since?: Date; // Time from.\n}\n\n/**\n * Internal format for readings.\n */\ntype ReadingResponse = [a: Reading[], b: ApiResponse<ReadingDTO[]>];\n\n/**\n * Data transfer object for readings provided by the API.\n */\ninterface ReadingDTO {\n '@id': string; // The URL of this reading.\n dateTime: string; // e.g. '2023-05-13T09:00:00Z'.\n measure: string; // The URL of the measure.\n value: number; // The value in the appropriate units.\n}\n\ninterface StoredReadings {\n storedSince: number;\n lastCheck: number;\n data: Reading[];\n}\n\n/**\n * Fetch the readings for a measure.\n *\n * @param id The EA measure id.\n * @returns A promise for an array of readings for the measure.\n */\nconst fetchMeasureReadings = async (\n id: string,\n options: ReadingOptions = {}\n): Promise<ReadingResponse> => {\n // Set the parameters for the request.\n const params: ApiParameters = { _sorted: '' };\n if (options.since) {\n params.since = toTimeParameter(options.since);\n }\n // Get the response, casting the items to ReadingDTOs.\n const response = <ApiResponse<ReadingDTO[]>>(\n await apiFetch(`/id/measures/${id}/readings`, params)\n );\n return [parseReadings(response.data.items)[id] || [], response];\n};\n\nexport const filterSince = (data: Reading[], since: number) => {\n const position = data.findIndex((reading) => reading[0] >= since);\n return position < 0 ? [] : data.slice(position);\n};\n\n/**\n * Get the readings for a measure.\n *\n * @todo Caching and throttling.\n *\n * @param id The EA measure id.\n * @returns A promise for an array of readings for the measure.\n */\nexport const getMeasureReadings = async (\n id: string,\n options: ReadingOptions = {}\n): Promise<Reading[]> => {\n // Get the saved readings.\n const key = `readings|${id}`;\n const store = useStore();\n\n const { data, lastCheck, storedSince }: StoredReadings = store.get(key) || {\n data: [],\n lastCheck: 0,\n storedSince: Infinity,\n };\n const discardBefore = startOfDay(null, -30, true).valueOf() / 1000;\n\n // Discard any older than 30 days.\n while (data.length && data[0][0] < discardBefore) {\n data.shift();\n }\n\n // If we have data early enough Throttle at 15 mins.\n const lastStored = data.length ? data[data.length - 1][0] : 0;\n const requestedSince = (options.since && options.since.valueOf() / 1000) || 0;\n\n if (\n storedSince <= requestedSince &&\n Date.now() < lastCheck * 1000 + THROTTLE_MS\n ) {\n // Throttled.\n return filterSince(data, requestedSince);\n }\n\n const fetchOptions: ReadingOptions = {\n ...options,\n since: new Date(Math.max(requestedSince, lastStored) * 1000),\n };\n\n const [newData] = await fetchMeasureReadings(id, fetchOptions);\n mergeReadings(data, newData);\n const newSince = Math.min(requestedSince, storedSince);\n store.set(key, { lastCheck: Date.now() / 1000, data, storedSince: newSince });\n return filterSince(data, requestedSince);\n};\n\nexport const mergeReadings = (first: Reading[], second: Reading[]): void => {\n if (!second.length) return;\n\n let firstPos = first.length - 1;\n while (firstPos >= 0 && first[firstPos][0] >= second[0][0]) {\n --firstPos;\n }\n first.splice(firstPos + 1, Infinity, ...second);\n};\n\nconst parseReadings = (items: ReadingDTO[]): Record<string, Reading[]> => {\n const ranges: Record<string, Reading[]> = {};\n items.forEach(({ measure, dateTime, value }) => {\n if (ranges[measure] == null) {\n ranges[measure] = [];\n }\n ranges[measure].unshift([new Date(dateTime).valueOf() / 1000, value]);\n });\n\n const rangesById: Record<string, Reading[]> = {};\n Object.entries(ranges).forEach(([key, range]) => {\n rangesById[key.substring(key.lastIndexOf('/') + 1)] = range;\n });\n\n return rangesById;\n};\n","import { RiverDataWidgetError } from '../error';\nimport { round3 } from '../helpers/format';\nimport {\n parseMeasureId,\n translateMeasureProperties,\n} from '../flood-monitoring-api/measure';\nimport { Chart } from './chart';\nimport { getMeasureReadings } from '../flood-monitoring-api';\nimport { startOfDay } from '../helpers/time';\n\nimport type { ChartSeries } from './chart';\n\nconst drawMeasureWidget = async (\n parentEl: HTMLElement,\n measureId: string,\n options: Record<string, unknown> = {}\n) => {\n // Get readings for the last 7 days in local time.\n const since = startOfDay(null, -7, true);\n\n const data = await getMeasureReadings(measureId, { since });\n\n parentEl.replaceChildren();\n\n const measure = parseMeasureId(measureId);\n const { unit } = translateMeasureProperties(measure);\n\n // const [time, value] = data[data.length - 1];\n // const v = round3(value);\n // const param = m.qualifiedParameter;\n // const station = measure.stationId;\n // const unit = m.unit;\n\n // let textEl = createElement('div');\n // const d = dateFormatter.format(new Date(time * 1000));\n // const t = timeFormatter.format(new Date(time * 1000));\n // textEl.innerHTML = `The most recent ${param} reading for station ${station} was ${v} ${unit} at ${t} on ${d}.`;\n // widgetEl.append(textEl);\n\n const series1: ChartSeries = { data, unit, formatter: round3 };\n if (options.riverDataWidgetMinValue != null) {\n series1.min = parseFloat(<string>options.riverDataWidgetMinValue);\n }\n const minTime = startOfDay(new Date(data[0][0] * 1000)).valueOf() / 1000;\n const maxTime =\n startOfDay(new Date(data[data.length - 1][0] * 1000), 1).valueOf() / 1000;\n const chartOptions = {\n minTime,\n maxTime,\n // attribution: `www.riverdata.co.uk/station/${measure.stationId}`,\n };\n\n const chart = new Chart(parentEl, [series1], chartOptions);\n chart.render();\n};\n\n/**\n * Load a widget specified by a DOM element.\n */\nexport const loadWidget = (el: HTMLElement | string) => {\n // Get the target element from a query selector if necessary and check it\n // exists.\n const targetEl =\n typeof el === 'string' ? <HTMLElement>document.querySelector(el) : el;\n if (targetEl === null) {\n throw new Error('Target element not found');\n }\n\n // Parse element for widget type and options.\n const widgetIdParts = targetEl.dataset.riverDataWidget?.split(':') ?? [];\n const type = widgetIdParts.shift();\n const id = widgetIdParts.join(':');\n const options = targetEl.dataset;\n\n switch (type) {\n case 'measure':\n drawMeasureWidget(targetEl, id, options);\n break;\n\n // The 'station' widget is experimental in v1.0 and should not be used.\n // case 'station':\n // break;\n\n default:\n throw new RiverDataWidgetError('Unknown widget definition', { type, id });\n }\n};\n","import { loadWidget } from './widget/render';\n\nconst autoload = async () => {\n document.querySelectorAll('[data-river-data-widget]').forEach((el) => {\n try {\n loadWidget(<HTMLElement>el);\n } catch (error) {\n console.error(error, { error });\n }\n });\n};\n\nif (document.readyState === 'loading') {\n // Loading hasn't finished yet.\n document.addEventListener('DOMContentLoaded', autoload);\n} else {\n // `DOMContentLoaded` has already fired.\n autoload();\n}\n"],"names":["RiverDataWidgetError","Error","constructor","msg","info","super","this","name","round3","value","toPrecision","Math","round","toString","FloodMonitoringApiError","measureTranslations","unit","m3_s","mAOD","mASD","qualifiedParameter","createSvgElement","attributes","styles","innerHTML","el","document","createElementNS","Object","entries","forEach","key","style","setStyles","setAttribute","setAttributes","DAY_MS","startOfDay","date","offset","timeZone","base","Date","now","valueOf","floor","tz","getTimezoneOffset","local","timeFormatter","Intl","DateTimeFormat","hour","minute","hour12","timeZoneName","dddFormatter","weekday","dMmmFormatter","day","month","PLOT_COLOR","Chart","series","options","fontSizePx","width","height","plotHeight","strokeWidth","attribution","display","margin","viewBox","_a","append","getLimits","limits","getHorizontalGridlines","minTime","maxTime","timeScale","minValue","maxValue","valueScale","xOffset","yOffset","x1","x2","lines","stroke","labels","valueRange","interval","exponent","getInterval","factor","ceil","i","current","y1","y2","x","y","getTimeScale","labelOffset","fill","d","format","render","data","min","_b","max","_c","_d","assign","timeLines","timeLabels","valueLines","valueLabels","timeAxisLine","plotData","plotLastValue","formatter","time","length","v","highLabel","points","push","path","join","Infinity","range","maxDivisions","log10","k","baseUrl","prefix","addPrefix","instance","Store","clear","destroy","keys","localStorage","removeItem","setItem","JSON","stringify","get","getItem","parse","has","includes","isActive","storedKeys","set","json","unset","index","indexOf","splice","fetchMeasureReadings","async","id","params","_sorted","since","toISOString","substring","response","query","queryString","URLSearchParams","uri","fetch","apiFetch","parseReadings","items","filterSince","position","findIndex","reading","slice","getMeasureReadings","store","lastCheck","storedSince","discardBefore","shift","lastStored","requestedSince","fetchOptions","newData","mergeReadings","newSince","first","second","firstPos","ranges","measure","dateTime","unshift","rangesById","lastIndexOf","drawMeasureWidget","parentEl","measureId","replaceChildren","matches","match","type","qualifier","parameter","stationId","reverse","parseMeasureId","translated","prop","translateMeasureProperties","series1","riverDataWidgetMinValue","parseFloat","autoload","querySelectorAll","targetEl","querySelector","widgetIdParts","dataset","riverDataWidget","split","loadWidget","error","console","readyState","addEventListener"],"mappings":"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"}
{"version":3,"file":"river-data-widget.min.js","sources":["../src/error.ts","../src/helpers/format.ts","../src/flood-monitoring-api/error.ts","../src/flood-monitoring-api/measure.ts","../src/helpers/dom.ts","../src/helpers/time.ts","../src/widget/chart.ts","../src/flood-monitoring-api/api.ts","../src/flood-monitoring-api/store.ts","../src/flood-monitoring-api/reading.ts","../src/widget/render.ts","../src/autoload.ts"],"sourcesContent":["export type RiverDataWidgetErrorInfo = Record<string, unknown>;\n\nexport class RiverDataWidgetError extends Error {\n public info: RiverDataWidgetErrorInfo;\n\n constructor(msg: string, info: RiverDataWidgetErrorInfo = {}) {\n super(msg);\n this.name = 'RiverDataWidgetError';\n this.info = info;\n }\n}\n","export const FONT_STACK =\n '-apple-system,BlinkMacSystemFont,\"Segoe UI\",\"Roboto\",\"Helvetica Neue\",Arial,sans-serif';\n\nexport const round3 = (value: number) =>\n value < 100 ? value.toPrecision(3) : Math.round(value).toString();\n","export class FloodMonitoringApiError extends Error {\n public info: Record<string, unknown>;\n\n constructor(msg: string, info: Record<string, unknown> = {}) {\n super(msg);\n this.name = 'FloodMonitoringApiError';\n this.info = info;\n }\n}\n","import { FloodMonitoringApiError } from './error';\n\nexport { parseMeasureId };\n\nconst parseMeasureId = (measureId: string) => {\n // ............base/ stat-paramet-qualifi- type -interva-unit\n const regExp = /(.*)-([^-]*)-([^-]*)-([^-]*)-([^-]*)-([^-]*)$/;\n const matches = measureId.match(regExp);\n if (matches === null) {\n throw new FloodMonitoringApiError('Cannot parse measure id', { measureId });\n }\n const [unit, interval, type, qualifier, parameter, stationId] =\n matches.reverse();\n const qualifiedParameter = qualifier.length\n ? `${parameter}-${qualifier}`\n : parameter;\n return {\n stationId,\n parameter,\n qualifier,\n type,\n interval,\n unit,\n qualifiedParameter,\n };\n};\n\nconst measureTranslations: Record<string, Record<string, string>> = {\n unit: {\n m3_s: 'm³/s',\n mAOD: 'm',\n mASD: 'm',\n },\n qualifiedParameter: {\n 'level-stage': 'level',\n 'level-downstage': 'downstream level',\n },\n};\n\nexport const translateMeasureProperties = (measure: Record<string, string>) => {\n const translated: Record<string, string> = {};\n for (const prop in measure) {\n const value = measure[prop];\n if (measureTranslations[prop] && measureTranslations[prop][value]) {\n translated[prop] = measureTranslations[prop][value];\n } else {\n translated[prop] = value;\n }\n }\n return translated;\n};\n","/**\n * RiverDataWidget https://github.com/pb-uk/river-data-widget.\n *\n * @copyright Copyright (C) 2022 pbuk https://github.com/pb-uk.\n * @license AGPL-3.0-or-later see LICENSE.md.\n */\n\nexport { createElement, createSvgElement, setAttributes, setStyles };\n\ntype AttributeList = Record<string, string | number>;\n\nconst setAttributes = <T extends HTMLElement | SVGElement>(\n el: T,\n attributes: AttributeList\n): T => {\n Object.entries(attributes).forEach(([key, value]) => {\n el.setAttribute(key, `${value}`);\n });\n return el;\n};\n\nconst setStyles = <T extends HTMLElement | SVGElement>(\n el: T,\n styles: AttributeList\n): T => {\n Object.entries(styles).forEach(([key, value]) => {\n // Workaround (el.style.setProperty uses kebab-case keys).\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (<any>el.style)[key] = value;\n });\n return el;\n};\n\nconst createElement = (\n name = 'div',\n attributes: AttributeList = {},\n styles: AttributeList = {},\n innerHTML: string | false = false\n): HTMLElement => {\n const el = document.createElement(name);\n if (innerHTML !== false) {\n el.innerHTML = innerHTML;\n }\n return setStyles(setAttributes(el, attributes), styles);\n};\n\nconst createSvgElement = (\n name = 'svg',\n attributes: AttributeList = {},\n styles: AttributeList = {},\n innerHTML: string | false = false\n) => {\n const el = document.createElementNS('http://www.w3.org/2000/svg', name);\n if (innerHTML !== false) {\n el.innerHTML = innerHTML;\n }\n return setStyles(setAttributes(el, attributes), styles);\n};\n","export const MINUTE_MS = 60000;\n// const HOUR_MS = 3600000;\nexport const DAY_MS = 86400000;\n\n/**\n * Get the Date at the start of a day in UTC or local time.\n *\n * @param offset\n * @param timeZone The time zone offset in minutes, or set to `true` to use the\n * local time zone (`false`, the default, uses UTC).\n * @returns The reqested date.\n */\nexport const startOfDay = (\n date: Date | null = null,\n offset = 0,\n timeZone: boolean | number = false\n): Date => {\n if (timeZone === false) {\n // Use UTC.\n const base = date === null ? Date.now() : date.valueOf();\n return new Date(Math.floor(base / DAY_MS + offset) * DAY_MS);\n }\n\n const now = new Date();\n const tz = timeZone === true ? now.getTimezoneOffset() : timeZone;\n const local = now.valueOf() + tz * MINUTE_MS;\n return new Date(Math.floor(local / DAY_MS + offset) * DAY_MS);\n};\n\n/**\n * | | long |short|narrow|numeric|2-digit|\n * |:-------:|:-----------:|:---:|:----:|:-----:|:-----:|\n * | weekday | Monday | Mon | M | | |\n * | era | Anno Domini | AD | A | | |\n * | year | | | | 2012 | 12 |\n * | month | March | Mar | M | 3 | 03 |\n * | day | | | | 1 | 01 |\n * | hour | | | | 1 | 01 |\n * | minute | | | | 1 | 01 |\n * | second | | | | 1 | 01 |\n *\n * * fractionalSecondDigits: 1, 2 or 3 for number of digits.\n * * timeZoneName: long (Pacific Standard Time), short (PST),\n * longOffset (GMT-0800), shortOffset (GMT-8), longGeneric (Pacific Time),\n * shortGeneric (PT).\n */\n\nexport const dateFormatter = new Intl.DateTimeFormat('en-GB', {\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n // year: 'numeric',\n});\n\nexport const timeFormatter = new Intl.DateTimeFormat('en-GB', {\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n timeZoneName: 'short',\n});\n\nexport const dddFormatter = new Intl.DateTimeFormat('en-GB', {\n weekday: 'short',\n});\n\nexport const dMmmFormatter = new Intl.DateTimeFormat('en-GB', {\n day: 'numeric',\n month: 'short',\n});\n","import { createSvgElement } from '../helpers/dom';\nimport { timeFormatter, dddFormatter, dMmmFormatter } from '../helpers/time';\nimport { FloodMonitoringApiError } from '../flood-monitoring-api/error';\nimport { FONT_STACK } from '../helpers/format';\n\nconst PLOT_COLOR = '#77C';\n\nexport interface ChartOptions {\n minTime?: number;\n maxTime?: number;\n attribution?: string;\n}\n\nexport interface ChartScaleLimits {\n minTime: number;\n maxTime: number;\n timeScale: number;\n minValue: number;\n maxValue: number;\n valueScale: number;\n}\n\nexport interface ChartSeries {\n data: TimeSeriesValue[];\n min?: number;\n max?: number;\n unit?: string;\n formatter?: (value: number) => string;\n}\n\nexport type TimeSeriesValue = [\n ts: number, // Unix time stamp (seconds).\n v: number // Value.\n];\n\nexport class Chart {\n protected fontSizePx = 14;\n\n protected el: SVGElement;\n protected series: ChartSeries[];\n protected options: ChartOptions;\n protected width = 480; // 400;\n protected height = 270; // 225;\n protected plotHeight = this.height - this.fontSizePx * 4.5;\n protected strokeWidth = 2;\n protected limits?: ChartScaleLimits;\n\n protected attribution =\n 'Uses Environment Agency data from the real-time API (Beta)';\n\n // CSS settings.\n // Just readable at 320x180.\n // Good from 400x225.\n // Perfect at 480x270 (font is 12px);\n protected styles = {\n 'font-family': FONT_STACK,\n 'font-size': `${this.fontSizePx}px`,\n display: 'block',\n margin: 'auto',\n 'max-width': '150vh',\n };\n\n constructor(\n el: HTMLElement,\n series: ChartSeries[],\n options: ChartOptions = {}\n ) {\n this.series = series;\n this.options = options;\n const viewBox = `0 0 ${this.width} ${this.height}`;\n this.attribution = options.attribution ?? this.attribution;\n this.el = createSvgElement('svg', { viewBox }, this.styles);\n el.append(this.el);\n }\n\n getLimits(): ChartScaleLimits {\n if (this.limits == null) {\n throw new FloodMonitoringApiError('Chart axis limits have not been set');\n }\n return this.limits;\n }\n\n getHorizontalGridlines(): SVGElement[] {\n const { minTime, maxTime, timeScale, minValue, maxValue, valueScale } =\n this.getLimits();\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight;\n const x1 = xOffset;\n const x2 = xOffset + (maxTime - minTime) * timeScale;\n // Horizontal grid lines.\n const stroke = '#ddd';\n const lines = createSvgElement('g', { stroke });\n const labels = createSvgElement('g');\n const valueRange = maxValue - minValue;\n // Horizontal grid interval.\n const [interval, exponent] = getInterval(valueRange, 9);\n const factor = 10 ** -exponent;\n const base = Math.ceil((minValue * factor) / interval + 1) * interval;\n let i = 0;\n let current = base / factor;\n while (current < maxValue) {\n const y1 = yOffset - (current - minValue) * valueScale;\n lines.append(createSvgElement('line', { x1, y1, x2, y2: y1 }));\n labels.append(\n createSvgElement('text', { x: x1 + 4, y: y1 + 4 }, {}, `${current}`)\n );\n ++i;\n current = (base + i * interval) / factor;\n }\n const timeAxisLine = createSvgElement(\n 'line',\n { x1, y1: yOffset, x2, y2: yOffset },\n { stroke: '#777' }\n );\n\n return [lines, labels, timeAxisLine];\n }\n\n getTimeScale(): SVGElement[] {\n const { minTime, maxTime, timeScale } = this.getLimits();\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight + this.strokeWidth / 2;\n const y1 = yOffset + this.fontSizePx * 3;\n const y2 = yOffset - this.plotHeight;\n // Vertical grid lines.\n const stroke = '#ddd';\n const lines = createSvgElement('g', { stroke });\n const labels = createSvgElement('g');\n // Vertical grid interval.\n const base = minTime;\n const interval = 86400;\n let i = 0;\n let current = base;\n const labelOffset = 43200 * timeScale;\n const fill = '#444';\n while (current <= maxTime) {\n const x1 = xOffset + (current - minTime) * timeScale;\n const d = new Date(current * 1000);\n // lines.append(createSvgElement('line', { x1, y1, x2, y2: y1 }));\n lines.append(createSvgElement('line', { x1, y1, x2: x1, y2 }));\n labels.append(\n createSvgElement(\n 'text',\n {\n x: x1 + labelOffset,\n y: y1 - this.fontSizePx * 1.8,\n 'text-anchor': 'middle',\n },\n { fill },\n `${dddFormatter.format(d)}`\n ),\n createSvgElement(\n 'text',\n {\n x: x1 + labelOffset,\n y: y1 - this.fontSizePx * 0.5,\n 'text-anchor': 'middle',\n },\n { fill },\n `${dMmmFormatter.format(d)}`\n )\n );\n ++i;\n current = base + i * interval;\n }\n return [lines, labels];\n }\n\n render() {\n // Calculate axis scales.\n const limits = getLimits(this.series[0].data);\n limits.minValue = this.series[0].min ?? limits.minValue;\n limits.maxValue = this.series[0].max ?? limits.maxValue;\n limits.minTime = this.options.minTime ?? limits.minTime;\n limits.maxTime = this.options.maxTime ?? limits.maxTime;\n\n this.limits = {\n ...limits,\n valueScale:\n (this.plotHeight - this.strokeWidth) /\n (limits.maxValue - limits.minValue),\n timeScale:\n (this.width - this.strokeWidth) / (limits.maxTime - limits.minTime),\n };\n\n // Time axis.\n const [timeLines, timeLabels] = this.getTimeScale();\n this.el.append(timeLines);\n\n // Value axis.\n const [valueLines, valueLabels, timeAxisLine] =\n this.getHorizontalGridlines();\n this.el.append(valueLines);\n this.el.append(timeAxisLine);\n\n this.plotData();\n\n // Plot labels on top of the line.\n this.el.append(timeLabels);\n this.el.append(valueLabels);\n\n this.el.append(\n createSvgElement(\n 'text',\n {\n x: this.width / 2,\n 'text-anchor': 'middle',\n y: this.height - this.fontSizePx * 0.5,\n },\n { fill: '#595959' },\n this.attribution\n )\n );\n\n this.plotLastValue();\n }\n\n plotLastValue() {\n const { data, unit, formatter } = this.series[0];\n const [time, value] = data[data.length - 1];\n const { minTime, timeScale, minValue, valueScale } = this.getLimits();\n\n const v = formatter == null ? value : formatter(value);\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight - this.strokeWidth / 2;\n const x = xOffset + (time - minTime) * timeScale;\n const highLabel = (value - minValue) / valueScale >= 0.5;\n const y =\n yOffset - this.plotHeight * (highLabel ? 1 : 0.5) + this.fontSizePx * 2;\n\n this.el.append(\n createSvgElement(\n 'text',\n { x, y, 'text-anchor': 'end' },\n { fill: PLOT_COLOR, 'font-size': '1.5em', 'font-weight': 'bold' },\n `${v} ${unit}`\n )\n );\n this.el.append(\n createSvgElement(\n 'text',\n { x, y: y + this.fontSizePx * 1.5, 'text-anchor': 'end' },\n { fill: PLOT_COLOR },\n `${timeFormatter.format(new Date(time * 1000))}`\n )\n );\n }\n\n plotData() {\n const xOffset = this.strokeWidth / 2;\n const yOffset = this.plotHeight - this.strokeWidth / 2;\n const { data } = this.series[0];\n const { minTime, timeScale, minValue, valueScale } = this.getLimits();\n // First data point.\n const x = xOffset + (data[0][0] - minTime) * timeScale;\n const y = yOffset - (data[0][1] - minValue) * valueScale;\n const points = [`M${x},${y}`];\n // Remaining data points.\n for (let i = 1; i < data.length; ++i) {\n const x = xOffset + (data[i][0] - minTime) * timeScale;\n const y = yOffset - (data[i][1] - minValue) * valueScale;\n points.push(`L${x},${y}`);\n }\n // Plot the data.\n const path = createSvgElement('path', {\n d: points.join(''),\n stroke: PLOT_COLOR,\n 'stroke-width': this.strokeWidth,\n fill: 'none',\n });\n this.el.append(path);\n }\n}\n\nexport const getLimits = (data: TimeSeriesValue[]) => {\n if (data.length < 1) {\n throw new Error('Readings must not be empty');\n }\n const minTime = data[0][0];\n const maxTime = data[data.length - 1][0];\n let minValue = Infinity;\n let maxValue = -minValue;\n data.forEach(([, value]) => {\n minValue = Math.min(minValue, value);\n maxValue = Math.max(maxValue, value);\n });\n return { minTime, maxTime, minValue, maxValue };\n};\n\nexport const getInterval = (range: number, maxDivisions: number) => {\n const exponent = Math.floor(Math.log10(range)) - 1;\n const k = range / (maxDivisions * 10 ** exponent);\n const mantissa = k <= 2 ? 2 : k <= 5 ? 5 : 10;\n return [mantissa, exponent];\n};\n","// There is no need to be secure about this!\nconst baseUrl = 'http://environment.data.gov.uk/flood-monitoring';\n\nexport interface ApiResponse<T> {\n data: {\n items: T;\n };\n response: Response;\n}\n\nexport interface ApiParameters {\n since?: string; // Time from.\n _sorted?: ''; // Flag for sorting.\n}\n\nexport const apiFetch = async (\n path: string,\n query = {}\n): Promise<ApiResponse<unknown>> => {\n const queryString = new URLSearchParams(query).toString();\n const uri = queryString\n ? `${baseUrl}${path}?${queryString}`\n : `${baseUrl}${path}`;\n const response = await fetch(uri);\n return { data: await response.json(), response };\n};\n\n/**\n * Convert a Date to a format recognized by the EA API for a query parameter.\n *\n * @param date Convert from.\n * @returns A string in the EA API query parameter format.\n */\nexport const toTimeParameter = (date: Date): string => {\n return date.toISOString().substring(0, 19) + 'Z';\n};\n\n/*\nUseful response headers\n Date: 'Sat, 13 May 2023 09:14:07 GMT',\n last-modified: Sat, 13 May 2023 09:03:13 GMT,\nResponse meta:\n publisher: 'Environment Agency',\n license: 'http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/',\n documentation: 'http://environment.data.gov.uk/flood-monitoring/doc/reference',\n version: '0.9',\n comment: 'Status: Beta service',\n hasFormat: [\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.csv?_sorted=&since=2023-05-12T08%3A00%3A00Z\",\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.rdf?_sorted=&since=2023-05-12T08%3A00%3A00Z\",\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.ttl?_sorted=&since=2023-05-12T08%3A00%3A00Z\",\n \"http://environment.data.gov.uk/flood-monitoring/id/measures/3400TH-level-stage-i-15_min-mAOD/readings.html?_sorted=&since=2023-05-12T08%3A00%3A00Z\"\n ],\n*/\n","const prefix = 'riverDataWidget';\n\nconst addPrefix = (key: string): string => `${prefix}|${key}`;\n\nlet instance: Store;\n\nclass Store {\n clear(destroy = false) {\n for (const key of this.keys()) {\n localStorage.removeItem(addPrefix(key));\n }\n if (destroy) {\n localStorage.removeItem(prefix);\n return;\n }\n localStorage.setItem(prefix, JSON.stringify([]));\n }\n\n get(key: string) {\n const value = localStorage.getItem(addPrefix(key));\n return value === null ? null : JSON.parse(value);\n }\n\n has(key: string): boolean {\n return this.keys().includes(key);\n }\n\n /**\n * Detect active localStorage.\n *\n * @returns true iff localStorage for the widget is active.\n */\n isActive() {\n return localStorage.getItem(prefix) !== null;\n }\n\n keys(): string[] {\n const storedKeys = localStorage.getItem(prefix);\n return storedKeys === null ? [] : JSON.parse(storedKeys);\n }\n\n set(key: string, value: unknown) {\n const json = JSON.stringify(value);\n const storedKeys = localStorage.getItem(prefix);\n const keys: string[] = storedKeys === null ? [] : JSON.parse(storedKeys);\n if (!keys.includes(key)) {\n keys.push(key);\n localStorage.setItem(prefix, JSON.stringify(keys));\n }\n localStorage.setItem(addPrefix(key), json);\n }\n\n unset(key: string): boolean {\n // Remove it before we do anything else.\n localStorage.removeItem(addPrefix(key));\n\n // Then remove it from the list of keys.\n const storedKeys = localStorage.getItem(prefix);\n const keys: string[] = storedKeys === null ? [] : JSON.parse(storedKeys);\n const index = keys.indexOf(key);\n\n // If it doesn't exist we don't have to remove it.\n if (index === -1) return false;\n\n keys.splice(index, 1);\n localStorage.setItem(prefix, JSON.stringify(keys));\n return true;\n }\n}\n\nexport const useStore = (): Store => {\n if (!instance) {\n instance = new Store();\n }\n return instance;\n};\n","import { apiFetch, toTimeParameter } from './api';\nimport { useStore } from './store';\nimport { MINUTE_MS, startOfDay } from '../helpers/time';\n\nimport type { ApiParameters, ApiResponse } from './api';\n\n// Throttle requests to five minutes.\nconst THROTTLE_MS = 5 * MINUTE_MS;\n\n/**\n * Internal format for readings.\n */\nexport type Reading = [\n timestamp: number, // Unix epoch timestamp (seconds).\n value: number // Value.\n];\n\n/**\n * Internal format for readings.\n */\nexport interface ReadingOptions {\n since?: Date; // Time from.\n}\n\n/**\n * Internal format for readings.\n */\ntype ReadingResponse = [a: Reading[], b: ApiResponse<ReadingDTO[]>];\n\n/**\n * Data transfer object for readings provided by the API.\n */\ninterface ReadingDTO {\n '@id': string; // The URL of this reading.\n dateTime: string; // e.g. '2023-05-13T09:00:00Z'.\n measure: string; // The URL of the measure.\n value: number; // The value in the appropriate units.\n}\n\ninterface StoredReadings {\n storedSince: number;\n lastCheck: number;\n data: Reading[];\n}\n\n/**\n * Fetch the readings for a measure.\n *\n * @param id The EA measure id.\n * @returns A promise for an array of readings for the measure.\n */\nconst fetchMeasureReadings = async (\n id: string,\n options: ReadingOptions = {}\n): Promise<ReadingResponse> => {\n // Set the parameters for the request.\n const params: ApiParameters = { _sorted: '' };\n if (options.since) {\n params.since = toTimeParameter(options.since);\n }\n // Get the response, casting the items to ReadingDTOs.\n const response = <ApiResponse<ReadingDTO[]>>(\n await apiFetch(`/id/measures/${id}/readings`, params)\n );\n return [parseReadings(response.data.items)[id] || [], response];\n};\n\nexport const filterSince = (data: Reading[], since: number) => {\n const position = data.findIndex((reading) => reading[0] >= since);\n return position < 0 ? [] : data.slice(position);\n};\n\n/**\n * Get the readings for a measure.\n *\n * @todo Caching and throttling.\n *\n * @param id The EA measure id.\n * @returns A promise for an array of readings for the measure.\n */\nexport const getMeasureReadings = async (\n id: string,\n options: ReadingOptions = {}\n): Promise<Reading[]> => {\n // Get the saved readings.\n const key = `readings|${id}`;\n const store = useStore();\n\n const stored: StoredReadings = store.get(key) || {\n data: [],\n lastCheck: 0,\n storedSince: Infinity,\n };\n const { data, lastCheck } = stored;\n let { storedSince } = stored;\n\n const discardBefore = startOfDay(null, -8, true).valueOf() / 1000;\n\n // Discard any older than 30 days.\n while (data.length && data[0][0] < discardBefore) {\n [storedSince] = data[0];\n data.shift();\n }\n\n // If we have data early enough apply throttle.\n const lastStored = data.length ? data[data.length - 1][0] : 0;\n const requestedSince = (options.since && options.since.valueOf() / 1000) || 0;\n if (\n storedSince <= requestedSince &&\n Date.now() < lastCheck * 1000 + THROTTLE_MS\n ) {\n // Throttled.\n return filterSince(data, requestedSince);\n }\n\n const fetchOptions: ReadingOptions = {\n ...options,\n since: new Date(Math.max(requestedSince, lastStored) * 1000),\n };\n\n const [newData] = await fetchMeasureReadings(id, fetchOptions);\n mergeReadings(data, newData);\n storedSince = Math.min(requestedSince, storedSince);\n store.set(key, { lastCheck: Date.now() / 1000, data, storedSince });\n return filterSince(data, requestedSince);\n};\n\nexport const mergeReadings = (first: Reading[], second: Reading[]): void => {\n if (!second.length) return;\n\n let firstPos = first.length - 1;\n while (firstPos >= 0 && first[firstPos][0] >= second[0][0]) {\n --firstPos;\n }\n first.splice(firstPos + 1, Infinity, ...second);\n};\n\nconst parseReadings = (items: ReadingDTO[]): Record<string, Reading[]> => {\n const ranges: Record<string, Reading[]> = {};\n items.forEach(({ measure, dateTime, value }) => {\n if (ranges[measure] == null) {\n ranges[measure] = [];\n }\n ranges[measure].unshift([new Date(dateTime).valueOf() / 1000, value]);\n });\n\n const rangesById: Record<string, Reading[]> = {};\n Object.entries(ranges).forEach(([key, range]) => {\n rangesById[key.substring(key.lastIndexOf('/') + 1)] = range;\n });\n\n return rangesById;\n};\n","import { RiverDataWidgetError } from '../error';\nimport { round3 } from '../helpers/format';\nimport {\n parseMeasureId,\n translateMeasureProperties,\n} from '../flood-monitoring-api/measure';\nimport { Chart } from './chart';\nimport { getMeasureReadings } from '../flood-monitoring-api';\nimport { startOfDay } from '../helpers/time';\n\nimport type { ChartSeries } from './chart';\n\nconst drawMeasureWidget = async (\n parentEl: HTMLElement,\n measureId: string,\n options: Record<string, unknown> = {}\n) => {\n // Get readings for the last 7 days in local time.\n const since = startOfDay(null, -7, true);\n\n const data = await getMeasureReadings(measureId, { since });\n\n parentEl.replaceChildren();\n\n const measure = parseMeasureId(measureId);\n const { unit } = translateMeasureProperties(measure);\n\n // const [time, value] = data[data.length - 1];\n // const v = round3(value);\n // const param = m.qualifiedParameter;\n // const station = measure.stationId;\n // const unit = m.unit;\n\n // let textEl = createElement('div');\n // const d = dateFormatter.format(new Date(time * 1000));\n // const t = timeFormatter.format(new Date(time * 1000));\n // textEl.innerHTML = `The most recent ${param} reading for station ${station} was ${v} ${unit} at ${t} on ${d}.`;\n // widgetEl.append(textEl);\n\n const series1: ChartSeries = { data, unit, formatter: round3 };\n if (options.riverDataWidgetMinValue != null) {\n series1.min = parseFloat(<string>options.riverDataWidgetMinValue);\n }\n const minTime = startOfDay(new Date(data[0][0] * 1000)).valueOf() / 1000;\n const maxTime =\n startOfDay(new Date(data[data.length - 1][0] * 1000), 1).valueOf() / 1000;\n const chartOptions = {\n minTime,\n maxTime,\n // attribution: `www.riverdata.co.uk/station/${measure.stationId}`,\n };\n\n const chart = new Chart(parentEl, [series1], chartOptions);\n chart.render();\n};\n\n/**\n * Load a widget specified by a DOM element.\n */\nexport const loadWidget = (el: HTMLElement | string) => {\n // Get the target element from a query selector if necessary and check it\n // exists.\n const targetEl =\n typeof el === 'string' ? <HTMLElement>document.querySelector(el) : el;\n if (targetEl === null) {\n throw new Error('Target element not found');\n }\n\n // Parse element for widget type and options.\n const widgetIdParts = targetEl.dataset.riverDataWidget?.split(':') ?? [];\n const type = widgetIdParts.shift();\n const id = widgetIdParts.join(':');\n const options = targetEl.dataset;\n\n switch (type) {\n case 'measure':\n drawMeasureWidget(targetEl, id, options);\n break;\n\n // The 'station' widget is experimental in v1.0 and should not be used.\n // case 'station':\n // break;\n\n default:\n throw new RiverDataWidgetError('Unknown widget definition', { type, id });\n }\n};\n","import { loadWidget } from './widget/render';\n\nconst autoload = async () => {\n document.querySelectorAll('[data-river-data-widget]').forEach((el) => {\n try {\n loadWidget(<HTMLElement>el);\n } catch (error) {\n console.error(error, { error });\n }\n });\n};\n\nif (document.readyState === 'loading') {\n // Loading hasn't finished yet.\n document.addEventListener('DOMContentLoaded', autoload);\n} else {\n // `DOMContentLoaded` has already fired.\n autoload();\n}\n"],"names":["RiverDataWidgetError","Error","constructor","msg","info","super","this","name","round3","value","toPrecision","Math","round","toString","FloodMonitoringApiError","measureTranslations","unit","m3_s","mAOD","mASD","qualifiedParameter","createSvgElement","attributes","styles","innerHTML","el","document","createElementNS","Object","entries","forEach","key","style","setStyles","setAttribute","setAttributes","DAY_MS","startOfDay","date","offset","timeZone","base","Date","now","valueOf","floor","tz","getTimezoneOffset","local","timeFormatter","Intl","DateTimeFormat","hour","minute","hour12","timeZoneName","dddFormatter","weekday","dMmmFormatter","day","month","PLOT_COLOR","Chart","series","options","fontSizePx","width","height","plotHeight","strokeWidth","attribution","display","margin","viewBox","_a","append","getLimits","limits","getHorizontalGridlines","minTime","maxTime","timeScale","minValue","maxValue","valueScale","xOffset","yOffset","x1","x2","lines","stroke","labels","valueRange","interval","exponent","getInterval","factor","ceil","i","current","y1","y2","x","y","getTimeScale","labelOffset","fill","d","format","render","data","min","_b","max","_c","_d","assign","timeLines","timeLabels","valueLines","valueLabels","timeAxisLine","plotData","plotLastValue","formatter","time","length","v","highLabel","points","push","path","join","Infinity","range","maxDivisions","log10","k","baseUrl","prefix","addPrefix","instance","Store","clear","destroy","keys","localStorage","removeItem","setItem","JSON","stringify","get","getItem","parse","has","includes","isActive","storedKeys","set","json","unset","index","indexOf","splice","fetchMeasureReadings","async","id","params","_sorted","since","toISOString","substring","response","query","queryString","URLSearchParams","uri","fetch","apiFetch","parseReadings","items","filterSince","position","findIndex","reading","slice","getMeasureReadings","store","stored","lastCheck","storedSince","discardBefore","shift","lastStored","requestedSince","fetchOptions","newData","mergeReadings","first","second","firstPos","ranges","measure","dateTime","unshift","rangesById","lastIndexOf","drawMeasureWidget","parentEl","measureId","replaceChildren","matches","match","type","qualifier","parameter","stationId","reverse","parseMeasureId","translated","prop","translateMeasureProperties","series1","riverDataWidgetMinValue","parseFloat","autoload","querySelectorAll","targetEl","querySelector","widgetIdParts","dataset","riverDataWidget","split","loadWidget","error","console","readyState","addEventListener"],"mappings":"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"}
{
"name": "river-data-widget",
"version": "1.0.0",
"version": "1.0.1",
"description": "A web widget to display river flow and other data.",

@@ -5,0 +5,0 @@ "type": "module",