@defra/forms-engine-plugin 4.0.42 → 4.0.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/.public/javascripts/shared.min.js +1 -1
  2. package/.public/javascripts/shared.min.js.map +1 -1
  3. package/.public/stylesheets/application.min.css +1 -1
  4. package/.public/stylesheets/application.min.css.map +1 -1
  5. package/.server/client/javascripts/location-map.js +8 -4
  6. package/.server/client/javascripts/location-map.js.map +1 -1
  7. package/.server/client/stylesheets/_payment-field.scss +8 -0
  8. package/.server/client/stylesheets/application.scss +2 -0
  9. package/.server/index.js +3 -1
  10. package/.server/index.js.map +1 -1
  11. package/.server/server/constants.d.ts +1 -0
  12. package/.server/server/constants.js +1 -0
  13. package/.server/server/constants.js.map +1 -1
  14. package/.server/server/forms/payment-test.yaml +42 -0
  15. package/.server/server/forms/register-as-a-unicorn-breeder.yaml +14 -0
  16. package/.server/server/plugins/engine/components/FormComponent.d.ts +1 -0
  17. package/.server/server/plugins/engine/components/FormComponent.js +1 -0
  18. package/.server/server/plugins/engine/components/FormComponent.js.map +1 -1
  19. package/.server/server/plugins/engine/components/PaymentField.d.ts +135 -0
  20. package/.server/server/plugins/engine/components/PaymentField.js +228 -0
  21. package/.server/server/plugins/engine/components/PaymentField.js.map +1 -0
  22. package/.server/server/plugins/engine/components/PaymentField.types.d.ts +21 -0
  23. package/.server/server/plugins/engine/components/PaymentField.types.js +2 -0
  24. package/.server/server/plugins/engine/components/PaymentField.types.js.map +1 -0
  25. package/.server/server/plugins/engine/components/UkAddressField.d.ts +1 -1
  26. package/.server/server/plugins/engine/components/UkAddressField.js +3 -1
  27. package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
  28. package/.server/server/plugins/engine/components/helpers/components.d.ts +1 -1
  29. package/.server/server/plugins/engine/components/helpers/components.js +3 -0
  30. package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
  31. package/.server/server/plugins/engine/components/index.d.ts +1 -0
  32. package/.server/server/plugins/engine/components/index.js +1 -0
  33. package/.server/server/plugins/engine/components/index.js.map +1 -1
  34. package/.server/server/plugins/engine/configureEnginePlugin.d.ts +1 -1
  35. package/.server/server/plugins/engine/configureEnginePlugin.js +4 -2
  36. package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
  37. package/.server/server/plugins/engine/helpers.d.ts +1 -0
  38. package/.server/server/plugins/engine/models/SummaryViewModel.d.ts +3 -0
  39. package/.server/server/plugins/engine/models/SummaryViewModel.js +7 -0
  40. package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
  41. package/.server/server/plugins/engine/options.js +2 -1
  42. package/.server/server/plugins/engine/options.js.map +1 -1
  43. package/.server/server/plugins/engine/outputFormatters/human/v1.js +34 -1
  44. package/.server/server/plugins/engine/outputFormatters/human/v1.js.map +1 -1
  45. package/.server/server/plugins/engine/outputFormatters/machine/v2.d.ts +22 -0
  46. package/.server/server/plugins/engine/outputFormatters/machine/v2.js +43 -1
  47. package/.server/server/plugins/engine/outputFormatters/machine/v2.js.map +1 -1
  48. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +29 -8
  49. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  50. package/.server/server/plugins/engine/pageControllers/StartPageController.d.ts +2 -0
  51. package/.server/server/plugins/engine/pageControllers/SummaryPageController.d.ts +17 -0
  52. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +173 -51
  53. package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
  54. package/.server/server/plugins/engine/pageControllers/errors.d.ts +31 -0
  55. package/.server/server/plugins/engine/pageControllers/errors.js +59 -2
  56. package/.server/server/plugins/engine/pageControllers/errors.js.map +1 -1
  57. package/.server/server/plugins/engine/pageControllers/helpers/submission.d.ts +27 -0
  58. package/.server/server/plugins/engine/pageControllers/helpers/submission.js +77 -0
  59. package/.server/server/plugins/engine/pageControllers/helpers/submission.js.map +1 -0
  60. package/.server/server/plugins/engine/plugin.js +10 -5
  61. package/.server/server/plugins/engine/plugin.js.map +1 -1
  62. package/.server/server/plugins/engine/routes/index.js +8 -4
  63. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  64. package/.server/server/plugins/engine/routes/payment-helper.d.ts +14 -0
  65. package/.server/server/plugins/engine/routes/payment-helper.js +41 -0
  66. package/.server/server/plugins/engine/routes/payment-helper.js.map +1 -0
  67. package/.server/server/plugins/engine/routes/payment-helper.test.js +81 -0
  68. package/.server/server/plugins/engine/routes/payment-helper.test.js.map +1 -0
  69. package/.server/server/plugins/engine/routes/payment.d.ts +8 -0
  70. package/.server/server/plugins/engine/routes/payment.js +140 -0
  71. package/.server/server/plugins/engine/routes/payment.js.map +1 -0
  72. package/.server/server/plugins/engine/routes/payment.test.js +187 -0
  73. package/.server/server/plugins/engine/routes/payment.test.js.map +1 -0
  74. package/.server/server/plugins/engine/services/localFormsService.js +6 -0
  75. package/.server/server/plugins/engine/services/localFormsService.js.map +1 -1
  76. package/.server/server/plugins/engine/types/schema.js +7 -0
  77. package/.server/server/plugins/engine/types/schema.js.map +1 -1
  78. package/.server/server/plugins/engine/types.d.ts +20 -1
  79. package/.server/server/plugins/engine/types.js +4 -0
  80. package/.server/server/plugins/engine/types.js.map +1 -1
  81. package/.server/server/plugins/engine/validationHelpers.d.ts +1 -1
  82. package/.server/server/plugins/engine/validationHelpers.js.map +1 -1
  83. package/.server/server/plugins/engine/views/components/paymentfield.html +42 -0
  84. package/.server/server/plugins/engine/views/index.html +9 -1
  85. package/.server/server/plugins/engine/views/partials/form.html +20 -5
  86. package/.server/server/plugins/engine/views/summary.html +17 -1
  87. package/.server/server/plugins/map/routes/get-os-token.d.ts +6 -0
  88. package/.server/server/plugins/map/routes/get-os-token.js +41 -0
  89. package/.server/server/plugins/map/routes/get-os-token.js.map +1 -0
  90. package/.server/server/plugins/map/routes/get-os-token.test.js +49 -0
  91. package/.server/server/plugins/map/routes/get-os-token.test.js.map +1 -0
  92. package/.server/server/plugins/map/routes/index.d.ts +1 -11
  93. package/.server/server/plugins/map/routes/index.js +60 -16
  94. package/.server/server/plugins/map/routes/index.js.map +1 -1
  95. package/.server/server/plugins/map/types.d.ts +1 -0
  96. package/.server/server/plugins/map/types.js +1 -0
  97. package/.server/server/plugins/map/types.js.map +1 -1
  98. package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
  99. package/.server/server/plugins/payment/helper.d.ts +30 -0
  100. package/.server/server/plugins/payment/helper.js +49 -0
  101. package/.server/server/plugins/payment/helper.js.map +1 -0
  102. package/.server/server/plugins/payment/helper.test.js +37 -0
  103. package/.server/server/plugins/payment/helper.test.js.map +1 -0
  104. package/.server/server/plugins/payment/service.d.ts +40 -0
  105. package/.server/server/plugins/payment/service.js +129 -0
  106. package/.server/server/plugins/payment/service.js.map +1 -0
  107. package/.server/server/plugins/payment/service.test.js +162 -0
  108. package/.server/server/plugins/payment/service.test.js.map +1 -0
  109. package/.server/server/plugins/payment/types.d.ts +172 -0
  110. package/.server/server/plugins/payment/types.js +78 -0
  111. package/.server/server/plugins/payment/types.js.map +1 -0
  112. package/.server/server/types.d.ts +3 -0
  113. package/.server/server/types.js.map +1 -1
  114. package/.server/typings/hapi/index.d.js.map +1 -1
  115. package/README.md +12 -9
  116. package/package.json +2 -2
  117. package/src/client/javascripts/location-map.js +12 -4
  118. package/src/client/stylesheets/_payment-field.scss +8 -0
  119. package/src/client/stylesheets/application.scss +2 -0
  120. package/src/index.ts +5 -1
  121. package/src/server/constants.js +1 -0
  122. package/src/server/forms/payment-test.yaml +42 -0
  123. package/src/server/forms/register-as-a-unicorn-breeder.yaml +14 -0
  124. package/src/server/plugins/engine/components/FormComponent.ts +1 -0
  125. package/src/server/plugins/engine/components/PaymentField.test.ts +611 -0
  126. package/src/server/plugins/engine/components/PaymentField.ts +367 -0
  127. package/src/server/plugins/engine/components/PaymentField.types.ts +21 -0
  128. package/src/server/plugins/engine/components/UkAddressField.ts +2 -1
  129. package/src/server/plugins/engine/components/helpers/components.ts +5 -0
  130. package/src/server/plugins/engine/components/index.ts +1 -0
  131. package/src/server/plugins/engine/configureEnginePlugin.ts +4 -2
  132. package/src/server/plugins/engine/models/SummaryViewModel.ts +8 -0
  133. package/src/server/plugins/engine/options.js +2 -1
  134. package/src/server/plugins/engine/outputFormatters/human/v1.payment.test.ts +147 -0
  135. package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +105 -103
  136. package/src/server/plugins/engine/outputFormatters/human/v1.ts +61 -2
  137. package/src/server/plugins/engine/outputFormatters/machine/v2.payment.test.ts +115 -0
  138. package/src/server/plugins/engine/outputFormatters/machine/v2.ts +60 -1
  139. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +32 -6
  140. package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +247 -72
  141. package/src/server/plugins/engine/pageControllers/errors.test.ts +13 -1
  142. package/src/server/plugins/engine/pageControllers/errors.ts +79 -4
  143. package/src/server/plugins/engine/pageControllers/helpers/submission.test.ts +299 -0
  144. package/src/server/plugins/engine/pageControllers/helpers/submission.ts +110 -0
  145. package/src/server/plugins/engine/plugin.ts +17 -10
  146. package/src/server/plugins/engine/routes/index.ts +17 -16
  147. package/src/server/plugins/engine/routes/payment-helper.js +39 -0
  148. package/src/server/plugins/engine/routes/payment-helper.test.js +90 -0
  149. package/src/server/plugins/engine/routes/payment.js +151 -0
  150. package/src/server/plugins/engine/routes/payment.test.js +180 -0
  151. package/src/server/plugins/engine/services/localFormsService.js +7 -0
  152. package/src/server/plugins/engine/types/schema.ts +9 -0
  153. package/src/server/plugins/engine/types.ts +25 -1
  154. package/src/server/plugins/engine/validationHelpers.ts +1 -1
  155. package/src/server/plugins/engine/views/components/paymentfield.html +42 -0
  156. package/src/server/plugins/engine/views/index.html +9 -1
  157. package/src/server/plugins/engine/views/partials/form.html +20 -5
  158. package/src/server/plugins/engine/views/summary.html +17 -1
  159. package/src/server/plugins/map/routes/get-os-token.js +41 -0
  160. package/src/server/plugins/map/routes/get-os-token.test.js +55 -0
  161. package/src/server/plugins/map/routes/index.js +70 -24
  162. package/src/server/plugins/map/types.js +1 -0
  163. package/src/server/plugins/payment/helper.js +56 -0
  164. package/src/server/plugins/payment/helper.test.js +52 -0
  165. package/src/server/plugins/payment/service.js +171 -0
  166. package/src/server/plugins/payment/service.test.js +205 -0
  167. package/src/server/plugins/payment/types.js +77 -0
  168. package/src/server/types.ts +3 -0
  169. package/src/typings/hapi/index.d.ts +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"javascripts/shared.min.js","mappings":"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","sources":["webpack:///webpack/runtime/create fake namespace object","webpack:///webpack/runtime/load script","webpack:///../../node_modules/geodesy/dms.js","webpack:///../../node_modules/geodesy/vector3d.js","webpack:///../../node_modules/geodesy/latlon-ellipsoidal.js","webpack:///../../node_modules/geodesy/latlon-ellipsoidal-datum.js","webpack:///../../node_modules/geodesy/osgridref.js","webpack:///./javascripts/location-map.js","webpack:///webpack/bootstrap","webpack:///webpack/runtime/define property getters","webpack:///webpack/runtime/ensure chunk","webpack:///webpack/runtime/get javascript chunk filename","webpack:///webpack/runtime/hasOwnProperty shorthand","webpack:///webpack/runtime/make namespace object","webpack:///webpack/runtime/publicPath","webpack:///webpack/runtime/jsonp chunk loading","webpack:///./javascripts/file-upload.js","webpack:///../../node_modules/govuk-frontend/src/govuk/common/index.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/errors/index.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/component.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/common/configuration.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/init.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/button/button.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/common/closest-attribute-value.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/i18n.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/character-count/character-count.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/checkboxes/checkboxes.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/error-summary/error-summary.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/header/header.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/notification-banner/notification-banner.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/radios/radios.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/skip-link/skip-link.mjs","webpack:///./javascripts/shared.js","webpack:///./javascripts/govuk.js","webpack:///./javascripts/autocomplete.js","webpack:///./javascripts/preview-close-link.js"],"sourcesContent":["var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__);\nvar leafPrototypes;\n// create a fake namespace object\n// mode & 1: value is a module id, require it\n// mode & 2: merge all properties of value into the ns\n// mode & 4: return value when already ns object\n// mode & 16: return value when it's Promise-like\n// mode & 8|1: behave like require\n__webpack_require__.t = function(value, mode) {\n\tif(mode & 1) value = this(value);\n\tif(mode & 8) return value;\n\tif(typeof value === 'object' && value) {\n\t\tif((mode & 4) && value.__esModule) return value;\n\t\tif((mode & 16) && typeof value.then === 'function') return value;\n\t}\n\tvar ns = Object.create(null);\n\t__webpack_require__.r(ns);\n\tvar def = {};\n\tleafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];\n\tfor(var current = mode & 2 && value; (typeof current == 'object' || typeof current == 'function') && !~leafPrototypes.indexOf(current); current = getProto(current)) {\n\t\tObject.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key])));\n\t}\n\tdef['default'] = () => (value);\n\t__webpack_require__.d(ns, def);\n\treturn ns;\n};","var inProgress = {};\n// data-webpack is not used as build has no uniqueName\n// loadScript function to load a script via script tag\n__webpack_require__.l = (url, done, key, chunkId) => {\n\tif(inProgress[url]) { inProgress[url].push(done); return; }\n\tvar script, needAttach;\n\tif(key !== undefined) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tfor(var i = 0; i < scripts.length; i++) {\n\t\t\tvar s = scripts[i];\n\t\t\tif(s.getAttribute(\"src\") == url) { script = s; break; }\n\t\t}\n\t}\n\tif(!script) {\n\t\tneedAttach = true;\n\t\tscript = document.createElement('script');\n\t\tscript.type = \"module\";\n\t\tscript.charset = 'utf-8';\n\t\tif (__webpack_require__.nc) {\n\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n\t\t}\n\n\n\t\tscript.src = url;\n\t}\n\tinProgress[url] = [done];\n\tvar onScriptComplete = (prev, event) => {\n\t\t// avoid mem leaks in IE.\n\t\tscript.onerror = script.onload = null;\n\t\tclearTimeout(timeout);\n\t\tvar doneFns = inProgress[url];\n\t\tdelete inProgress[url];\n\t\tscript.parentNode && script.parentNode.removeChild(script);\n\t\tdoneFns && doneFns.forEach((fn) => (fn(event)));\n\t\tif(prev) return prev(event);\n\t}\n\tvar timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);\n\tscript.onerror = onScriptComplete.bind(null, script.onerror);\n\tscript.onload = onScriptComplete.bind(null, script.onload);\n\tneedAttach && document.head.appendChild(script);\n};","/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n/* Geodesy representation conversion functions (c) Chris Veness 2002-2020 */\n/* MIT Licence */\n/* www.movable-type.co.uk/scripts/latlong.html */\n/* www.movable-type.co.uk/scripts/js/geodesy/geodesy-library.html#dms */\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n/* eslint no-irregular-whitespace: [2, { skipComments: true }] */\n\n\n/**\n * Latitude/longitude points may be represented as decimal degrees, or subdivided into sexagesimal\n * minutes and seconds. This module provides methods for parsing and representing degrees / minutes\n * / seconds.\n *\n * @module dms\n */\n\n\n/* Degree-minutes-seconds (& cardinal directions) separator character */\nlet dmsSeparator = '\\u202f'; // U+202F = 'narrow no-break space'\n\n\n/**\n * Functions for parsing and representing degrees / minutes / seconds.\n */\nclass Dms {\n\n // note Unicode Degree = U+00B0. Prime = U+2032, Double prime = U+2033\n\n /**\n * Separator character to be used to separate degrees, minutes, seconds, and cardinal directions.\n *\n * Default separator is U+202F ‘narrow no-break space’.\n *\n * To change this (e.g. to empty string or full space), set Dms.separator prior to invoking\n * formatting.\n *\n * @example\n * import LatLon, { Dms } from '/js/geodesy/latlon-spherical.js';\n * const p = new LatLon(51.2, 0.33).toString('dms'); // 51° 12′ 00″ N, 000° 19′ 48″ E\n * Dms.separator = ''; // no separator\n * const pʹ = new LatLon(51.2, 0.33).toString('dms'); // 51°12′00″N, 000°19′48″E\n */\n static get separator() { return dmsSeparator; }\n static set separator(char) { dmsSeparator = char; }\n\n\n /**\n * Parses string representing degrees/minutes/seconds into numeric degrees.\n *\n * This is very flexible on formats, allowing signed decimal degrees, or deg-min-sec optionally\n * suffixed by compass direction (NSEW); a variety of separators are accepted. Examples -3.62,\n * '3 37 12W', '3°37′12″W'.\n *\n * Thousands/decimal separators must be comma/dot; use Dms.fromLocale to convert locale-specific\n * thousands/decimal separators.\n *\n * @param {string|number} dms - Degrees or deg/min/sec in variety of formats.\n * @returns {number} Degrees as decimal number.\n *\n * @example\n * const lat = Dms.parse('51° 28′ 40.37″ N');\n * const lon = Dms.parse('000° 00′ 05.29″ W');\n * const p1 = new LatLon(lat, lon); // 51.4779°N, 000.0015°W\n */\n static parse(dms) {\n // check for signed decimal degrees without NSEW, if so return it directly\n if (!isNaN(parseFloat(dms)) && isFinite(dms)) return Number(dms);\n\n // strip off any sign or compass dir'n & split out separate d/m/s\n const dmsParts = String(dms).trim().replace(/^-/, '').replace(/[NSEW]$/i, '').split(/[^0-9.,]+/);\n if (dmsParts[dmsParts.length-1]=='') dmsParts.splice(dmsParts.length-1); // from trailing symbol\n\n if (dmsParts == '') return NaN;\n\n // and convert to decimal degrees...\n let deg = null;\n switch (dmsParts.length) {\n case 3: // interpret 3-part result as d/m/s\n deg = dmsParts[0]/1 + dmsParts[1]/60 + dmsParts[2]/3600;\n break;\n case 2: // interpret 2-part result as d/m\n deg = dmsParts[0]/1 + dmsParts[1]/60;\n break;\n case 1: // just d (possibly decimal) or non-separated dddmmss\n deg = dmsParts[0];\n // check for fixed-width unseparated format eg 0033709W\n //if (/[NS]/i.test(dmsParts)) deg = '0' + deg; // - normalise N/S to 3-digit degrees\n //if (/[0-9]{7}/.test(deg)) deg = deg.slice(0,3)/1 + deg.slice(3,5)/60 + deg.slice(5)/3600;\n break;\n default:\n return NaN;\n }\n if (/^-|[WS]$/i.test(dms.trim())) deg = -deg; // take '-', west and south as -ve\n\n return Number(deg);\n }\n\n\n /**\n * Converts decimal degrees to deg/min/sec format\n * - degree, prime, double-prime symbols are added, but sign is discarded, though no compass\n * direction is added.\n * - degrees are zero-padded to 3 digits; for degrees latitude, use .slice(1) to remove leading\n * zero.\n *\n * @private\n * @param {number} deg - Degrees to be formatted as specified.\n * @param {string} [format=d] - Return value as 'd', 'dm', 'dms' for deg, deg+min, deg+min+sec.\n * @param {number} [dp=4|2|0] - Number of decimal places to use – default 4 for d, 2 for dm, 0 for dms.\n * @returns {string} Degrees formatted as deg/min/secs according to specified format.\n */\n static toDms(deg, format='d', dp=undefined) {\n if (isNaN(deg)) return null; // give up here if we can't make a number from deg\n if (typeof deg == 'string' && deg.trim() == '') return null;\n if (typeof deg == 'boolean') return null;\n if (deg == Infinity) return null;\n if (deg == null) return null;\n\n // default values\n if (dp === undefined) {\n switch (format) {\n case 'd': case 'deg': dp = 4; break;\n case 'dm': case 'deg+min': dp = 2; break;\n case 'dms': case 'deg+min+sec': dp = 0; break;\n default: format = 'd'; dp = 4; break; // be forgiving on invalid format\n }\n }\n\n deg = Math.abs(deg); // (unsigned result ready for appending compass dir'n)\n\n let dms = null, d = null, m = null, s = null;\n switch (format) {\n default: // invalid format spec!\n case 'd': case 'deg':\n d = deg.toFixed(dp); // round/right-pad degrees\n if (d<100) d = '0' + d; // left-pad with leading zeros (note may include decimals)\n if (d<10) d = '0' + d;\n dms = d + '°';\n break;\n case 'dm': case 'deg+min':\n d = Math.floor(deg); // get component deg\n m = ((deg*60) % 60).toFixed(dp); // get component min & round/right-pad\n if (m == 60) { m = (0).toFixed(dp); d++; } // check for rounding up\n d = ('000'+d).slice(-3); // left-pad with leading zeros\n if (m<10) m = '0' + m; // left-pad with leading zeros (note may include decimals)\n dms = d + '°'+Dms.separator + m + '′';\n break;\n case 'dms': case 'deg+min+sec':\n d = Math.floor(deg); // get component deg\n m = Math.floor((deg*3600)/60) % 60; // get component min\n s = (deg*3600 % 60).toFixed(dp); // get component sec & round/right-pad\n if (s == 60) { s = (0).toFixed(dp); m++; } // check for rounding up\n if (m == 60) { m = 0; d++; } // check for rounding up\n d = ('000'+d).slice(-3); // left-pad with leading zeros\n m = ('00'+m).slice(-2); // left-pad with leading zeros\n if (s<10) s = '0' + s; // left-pad with leading zeros (note may include decimals)\n dms = d + '°'+Dms.separator + m + '′'+Dms.separator + s + '″';\n break;\n }\n\n return dms;\n }\n\n\n /**\n * Converts numeric degrees to deg/min/sec latitude (2-digit degrees, suffixed with N/S).\n *\n * @param {number} deg - Degrees to be formatted as specified.\n * @param {string} [format=d] - Return value as 'd', 'dm', 'dms' for deg, deg+min, deg+min+sec.\n * @param {number} [dp=4|2|0] - Number of decimal places to use – default 4 for d, 2 for dm, 0 for dms.\n * @returns {string} Degrees formatted as deg/min/secs according to specified format.\n *\n * @example\n * const lat = Dms.toLat(-3.62, 'dms'); // 3°37′12″S\n */\n static toLat(deg, format, dp) {\n const lat = Dms.toDms(Dms.wrap90(deg), format, dp);\n return lat===null ? '–' : lat.slice(1) + Dms.separator + (deg<0 ? 'S' : 'N'); // knock off initial '0' for lat!\n }\n\n\n /**\n * Convert numeric degrees to deg/min/sec longitude (3-digit degrees, suffixed with E/W).\n *\n * @param {number} deg - Degrees to be formatted as specified.\n * @param {string} [format=d] - Return value as 'd', 'dm', 'dms' for deg, deg+min, deg+min+sec.\n * @param {number} [dp=4|2|0] - Number of decimal places to use – default 4 for d, 2 for dm, 0 for dms.\n * @returns {string} Degrees formatted as deg/min/secs according to specified format.\n *\n * @example\n * const lon = Dms.toLon(-3.62, 'dms'); // 3°37′12″W\n */\n static toLon(deg, format, dp) {\n const lon = Dms.toDms(Dms.wrap180(deg), format, dp);\n return lon===null ? '–' : lon + Dms.separator + (deg<0 ? 'W' : 'E');\n }\n\n\n /**\n * Converts numeric degrees to deg/min/sec as a bearing (0°..360°).\n *\n * @param {number} deg - Degrees to be formatted as specified.\n * @param {string} [format=d] - Return value as 'd', 'dm', 'dms' for deg, deg+min, deg+min+sec.\n * @param {number} [dp=4|2|0] - Number of decimal places to use – default 4 for d, 2 for dm, 0 for dms.\n * @returns {string} Degrees formatted as deg/min/secs according to specified format.\n *\n * @example\n * const lon = Dms.toBrng(-3.62, 'dms'); // 356°22′48″\n */\n static toBrng(deg, format, dp) {\n const brng = Dms.toDms(Dms.wrap360(deg), format, dp);\n return brng===null ? '–' : brng.replace('360', '0'); // just in case rounding took us up to 360°!\n }\n\n\n /**\n * Converts DMS string from locale thousands/decimal separators to JavaScript comma/dot separators\n * for subsequent parsing.\n *\n * Both thousands and decimal separators must be followed by a numeric character, to facilitate\n * parsing of single lat/long string (in which whitespace must be left after the comma separator).\n *\n * @param {string} str - Degrees/minutes/seconds formatted with locale separators.\n * @returns {string} Degrees/minutes/seconds formatted with standard Javascript separators.\n *\n * @example\n * const lat = Dms.fromLocale('51°28′40,12″N'); // '51°28′40.12″N' in France\n * const p = new LatLon(Dms.fromLocale('51°28′40,37″N, 000°00′05,29″W'); // '51.4779°N, 000.0015°W' in France\n */\n static fromLocale(str) {\n const locale = (123456.789).toLocaleString();\n const separator = { thousands: locale.slice(3, 4), decimal: locale.slice(7, 8) };\n return str.replace(separator.thousands, '⁜').replace(separator.decimal, '.').replace('⁜', ',');\n }\n\n\n /**\n * Converts DMS string from JavaScript comma/dot thousands/decimal separators to locale separators.\n *\n * Can also be used to format standard numbers such as distances.\n *\n * @param {string} str - Degrees/minutes/seconds formatted with standard Javascript separators.\n * @returns {string} Degrees/minutes/seconds formatted with locale separators.\n *\n * @example\n * const Dms.toLocale('123,456.789'); // '123.456,789' in France\n * const Dms.toLocale('51°28′40.12″N, 000°00′05.31″W'); // '51°28′40,12″N, 000°00′05,31″W' in France\n */\n static toLocale(str) {\n const locale = (123456.789).toLocaleString();\n const separator = { thousands: locale.slice(3, 4), decimal: locale.slice(7, 8) };\n return str.replace(/,([0-9])/, '⁜$1').replace('.', separator.decimal).replace('⁜', separator.thousands);\n }\n\n\n /**\n * Returns compass point (to given precision) for supplied bearing.\n *\n * @param {number} bearing - Bearing in degrees from north.\n * @param {number} [precision=3] - Precision (1:cardinal / 2:intercardinal / 3:secondary-intercardinal).\n * @returns {string} Compass point for supplied bearing.\n *\n * @example\n * const point = Dms.compassPoint(24); // point = 'NNE'\n * const point = Dms.compassPoint(24, 1); // point = 'N'\n */\n static compassPoint(bearing, precision=3) {\n if (![ 1, 2, 3 ].includes(Number(precision))) throw new RangeError(`invalid precision ‘${precision}’`);\n // note precision could be extended to 4 for quarter-winds (eg NbNW), but I think they are little used\n\n bearing = Dms.wrap360(bearing); // normalise to range 0..360°\n\n const cardinals = [\n 'N', 'NNE', 'NE', 'ENE',\n 'E', 'ESE', 'SE', 'SSE',\n 'S', 'SSW', 'SW', 'WSW',\n 'W', 'WNW', 'NW', 'NNW' ];\n const n = 4 * 2**(precision-1); // no of compass points at req’d precision (1=>4, 2=>8, 3=>16)\n const cardinal = cardinals[Math.round(bearing*n/360)%n * 16/n];\n\n return cardinal;\n }\n\n\n /**\n * Constrain degrees to range -90..+90 (for latitude); e.g. -91 => -89, 91 => 89.\n *\n * @private\n * @param {number} degrees\n * @returns degrees within range -90..+90.\n */\n static wrap90(degrees) {\n if (-90<=degrees && degrees<=90) return degrees; // avoid rounding due to arithmetic ops if within range\n\n // latitude wrapping requires a triangle wave function; a general triangle wave is\n // f(x) = 4a/p ⋅ | (x-p/4)%p - p/2 | - a\n // where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator\n // not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n'\n const x = degrees, a = 90, p = 360;\n return 4*a/p * Math.abs((((x-p/4)%p)+p)%p - p/2) - a;\n }\n\n /**\n * Constrain degrees to range -180..+180 (for longitude); e.g. -181 => 179, 181 => -179.\n *\n * @private\n * @param {number} degrees\n * @returns degrees within range -180..+180.\n */\n static wrap180(degrees) {\n if (-180<=degrees && degrees<=180) return degrees; // avoid rounding due to arithmetic ops if within range\n\n // longitude wrapping requires a sawtooth wave function; a general sawtooth wave is\n // f(x) = (2ax/p - p/2) % p - a\n // where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator\n // not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n'\n const x = degrees, a = 180, p = 360;\n return (((2*a*x/p - p/2)%p)+p)%p - a;\n }\n\n /**\n * Constrain degrees to range 0..360 (for bearings); e.g. -1 => 359, 361 => 1.\n *\n * @private\n * @param {number} degrees\n * @returns degrees within range 0..360.\n */\n static wrap360(degrees) {\n if (0<=degrees && degrees<360) return degrees; // avoid rounding due to arithmetic ops if within range\n\n // bearing wrapping requires a sawtooth wave function with a vertical offset equal to the\n // amplitude and a corresponding phase shift; this changes the general sawtooth wave function from\n // f(x) = (2ax/p - p/2) % p - a\n // to\n // f(x) = (2ax/p) % p\n // where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator\n // not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n'\n const x = degrees, a = 180, p = 360;\n return (((2*a*x/p)%p)+p)%p;\n }\n\n}\n\n\n// Extend Number object with methods to convert between degrees & radians\nNumber.prototype.toRadians = function() { return this * Math.PI / 180; };\nNumber.prototype.toDegrees = function() { return this * 180 / Math.PI; };\n\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nexport default Dms;\n","/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n/* Vector handling functions (c) Chris Veness 2011-2019 */\n/* MIT Licence */\n/* www.movable-type.co.uk/scripts/geodesy-library.html#vector3d */\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * Library of 3-d vector manipulation routines.\n *\n * @module vector3d\n */\n\n\n/* Vector3d - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * Functions for manipulating generic 3-d vectors.\n *\n * Functions return vectors as return results, so that operations can be chained.\n *\n * @example\n * const v = v1.cross(v2).dot(v3) // ≡ v1×v2⋅v3\n */\nclass Vector3d {\n\n /**\n * Creates a 3-d vector.\n *\n * @param {number} x - X component of vector.\n * @param {number} y - Y component of vector.\n * @param {number} z - Z component of vector.\n *\n * @example\n * import Vector3d from '/js/geodesy/vector3d.js';\n * const v = new Vector3d(0.267, 0.535, 0.802);\n */\n constructor(x, y, z) {\n if (isNaN(x) || isNaN(y) || isNaN(z)) throw new TypeError(`invalid vector [${x},${y},${z}]`);\n\n this.x = Number(x);\n this.y = Number(y);\n this.z = Number(z);\n }\n\n\n /**\n * Length (magnitude or norm) of ‘this’ vector.\n *\n * @returns {number} Magnitude of this vector.\n */\n get length() {\n return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);\n }\n\n\n /**\n * Adds supplied vector to ‘this’ vector.\n *\n * @param {Vector3d} v - Vector to be added to this vector.\n * @returns {Vector3d} Vector representing sum of this and v.\n */\n plus(v) {\n if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');\n\n return new Vector3d(this.x + v.x, this.y + v.y, this.z + v.z);\n }\n\n\n /**\n * Subtracts supplied vector from ‘this’ vector.\n *\n * @param {Vector3d} v - Vector to be subtracted from this vector.\n * @returns {Vector3d} Vector representing difference between this and v.\n */\n minus(v) {\n if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');\n\n return new Vector3d(this.x - v.x, this.y - v.y, this.z - v.z);\n }\n\n\n /**\n * Multiplies ‘this’ vector by a scalar value.\n *\n * @param {number} x - Factor to multiply this vector by.\n * @returns {Vector3d} Vector scaled by x.\n */\n times(x) {\n if (isNaN(x)) throw new TypeError(`invalid scalar value ‘${x}’`);\n\n return new Vector3d(this.x * x, this.y * x, this.z * x);\n }\n\n\n /**\n * Divides ‘this’ vector by a scalar value.\n *\n * @param {number} x - Factor to divide this vector by.\n * @returns {Vector3d} Vector divided by x.\n */\n dividedBy(x) {\n if (isNaN(x)) throw new TypeError(`invalid scalar value ‘${x}’`);\n\n return new Vector3d(this.x / x, this.y / x, this.z / x);\n }\n\n\n /**\n * Multiplies ‘this’ vector by the supplied vector using dot (scalar) product.\n *\n * @param {Vector3d} v - Vector to be dotted with this vector.\n * @returns {number} Dot product of ‘this’ and v.\n */\n dot(v) {\n if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');\n\n return this.x * v.x + this.y * v.y + this.z * v.z;\n }\n\n\n /**\n * Multiplies ‘this’ vector by the supplied vector using cross (vector) product.\n *\n * @param {Vector3d} v - Vector to be crossed with this vector.\n * @returns {Vector3d} Cross product of ‘this’ and v.\n */\n cross(v) {\n if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');\n\n const x = this.y * v.z - this.z * v.y;\n const y = this.z * v.x - this.x * v.z;\n const z = this.x * v.y - this.y * v.x;\n\n return new Vector3d(x, y, z);\n }\n\n\n /**\n * Negates a vector to point in the opposite direction.\n *\n * @returns {Vector3d} Negated vector.\n */\n negate() {\n return new Vector3d(-this.x, -this.y, -this.z);\n }\n\n\n /**\n * Normalizes a vector to its unit vector\n * – if the vector is already unit or is zero magnitude, this is a no-op.\n *\n * @returns {Vector3d} Normalised version of this vector.\n */\n unit() {\n const norm = this.length;\n if (norm == 1) return this;\n if (norm == 0) return this;\n\n const x = this.x / norm;\n const y = this.y / norm;\n const z = this.z / norm;\n\n return new Vector3d(x, y, z);\n }\n\n\n /**\n * Calculates the angle between ‘this’ vector and supplied vector atan2(|p₁×p₂|, p₁·p₂) (or if\n * (extra-planar) ‘n’ supplied then atan2(n·p₁×p₂, p₁·p₂).\n *\n * @param {Vector3d} v - Vector whose angle is to be determined from ‘this’ vector.\n * @param {Vector3d} [n] - Plane normal: if supplied, angle is signed +ve if this->v is\n * clockwise looking along n, -ve in opposite direction.\n * @returns {number} Angle (in radians) between this vector and supplied vector (in range 0..π\n * if n not supplied, range -π..+π if n supplied).\n */\n angleTo(v, n=undefined) {\n if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');\n if (!(n instanceof Vector3d || n == undefined)) throw new TypeError('n is not Vector3d object');\n\n // q.v. stackoverflow.com/questions/14066933#answer-16544330, but n·p₁×p₂ is numerically\n // ill-conditioned, so just calculate sign to apply to |p₁×p₂|\n\n // if n·p₁×p₂ is -ve, negate |p₁×p₂|\n const sign = n==undefined || this.cross(v).dot(n)>=0 ? 1 : -1;\n\n const sinθ = this.cross(v).length * sign;\n const cosθ = this.dot(v);\n\n return Math.atan2(sinθ, cosθ);\n }\n\n\n /**\n * Rotates ‘this’ point around an axis by a specified angle.\n *\n * @param {Vector3d} axis - The axis being rotated around.\n * @param {number} angle - The angle of rotation (in degrees).\n * @returns {Vector3d} The rotated point.\n */\n rotateAround(axis, angle) {\n if (!(axis instanceof Vector3d)) throw new TypeError('axis is not Vector3d object');\n\n const θ = angle.toRadians();\n\n // en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle\n // en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Quaternion-derived_rotation_matrix\n const p = this.unit();\n const a = axis.unit();\n\n const s = Math.sin(θ);\n const c = Math.cos(θ);\n const t = 1-c;\n const x = a.x, y = a.y, z = a.z;\n\n const r = [ // rotation matrix for rotation about supplied axis\n [ t*x*x + c, t*x*y - s*z, t*x*z + s*y ],\n [ t*x*y + s*z, t*y*y + c, t*y*z - s*x ],\n [ t*x*z - s*y, t*y*z + s*x, t*z*z + c ],\n ];\n\n // multiply r × p\n const rp = [\n r[0][0]*p.x + r[0][1]*p.y + r[0][2]*p.z,\n r[1][0]*p.x + r[1][1]*p.y + r[1][2]*p.z,\n r[2][0]*p.x + r[2][1]*p.y + r[2][2]*p.z,\n ];\n const p2 = new Vector3d(rp[0], rp[1], rp[2]);\n\n return p2;\n // qv en.wikipedia.org/wiki/Rodrigues'_rotation_formula...\n }\n\n\n /**\n * String representation of vector.\n *\n * @param {number} [dp=3] - Number of decimal places to be used.\n * @returns {string} Vector represented as [x,y,z].\n */\n toString(dp=3) {\n return `[${this.x.toFixed(dp)},${this.y.toFixed(dp)},${this.z.toFixed(dp)}]`;\n }\n\n}\n\n\n// Extend Number object with methods to convert between degrees & radians\nNumber.prototype.toRadians = function() { return this * Math.PI / 180; };\nNumber.prototype.toDegrees = function() { return this * 180 / Math.PI; };\n\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nexport default Vector3d;\n","/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n/* Geodesy tools for an ellipsoidal earth model (c) Chris Veness 2005-2022 */\n/* MIT Licence */\n/* Core class for latlon-ellipsoidal-datum & latlon-ellipsoidal-referenceframe. */\n/* */\n/* www.movable-type.co.uk/scripts/latlong-convert-coords.html */\n/* www.movable-type.co.uk/scripts/geodesy-library.html#latlon-ellipsoidal */\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nimport Dms from './dms.js';\nimport Vector3d from './vector3d.js';\n\n\n/**\n * A latitude/longitude point defines a geographic location on or above/below the earth’s surface,\n * measured in degrees from the equator & the International Reference Meridian and in metres above\n * the ellipsoid, and based on a given datum.\n *\n * As so much modern geodesy is based on WGS-84 (as used by GPS), this module includes WGS-84\n * ellipsoid parameters, and it has methods for converting geodetic (latitude/longitude) points to/from\n * geocentric cartesian points; the latlon-ellipsoidal-datum and latlon-ellipsoidal-referenceframe\n * modules provide transformation parameters for converting between historical datums and between\n * modern reference frames.\n *\n * This module is used for both trigonometric geodesy (eg latlon-ellipsoidal-vincenty) and n-vector\n * geodesy (eg latlon-nvector-ellipsoidal), and also for UTM/MGRS mapping.\n *\n * @module latlon-ellipsoidal\n */\n\n\n/*\n * Ellipsoid parameters; exposed through static getter below.\n *\n * The only ellipsoid defined is WGS84, for use in utm/mgrs, vincenty, nvector.\n */\nconst ellipsoids = {\n WGS84: { a: 6378137, b: 6356752.314245, f: 1/298.257223563 },\n};\n\n\n/*\n * Datums; exposed through static getter below.\n *\n * The only datum defined is WGS84, for use in utm/mgrs, vincenty, nvector.\n */\nconst datums = {\n WGS84: { ellipsoid: ellipsoids.WGS84 },\n};\n\n\n// freeze static properties\nObject.freeze(ellipsoids.WGS84);\nObject.freeze(datums.WGS84);\n\n\n/* LatLonEllipsoidal - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * Latitude/longitude points on an ellipsoidal model earth, with ellipsoid parameters and methods\n * for converting points to/from cartesian (ECEF) coordinates.\n *\n * This is the core class, which will usually be used via LatLonEllipsoidal_Datum or\n * LatLonEllipsoidal_ReferenceFrame.\n */\nclass LatLonEllipsoidal {\n\n /**\n * Creates a geodetic latitude/longitude point on a (WGS84) ellipsoidal model earth.\n *\n * @param {number} lat - Latitude (in degrees).\n * @param {number} lon - Longitude (in degrees).\n * @param {number} [height=0] - Height above ellipsoid in metres.\n * @throws {TypeError} Invalid lat/lon/height.\n *\n * @example\n * import LatLon from '/js/geodesy/latlon-ellipsoidal.js';\n * const p = new LatLon(51.47788, -0.00147, 17);\n */\n constructor(lat, lon, height=0) {\n if (isNaN(lat) || lat == null) throw new TypeError(`invalid lat ‘${lat}’`);\n if (isNaN(lon) || lon == null) throw new TypeError(`invalid lon ‘${lon}’`);\n if (isNaN(height) || height == null) throw new TypeError(`invalid height ‘${height}’`);\n\n this._lat = Dms.wrap90(Number(lat));\n this._lon = Dms.wrap180(Number(lon));\n this._height = Number(height);\n }\n\n\n /**\n * Latitude in degrees north from equator (including aliases lat, latitude): can be set as\n * numeric or hexagesimal (deg-min-sec); returned as numeric.\n */\n get lat() { return this._lat; }\n get latitude() { return this._lat; }\n set lat(lat) {\n this._lat = isNaN(lat) ? Dms.wrap90(Dms.parse(lat)) : Dms.wrap90(Number(lat));\n if (isNaN(this._lat)) throw new TypeError(`invalid lat ‘${lat}’`);\n }\n set latitude(lat) {\n this._lat = isNaN(lat) ? Dms.wrap90(Dms.parse(lat)) : Dms.wrap90(Number(lat));\n if (isNaN(this._lat)) throw new TypeError(`invalid latitude ‘${lat}’`);\n }\n\n /**\n * Longitude in degrees east from international reference meridian (including aliases lon, lng,\n * longitude): can be set as numeric or hexagesimal (deg-min-sec); returned as numeric.\n */\n get lon() { return this._lon; }\n get lng() { return this._lon; }\n get longitude() { return this._lon; }\n set lon(lon) {\n this._lon = isNaN(lon) ? Dms.wrap180(Dms.parse(lon)) : Dms.wrap180(Number(lon));\n if (isNaN(this._lon)) throw new TypeError(`invalid lon ‘${lon}’`);\n }\n set lng(lon) {\n this._lon = isNaN(lon) ? Dms.wrap180(Dms.parse(lon)) : Dms.wrap180(Number(lon));\n if (isNaN(this._lon)) throw new TypeError(`invalid lng ‘${lon}’`);\n }\n set longitude(lon) {\n this._lon = isNaN(lon) ? Dms.wrap180(Dms.parse(lon)) : Dms.wrap180(Number(lon));\n if (isNaN(this._lon)) throw new TypeError(`invalid longitude ‘${lon}’`);\n }\n\n /**\n * Height in metres above ellipsoid.\n */\n get height() { return this._height; }\n set height(height) { this._height = Number(height); if (isNaN(this._height)) throw new TypeError(`invalid height ‘${height}’`); }\n\n\n /**\n * Datum.\n *\n * Note this is replicated within LatLonEllipsoidal in order that a LatLonEllipsoidal object can\n * be monkey-patched to look like a LatLonEllipsoidal_Datum, for Vincenty calculations on\n * different ellipsoids.\n *\n * @private\n */\n get datum() { return this._datum; }\n set datum(datum) { this._datum = datum; }\n\n\n /**\n * Ellipsoids with their parameters; this module only defines WGS84 parameters a = 6378137, b =\n * 6356752.314245, f = 1/298.257223563.\n *\n * @example\n * const a = LatLon.ellipsoids.WGS84.a; // 6378137\n */\n static get ellipsoids() {\n return ellipsoids;\n }\n\n /**\n * Datums; this module only defines WGS84 datum, hence no datum transformations.\n *\n * @example\n * const a = LatLon.datums.WGS84.ellipsoid.a; // 6377563.396\n */\n static get datums() {\n return datums;\n }\n\n\n /**\n * Parses a latitude/longitude point from a variety of formats.\n *\n * Latitude & longitude (in degrees) can be supplied as two separate parameters, as a single\n * comma-separated lat/lon string, or as a single object with { lat, lon } or GeoJSON properties.\n *\n * The latitude/longitude values may be numeric or strings; they may be signed decimal or\n * deg-min-sec (hexagesimal) suffixed by compass direction (NSEW); a variety of separators are\n * accepted. Examples -3.62, '3 37 12W', '3°37′12″W'.\n *\n * Thousands/decimal separators must be comma/dot; use Dms.fromLocale to convert locale-specific\n * thousands/decimal separators.\n *\n * @param {number|string|Object} lat|latlon - Latitude (in degrees), or comma-separated lat/lon, or lat/lon object.\n * @param {number} [lon] - Longitude (in degrees).\n * @param {number} [height=0] - Height above ellipsoid in metres.\n * @returns {LatLon} Latitude/longitude point on WGS84 ellipsoidal model earth.\n * @throws {TypeError} Invalid coordinate.\n *\n * @example\n * const p1 = LatLon.parse(51.47788, -0.00147); // numeric pair\n * const p2 = LatLon.parse('51°28′40″N, 000°00′05″W', 17); // dms string + height\n * const p3 = LatLon.parse({ lat: 52.205, lon: 0.119 }, 17); // { lat, lon } object numeric + height\n */\n static parse(...args) {\n if (args.length == 0) throw new TypeError('invalid (empty) point');\n\n let lat=undefined, lon=undefined, height=undefined;\n\n // single { lat, lon } object\n if (typeof args[0]=='object' && (args.length==1 || !isNaN(parseFloat(args[1])))) {\n const ll = args[0];\n if (ll.type == 'Point' && Array.isArray(ll.coordinates)) { // GeoJSON\n [ lon, lat, height ] = ll.coordinates;\n height = height || 0;\n } else { // regular { lat, lon } object\n if (ll.latitude != undefined) lat = ll.latitude;\n if (ll.lat != undefined) lat = ll.lat;\n if (ll.longitude != undefined) lon = ll.longitude;\n if (ll.lng != undefined) lon = ll.lng;\n if (ll.lon != undefined) lon = ll.lon;\n if (ll.height != undefined) height = ll.height;\n lat = Dms.wrap90(Dms.parse(lat));\n lon = Dms.wrap180(Dms.parse(lon));\n }\n if (args[1] != undefined) height = args[1];\n if (isNaN(lat) || isNaN(lon)) throw new TypeError(`invalid point ‘${JSON.stringify(args[0])}’`);\n }\n\n // single comma-separated lat/lon\n if (typeof args[0] == 'string' && args[0].split(',').length == 2) {\n [ lat, lon ] = args[0].split(',');\n lat = Dms.wrap90(Dms.parse(lat));\n lon = Dms.wrap180(Dms.parse(lon));\n height = args[1] || 0;\n if (isNaN(lat) || isNaN(lon)) throw new TypeError(`invalid point ‘${args[0]}’`);\n }\n\n // regular (lat, lon) arguments\n if (lat==undefined && lon==undefined) {\n [ lat, lon ] = args;\n lat = Dms.wrap90(Dms.parse(lat));\n lon = Dms.wrap180(Dms.parse(lon));\n height = args[2] || 0;\n if (isNaN(lat) || isNaN(lon)) throw new TypeError(`invalid point ‘${args.toString()}’`);\n }\n\n return new this(lat, lon, height); // 'new this' as may return subclassed types\n }\n\n\n /**\n * Converts ‘this’ point from (geodetic) latitude/longitude coordinates to (geocentric)\n * cartesian (x/y/z) coordinates.\n *\n * @returns {Cartesian} Cartesian point equivalent to lat/lon point, with x, y, z in metres from\n * earth centre.\n */\n toCartesian() {\n // x = (ν+h)⋅cosφ⋅cosλ, y = (ν+h)⋅cosφ⋅sinλ, z = (ν⋅(1-e²)+h)⋅sinφ\n // where ν = a/√(1−e²⋅sinφ⋅sinφ), e² = (a²-b²)/a² or (better conditioned) 2⋅f-f²\n const ellipsoid = this.datum\n ? this.datum.ellipsoid\n : this.referenceFrame ? this.referenceFrame.ellipsoid : ellipsoids.WGS84;\n\n const φ = this.lat.toRadians();\n const λ = this.lon.toRadians();\n const h = this.height;\n const { a, f } = ellipsoid;\n\n const sinφ = Math.sin(φ), cosφ = Math.cos(φ);\n const sinλ = Math.sin(λ), cosλ = Math.cos(λ);\n\n const eSq = 2*f - f*f; // 1st eccentricity squared ≡ (a²-b²)/a²\n const ν = a / Math.sqrt(1 - eSq*sinφ*sinφ); // radius of curvature in prime vertical\n\n const x = (ν+h) * cosφ * cosλ;\n const y = (ν+h) * cosφ * sinλ;\n const z = (ν*(1-eSq)+h) * sinφ;\n\n return new Cartesian(x, y, z);\n }\n\n\n /**\n * Checks if another point is equal to ‘this’ point.\n *\n * @param {LatLon} point - Point to be compared against this point.\n * @returns {bool} True if points have identical latitude, longitude, height, and datum/referenceFrame.\n * @throws {TypeError} Invalid point.\n *\n * @example\n * const p1 = new LatLon(52.205, 0.119);\n * const p2 = new LatLon(52.205, 0.119);\n * const equal = p1.equals(p2); // true\n */\n equals(point) {\n if (!(point instanceof LatLonEllipsoidal)) throw new TypeError(`invalid point ‘${point}’`);\n\n if (Math.abs(this.lat - point.lat) > Number.EPSILON) return false;\n if (Math.abs(this.lon - point.lon) > Number.EPSILON) return false;\n if (Math.abs(this.height - point.height) > Number.EPSILON) return false;\n if (this.datum != point.datum) return false;\n if (this.referenceFrame != point.referenceFrame) return false;\n if (this.epoch != point.epoch) return false;\n\n return true;\n }\n\n\n /**\n * Returns a string representation of ‘this’ point, formatted as degrees, degrees+minutes, or\n * degrees+minutes+seconds.\n *\n * @param {string} [format=d] - Format point as 'd', 'dm', 'dms', or 'n' for signed numeric.\n * @param {number} [dp=4|2|0] - Number of decimal places to use: default 4 for d, 2 for dm, 0 for dms.\n * @param {number} [dpHeight=null] - Number of decimal places to use for height; default is no height display.\n * @returns {string} Comma-separated formatted latitude/longitude.\n * @throws {RangeError} Invalid format.\n *\n * @example\n * const greenwich = new LatLon(51.47788, -0.00147, 46);\n * const d = greenwich.toString(); // 51.4779°N, 000.0015°W\n * const dms = greenwich.toString('dms', 2); // 51°28′40″N, 000°00′05″W\n * const [lat, lon] = greenwich.toString('n').split(','); // 51.4779, -0.0015\n * const dmsh = greenwich.toString('dms', 0, 0); // 51°28′40″N, 000°00′06″W +46m\n */\n toString(format='d', dp=undefined, dpHeight=null) {\n // note: explicitly set dp to undefined for passing through to toLat/toLon\n if (![ 'd', 'dm', 'dms', 'n' ].includes(format)) throw new RangeError(`invalid format ‘${format}’`);\n\n const height = (this.height>=0 ? ' +' : ' ') + this.height.toFixed(dpHeight) + 'm';\n if (format == 'n') { // signed numeric degrees\n if (dp == undefined) dp = 4;\n const lat = this.lat.toFixed(dp);\n const lon = this.lon.toFixed(dp);\n return `${lat}, ${lon}${dpHeight==null ? '' : height}`;\n }\n\n const lat = Dms.toLat(this.lat, format, dp);\n const lon = Dms.toLon(this.lon, format, dp);\n\n return `${lat}, ${lon}${dpHeight==null ? '' : height}`;\n }\n\n}\n\n\n/* Cartesian - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * ECEF (earth-centered earth-fixed) geocentric cartesian coordinates.\n *\n * @extends Vector3d\n */\nclass Cartesian extends Vector3d {\n\n /**\n * Creates cartesian coordinate representing ECEF (earth-centric earth-fixed) point.\n *\n * @param {number} x - X coordinate in metres (=> 0°N,0°E).\n * @param {number} y - Y coordinate in metres (=> 0°N,90°E).\n * @param {number} z - Z coordinate in metres (=> 90°N).\n *\n * @example\n * import { Cartesian } from '/js/geodesy/latlon-ellipsoidal.js';\n * const coord = new Cartesian(3980581.210, -111.159, 4966824.522);\n */\n constructor(x, y, z) {\n super(x, y, z); // arguably redundant constructor, but specifies units & axes\n }\n\n\n /**\n * Converts ‘this’ (geocentric) cartesian (x/y/z) coordinate to (geodetic) latitude/longitude\n * point on specified ellipsoid.\n *\n * Uses Bowring’s (1985) formulation for μm precision in concise form; ‘The accuracy of geodetic\n * latitude and height equations’, B R Bowring, Survey Review vol 28, 218, Oct 1985.\n *\n * @param {LatLon.ellipsoids} [ellipsoid=WGS84] - Ellipsoid to use when converting point.\n * @returns {LatLon} Latitude/longitude point defined by cartesian coordinates, on given ellipsoid.\n * @throws {TypeError} Invalid ellipsoid.\n *\n * @example\n * const c = new Cartesian(4027893.924, 307041.993, 4919474.294);\n * const p = c.toLatLon(); // 50.7978°N, 004.3592°E\n */\n toLatLon(ellipsoid=ellipsoids.WGS84) {\n // note ellipsoid is available as a parameter for when toLatLon gets subclassed to\n // Ellipsoidal_Datum / Ellipsoidal_Referenceframe.\n if (!ellipsoid || !ellipsoid.a) throw new TypeError(`invalid ellipsoid ‘${ellipsoid}’`);\n\n const { x, y, z } = this;\n const { a, b, f } = ellipsoid;\n\n const e2 = 2*f - f*f; // 1st eccentricity squared ≡ (a²−b²)/a²\n const ε2 = e2 / (1-e2); // 2nd eccentricity squared ≡ (a²−b²)/b²\n const p = Math.sqrt(x*x + y*y); // distance from minor axis\n const R = Math.sqrt(p*p + z*z); // polar radius\n\n // parametric latitude (Bowring eqn.17, replacing tanβ = z·a / p·b)\n const tanβ = (b*z)/(a*p) * (1+ε2*b/R);\n const sinβ = tanβ / Math.sqrt(1+tanβ*tanβ);\n const cosβ = sinβ / tanβ;\n\n // geodetic latitude (Bowring eqn.18: tanφ = z+ε²⋅b⋅sin³β / p−e²⋅cos³β)\n const φ = isNaN(cosβ) ? 0 : Math.atan2(z + ε2*b*sinβ*sinβ*sinβ, p - e2*a*cosβ*cosβ*cosβ);\n\n // longitude\n const λ = Math.atan2(y, x);\n\n // height above ellipsoid (Bowring eqn.7)\n const sinφ = Math.sin(φ), cosφ = Math.cos(φ);\n const ν = a / Math.sqrt(1-e2*sinφ*sinφ); // length of the normal terminated by the minor axis\n const h = p*cosφ + z*sinφ - (a*a/ν);\n\n const point = new LatLonEllipsoidal(φ.toDegrees(), λ.toDegrees(), h);\n\n return point;\n }\n\n\n /**\n * Returns a string representation of ‘this’ cartesian point.\n *\n * @param {number} [dp=0] - Number of decimal places to use.\n * @returns {string} Comma-separated latitude/longitude.\n */\n toString(dp=0) {\n const x = this.x.toFixed(dp), y = this.y.toFixed(dp), z = this.z.toFixed(dp);\n return `[${x},${y},${z}]`;\n }\n\n}\n\n\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nexport { LatLonEllipsoidal as default, Cartesian, Vector3d, Dms };\n","/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n/* Geodesy tools for conversions between (historical) datums (c) Chris Veness 2005-2019 */\n/* MIT Licence */\n/* www.movable-type.co.uk/scripts/latlong-convert-coords.html */\n/* www.movable-type.co.uk/scripts/geodesy-library.html#latlon-ellipsoidal-datum */\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nimport LatLonEllipsoidal, { Cartesian, Dms } from './latlon-ellipsoidal.js';\n\n\n/**\n * Historical geodetic datums: a latitude/longitude point defines a geographic location on or\n * above/below the earth’s surface, measured in degrees from the equator & the International\n * Reference Meridian and metres above the ellipsoid, and based on a given datum. The datum is\n * based on a reference ellipsoid and tied to geodetic survey reference points.\n *\n * Modern geodesy is generally based on the WGS84 datum (as used for instance by GPS systems), but\n * previously various reference ellipsoids and datum references were used.\n *\n * This module extends the core latlon-ellipsoidal module to include ellipsoid parameters and datum\n * transformation parameters, and methods for converting between different (generally historical)\n * datums.\n *\n * It can be used for UK Ordnance Survey mapping (OS National Grid References are still based on the\n * otherwise historical OSGB36 datum), as well as for historical purposes.\n *\n * q.v. Ordnance Survey ‘A guide to coordinate systems in Great Britain’ Section 6,\n * www.ordnancesurvey.co.uk/docs/support/guide-coordinate-systems-great-britain.pdf, and also\n * www.ordnancesurvey.co.uk/blog/2014/12/2.\n *\n * @module latlon-ellipsoidal-datum\n */\n\n\n/*\n * Ellipsoid parameters; exposed through static getter below.\n */\nconst ellipsoids = {\n WGS84: { a: 6378137, b: 6356752.314245, f: 1/298.257223563 },\n Airy1830: { a: 6377563.396, b: 6356256.909, f: 1/299.3249646 },\n AiryModified: { a: 6377340.189, b: 6356034.448, f: 1/299.3249646 },\n Bessel1841: { a: 6377397.155, b: 6356078.962818, f: 1/299.1528128 },\n Clarke1866: { a: 6378206.4, b: 6356583.8, f: 1/294.978698214 },\n Clarke1880IGN: { a: 6378249.2, b: 6356515.0, f: 1/293.466021294 },\n GRS80: { a: 6378137, b: 6356752.314140, f: 1/298.257222101 },\n Intl1924: { a: 6378388, b: 6356911.946, f: 1/297 }, // aka Hayford\n WGS72: { a: 6378135, b: 6356750.5, f: 1/298.26 },\n};\n\n\n/*\n * Datums; exposed through static getter below.\n */\nconst datums = {\n // transforms: t in metres, s in ppm, r in arcseconds tx ty tz s rx ry rz\n ED50: { ellipsoid: ellipsoids.Intl1924, transform: [ 89.5, 93.8, 123.1, -1.2, 0.0, 0.0, 0.156 ] }, // epsg.io/1311\n ETRS89: { ellipsoid: ellipsoids.GRS80, transform: [ 0, 0, 0, 0, 0, 0, 0 ] }, // epsg.io/1149; @ 1-metre level\n Irl1975: { ellipsoid: ellipsoids.AiryModified, transform: [ -482.530, 130.596, -564.557, -8.150, 1.042, 0.214, 0.631 ] }, // epsg.io/1954\n NAD27: { ellipsoid: ellipsoids.Clarke1866, transform: [ 8, -160, -176, 0, 0, 0, 0 ] },\n NAD83: { ellipsoid: ellipsoids.GRS80, transform: [ 0.9956, -1.9103, -0.5215, -0.00062, 0.025915, 0.009426, 0.011599 ] },\n NTF: { ellipsoid: ellipsoids.Clarke1880IGN, transform: [ 168, 60, -320, 0, 0, 0, 0 ] },\n OSGB36: { ellipsoid: ellipsoids.Airy1830, transform: [ -446.448, 125.157, -542.060, 20.4894, -0.1502, -0.2470, -0.8421 ] }, // epsg.io/1314\n Potsdam: { ellipsoid: ellipsoids.Bessel1841, transform: [ -582, -105, -414, -8.3, 1.04, 0.35, -3.08 ] },\n TokyoJapan: { ellipsoid: ellipsoids.Bessel1841, transform: [ 148, -507, -685, 0, 0, 0, 0 ] },\n WGS72: { ellipsoid: ellipsoids.WGS72, transform: [ 0, 0, -4.5, -0.22, 0, 0, 0.554 ] },\n WGS84: { ellipsoid: ellipsoids.WGS84, transform: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] },\n};\n/* sources:\n * - ED50: www.gov.uk/guidance/oil-and-gas-petroleum-operations-notices#pon-4\n * - Irl1975: www.osi.ie/wp-content/uploads/2015/05/transformations_booklet.pdf\n * - NAD27: en.wikipedia.org/wiki/Helmert_transformation\n * - NAD83: www.uvm.edu/giv/resources/WGS84_NAD83.pdf [strictly, WGS84(G1150) -> NAD83(CORS96) @ epoch 1997.0]\n * (note NAD83(1986) ≡ WGS84(Original); confluence.qps.nl/pages/viewpage.action?pageId=29855173)\n * - NTF: Nouvelle Triangulation Francaise geodesie.ign.fr/contenu/fichiers/Changement_systeme_geodesique.pdf\n * - OSGB36: www.ordnancesurvey.co.uk/docs/support/guide-coordinate-systems-great-britain.pdf\n * - Potsdam: kartoweb.itc.nl/geometrics/Coordinate%20transformations/coordtrans.html\n * - TokyoJapan: www.geocachingtoolbox.com?page=datumEllipsoidDetails\n * - WGS72: www.icao.int/safety/pbn/documentation/eurocontrol/eurocontrol wgs 84 implementation manual.pdf\n *\n * more transform parameters are available from earth-info.nga.mil/GandG/coordsys/datums/NATO_DT.pdf,\n * www.fieldenmaps.info/cconv/web/cconv_params.js\n */\n/* note:\n * - ETRS89 reference frames are coincident with WGS-84 at epoch 1989.0 (ie null transform) at the one metre level.\n */\n\n\n// freeze static properties\nObject.keys(ellipsoids).forEach(e => Object.freeze(ellipsoids[e]));\nObject.keys(datums).forEach(d => { Object.freeze(datums[d]); Object.freeze(datums[d].transform); });\n\n\n/* LatLon - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * Latitude/longitude points on an ellipsoidal model earth, with ellipsoid parameters and methods\n * for converting between datums and to geocentric (ECEF) cartesian coordinates.\n *\n * @extends LatLonEllipsoidal\n */\nclass LatLonEllipsoidal_Datum extends LatLonEllipsoidal {\n\n /**\n * Creates a geodetic latitude/longitude point on an ellipsoidal model earth using given datum.\n *\n * @param {number} lat - Latitude (in degrees).\n * @param {number} lon - Longitude (in degrees).\n * @param {number} [height=0] - Height above ellipsoid in metres.\n * @param {LatLon.datums} datum - Datum this point is defined within.\n *\n * @example\n * import LatLon from '/js/geodesy/latlon-ellipsoidal-datum.js';\n * const p = new LatLon(53.3444, -6.2577, 17, LatLon.datums.Irl1975);\n */\n constructor(lat, lon, height=0, datum=datums.WGS84) {\n if (!datum || datum.ellipsoid==undefined) throw new TypeError(`unrecognised datum ‘${datum}’`);\n\n super(lat, lon, height);\n\n this._datum = datum;\n }\n\n\n /**\n * Datum this point is defined within.\n */\n get datum() {\n return this._datum;\n }\n\n\n /**\n * Ellipsoids with their parameters; semi-major axis (a), semi-minor axis (b), and flattening (f).\n *\n * Flattening f = (a−b)/a; at least one of these parameters is derived from defining constants.\n *\n * @example\n * const a = LatLon.ellipsoids.Airy1830.a; // 6377563.396\n */\n static get ellipsoids() {\n return ellipsoids;\n }\n\n\n /**\n * Datums; with associated ellipsoid, and Helmert transform parameters to convert from WGS-84\n * into given datum.\n *\n * Note that precision of various datums will vary, and WGS-84 (original) is not defined to be\n * accurate to better than ±1 metre. No transformation should be assumed to be accurate to\n * better than a metre, for many datums somewhat less.\n *\n * This is a small sample of commoner datums from a large set of historical datums. I will add\n * new datums on request.\n *\n * @example\n * const a = LatLon.datums.OSGB36.ellipsoid.a; // 6377563.396\n * const tx = LatLon.datums.OSGB36.transform; // [ tx, ty, tz, s, rx, ry, rz ]\n * const availableDatums = Object.keys(LatLon.datums).join(', '); // ED50, Irl1975, NAD27, ...\n */\n static get datums() {\n return datums;\n }\n\n\n // note instance datum getter/setters are in LatLonEllipsoidal\n\n\n /**\n * Parses a latitude/longitude point from a variety of formats.\n *\n * Latitude & longitude (in degrees) can be supplied as two separate parameters, as a single\n * comma-separated lat/lon string, or as a single object with { lat, lon } or GeoJSON properties.\n *\n * The latitude/longitude values may be numeric or strings; they may be signed decimal or\n * deg-min-sec (hexagesimal) suffixed by compass direction (NSEW); a variety of separators are\n * accepted. Examples -3.62, '3 37 12W', '3°37′12″W'.\n *\n * Thousands/decimal separators must be comma/dot; use Dms.fromLocale to convert locale-specific\n * thousands/decimal separators.\n *\n * @param {number|string|Object} lat|latlon - Geodetic Latitude (in degrees) or comma-separated lat/lon or lat/lon object.\n * @param {number} [lon] - Longitude in degrees.\n * @param {number} [height=0] - Height above ellipsoid in metres.\n * @param {LatLon.datums} [datum=WGS84] - Datum this point is defined within.\n * @returns {LatLon} Latitude/longitude point on ellipsoidal model earth using given datum.\n * @throws {TypeError} Unrecognised datum.\n *\n * @example\n * const p = LatLon.parse('51.47736, 0.0000', 0, LatLon.datums.OSGB36);\n */\n static parse(...args) {\n let datum = datums.WGS84;\n\n // if the last argument is a datum, use that, otherwise use default WGS-84\n if (args.length==4 || (args.length==3 && typeof args[2] == 'object')) datum = args.pop();\n\n if (!datum || datum.ellipsoid==undefined) throw new TypeError(`unrecognised datum ‘${datum}’`);\n\n const point = super.parse(...args);\n\n point._datum = datum;\n\n return point;\n }\n\n\n /**\n * Converts ‘this’ lat/lon coordinate to new coordinate system.\n *\n * @param {LatLon.datums} toDatum - Datum this coordinate is to be converted to.\n * @returns {LatLon} This point converted to new datum.\n * @throws {TypeError} Unrecognised datum.\n *\n * @example\n * const pWGS84 = new LatLon(51.47788, -0.00147, 0, LatLon.datums.WGS84);\n * const pOSGB = pWGS84.convertDatum(LatLon.datums.OSGB36); // 51.4773°N, 000.0001°E\n */\n convertDatum(toDatum) {\n if (!toDatum || toDatum.ellipsoid==undefined) throw new TypeError(`unrecognised datum ‘${toDatum}’`);\n\n const oldCartesian = this.toCartesian(); // convert geodetic to cartesian\n const newCartesian = oldCartesian.convertDatum(toDatum); // convert datum\n const newLatLon = newCartesian.toLatLon(); // convert cartesian back to geodetic\n\n return newLatLon;\n }\n\n\n /**\n * Converts ‘this’ point from (geodetic) latitude/longitude coordinates to (geocentric) cartesian\n * (x/y/z) coordinates, based on the same datum.\n *\n * Shadow of LatLonEllipsoidal.toCartesian(), returning Cartesian augmented with\n * LatLonEllipsoidal_Datum methods/properties.\n *\n * @returns {Cartesian} Cartesian point equivalent to lat/lon point, with x, y, z in metres from\n * earth centre, augmented with reference frame conversion methods and properties.\n */\n toCartesian() {\n const cartesian = super.toCartesian();\n const cartesianDatum = new Cartesian_Datum(cartesian.x, cartesian.y, cartesian.z, this.datum);\n return cartesianDatum;\n }\n\n}\n\n\n/* Cartesian - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * Augments Cartesian with datum the cooordinate is based on, and methods to convert between datums\n * (using Helmert 7-parameter transforms) and to convert cartesian to geodetic latitude/longitude\n * point.\n *\n * @extends Cartesian\n */\nclass Cartesian_Datum extends Cartesian {\n\n /**\n * Creates cartesian coordinate representing ECEF (earth-centric earth-fixed) point, on a given\n * datum. The datum will identify the primary meridian (for the x-coordinate), and is also\n * useful in transforming to/from geodetic (lat/lon) coordinates.\n *\n * @param {number} x - X coordinate in metres (=> 0°N,0°E).\n * @param {number} y - Y coordinate in metres (=> 0°N,90°E).\n * @param {number} z - Z coordinate in metres (=> 90°N).\n * @param {LatLon.datums} [datum] - Datum this coordinate is defined within.\n * @throws {TypeError} Unrecognised datum.\n *\n * @example\n * import { Cartesian } from '/js/geodesy/latlon-ellipsoidal-datum.js';\n * const coord = new Cartesian(3980581.210, -111.159, 4966824.522);\n */\n constructor(x, y, z, datum=undefined) {\n if (datum && datum.ellipsoid==undefined) throw new TypeError(`unrecognised datum ‘${datum}’`);\n\n super(x, y, z);\n\n if (datum) this._datum = datum;\n }\n\n\n /**\n * Datum this point is defined within.\n */\n get datum() {\n return this._datum;\n }\n set datum(datum) {\n if (!datum || datum.ellipsoid==undefined) throw new TypeError(`unrecognised datum ‘${datum}’`);\n this._datum = datum;\n }\n\n\n /**\n * Converts ‘this’ (geocentric) cartesian (x/y/z) coordinate to (geodetic) latitude/longitude\n * point (based on the same datum, or WGS84 if unset).\n *\n * Shadow of Cartesian.toLatLon(), returning LatLon augmented with LatLonEllipsoidal_Datum\n * methods convertDatum, toCartesian, etc.\n *\n * @returns {LatLon} Latitude/longitude point defined by cartesian coordinates.\n * @throws {TypeError} Unrecognised datum\n *\n * @example\n * const c = new Cartesian(4027893.924, 307041.993, 4919474.294);\n * const p = c.toLatLon(); // 50.7978°N, 004.3592°E\n */\n toLatLon(deprecatedDatum=undefined) {\n if (deprecatedDatum) {\n console.info('datum parameter to Cartesian_Datum.toLatLon is deprecated: set datum before calling toLatLon()');\n this.datum = deprecatedDatum;\n }\n const datum = this.datum || datums.WGS84;\n if (!datum || datum.ellipsoid==undefined) throw new TypeError(`unrecognised datum ‘${datum}’`);\n\n const latLon = super.toLatLon(datum.ellipsoid); // TODO: what if datum is not geocentric?\n const point = new LatLonEllipsoidal_Datum(latLon.lat, latLon.lon, latLon.height, this.datum);\n return point;\n }\n\n\n /**\n * Converts ‘this’ cartesian coordinate to new datum using Helmert 7-parameter transformation.\n *\n * @param {LatLon.datums} toDatum - Datum this coordinate is to be converted to.\n * @returns {Cartesian} This point converted to new datum.\n * @throws {Error} Undefined datum.\n *\n * @example\n * const c = new Cartesian(3980574.247, -102.127, 4966830.065, LatLon.datums.OSGB36);\n * c.convertDatum(LatLon.datums.Irl1975); // [??,??,??]\n */\n convertDatum(toDatum) {\n // TODO: what if datum is not geocentric?\n if (!toDatum || toDatum.ellipsoid == undefined) throw new TypeError(`unrecognised datum ‘${toDatum}’`);\n if (!this.datum) throw new TypeError('cartesian coordinate has no datum');\n\n let oldCartesian = null;\n let transform = null;\n\n if (this.datum == undefined || this.datum == datums.WGS84) {\n // converting from WGS 84\n oldCartesian = this;\n transform = toDatum.transform;\n }\n if (toDatum == datums.WGS84) {\n // converting to WGS 84; use inverse transform\n oldCartesian = this;\n transform = this.datum.transform.map(p => -p);\n }\n if (transform == null) {\n // neither this.datum nor toDatum are WGS84: convert this to WGS84 first\n oldCartesian = this.convertDatum(datums.WGS84);\n transform = toDatum.transform;\n }\n\n const newCartesian = oldCartesian.applyTransform(transform);\n newCartesian.datum = toDatum;\n\n return newCartesian;\n }\n\n\n /**\n * Applies Helmert 7-parameter transformation to ‘this’ coordinate using transform parameters t.\n *\n * This is used in converting datums (geodetic->cartesian, apply transform, cartesian->geodetic).\n *\n * @private\n * @param {number[]} t - Transformation to apply to this coordinate.\n * @returns {Cartesian} Transformed point.\n */\n applyTransform(t) {\n // this point\n const { x: x1, y: y1, z: z1 } = this;\n\n // transform parameters\n const tx = t[0]; // x-shift in metres\n const ty = t[1]; // y-shift in metres\n const tz = t[2]; // z-shift in metres\n const s = t[3]/1e6 + 1; // scale: normalise parts-per-million to (s+1)\n const rx = (t[4]/3600).toRadians(); // x-rotation: normalise arcseconds to radians\n const ry = (t[5]/3600).toRadians(); // y-rotation: normalise arcseconds to radians\n const rz = (t[6]/3600).toRadians(); // z-rotation: normalise arcseconds to radians\n\n // apply transform\n const x2 = tx + x1*s - y1*rz + z1*ry;\n const y2 = ty + x1*rz + y1*s - z1*rx;\n const z2 = tz - x1*ry + y1*rx + z1*s;\n\n return new Cartesian_Datum(x2, y2, z2);\n }\n}\n\n\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nexport { LatLonEllipsoidal_Datum as default, Cartesian_Datum as Cartesian, datums, Dms };\n","/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n/* Ordnance Survey Grid Reference functions (c) Chris Veness 2005-2021 */\n/* MIT Licence */\n/* www.movable-type.co.uk/scripts/latlong-gridref.html */\n/* www.movable-type.co.uk/scripts/geodesy-library.html#osgridref */\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nimport LatLonEllipsoidal, { Dms } from './latlon-ellipsoidal-datum.js';\n\n\n/**\n * Ordnance Survey OSGB grid references provide geocoordinate references for UK mapping purposes.\n *\n * Formulation implemented here due to Thomas, Redfearn, etc is as published by OS, but is inferior\n * to Krüger as used by e.g. Karney 2011.\n *\n * www.ordnancesurvey.co.uk/documents/resources/guide-coordinate-systems-great-britain.pdf.\n *\n * Note OSGB grid references cover Great Britain only; Ireland and the Channel Islands have their\n * own references.\n *\n * Note that these formulae are based on ellipsoidal calculations, and according to the OS are\n * accurate to about 4–5 metres – for greater accuracy, a geoid-based transformation (OSTN15) must\n * be used.\n */\n\n/*\n * Converted 2015 to work with WGS84 by default, OSGB36 as option;\n * www.ordnancesurvey.co.uk/blog/2014/12/confirmation-on-changes-to-latitude-and-longitude\n */\n\n\n/* OsGridRef - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\nconst nationalGrid = {\n trueOrigin: { lat: 49, lon: -2 }, // true origin of grid 49°N,2°W on OSGB36 datum\n falseOrigin: { easting: -400e3, northing: 100e3 }, // easting & northing of false origin, metres from true origin\n scaleFactor: 0.9996012717, // scale factor on central meridian\n ellipsoid: LatLonEllipsoidal.ellipsoids.Airy1830,\n};\n// note Irish National Grid uses t/o 53°30′N, 8°W, f/o 200kmW, 250kmS, scale factor 1.000035, on Airy 1830 Modified ellipsoid\n\n\n/**\n * OS Grid References with methods to parse and convert them to latitude/longitude points.\n */\nclass OsGridRef {\n\n /**\n * Creates an OsGridRef object.\n *\n * @param {number} easting - Easting in metres from OS Grid false origin.\n * @param {number} northing - Northing in metres from OS Grid false origin.\n *\n * @example\n * import OsGridRef from '/js/geodesy/osgridref.js';\n * const gridref = new OsGridRef(651409, 313177);\n */\n constructor(easting, northing) {\n this.easting = Number(easting);\n this.northing = Number(northing);\n\n if (isNaN(easting) || this.easting<0 || this.easting>700e3) throw new RangeError(`invalid easting ‘${easting}’`);\n if (isNaN(northing) || this.northing<0 || this.northing>1300e3) throw new RangeError(`invalid northing ‘${northing}’`);\n }\n\n\n /**\n * Converts ‘this’ Ordnance Survey Grid Reference easting/northing coordinate to latitude/longitude\n * (SW corner of grid square).\n *\n * While OS Grid References are based on OSGB-36, the Ordnance Survey have deprecated the use of\n * OSGB-36 for latitude/longitude coordinates (in favour of WGS-84), hence this function returns\n * WGS-84 by default, with OSGB-36 as an option. See www.ordnancesurvey.co.uk/blog/2014/12/2.\n *\n * Note formulation implemented here due to Thomas, Redfearn, etc is as published by OS, but is\n * inferior to Krüger as used by e.g. Karney 2011.\n *\n * @param {LatLon.datum} [datum=WGS84] - Datum to convert grid reference into.\n * @returns {LatLon} Latitude/longitude of supplied grid reference.\n *\n * @example\n * const gridref = new OsGridRef(651409.903, 313177.270);\n * const pWgs84 = gridref.toLatLon(); // 52°39′28.723″N, 001°42′57.787″E\n * // to obtain (historical) OSGB36 lat/lon point:\n * const pOsgb = gridref.toLatLon(LatLon.datums.OSGB36); // 52°39′27.253″N, 001°43′04.518″E\n */\n toLatLon(datum=LatLonEllipsoidal.datums.WGS84) {\n const { easting: E, northing: N } = this;\n\n const { a, b } = nationalGrid.ellipsoid; // a = 6377563.396, b = 6356256.909\n const φ0 = nationalGrid.trueOrigin.lat.toRadians(); // latitude of true origin, 49°N\n const λ0 = nationalGrid.trueOrigin.lon.toRadians(); // longitude of true origin, 2°W\n const E0 = -nationalGrid.falseOrigin.easting; // easting of true origin, 400km\n const N0 = -nationalGrid.falseOrigin.northing; // northing of true origin, -100km\n const F0 = nationalGrid.scaleFactor; // 0.9996012717\n\n const e2 = 1 - (b*b)/(a*a); // eccentricity squared\n const n = (a-b)/(a+b), n2 = n*n, n3 = n*n*n; // n, n², n³\n\n let φ=φ0, M=0;\n do {\n φ = (N-N0-M)/(a*F0) + φ;\n\n const Ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (φ-φ0);\n const Mb = (3*n + 3*n*n + (21/8)*n3) * Math.sin(φ-φ0) * Math.cos(φ+φ0);\n const Mc = ((15/8)*n2 + (15/8)*n3) * Math.sin(2*(φ-φ0)) * Math.cos(2*(φ+φ0));\n const Md = (35/24)*n3 * Math.sin(3*(φ-φ0)) * Math.cos(3*(φ+φ0));\n M = b * F0 * (Ma - Mb + Mc - Md); // meridional arc\n\n } while (Math.abs(N-N0-M) >= 0.00001); // ie until < 0.01mm\n\n const cosφ = Math.cos(φ), sinφ = Math.sin(φ);\n const ν = a*F0/Math.sqrt(1-e2*sinφ*sinφ); // nu = transverse radius of curvature\n const ρ = a*F0*(1-e2)/Math.pow(1-e2*sinφ*sinφ, 1.5); // rho = meridional radius of curvature\n const η2 = ν/ρ-1; // eta = ?\n\n const tanφ = Math.tan(φ);\n const tan2φ = tanφ*tanφ, tan4φ = tan2φ*tan2φ, tan6φ = tan4φ*tan2φ;\n const secφ = 1/cosφ;\n const ν3 = ν*ν*ν, ν5 = ν3*ν*ν, ν7 = ν5*ν*ν;\n const VII = tanφ/(2*ρ*ν);\n const VIII = tanφ/(24*ρ*ν3)*(5+3*tan2φ+η2-9*tan2φ*η2);\n const IX = tanφ/(720*ρ*ν5)*(61+90*tan2φ+45*tan4φ);\n const X = secφ/ν;\n const XI = secφ/(6*ν3)*(ν/ρ+2*tan2φ);\n const XII = secφ/(120*ν5)*(5+28*tan2φ+24*tan4φ);\n const XIIA = secφ/(5040*ν7)*(61+662*tan2φ+1320*tan4φ+720*tan6φ);\n\n const dE = (E-E0), dE2 = dE*dE, dE3 = dE2*dE, dE4 = dE2*dE2, dE5 = dE3*dE2, dE6 = dE4*dE2, dE7 = dE5*dE2;\n φ = φ - VII*dE2 + VIII*dE4 - IX*dE6;\n const λ = λ0 + X*dE - XI*dE3 + XII*dE5 - XIIA*dE7;\n\n let point = new LatLon_OsGridRef(φ.toDegrees(), λ.toDegrees(), 0, LatLonEllipsoidal.datums.OSGB36);\n\n if (datum != LatLonEllipsoidal.datums.OSGB36) {\n // if point is required in datum other than OSGB36, convert it\n point = point.convertDatum(datum);\n // convertDatum() gives us a LatLon: convert to LatLon_OsGridRef which includes toOsGrid()\n point = new LatLon_OsGridRef(point.lat, point.lon, point.height, point.datum);\n }\n\n return point;\n }\n\n\n /**\n * Parses grid reference to OsGridRef object.\n *\n * Accepts standard grid references (eg 'SU 387 148'), with or without whitespace separators, from\n * two-digit references up to 10-digit references (1m × 1m square), or fully numeric comma-separated\n * references in metres (eg '438700,114800').\n *\n * @param {string} gridref - Standard format OS Grid Reference.\n * @returns {OsGridRef} Numeric version of grid reference in metres from false origin (SW corner of\n * supplied grid square).\n * @throws {Error} Invalid grid reference.\n *\n * @example\n * const grid = OsGridRef.parse('TG 51409 13177'); // grid: { easting: 651409, northing: 313177 }\n */\n static parse(gridref) {\n gridref = String(gridref).trim();\n\n // check for fully numeric comma-separated gridref format\n let match = gridref.match(/^(\\d+),\\s*(\\d+)$/);\n if (match) return new OsGridRef(match[1], match[2]);\n\n // validate format\n match = gridref.match(/^[HNST][ABCDEFGHJKLMNOPQRSTUVWXYZ]\\s*[0-9]+\\s*[0-9]+$/i);\n if (!match) throw new Error(`invalid grid reference ‘${gridref}’`);\n\n // get numeric values of letter references, mapping A->0, B->1, C->2, etc:\n let l1 = gridref.toUpperCase().charCodeAt(0) - 'A'.charCodeAt(0); // 500km square\n let l2 = gridref.toUpperCase().charCodeAt(1) - 'A'.charCodeAt(0); // 100km square\n // shuffle down letters after 'I' since 'I' is not used in grid:\n if (l1 > 7) l1--;\n if (l2 > 7) l2--;\n\n // convert grid letters into 100km-square indexes from false origin (grid square SV):\n const e100km = ((l1 - 2) % 5) * 5 + (l2 % 5);\n const n100km = (19 - Math.floor(l1 / 5) * 5) - Math.floor(l2 / 5);\n\n // skip grid letters to get numeric (easting/northing) part of ref\n let en = gridref.slice(2).trim().split(/\\s+/);\n // if e/n not whitespace separated, split half way\n if (en.length == 1) en = [ en[0].slice(0, en[0].length / 2), en[0].slice(en[0].length / 2) ];\n\n // validation\n if (en[0].length != en[1].length) throw new Error(`invalid grid reference ‘${gridref}’`);\n\n // standardise to 10-digit refs (metres)\n en[0] = en[0].padEnd(5, '0');\n en[1] = en[1].padEnd(5, '0');\n\n const e = e100km + en[0];\n const n = n100km + en[1];\n\n return new OsGridRef(e, n);\n }\n\n\n /**\n * Converts ‘this’ numeric grid reference to standard OS Grid Reference.\n *\n * @param {number} [digits=10] - Precision of returned grid reference (10 digits = metres);\n * digits=0 will return grid reference in numeric format.\n * @returns {string} This grid reference in standard format.\n *\n * @example\n * const gridref = new OsGridRef(651409, 313177).toString(8); // 'TG 5140 1317'\n * const gridref = new OsGridRef(651409, 313177).toString(0); // '651409,313177'\n */\n toString(digits=10) {\n if (![ 0,2,4,6,8,10,12,14,16 ].includes(Number(digits))) throw new RangeError(`invalid precision ‘${digits}’`); // eslint-disable-line comma-spacing\n\n let { easting: e, northing: n } = this;\n\n // use digits = 0 to return numeric format (in metres) - note northing may be >= 1e7\n if (digits == 0) {\n const format = { useGrouping: false, minimumIntegerDigits: 6, maximumFractionDigits: 3 };\n const ePad = e.toLocaleString('en', format);\n const nPad = n.toLocaleString('en', format);\n return `${ePad},${nPad}`;\n }\n\n // get the 100km-grid indices\n const e100km = Math.floor(e / 100000), n100km = Math.floor(n / 100000);\n\n // translate those into numeric equivalents of the grid letters\n let l1 = (19 - n100km) - (19 - n100km) % 5 + Math.floor((e100km + 10) / 5);\n let l2 = (19 - n100km) * 5 % 25 + e100km % 5;\n\n // compensate for skipped 'I' and build grid letter-pairs\n if (l1 > 7) l1++;\n if (l2 > 7) l2++;\n const letterPair = String.fromCharCode(l1 + 'A'.charCodeAt(0), l2 + 'A'.charCodeAt(0));\n\n // strip 100km-grid indices from easting & northing, and reduce precision\n e = Math.floor((e % 100000) / Math.pow(10, 5 - digits / 2));\n n = Math.floor((n % 100000) / Math.pow(10, 5 - digits / 2));\n\n // pad eastings & northings with leading zeros\n e = e.toString().padStart(digits/2, '0');\n n = n.toString().padStart(digits/2, '0');\n\n return `${letterPair} ${e} ${n}`;\n }\n\n}\n\n\n/* LatLon_OsGridRef - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * Extends LatLon class with method to convert LatLon point to OS Grid Reference.\n *\n * @extends LatLonEllipsoidal\n */\nclass LatLon_OsGridRef extends LatLonEllipsoidal {\n\n /**\n * Converts latitude/longitude to Ordnance Survey grid reference easting/northing coordinate.\n *\n * @returns {OsGridRef} OS Grid Reference easting/northing.\n *\n * @example\n * const grid = new LatLon(52.65798, 1.71605).toOsGrid(); // TG 51409 13177\n * // for conversion of (historical) OSGB36 latitude/longitude point:\n * const grid = new LatLon(52.65798, 1.71605).toOsGrid(LatLon.datums.OSGB36);\n */\n toOsGrid() {\n // if necessary convert to OSGB36 first\n const point = this.datum == LatLonEllipsoidal.datums.OSGB36\n ? this\n : this.convertDatum(LatLonEllipsoidal.datums.OSGB36);\n\n const φ = point.lat.toRadians();\n const λ = point.lon.toRadians();\n\n const { a, b } = nationalGrid.ellipsoid; // a = 6377563.396, b = 6356256.909\n const φ0 = nationalGrid.trueOrigin.lat.toRadians(); // latitude of true origin, 49°N\n const λ0 = nationalGrid.trueOrigin.lon.toRadians(); // longitude of true origin, 2°W\n const E0 = -nationalGrid.falseOrigin.easting; // easting of true origin, 400km\n const N0 = -nationalGrid.falseOrigin.northing; // northing of true origin, -100km\n const F0 = nationalGrid.scaleFactor; // 0.9996012717\n\n const e2 = 1 - (b*b)/(a*a); // eccentricity squared\n const n = (a-b)/(a+b), n2 = n*n, n3 = n*n*n; // n, n², n³\n\n const cosφ = Math.cos(φ), sinφ = Math.sin(φ);\n const ν = a*F0/Math.sqrt(1-e2*sinφ*sinφ); // nu = transverse radius of curvature\n const ρ = a*F0*(1-e2)/Math.pow(1-e2*sinφ*sinφ, 1.5); // rho = meridional radius of curvature\n const η2 = ν/ρ-1; // eta = ?\n\n const Ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (φ-φ0);\n const Mb = (3*n + 3*n*n + (21/8)*n3) * Math.sin(φ-φ0) * Math.cos(φ+φ0);\n const Mc = ((15/8)*n2 + (15/8)*n3) * Math.sin(2*(φ-φ0)) * Math.cos(2*(φ+φ0));\n const Md = (35/24)*n3 * Math.sin(3*(φ-φ0)) * Math.cos(3*(φ+φ0));\n const M = b * F0 * (Ma - Mb + Mc - Md); // meridional arc\n\n const cos3φ = cosφ*cosφ*cosφ;\n const cos5φ = cos3φ*cosφ*cosφ;\n const tan2φ = Math.tan(φ)*Math.tan(φ);\n const tan4φ = tan2φ*tan2φ;\n\n const I = M + N0;\n const II = (ν/2)*sinφ*cosφ;\n const III = (ν/24)*sinφ*cos3φ*(5-tan2φ+9*η2);\n const IIIA = (ν/720)*sinφ*cos5φ*(61-58*tan2φ+tan4φ);\n const IV = ν*cosφ;\n const V = (ν/6)*cos3φ*(ν/ρ-tan2φ);\n const VI = (ν/120) * cos5φ * (5 - 18*tan2φ + tan4φ + 14*η2 - 58*tan2φ*η2);\n\n const Δλ = λ-λ0;\n const Δλ2 = Δλ*Δλ, Δλ3 = Δλ2*Δλ, Δλ4 = Δλ3*Δλ, Δλ5 = Δλ4*Δλ, Δλ6 = Δλ5*Δλ;\n\n let N = I + II*Δλ2 + III*Δλ4 + IIIA*Δλ6;\n let E = E0 + IV*Δλ + V*Δλ3 + VI*Δλ5;\n\n N = Number(N.toFixed(3)); // round to mm precision\n E = Number(E.toFixed(3));\n\n try {\n return new OsGridRef(E, N); // note: gets truncated to SW corner of 1m grid square\n } catch (e) {\n throw new Error(`${e.message} from (${point.lat.toFixed(6)},${point.lon.toFixed(6)}).toOsGrid()`);\n }\n }\n\n\n /**\n * Override LatLonEllipsoidal.convertDatum() with version which returns LatLon_OsGridRef.\n */\n convertDatum(toDatum) {\n const osgbED = super.convertDatum(toDatum); // returns LatLonEllipsoidal_Datum\n const osgbOSGR = new LatLon_OsGridRef(osgbED.lat, osgbED.lon, osgbED.height, osgbED.datum);\n return osgbOSGR;\n }\n\n}\n\n\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nexport { OsGridRef as default, LatLon_OsGridRef as LatLon, Dms };\n","// @ts-expect-error - no types\nimport OsGridRef, { LatLon } from 'geodesy/osgridref.js'\n\n/**\n * Converts lat long to easting and northing\n * @param {object} param\n * @param {number} param.lat\n * @param {number} param.long\n * @returns {{ easting: number, northing: number }}\n */\nfunction latLongToEastingNorthing({ lat, long }) {\n const point = new LatLon(lat, long)\n\n return point.toOsGrid()\n}\n\n/**\n * Converts easting and northing to lat long\n * @param {object} param\n * @param {number} param.easting\n * @param {number} param.northing\n * @returns {{ lat: number, long: number }}\n */\nfunction eastingNorthingToLatLong({ easting, northing }) {\n const point = new OsGridRef(easting, northing)\n const latLong = point.toLatLon()\n\n return { lat: latLong.latitude, long: latLong.longitude }\n}\n\n/**\n * Converts lat long to an ordnance survey grid reference\n * @param {object} param\n * @param {number} param.lat\n * @param {number} param.long\n * @returns {string}\n */\nfunction latLongToOsGridRef({ lat, long }) {\n const point = new LatLon(lat, long)\n\n return point.toOsGrid().toString()\n}\n\n/**\n * Converts an ordnance survey grid reference to lat long\n * @param {string} osGridRef\n * @returns {{ lat: number, long: number }}\n */\nfunction osGridRefToLatLong(osGridRef) {\n const point = OsGridRef.parse(osGridRef)\n const latLong = point.toLatLon()\n\n return { lat: latLong.latitude, long: latLong.longitude }\n}\n\n// Center of UK\nconst DEFAULT_LAT = 53.825564\nconst DEFAULT_LONG = -2.421975\n\n/** @type {InteractiveMapInitConfig} */\nconst defaultConfig = {\n zoom: '6',\n center: [DEFAULT_LONG, DEFAULT_LAT]\n}\n\nconst COMPANY_SYMBOL_CODE = 169\nconst LOCATION_FIELD_SELECTOR = 'input.govuk-input'\nconst EVENTS = {\n interactMarkerChange: 'interact:markerchange'\n}\n\nconst defaultData = {\n VTS_OUTDOOR_URL: '/api/maps/vts/OS_VTS_3857_Outdoor.json',\n VTS_DARK_URL: '/api/maps/vts/OS_VTS_3857_Dark.json',\n VTS_BLACK_AND_WHITE_URL: '/api/maps/vts/OS_VTS_3857_Black_and_White.json'\n}\n\n/**\n * Make a form submit handler that only allows submissions from allowed buttons\n * @param {HTMLButtonElement[]} buttons - the form buttons to allow submissions\n */\nexport function formSubmitFactory(buttons) {\n /**\n * The submit handler\n * @param {SubmitEvent} e\n */\n const onFormSubmit = function (e) {\n if (\n !(e.submitter instanceof HTMLButtonElement) ||\n !buttons.includes(e.submitter)\n ) {\n e.preventDefault()\n }\n }\n\n return onFormSubmit\n}\n\n/**\n * Initialise location maps\n * @param {Partial<MapsEnvironmentConfig>} config - the map configuration\n */\nexport function initMaps(config = {}) {\n const {\n assetPath = '/assets',\n apiPath = '/form/api',\n data = defaultData\n } = config\n const locations = document.querySelectorAll('.app-location-field')\n\n // TODO: Fix this in `interactive-map`\n // If there are location components on the page fix up the main form submit\n // handler so it doesn't fire when using the integrated map search feature\n if (locations.length) {\n const form = document.querySelector('form')\n\n if (form === null) {\n return\n }\n\n const buttons = Array.from(form.querySelectorAll('button'))\n form.addEventListener('submit', formSubmitFactory(buttons), false)\n }\n\n locations.forEach((location, index) => {\n processLocation({ assetPath, apiPath, data }, location, index)\n })\n}\n\n/**\n * OS API request proxy factory\n * @param {string} apiPath - the root API path\n */\nexport function makeTileRequestTransformer(apiPath) {\n /**\n * Proxy OS API requests via our server\n * @param {string} url - the request URL\n * @param {string} resourceType - the resource type\n */\n return function transformTileRequest(url, resourceType) {\n // Only proxy OS API requests that don't already have a key\n if (resourceType !== 'Style' && url.startsWith('https://api.os.uk')) {\n const urlObj = new URL(url)\n if (!urlObj.searchParams.has('key')) {\n return {\n url: `${apiPath}/map-proxy?url=${encodeURIComponent(url)}`,\n headers: {}\n }\n }\n }\n\n const spritesPath =\n 'https://raw.githubusercontent.com/OrdnanceSurvey/OS-Vector-Tile-API-Stylesheets/main'\n\n // Proxy sprite requests\n if (url.startsWith(spritesPath)) {\n const path = url.substring(spritesPath.length)\n return {\n url: `${apiPath}/maps/vts${path}`,\n headers: {}\n }\n }\n\n return { url, headers: {} }\n }\n}\n\n/**\n * Processes a location field to add map capability\n * @param {MapsEnvironmentConfig} config - the location field element\n * @param {Element} location - the location field element\n * @param {*} index - the 0-based index\n */\nfunction processLocation(config, location, index) {\n if (!(location instanceof HTMLDivElement)) {\n return\n }\n\n const locationInputs = location.querySelector('.app-location-field-inputs')\n if (!(locationInputs instanceof HTMLDivElement)) {\n return\n }\n const locationType = location.dataset.locationtype\n\n // Check for support\n const supportedLocations = [\n 'latlongfield',\n 'eastingnorthingfield',\n 'osgridreffield'\n ]\n if (!locationType || !supportedLocations.includes(locationType)) {\n return\n }\n\n const mapContainer = document.createElement('div')\n const mapId = `map_${index}`\n\n mapContainer.setAttribute('id', mapId)\n mapContainer.setAttribute('class', 'map-container')\n\n const initConfig = getInitMapConfig(location) ?? defaultConfig\n\n locationInputs.after(mapContainer)\n\n const { map, interactPlugin } = createMap(mapId, initConfig, config)\n\n map.on(\n 'map:ready',\n /**\n * Callback function which fires when the map is ready\n * @param {object} e - the event\n * @param {MapLibreMap} e.map - the map provider instance\n */\n function onMapReady(e) {\n switch (locationType) {\n case 'latlongfield':\n bindLatLongField(location, map, e.map)\n break\n case 'eastingnorthingfield':\n bindEastingNorthingField(location, map, e.map)\n break\n case 'osgridreffield':\n bindOsGridRefField(location, map, e.map)\n break\n default:\n throw new Error('Not implemented')\n }\n\n // Add info panel\n map.addPanel('info', {\n showLabel: true,\n label: 'How to use the map',\n mobile: {\n slot: 'bottom',\n initiallyOpen: true,\n dismissable: true,\n modal: false\n },\n tablet: {\n slot: 'bottom',\n initiallyOpen: true,\n dismissable: true,\n modal: false\n },\n desktop: {\n slot: 'bottom',\n initiallyOpen: true,\n dismissable: true,\n modal: false\n },\n html: 'If using a map click on a point to update the location.<br><br>If using a keyboard, navigate to the point, centering the crosshair at the location and press enter.'\n })\n\n // Enable the interact plugin\n interactPlugin.enable()\n }\n )\n}\n\n/**\n * Create a Defra map instance\n * @param {string} mapId - the map id\n * @param {InteractiveMapInitConfig} initConfig - the map initial configuration\n * @param {MapsEnvironmentConfig} mapsConfig - the map environment params\n */\nfunction createMap(mapId, initConfig, mapsConfig) {\n const { assetPath, apiPath, data = defaultData } = mapsConfig\n const logoAltText = 'Ordnance survey logo'\n\n // @ts-expect-error - Defra namespace currently comes from UMD support files\n const defra = window.defra\n\n const interactPlugin = defra.interactPlugin({\n dataLayers: [],\n markerColor: { outdoor: '#ff0000', dark: '#00ff00' },\n interactionMode: 'marker',\n multiSelect: false\n })\n\n /** @type {InteractiveMap} */\n const map = new defra.InteractiveMap(mapId, {\n ...initConfig,\n mapProvider: defra.maplibreProvider(),\n reverseGeocodeProvider: defra.openNamesProvider({\n url: `${apiPath}/reverse-geocode-proxy?easting={easting}&northing={northing}`\n }),\n behaviour: 'inline',\n minZoom: 6,\n maxZoom: 18,\n containerHeight: '400px',\n enableZoomControls: true,\n transformRequest: makeTileRequestTransformer(apiPath),\n plugins: [\n defra.mapStylesPlugin({\n mapStyles: [\n {\n id: 'outdoor',\n label: 'Outdoor',\n url: data.VTS_OUTDOOR_URL,\n thumbnail: `${assetPath}/interactive-map/assets/images/outdoor-map-thumb.jpg`,\n logo: `${assetPath}/interactive-map/assets/images/os-logo.svg`,\n logoAltText,\n attribution: `Contains OS data ${String.fromCodePoint(COMPANY_SYMBOL_CODE)} Crown copyright and database rights ${new Date().getFullYear()}`,\n backgroundColor: '#f5f5f0'\n },\n {\n id: 'dark',\n label: 'Dark',\n url: data.VTS_DARK_URL,\n mapColorScheme: 'dark',\n appColorScheme: 'dark',\n thumbnail: `${assetPath}/interactive-map/assets/images/dark-map-thumb.jpg`,\n logo: `${assetPath}/interactive-map/assets/images/os-logo-white.svg`,\n logoAltText,\n attribution: `Contains OS data ${String.fromCodePoint(COMPANY_SYMBOL_CODE)} Crown copyright and database rights ${new Date().getFullYear()}`\n },\n {\n id: 'black-and-white',\n label: 'Black/White',\n url: data.VTS_BLACK_AND_WHITE_URL,\n thumbnail: `${assetPath}/interactive-map/assets/images/black-and-white-map-thumb.jpg`,\n logo: `${assetPath}/interactive-map/assets/images/os-logo-black.svg`,\n logoAltText,\n attribution: `Contains OS data ${String.fromCodePoint(COMPANY_SYMBOL_CODE)} Crown copyright and database rights ${new Date().getFullYear()}`\n }\n ]\n }),\n interactPlugin,\n defra.searchPlugin({\n osNamesURL: `${apiPath}/geocode-proxy?query={query}`,\n width: '300px',\n showMarker: false\n }),\n defra.scaleBarPlugin({\n units: 'metric'\n })\n ]\n })\n\n return { map, interactPlugin }\n}\n\n/**\n * Gets initial map config for a location field\n * @param {HTMLDivElement} locationField - the location field element\n */\nfunction getInitMapConfig(locationField) {\n const locationType = locationField.dataset.locationtype\n\n switch (locationType) {\n case 'latlongfield':\n return getInitLatLongMapConfig(locationField)\n case 'eastingnorthingfield':\n return getInitEastingNorthingMapConfig(locationField)\n case 'osgridreffield':\n return getInitOsGridRefMapConfig(locationField)\n default:\n throw new Error('Not implemented')\n }\n}\n\n/**\n * Validates lat and long is numeric and within UK bounds\n * @param {string} strLat - the latitude string\n * @param {string} strLong - the longitude string\n * @returns {{ valid: false } | { valid: true, value: { lat: number, long: number } }}\n */\nfunction validateLatLong(strLat, strLong) {\n const lat = strLat.trim() && Number(strLat.trim())\n const long = strLong.trim() && Number(strLong.trim())\n\n if (!lat || !long) {\n return { valid: false }\n }\n\n const latMin = 49.85\n const latMax = 60.859\n const longMin = -13.687\n const longMax = 1.767\n\n const latInBounds = lat >= latMin && lat <= latMax\n const longInBounds = long >= longMin && long <= longMax\n\n if (!latInBounds || !longInBounds) {\n return { valid: false }\n }\n\n return { valid: true, value: { lat, long } }\n}\n\n/**\n * Validates easting and northing is numeric and within UK bounds\n * @param {string} strEasting - the easting string\n * @param {string} strNorthing - the northing string\n * @returns {{ valid: false } | { valid: true, value: { easting: number, northing: number } }}\n */\nfunction validateEastingNorthing(strEasting, strNorthing) {\n const easting = strEasting.trim() && Number(strEasting.trim())\n const northing = strNorthing.trim() && Number(strNorthing.trim())\n\n if (!easting || !northing) {\n return { valid: false }\n }\n\n const eastingMin = 0\n const eastingMax = 700000\n const northingMin = 0\n const northingMax = 1300000\n\n const latInBounds = easting >= eastingMin && easting <= eastingMax\n const longInBounds = northing >= northingMin && northing <= northingMax\n\n if (!latInBounds || !longInBounds) {\n return { valid: false }\n }\n\n return { valid: true, value: { easting, northing } }\n}\n\n/**\n * Validates OS grid reference is correct\n * @param {string} osGridRef - the OsGridRef\n * @returns {{ valid: false } | { valid: true, value: string }}\n */\nfunction validateOsGridRef(osGridRef) {\n if (!osGridRef) {\n return { valid: false }\n }\n\n const pattern =\n /^((([sS]|[nN])[a-hA-Hj-zJ-Z])|(([tT]|[oO])[abfglmqrvwABFGLMQRVW])|([hH][l-zL-Z])|([jJ][lmqrvwLMQRVW]))\\s?(([0-9]{3})\\s?([0-9]{3})|([0-9]{4})\\s?([0-9]{4})|([0-9]{5})\\s?([0-9]{5}))$/\n\n const match = pattern.exec(osGridRef)\n\n if (match === null) {\n return { valid: false }\n }\n\n return { valid: true, value: match[0] }\n}\n\n/**\n * Gets the inputs for a latlong location field\n * @param {HTMLDivElement} locationField - the latlong location field element\n */\nfunction getLatLongInputs(locationField) {\n const inputs = locationField.querySelectorAll(LOCATION_FIELD_SELECTOR)\n\n if (inputs.length !== 2) {\n throw new Error('Expected 2 inputs for lat and long')\n }\n\n const latInput = /** @type {HTMLInputElement} */ (inputs[0])\n const longInput = /** @type {HTMLInputElement} */ (inputs[1])\n\n return { latInput, longInput }\n}\n\n/**\n * Gets the inputs for a easting/northing location field\n * @param {HTMLDivElement} locationField - the eastingnorthing location field element\n */\nfunction getEastingNorthingInputs(locationField) {\n const inputs = locationField.querySelectorAll(LOCATION_FIELD_SELECTOR)\n\n if (inputs.length !== 2) {\n throw new Error('Expected 2 inputs for easting and northing')\n }\n\n const eastingInput = /** @type {HTMLInputElement} */ (inputs[0])\n const northingInput = /** @type {HTMLInputElement} */ (inputs[1])\n\n return { eastingInput, northingInput }\n}\n\n/**\n * Gets the input for a OS grid reference location field\n * @param {HTMLDivElement} locationField - the osgridref location field element\n */\nfunction getOsGridRefInput(locationField) {\n const input = locationField.querySelector(LOCATION_FIELD_SELECTOR)\n\n if (input === null) {\n throw new Error('Expected 1 input for osgridref')\n }\n\n return /** @type {HTMLInputElement} */ (input)\n}\n\n/**\n * Get the initial map config for a center point\n * @param {MapCenter} center - the point\n */\nfunction getInitMapCenterConfig(center) {\n return {\n zoom: '16',\n center,\n markers: [\n {\n id: 'location',\n coords: center\n }\n ]\n }\n}\n\n/**\n * Gets initial map config for a latlong location field\n * @param {HTMLDivElement} locationField - the latlong location field element\n * @returns {InteractiveMapInitConfig | undefined}\n */\nfunction getInitLatLongMapConfig(locationField) {\n const { latInput, longInput } = getLatLongInputs(locationField)\n const result = validateLatLong(latInput.value, longInput.value)\n\n if (!result.valid) {\n return undefined\n }\n\n /** @type {MapCenter} */\n const center = [result.value.long, result.value.lat]\n\n return getInitMapCenterConfig(center)\n}\n\n/**\n * Gets initial map config for a easting/northing location field\n * @param {HTMLDivElement} locationField - the eastingnorthing location field element\n * @returns {InteractiveMapInitConfig | undefined}\n */\nfunction getInitEastingNorthingMapConfig(locationField) {\n const { eastingInput, northingInput } =\n getEastingNorthingInputs(locationField)\n const result = validateEastingNorthing(\n eastingInput.value,\n northingInput.value\n )\n\n if (!result.valid) {\n return undefined\n }\n\n const latlong = eastingNorthingToLatLong(result.value)\n\n /** @type {MapCenter} */\n const center = [latlong.long, latlong.lat]\n\n return getInitMapCenterConfig(center)\n}\n\n/**\n * Gets initial map config for an OS grid reference location field\n * @param {HTMLDivElement} locationField - the osgridref location field element\n * @returns {InteractiveMapInitConfig | undefined}\n */\nfunction getInitOsGridRefMapConfig(locationField) {\n const osGridRefInput = getOsGridRefInput(locationField)\n const result = validateOsGridRef(osGridRefInput.value)\n\n if (!result.valid) {\n return undefined\n }\n\n const latlong = osGridRefToLatLong(result.value)\n\n /** @type {MapCenter} */\n const center = [latlong.long, latlong.lat]\n\n return getInitMapCenterConfig(center)\n}\n\n/**\n * Bind a latlong field to the map\n * @param {HTMLDivElement} locationField - the latlong location field\n * @param {InteractiveMap} map - the map component instance (of InteractiveMap)\n * @param {MapLibreMap} mapProvider - the map provider instance (of MapLibreMap)\n */\nfunction bindLatLongField(locationField, map, mapProvider) {\n const { latInput, longInput } = getLatLongInputs(locationField)\n\n map.on(\n EVENTS.interactMarkerChange,\n /**\n * Callback function which fires when the map marker changes\n * @param {object} e - the event\n * @param {[number, number]} e.coords - the map marker coordinates\n */\n function onInteractMarkerChange(e) {\n const maxPrecision = 7\n latInput.value = e.coords[1].toFixed(maxPrecision)\n longInput.value = e.coords[0].toFixed(maxPrecision)\n }\n )\n\n /**\n * Lat & long input change event listener\n * Update the map view location when the inputs are changed\n */\n function onUpdateInputs() {\n const result = validateLatLong(latInput.value, longInput.value)\n\n if (result.valid) {\n /** @type {MapCenter} */\n const center = [result.value.long, result.value.lat]\n\n centerMap(map, mapProvider, center)\n }\n }\n\n latInput.addEventListener('change', onUpdateInputs, false)\n longInput.addEventListener('change', onUpdateInputs, false)\n}\n\n/**\n * Bind an eastingnorthing field to the map\n * @param {HTMLDivElement} locationField - the eastingnorthing location field\n * @param {InteractiveMap} map - the map component instance (of InteractiveMap)\n * @param {MapLibreMap} mapProvider - the map provider instance (of MapLibreMap)\n */\nfunction bindEastingNorthingField(locationField, map, mapProvider) {\n const { eastingInput, northingInput } =\n getEastingNorthingInputs(locationField)\n\n map.on(\n EVENTS.interactMarkerChange,\n /**\n * Callback function which fires when the map marker changes\n * @param {object} e - the event\n * @param {[number, number]} e.coords - the map marker coordinates\n */\n function onInteractMarkerChange(e) {\n const maxPrecision = 0\n const point = latLongToEastingNorthing({\n lat: e.coords[1],\n long: e.coords[0]\n })\n\n eastingInput.value = point.easting.toFixed(maxPrecision)\n northingInput.value = point.northing.toFixed(maxPrecision)\n }\n )\n\n /**\n * Easting & northing input change event listener\n * Update the map view location when the inputs are changed\n */\n function onUpdateInputs() {\n const result = validateEastingNorthing(\n eastingInput.value,\n northingInput.value\n )\n\n if (result.valid) {\n const latlong = eastingNorthingToLatLong(result.value)\n\n /** @type {MapCenter} */\n const center = [latlong.long, latlong.lat]\n\n centerMap(map, mapProvider, center)\n }\n }\n\n eastingInput.addEventListener('change', onUpdateInputs, false)\n northingInput.addEventListener('change', onUpdateInputs, false)\n}\n\n/**\n * Bind an OS grid reference field to the map\n * @param {HTMLDivElement} locationField - the osgridref location field\n * @param {InteractiveMap} map - the map component instance (of InteractiveMap)\n * @param {MapLibreMap} mapProvider - the map provider instance (of MapLibreMap)\n */\nfunction bindOsGridRefField(locationField, map, mapProvider) {\n const osGridRefInput = getOsGridRefInput(locationField)\n\n map.on(\n EVENTS.interactMarkerChange,\n /**\n * Callback function which fires when the map marker changes\n * @param {object} e - the event\n * @param {[number, number]} e.coords - the map marker coordinates\n */\n function onInteractMarkerChange(e) {\n const point = latLongToOsGridRef({\n lat: e.coords[1],\n long: e.coords[0]\n })\n\n osGridRefInput.value = point\n }\n )\n\n /**\n * OS grid reference input change event listener\n * Update the map view location when the input is changed\n */\n function onUpdateInput() {\n const result = validateOsGridRef(osGridRefInput.value)\n\n if (result.valid) {\n const latlong = osGridRefToLatLong(result.value)\n\n /** @type {MapCenter} */\n const center = [latlong.long, latlong.lat]\n\n centerMap(map, mapProvider, center)\n }\n }\n\n osGridRefInput.addEventListener('change', onUpdateInput, false)\n}\n\n/**\n * Updates the marker position and moves the map view port the new location\n * @param {InteractiveMap} map - the map component instance (of InteractiveMap)\n * @param {MapLibreMap} mapProvider - the map provider instance (of MapLibreMap)\n * @param {MapCenter} center - the point\n */\nfunction centerMap(map, mapProvider, center) {\n // Move the 'location' marker to the new point\n map.addMarker('location', center)\n\n // Pan & zoom the map to the new valid location\n mapProvider.flyTo({\n center,\n zoom: 14,\n essential: true\n })\n}\n\n/**\n * @typedef {object} InteractiveMap - an instance of a InteractiveMap\n * @property {Function} on - register callback listeners to map events\n * @property {Function} addPanel - adds a new panel to the map\n * @property {Function} addMarker - adds/updates a marker\n */\n\n/**\n * @typedef {object} MapLibreMap\n * @property {Function} flyTo - pans/zooms to a new location\n */\n\n/**\n * @typedef {[number, number]} MapCenter - Map center point as [long, lat]\n */\n\n/**\n * @typedef {object} InteractiveMapInitConfig - additional config that can be provided to InteractiveMap\n * @property {string} zoom - the zoom level of the map\n * @property {MapCenter} center - the center point of the map\n * @property {{ id: string, coords: MapCenter}[]} [markers] - the markers to add to the map\n */\n\n/**\n * @typedef {object} TileData\n * @property {string} VTS_OUTDOOR_URL - the outdoor tile URL\n * @property {string} VTS_DARK_URL - the dark tile URL\n * @property {string} VTS_BLACK_AND_WHITE_URL - the black and white tile URL\n */\n\n/**\n * @typedef {object} MapsEnvironmentConfig\n * @property {string} assetPath - the root asset path\n * @property {string} apiPath - the root API path\n * @property {TileData} data - the tile data config\n */\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.f = {};\n// This file contains only the entry chunk.\n// The chunk loading function for additional chunks\n__webpack_require__.e = (chunkId) => {\n\treturn Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {\n\t\t__webpack_require__.f[key](chunkId, promises);\n\t\treturn promises;\n\t}, []));\n};","// This function allow to reference async chunks\n__webpack_require__.u = (chunkId) => {\n\t// return url for filenames based on template\n\treturn \"javascripts/\" + \"vendor/accessible-autocomplete\" + \".min.js\";\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var scriptUrl;\nif (typeof import.meta.url === \"string\") scriptUrl = import.meta.url\n// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration\n// or pass an empty string (\"\") and set the __webpack_public_path__ variable from your code to use your own logic.\nif (!scriptUrl) throw new Error(\"Automatic publicPath is not supported in this browser\");\nscriptUrl = scriptUrl.replace(/^blob:/, \"\").replace(/#.*$/, \"\").replace(/\\?.*$/, \"\").replace(/\\/[^\\/]+$/, \"/\");\n__webpack_require__.p = scriptUrl + \"../\";","// no baseURI\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t804: 0\n};\n\n__webpack_require__.f.j = (chunkId, promises) => {\n\t\t// JSONP chunk loading for javascript\n\t\tvar installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;\n\t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n\t\t\t// a Promise means \"currently loading\".\n\t\t\tif(installedChunkData) {\n\t\t\t\tpromises.push(installedChunkData[2]);\n\t\t\t} else {\n\t\t\t\tif(true) { // all chunks have JS\n\t\t\t\t\t// setup Promise in chunk cache\n\t\t\t\t\tvar promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));\n\t\t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n\t\t\t\t\t// start chunk loading\n\t\t\t\t\tvar url = __webpack_require__.p + __webpack_require__.u(chunkId);\n\t\t\t\t\t// create error before stack unwound to get useful stacktrace later\n\t\t\t\t\tvar error = new Error();\n\t\t\t\t\tvar loadingEnded = (event) => {\n\t\t\t\t\t\tif(__webpack_require__.o(installedChunks, chunkId)) {\n\t\t\t\t\t\t\tinstalledChunkData = installedChunks[chunkId];\n\t\t\t\t\t\t\tif(installedChunkData !== 0) installedChunks[chunkId] = undefined;\n\t\t\t\t\t\t\tif(installedChunkData) {\n\t\t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n\t\t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n\t\t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n\t\t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n\t\t\t\t\t\t\t\terror.type = errorType;\n\t\t\t\t\t\t\t\terror.request = realSrc;\n\t\t\t\t\t\t\t\tinstalledChunkData[1](error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\t__webpack_require__.l(url, loadingEnded, \"chunk-\" + chunkId, chunkId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n};\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n// no on chunks loaded\n\n// install a JSONP callback for chunk loading\nvar webpackJsonpCallback = (parentChunkLoadingFunction, data) => {\n\tvar chunkIds = data[0];\n\tvar moreModules = data[1];\n\tvar runtime = data[2];\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some((id) => (installedChunks[id] !== 0))) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\n}\n\nvar chunkLoadingGlobal = self[\"webpackChunk\"] = self[\"webpackChunk\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));","export const MAX_POLLING_DURATION = 300 // 5 minutes\nconst ARIA_DESCRIBEDBY = 'aria-describedby'\nconst ERROR_SUMMARY_TITLE_ID = 'error-summary-title'\n\n/**\n * Creates or updates status announcer for screen readers\n * @param {HTMLElement | null} form - The form element\n * @param {HTMLElement | null} fileCountP - The file count paragraph element\n * @returns {HTMLElement} The status announcer element\n */\nfunction createOrUpdateStatusAnnouncer(form, fileCountP) {\n let statusAnnouncer = form?.querySelector('#statusInformation')\n\n if (!statusAnnouncer) {\n statusAnnouncer = document.createElement('div')\n statusAnnouncer.id = 'statusInformation'\n statusAnnouncer.className = 'govuk-visually-hidden'\n statusAnnouncer.setAttribute('aria-live', 'polite')\n\n // multiple fallbacks to ensure the status announcer is always added to the DOM\n // this helps with cross-browser compatibility and unexpected DOM structures encountered during QA\n try {\n addStatusAnnouncerToDOM(\n asHTMLElement(form),\n asHTMLElement(fileCountP),\n asHTMLElement(statusAnnouncer)\n )\n } catch {\n try {\n form?.appendChild(statusAnnouncer)\n } catch {\n document.body.appendChild(statusAnnouncer)\n }\n }\n }\n\n return /** @type {HTMLElement} */ (statusAnnouncer)\n}\n\n/**\n * Helper function to add the status announcer to the DOM\n * @param {HTMLElement} form - The form element\n * @param {HTMLElement | null} fileCountP - The file count paragraph element\n * @param {HTMLElement} statusAnnouncer - The status announcer element to add\n */\nfunction addStatusAnnouncerToDOM(form, fileCountP, statusAnnouncer) {\n if (fileCountP?.nextSibling && fileCountP.parentNode === form) {\n form.insertBefore(statusAnnouncer, fileCountP.nextSibling)\n return\n }\n\n const parentElement = fileCountP?.parentNode ?? form\n parentElement.appendChild(statusAnnouncer)\n}\n\n/**\n * Finds an existing summary list or creates a new one\n * @param {HTMLFormElement} form - The form element\n * @param {HTMLElement} fileCountP - The file count paragraph element\n * @returns {HTMLElement} The summary list element\n */\nfunction findOrCreateSummaryList(form, fileCountP) {\n let summaryList = form.querySelector('dl.govuk-summary-list')\n\n if (!summaryList) {\n summaryList = document.createElement('dl')\n summaryList.className = 'govuk-summary-list govuk-summary-list--long-key'\n\n const buttonGroup = form.querySelector('.govuk-button-group')\n\n if (buttonGroup) {\n form.insertBefore(summaryList, buttonGroup)\n } else {\n form.insertBefore(summaryList, fileCountP.nextSibling)\n }\n }\n\n return /** @type {HTMLElement} */ (summaryList)\n}\n\n/**\n * Creates a file row element for the summary list\n * @param {File | null} selectedFile - The selected file\n * @param {string} statusText - The status to display\n * @returns {HTMLElement} The created row element\n */\nfunction createFileRow(selectedFile, statusText) {\n const row = document.createElement('div')\n row.className = 'govuk-summary-list__row'\n row.setAttribute('data-filename', selectedFile?.name ?? '')\n row.innerHTML = `\n <dt class=\"govuk-summary-list__key\">\n ${selectedFile?.name ?? ''}\n </dt>\n <dd class=\"govuk-summary-list__value\">\n <strong class=\"govuk-tag govuk-tag--yellow\">${statusText}</strong>\n </dd>\n <dd class=\"govuk-summary-list__actions\">\n </dd>\n `\n return row\n}\n\n/**\n * Renders or updates the file summary box for the selected file\n * @param {File | null} selectedFile - The selected file\n * @param {string} statusText - The status to display\n * @param {HTMLElement} form - The form element\n */\nfunction renderSummary(selectedFile, statusText, form) {\n const container = document.getElementById('uploadedFilesContainer')\n const uploadForm = container ? container.closest('form') : null\n\n if (!uploadForm || !(uploadForm instanceof HTMLFormElement)) {\n return\n }\n\n const fileCountP = uploadForm.querySelector('p.govuk-body')\n\n if (!fileCountP) {\n return\n }\n\n const statusAnnouncer = createOrUpdateStatusAnnouncer(\n /** @type {HTMLElement} */ (uploadForm),\n /** @type {HTMLElement | null} */ (fileCountP)\n )\n\n const fileInput = form.querySelector('input[type=\"file\"]')\n\n if (fileInput) {\n fileInput.setAttribute(ARIA_DESCRIBEDBY, 'statusInformation')\n }\n\n const summaryList = findOrCreateSummaryList(\n /** @type {HTMLFormElement} */ (uploadForm),\n /** @type {HTMLElement} */ (fileCountP)\n )\n\n const existingRow = document.querySelector(\n `[data-filename=\"${selectedFile?.name}\"]`\n )\n\n if (existingRow) {\n existingRow.remove()\n }\n\n const row = createFileRow(selectedFile, statusText)\n summaryList.insertBefore(row, summaryList.firstChild)\n statusAnnouncer.textContent = `${selectedFile?.name ?? ''} ${statusText}`\n}\n\n/**\n * Shows an error message using the GOV.UK error summary component\n * and adds inline error styling to the file input\n * @param {string} message - The error message to display\n * @param {HTMLElement | null} errorSummary - The error summary container\n * @param {HTMLInputElement} fileInput - The file input element\n * @returns {void}\n */\nfunction showError(message, errorSummary, fileInput) {\n const topErrorSummary = document.querySelector('.govuk-error-summary')\n\n if (topErrorSummary) {\n const titleElement = document.getElementById(ERROR_SUMMARY_TITLE_ID)\n if (titleElement) {\n fileInput.setAttribute(ARIA_DESCRIBEDBY, ERROR_SUMMARY_TITLE_ID)\n } else {\n fileInput.removeAttribute(ARIA_DESCRIBEDBY)\n }\n return\n }\n\n if (errorSummary) {\n errorSummary.innerHTML = `\n <div class=\"govuk-error-summary\" data-module=\"govuk-error-summary\">\n <div role=\"alert\">\n <h2 class=\"govuk-error-summary__title\" id=\"${ERROR_SUMMARY_TITLE_ID}\">\n There is a problem\n </h2>\n <div class=\"govuk-error-summary__body\">\n <ul class=\"govuk-list govuk-error-summary__list\">\n <li>\n <a href=\"#file-upload\">${message}</a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n `\n\n fileInput.setAttribute(ARIA_DESCRIBEDBY, ERROR_SUMMARY_TITLE_ID)\n }\n\n const formGroup = fileInput.closest('.govuk-form-group')\n if (formGroup) {\n formGroup.classList.add('govuk-form-group--error')\n fileInput.classList.add('govuk-file-upload--error')\n\n const inputId = fileInput.id\n let errorMessage = document.getElementById(`${inputId}-error`)\n\n if (!errorMessage) {\n errorMessage = document.createElement('p')\n errorMessage.id = `${inputId}-error`\n errorMessage.className = 'govuk-error-message'\n errorMessage.innerHTML = `<span class=\"govuk-visually-hidden\">Error:</span> ${message}`\n formGroup.insertBefore(errorMessage, fileInput)\n }\n\n fileInput.setAttribute(\n ARIA_DESCRIBEDBY,\n `error-summary-title ${inputId}-error`\n )\n }\n}\n\n/**\n * Helper to safely convert an Element to HTMLElement\n * @param {Element | null} element - The element to convert\n */\nfunction asHTMLElement(element) {\n return /** @type {HTMLElement} */ (element)\n}\n\nfunction reloadPage() {\n window.history.replaceState(null, '', window.location.href)\n window.location.href = window.location.pathname\n}\n\n/**\n * Build the upload status URL given the current pathname and the upload ID.\n * This only works when called on a file upload page that has a maximum depth of 1 URL segments following the slug.\n * @param {string} pathname – e.g. window.location.pathname\n * @param {string} uploadId\n * @returns {string} e.g. \"/form/upload-status/abc123\"\n */\nexport function buildUploadStatusUrl(pathname, uploadId) {\n // Remove preview markers and duplicate slashes\n const normalisedPath = pathname\n .replace(/\\/preview\\/(draft|live)/g, '')\n .replace(/\\/{2,}/g, '/')\n .replace(/\\/$/, '')\n\n const segments = normalisedPath.split('/').filter(Boolean)\n\n // The slug is always the second to last segment\n // The prefix is everything before the slug\n const prefix =\n segments.length > 2\n ? `/${segments.slice(0, segments.length - 2).join('/')}`\n : ''\n\n return `${prefix}/upload-status/${uploadId}`\n}\n\n/**\n * Polls the upload status endpoint until the file is ready or timeout occurs\n * @param {string} uploadId - The upload ID to check\n */\nfunction pollUploadStatus(uploadId) {\n let attempts = 0\n const interval = setInterval(() => {\n attempts++\n\n if (attempts >= MAX_POLLING_DURATION) {\n clearInterval(interval)\n reloadPage()\n return\n }\n\n const uploadStatusUrl = buildUploadStatusUrl(\n window.location.pathname,\n uploadId\n )\n\n fetch(uploadStatusUrl, {\n headers: {\n Accept: 'application/json'\n }\n })\n .then((response) => {\n if (!response.ok) {\n throw new Error('Network response was not ok')\n }\n return response.json()\n })\n .then((data) => {\n if (data.uploadStatus === 'ready') {\n clearInterval(interval)\n reloadPage()\n }\n })\n .catch(() => {\n clearInterval(interval)\n reloadPage()\n })\n }, 1000)\n}\n\n/**\n * Handle standard form submission for file upload\n * @param {HTMLFormElement} formElement - The form element\n * @param {HTMLInputElement} fileInput - The file input element\n * @param {HTMLButtonElement} uploadButton - The upload button\n * @param {HTMLButtonElement} continueButton - The continue button\n * @param {File | null} selectedFile - The selected file\n */\nfunction handleStandardFormSubmission(\n formElement,\n fileInput,\n uploadButton,\n continueButton,\n selectedFile\n) {\n renderSummary(selectedFile, 'Uploading…', formElement)\n\n fileInput.focus()\n\n setTimeout(() => {\n fileInput.disabled = true\n uploadButton.disabled = true\n continueButton.disabled = true\n }, 100)\n}\n\n/**\n * Handle AJAX form submission with upload ID\n * @param {Event} event - The click event\n * @param {HTMLFormElement} formElement - The form element\n * @param {HTMLInputElement} fileInput - The file input element\n * @param {HTMLButtonElement} uploadButton - The upload button\n * @param {HTMLElement | null} errorSummary - The error summary container\n * @param {string | undefined} uploadId - The upload ID\n * @returns {boolean} Whether the event was handled\n */\nfunction handleAjaxFormSubmission(\n event,\n formElement,\n fileInput,\n uploadButton,\n errorSummary,\n uploadId\n) {\n if (!formElement.action || !uploadId) {\n return false\n }\n\n event.preventDefault()\n\n const formData = new FormData(formElement)\n const isLocalDev = !!formElement.dataset.proxyUrl\n const uploadUrl = formElement.dataset.proxyUrl ?? formElement.action\n\n const fetchOptions = /** @type {RequestInit} */ ({\n method: 'POST',\n body: formData,\n redirect: isLocalDev ? 'follow' : 'manual' // follow mode if local development with the proxy\n })\n\n // no-cors mode if needed local development with the proxy\n if (isLocalDev) {\n fetchOptions.mode = 'no-cors'\n }\n\n fetch(uploadUrl, fetchOptions)\n .then(() => {\n pollUploadStatus(uploadId)\n })\n .catch(() => {\n fileInput.disabled = false\n uploadButton.disabled = false\n\n showError(\n 'There was a problem uploading the file',\n errorSummary,\n fileInput\n )\n\n return null\n })\n\n return true\n}\n\nfunction initUpload() {\n const form = document.querySelector('form:has(input[type=\"file\"])')\n /** @type {HTMLInputElement | null} */\n const fileInput = form ? form.querySelector('input[type=\"file\"]') : null\n /** @type {HTMLButtonElement | null} */\n const uploadButton = form ? form.querySelector('.upload-file-button') : null\n const continueButton =\n /** @type {HTMLButtonElement} */ (\n Array.from(document.querySelectorAll('button.govuk-button')).find(\n (button) => button.textContent.trim() === 'Continue'\n )\n ) ?? null\n\n const errorSummary = document.querySelector('.govuk-error-summary-container')\n\n if (!form || !fileInput || !uploadButton) {\n return\n }\n\n const formElement = /** @type {HTMLFormElement} */ (form)\n /** @type {File | null} */\n let selectedFile = null\n let isSubmitting = false\n const uploadId = formElement.dataset.uploadId\n\n fileInput.addEventListener('change', () => {\n if (errorSummary) {\n errorSummary.innerHTML = ''\n }\n\n if (fileInput.files && fileInput.files.length > 0) {\n selectedFile = fileInput.files[0]\n }\n })\n\n uploadButton.addEventListener('click', (event) => {\n if (!selectedFile) {\n event.preventDefault()\n showError(\n 'Select a file',\n /** @type {HTMLElement | null} */ (errorSummary),\n fileInput\n )\n return\n }\n\n if (isSubmitting) {\n event.preventDefault()\n return\n }\n\n isSubmitting = true\n\n handleStandardFormSubmission(\n formElement,\n fileInput,\n uploadButton,\n continueButton,\n selectedFile\n )\n\n handleAjaxFormSubmission(\n event,\n formElement,\n fileInput,\n uploadButton,\n /** @type {HTMLElement | null} */ (errorSummary),\n uploadId\n )\n })\n}\n\nexport function initFileUpload() {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', initUpload)\n } else {\n initUpload()\n }\n}\n","/**\n * Common helpers which do not require polyfill.\n *\n * IMPORTANT: If a helper require a polyfill, please isolate it in its own module\n * so that the polyfill can be properly tree-shaken and does not burden\n * the components that do not need that helper\n */\n\n/**\n * Get GOV.UK Frontend breakpoint value from CSS custom property\n *\n * @private\n * @param {string} name - Breakpoint name\n * @returns {{ property: string, value?: string }} Breakpoint object\n */\nexport function getBreakpoint(name) {\n const property = `--govuk-breakpoint-${name}`\n\n // Get value from `<html>` with breakpoints on CSS :root\n const value = window\n .getComputedStyle(document.documentElement)\n .getPropertyValue(property)\n\n return {\n property,\n value: value || undefined\n }\n}\n\n/**\n * Move focus to element\n *\n * Sets tabindex to -1 to make the element programmatically focusable,\n * but removes it on blur as the element doesn't need to be focused again.\n *\n * @private\n * @template {HTMLElement} FocusElement\n * @param {FocusElement} $element - HTML element\n * @param {object} [options] - Handler options\n * @param {function(this: FocusElement): void} [options.onBeforeFocus] - Callback before focus\n * @param {function(this: FocusElement): void} [options.onBlur] - Callback on blur\n */\nexport function setFocus($element, options = {}) {\n const isFocusable = $element.getAttribute('tabindex')\n\n if (!isFocusable) {\n $element.setAttribute('tabindex', '-1')\n }\n\n /**\n * Handle element focus\n */\n function onFocus() {\n $element.addEventListener('blur', onBlur, { once: true })\n }\n\n /**\n * Handle element blur\n */\n function onBlur() {\n options.onBlur?.call($element)\n\n if (!isFocusable) {\n $element.removeAttribute('tabindex')\n }\n }\n\n // Add listener to reset element on blur, after focus\n $element.addEventListener('focus', onFocus, { once: true })\n\n // Focus element\n options.onBeforeFocus?.call($element)\n $element.focus()\n}\n\n/**\n * Checks if component is already initialised\n *\n * @internal\n * @param {Element} $root - HTML element to be checked\n * @param {string} moduleName - name of component module\n * @returns {boolean} Whether component is already initialised\n */\nexport function isInitialised($root, moduleName) {\n return (\n $root instanceof HTMLElement &&\n $root.hasAttribute(`data-${moduleName}-init`)\n )\n}\n\n/**\n * Checks if GOV.UK Frontend is supported on this page\n *\n * Some browsers will load and run our JavaScript but GOV.UK Frontend\n * won't be supported.\n *\n * @param {HTMLElement | null} [$scope] - (internal) `<body>` HTML element checked for browser support\n * @returns {boolean} Whether GOV.UK Frontend is supported on this page\n */\nexport function isSupported($scope = document.body) {\n if (!$scope) {\n return false\n }\n\n return $scope.classList.contains('govuk-frontend-supported')\n}\n\n/**\n * Check for an array\n *\n * @internal\n * @param {unknown} option - Option to check\n * @returns {boolean} Whether the option is an array\n */\nfunction isArray(option) {\n return Array.isArray(option)\n}\n\n/**\n * Check for an object\n *\n * @internal\n * @template {Partial<Record<keyof ObjectType, unknown>>} ObjectType\n * @param {unknown | ObjectType} option - Option to check\n * @returns {option is ObjectType} Whether the option is an object\n */\nexport function isObject(option) {\n return !!option && typeof option === 'object' && !isArray(option)\n}\n\n/**\n * Check for valid scope\n *\n * @internal\n * @template {Element | Document} ScopeType\n * @param {unknown | ScopeType} $scope - Scope of the document to search within\n * @returns {$scope is ScopeType} Whether the scope can be queried\n */\nexport function isScope($scope) {\n return !!$scope && ($scope instanceof Element || $scope instanceof Document)\n}\n\n/**\n * Format error message\n *\n * @internal\n * @param {ComponentWithModuleName} Component - Component that threw the error\n * @param {string} message - Error message\n * @returns {string} - Formatted error message\n */\nexport function formatErrorMessage(Component, message) {\n return `${Component.moduleName}: ${message}`\n}\n\n/* eslint-disable jsdoc/valid-types --\n * `{new(...args: any[] ): object}` is not recognised as valid\n * https://github.com/gajus/eslint-plugin-jsdoc/issues/145#issuecomment-1308722878\n * https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/131\n **/\n\n/**\n * @typedef ComponentWithModuleName\n * @property {string} moduleName - Name of the component\n */\n\n/* eslint-enable jsdoc/valid-types */\n","import { formatErrorMessage, isObject } from '../common/index.mjs'\n\n/**\n * GOV.UK Frontend error\n *\n * A base class for `Error`s thrown by GOV.UK Frontend.\n *\n * It is meant to be extended into specific types of errors\n * to be thrown by our code.\n *\n * @example\n * ```js\n * class MissingRootError extends GOVUKFrontendError {\n * // Setting an explicit name is important as extending the class will not\n * // set a new `name` on the subclass. The `name` property is important\n * // to ensure intelligible error names even if the class name gets\n * // mangled by a minifier\n * name = \"MissingRootError\"\n * }\n * ```\n * @virtual\n */\nexport class GOVUKFrontendError extends Error {\n name = 'GOVUKFrontendError'\n}\n\n/**\n * Indicates that GOV.UK Frontend is not supported\n */\nexport class SupportError extends GOVUKFrontendError {\n name = 'SupportError'\n\n /**\n * Checks if GOV.UK Frontend is supported on this page\n *\n * @param {HTMLElement | null} [$scope] - HTML element `<body>` checked for browser support\n */\n constructor($scope = document.body) {\n const supportMessage =\n 'noModule' in HTMLScriptElement.prototype\n ? 'GOV.UK Frontend initialised without `<body class=\"govuk-frontend-supported\">` from template `<script>` snippet'\n : 'GOV.UK Frontend is not supported in this browser'\n\n super(\n $scope\n ? supportMessage\n : 'GOV.UK Frontend initialised without `<script type=\"module\">`'\n )\n }\n}\n\n/**\n * Indicates that a component has received an illegal configuration\n */\nexport class ConfigError extends GOVUKFrontendError {\n name = 'ConfigError'\n}\n\n/**\n * Indicates an issue with an element (possibly `null` or `undefined`)\n */\nexport class ElementError extends GOVUKFrontendError {\n name = 'ElementError'\n\n /**\n * @internal\n * @overload\n * @param {string} message - Element error message\n */\n\n /**\n * @internal\n * @overload\n * @param {ElementErrorOptions} options - Element error options\n */\n\n /**\n * @internal\n * @param {string | ElementErrorOptions} messageOrOptions - Element error message or options\n */\n constructor(messageOrOptions) {\n let message = typeof messageOrOptions === 'string' ? messageOrOptions : ''\n\n // Build message from options\n if (isObject(messageOrOptions)) {\n const { component, identifier, element, expectedType } = messageOrOptions\n\n message = identifier\n\n // Append reason\n message += element\n ? ` is not of type ${expectedType ?? 'HTMLElement'}`\n : ' not found'\n\n // Prepend with module name (optional)\n if (component) {\n message = formatErrorMessage(component, message)\n }\n }\n\n super(message)\n }\n}\n\n/**\n * Indicates that a component is already initialised\n */\nexport class InitError extends GOVUKFrontendError {\n name = 'InitError'\n\n /**\n * @internal\n * @param {ComponentWithModuleName | string} componentOrMessage - name of the component module\n */\n constructor(componentOrMessage) {\n const message =\n typeof componentOrMessage === 'string'\n ? componentOrMessage\n : formatErrorMessage(\n componentOrMessage,\n `Root element (\\`$root\\`) already initialised`\n )\n\n super(message)\n }\n}\n\n/**\n * Element error options\n *\n * @internal\n * @typedef {object} ElementErrorOptions\n * @property {Element | Document | null} [element] - The element in error (optional)\n * @property {ComponentWithModuleName} [component] - Component throwing the error (optional)\n * @property {string} identifier - An identifier that'll let the user understand which element has an error. This is whatever makes the most sense\n * @property {string} [expectedType] - The type that was expected for the identifier (optional)\n */\n\n/**\n * @import { ComponentWithModuleName } from '../common/index.mjs'\n */\n","import { isInitialised, isSupported } from './common/index.mjs'\nimport { ElementError, InitError, SupportError } from './errors/index.mjs'\n\n/**\n * Base Component class\n *\n * Centralises the behaviours shared by our components\n *\n * @virtual\n * @template {Element} [RootElementType=HTMLElement]\n */\nexport class Component {\n /**\n * @type {typeof Element}\n */\n static elementType = HTMLElement\n\n // allows Typescript user to work around the lack of types\n // in GOVUKFrontend package, Typescript is not aware of $root\n // in components that extend GOVUKFrontendComponent\n /**\n * Returns the root element of the component\n *\n * @protected\n * @returns {RootElementType} - the root element of component\n */\n get $root() {\n return this._$root\n }\n\n /**\n * @protected\n * @type {RootElementType}\n */\n _$root\n\n /**\n * Constructs a new component, validating that GOV.UK Frontend is supported\n *\n * @internal\n * @param {Element | null} [$root] - HTML element to use for component\n */\n constructor($root) {\n const childConstructor = /** @type {ChildClassConstructor} */ (\n this.constructor\n )\n\n // TypeScript does not enforce that inheriting classes will define a `moduleName`\n // (even if we add a `@virtual` `static moduleName` property to this class).\n // While we trust users to do this correctly, we do a little check to provide them\n // a helpful error message.\n //\n // After this, we'll be sure that `childConstructor` has a `moduleName`\n // as expected of the `ChildClassConstructor` we've cast `this.constructor` to.\n if (typeof childConstructor.moduleName !== 'string') {\n throw new InitError(`\\`moduleName\\` not defined in component`)\n }\n\n if (!($root instanceof childConstructor.elementType)) {\n throw new ElementError({\n element: $root,\n component: childConstructor,\n identifier: 'Root element (`$root`)',\n expectedType: childConstructor.elementType.name\n })\n } else {\n this._$root = /** @type {RootElementType} */ ($root)\n }\n\n childConstructor.checkSupport()\n\n this.checkInitialised()\n\n const moduleName = childConstructor.moduleName\n\n this.$root.setAttribute(`data-${moduleName}-init`, '')\n }\n\n /**\n * Validates whether component is already initialised\n *\n * @private\n * @throws {InitError} when component is already initialised\n */\n checkInitialised() {\n const constructor = /** @type {ChildClassConstructor} */ (this.constructor)\n const moduleName = constructor.moduleName\n\n if (moduleName && isInitialised(this.$root, moduleName)) {\n throw new InitError(constructor)\n }\n }\n\n /**\n * Validates whether components are supported\n *\n * @throws {SupportError} when the components are not supported\n */\n static checkSupport() {\n if (!isSupported()) {\n throw new SupportError()\n }\n }\n}\n\n/**\n * @typedef ChildClass\n * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component\n */\n\n/**\n * @typedef {typeof Component & ChildClass} ChildClassConstructor\n */\n","import { Component } from '../component.mjs'\nimport { ConfigError } from '../errors/index.mjs'\n\nimport { isObject, isScope, formatErrorMessage } from './index.mjs'\n\nexport const configOverride = Symbol.for('configOverride')\n\n/**\n * Base Component class\n *\n * Centralises the behaviours shared by our components\n *\n * @virtual\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]\n * @template {Element & { dataset: DOMStringMap }} [RootElementType=HTMLElement]\n * @augments Component<RootElementType>\n */\nexport class ConfigurableComponent extends Component {\n /**\n * configOverride\n *\n * Function which defines configuration overrides to prioritize\n * properties from the root element's dataset.\n *\n * It should take a subset of configuration as input and return\n * a new configuration object with properties that should be\n * overridden based on the root element's dataset. A Symbol\n * is used for indexing to prevent conflicts.\n *\n * @internal\n * @virtual\n * @param {Partial<ConfigurationType>} [param] - Configuration object\n * @returns {Partial<ConfigurationType>} return - Configuration object\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n [configOverride](param) {\n return {}\n }\n\n /**\n * Returns the root element of the component\n *\n * @protected\n * @returns {ConfigurationType} - the root element of component\n */\n get config() {\n return this._config\n }\n\n /**\n *\n * @type {ConfigurationType}\n */\n _config\n\n /**\n * Constructs a new component, validating that GOV.UK Frontend is supported\n *\n * @internal\n * @param {Element | null} [$root] - HTML element to use for component\n * @param {ConfigurationType} [config] - HTML element to use for component\n */\n constructor($root, config) {\n super($root)\n\n const childConstructor =\n /** @type {ChildClassConstructor<ConfigurationType>} */ (this.constructor)\n\n if (!isObject(childConstructor.defaults)) {\n throw new ConfigError(\n formatErrorMessage(\n childConstructor,\n 'Config passed as parameter into constructor but no defaults defined'\n )\n )\n }\n\n const datasetConfig = /** @type {ConfigurationType} */ (\n normaliseDataset(childConstructor, this._$root.dataset)\n )\n\n this._config = /** @type {ConfigurationType} */ (\n mergeConfigs(\n childConstructor.defaults,\n config ?? {},\n this[configOverride](datasetConfig),\n datasetConfig\n )\n )\n }\n}\n\n/**\n * Normalise string\n *\n * 'If it looks like a duck, and it quacks like a duck…' 🦆\n *\n * If the passed value looks like a boolean or a number, convert it to a boolean\n * or number.\n *\n * Designed to be used to convert config passed via data attributes (which are\n * always strings) into something sensible.\n *\n * @internal\n * @param {DOMStringMap[string]} value - The value to normalise\n * @param {SchemaProperty} [property] - Component schema property\n * @returns {string | boolean | number | undefined} Normalised data\n */\nexport function normaliseString(value, property) {\n const trimmedValue = value ? value.trim() : ''\n\n let output\n let outputType = property?.type\n\n // No schema type set? Determine automatically\n if (!outputType) {\n if (['true', 'false'].includes(trimmedValue)) {\n outputType = 'boolean'\n }\n\n // Empty / whitespace-only strings are considered finite so we need to check\n // the length of the trimmed string as well\n if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {\n outputType = 'number'\n }\n }\n\n switch (outputType) {\n case 'boolean':\n output = trimmedValue === 'true'\n break\n\n case 'number':\n output = Number(trimmedValue)\n break\n\n default:\n output = value\n }\n\n return output\n}\n\n/**\n * Normalise dataset\n *\n * Loop over an object and normalise each value using {@link normaliseString},\n * optionally expanding nested `i18n.field`\n *\n * @internal\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType\n * @template {[keyof ConfigurationType, SchemaProperty | undefined][]} SchemaEntryType\n * @param {{ schema?: Schema<ConfigurationType>, moduleName: string }} Component - Component class\n * @param {DOMStringMap} dataset - HTML element dataset\n * @returns {ObjectNested} Normalised dataset\n */\nexport function normaliseDataset(Component, dataset) {\n if (!isObject(Component.schema)) {\n throw new ConfigError(\n formatErrorMessage(\n Component,\n 'Config passed as parameter into constructor but no schema defined'\n )\n )\n }\n\n const out = /** @type {ObjectNested} */ ({})\n const entries = /** @type {SchemaEntryType} */ (\n Object.entries(Component.schema.properties)\n )\n\n // Normalise top-level dataset ('data-*') values using schema types\n for (const entry of entries) {\n const [namespace, property] = entry\n\n // Cast the `namespace` to string so it can be used to access the dataset\n const field = namespace.toString()\n\n if (field in dataset) {\n out[field] = normaliseString(dataset[field], property)\n }\n\n /**\n * Extract and normalise nested object values automatically using\n * {@link normaliseString} but only schema object types are allowed\n */\n if (property?.type === 'object') {\n out[field] = extractConfigByNamespace(\n Component.schema,\n dataset,\n namespace\n )\n }\n }\n\n return out\n}\n\n/**\n * Normalise options passed to `initAll` or `createAll`\n *\n * @internal\n * @template {CompatibleClass} ComponentClass\n * @param {Config | CreateAllOptions<ComponentClass> | OnErrorCallback<ComponentClass> | Element | Document | null} [scopeOrOptions] - Scope of the document to search within, initialisation options or error callback function\n * @returns {CreateAllOptions<ComponentClass>} Normalised options\n */\nexport function normaliseOptions(scopeOrOptions) {\n let /** @type {Element | Document | null} */ $scope = document\n let /** @type {OnErrorCallback<ComponentClass> | undefined} */ onError\n\n // Handle options object\n if (isObject(scopeOrOptions)) {\n const options = scopeOrOptions\n\n // Scope must be valid or null\n if (isScope(options.scope) || options.scope === null) {\n $scope = options.scope\n }\n\n // Error handler must be a function\n if (typeof options.onError === 'function') {\n onError = options.onError\n }\n }\n\n if (isScope(scopeOrOptions)) {\n $scope = scopeOrOptions\n } else if (scopeOrOptions === null) {\n $scope = null\n } else if (typeof scopeOrOptions === 'function') {\n onError = scopeOrOptions\n }\n\n return {\n scope: $scope,\n onError\n }\n}\n\n/**\n * Config merging function\n *\n * Takes any number of objects and combines them together, with\n * greatest priority on the LAST item passed in.\n *\n * @internal\n * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge\n * @returns {{ [key: string]: unknown }} A merged config object\n */\nexport function mergeConfigs(...configObjects) {\n // Start with an empty object as our base\n /** @type {{ [key: string]: unknown }} */\n const formattedConfigObject = {}\n\n // Loop through each of the passed objects\n for (const configObject of configObjects) {\n for (const key of Object.keys(configObject)) {\n const option = formattedConfigObject[key]\n const override = configObject[key]\n\n // Push their keys one-by-one into formattedConfigObject. Any duplicate\n // keys with object values will be merged, otherwise the new value will\n // override the existing value.\n if (isObject(option) && isObject(override)) {\n formattedConfigObject[key] = mergeConfigs(option, override)\n } else {\n // Apply override\n formattedConfigObject[key] = override\n }\n }\n }\n\n return formattedConfigObject\n}\n\n/**\n * Validate component config by schema\n *\n * Follows limited examples in JSON schema for wider support in future\n *\n * {@link https://ajv.js.org/json-schema.html#compound-keywords}\n * {@link https://ajv.js.org/packages/ajv-errors.html#single-message}\n *\n * @internal\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType\n * @param {Schema<ConfigurationType>} schema - The schema of a component\n * @param {ConfigurationType} config - Component config\n * @returns {string[]} List of validation errors\n */\nexport function validateConfig(schema, config) {\n const validationErrors = []\n\n // Check errors for each schema\n for (const [name, conditions] of Object.entries(schema)) {\n const errors = []\n\n // Check errors for each schema condition\n if (Array.isArray(conditions)) {\n for (const { required, errorMessage } of conditions) {\n if (!required.every((key) => !!config[key])) {\n errors.push(errorMessage) // Missing config key value\n }\n }\n\n // Check one condition passes or add errors\n if (name === 'anyOf' && !(conditions.length - errors.length >= 1)) {\n validationErrors.push(...errors)\n }\n }\n }\n\n return validationErrors\n}\n\n/**\n * Extracts keys starting with a particular namespace from dataset ('data-*')\n * object, removing the namespace in the process, normalising all values\n *\n * @internal\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType\n * @param {Schema<ConfigurationType>} schema - The schema of a component\n * @param {DOMStringMap} dataset - The object to extract key-value pairs from\n * @param {keyof ConfigurationType} namespace - The namespace to filter keys with\n * @returns {ObjectNested | undefined} Nested object with dot-separated key namespace removed\n */\nexport function extractConfigByNamespace(schema, dataset, namespace) {\n const property = schema.properties[namespace]\n\n // Only extract configs for object schema properties\n if (property?.type !== 'object') {\n return\n }\n\n // Add default empty config\n const newObject = /** @type {Record<typeof namespace, ObjectNested>} */ ({\n [namespace]: {}\n })\n\n for (const [key, value] of Object.entries(dataset)) {\n /** @type {ObjectNested | ObjectNested[NestedKey]} */\n let current = newObject\n\n // Split the key into parts, using . as our namespace separator\n const keyParts = key.split('.')\n\n /**\n * Create new level per part\n *\n * e.g. 'i18n.textareaDescription.other' becomes\n * `{ i18n: { textareaDescription: { other } } }`\n */\n for (const [index, name] of keyParts.entries()) {\n if (isObject(current)) {\n // Drop down to nested object until the last part\n if (index < keyParts.length - 1) {\n // New nested object (optionally) replaces existing value\n if (!isObject(current[name])) {\n current[name] = {}\n }\n\n // Drop down into new or existing nested object\n current = current[name]\n } else if (key !== namespace) {\n // Normalised value (optionally) replaces existing value\n current[name] = normaliseString(value)\n }\n }\n }\n }\n\n return newObject[namespace]\n}\n\n/**\n * @internal\n * @typedef {keyof ObjectNested} NestedKey\n * @typedef {{ [key: string]: string | boolean | number | ObjectNested | undefined }} ObjectNested\n */\n\n/**\n * Schema for component config\n *\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType\n * @typedef {object} Schema\n * @property {Record<keyof ConfigurationType, SchemaProperty | undefined>} properties - Schema properties\n * @property {SchemaCondition<ConfigurationType>[]} [anyOf] - List of schema conditions\n */\n\n/**\n * Schema property for component config\n *\n * @typedef {object} SchemaProperty\n * @property {'string' | 'boolean' | 'number' | 'object'} type - Property type\n */\n\n/**\n * Schema condition for component config\n *\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType\n * @typedef {object} SchemaCondition\n * @property {(keyof ConfigurationType)[]} required - List of required config fields\n * @property {string} errorMessage - Error message when required config fields not provided\n */\n\n/**\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]\n * @typedef ChildClass\n * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component\n * @property {Schema<ConfigurationType>} [schema] - The schema of the component configuration\n * @property {ConfigurationType} [defaults] - The default values of the configuration of the component\n */\n\n/**\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]\n * @typedef {typeof Component & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>\n */\n\n/**\n * @import { CompatibleClass, Config, CreateAllOptions, OnErrorCallback } from '../init.mjs'\n */\n","import { normaliseOptions } from './common/configuration.mjs'\nimport { isObject, isSupported } from './common/index.mjs'\nimport { Accordion } from './components/accordion/accordion.mjs'\nimport { Button } from './components/button/button.mjs'\nimport { CharacterCount } from './components/character-count/character-count.mjs'\nimport { Checkboxes } from './components/checkboxes/checkboxes.mjs'\nimport { ErrorSummary } from './components/error-summary/error-summary.mjs'\nimport { ExitThisPage } from './components/exit-this-page/exit-this-page.mjs'\nimport { FileUpload } from './components/file-upload/file-upload.mjs'\nimport { Header } from './components/header/header.mjs'\nimport { NotificationBanner } from './components/notification-banner/notification-banner.mjs'\nimport { PasswordInput } from './components/password-input/password-input.mjs'\nimport { Radios } from './components/radios/radios.mjs'\nimport { ServiceNavigation } from './components/service-navigation/service-navigation.mjs'\nimport { SkipLink } from './components/skip-link/skip-link.mjs'\nimport { Tabs } from './components/tabs/tabs.mjs'\nimport { ElementError, SupportError } from './errors/index.mjs'\n\n/**\n * Initialise all components\n *\n * Use the `data-module` attributes to find, instantiate and init all of the\n * components provided as part of GOV.UK Frontend.\n *\n * @param {Config | Element | Document | null} [scopeOrConfig] - Scope of the document to search within or config for all components (with optional scope)\n */\nfunction initAll(scopeOrConfig = {}) {\n const config = isObject(scopeOrConfig) ? scopeOrConfig : {}\n\n // Extract initialisation options\n const options = normaliseOptions(scopeOrConfig)\n\n try {\n // Skip initialisation when GOV.UK Frontend is not supported\n if (!isSupported()) {\n throw new SupportError()\n }\n\n // Users can initialise GOV.UK Frontend in certain sections of the page\n // unless the scope is null (for example, query selector not found)\n if (options.scope === null) {\n throw new ElementError({\n element: options.scope,\n identifier: 'GOV.UK Frontend scope element (`$scope`)'\n })\n }\n } catch (error) {\n if (options.onError) {\n options.onError(error, {\n config\n })\n } else {\n console.log(error)\n }\n\n return\n }\n\n const components = /** @type {const} */ ([\n [Accordion, config.accordion],\n [Button, config.button],\n [CharacterCount, config.characterCount],\n [Checkboxes],\n [ErrorSummary, config.errorSummary],\n [ExitThisPage, config.exitThisPage],\n [FileUpload, config.fileUpload],\n [Header],\n [NotificationBanner, config.notificationBanner],\n [PasswordInput, config.passwordInput],\n [Radios],\n [ServiceNavigation],\n [SkipLink],\n [Tabs]\n ])\n\n components.forEach(([Component, componentConfig]) => {\n createAll(Component, componentConfig, options)\n })\n}\n\n/**\n * Create all instances of a specific component on the page\n *\n * Uses the `data-module` attribute to find all elements matching the specified\n * component on the page, creating instances of the component object for each\n * of them.\n *\n * Any component errors will be caught and logged to the console.\n *\n * @template {CompatibleClass} ComponentClass\n * @param {ComponentClass} Component - class of the component to create\n * @param {ComponentConfig<ComponentClass>} [config] - Config supplied to component\n * @param {OnErrorCallback<ComponentClass> | Element | Document | null | CreateAllOptions<ComponentClass>} [scopeOrOptions] - options for createAll including scope of the document to search within and callback function if error throw by component on init\n * @returns {Array<InstanceType<ComponentClass>>} - array of instantiated components\n */\nfunction createAll(Component, config, scopeOrOptions) {\n let /** @type {NodeListOf<Element> | undefined} */ $elements\n\n // Extract initialisation options\n const options = normaliseOptions(scopeOrOptions)\n\n try {\n // Skip initialisation when GOV.UK Frontend is not supported\n if (!isSupported()) {\n throw new SupportError()\n }\n\n // Users can initialise GOV.UK Frontend in certain sections of the page\n // unless the scope is null (for example, query selector not found)\n if (options.scope === null) {\n throw new ElementError({\n element: options.scope,\n component: Component,\n identifier: 'Scope element (`$scope`)'\n })\n }\n\n $elements = options.scope?.querySelectorAll(\n `[data-module=\"${Component.moduleName}\"]`\n )\n } catch (error) {\n if (options.onError) {\n options.onError(error, {\n component: Component,\n config\n })\n } else {\n console.log(error)\n }\n\n return []\n }\n\n /* eslint-disable-next-line @typescript-eslint/no-unsafe-return --\n * We can't define CompatibleClass as `{new(): CompatibleClass, moduleName: string}`,\n * as when doing `typeof Accordion` (or any component), TypeScript doesn't seem\n * to acknowledge the static `moduleName` that's set in our component classes.\n * This means we have to set the constructor of `CompatibleClass` as `{new(): any}`,\n * leading to ESLint frowning that we're returning `any[]`.\n */\n return Array.from($elements ?? [])\n .map(($element) => {\n try {\n // Only pass config to components that accept it\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return typeof config !== 'undefined'\n ? new Component($element, config)\n : new Component($element)\n } catch (error) {\n if (options.onError) {\n options.onError(error, {\n element: $element,\n component: Component,\n config\n })\n } else {\n console.log(error)\n }\n\n return null\n }\n })\n .filter(Boolean) // Exclude components that errored\n}\n\nexport { initAll, createAll }\n\n/* eslint-disable jsdoc/valid-types --\n * `{new(...args: any[] ): object}` is not recognised as valid\n * https://github.com/gajus/eslint-plugin-jsdoc/issues/145#issuecomment-1308722878\n * https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/131\n **/\n\n/**\n * @typedef {{new (...args: any[]): any, moduleName: string}} CompatibleClass\n */\n\n/* eslint-enable jsdoc/valid-types */\n\n/**\n * Config for all components via `initAll()`\n *\n * @typedef {object} Config\n * @property {Element | Document | null} [scope] - Scope of the document to search within\n * @property {OnErrorCallback<CompatibleClass>} [onError] - Initialisation error callback\n * @property {AccordionConfig} [accordion] - Accordion config\n * @property {ButtonConfig} [button] - Button config\n * @property {CharacterCountConfig} [characterCount] - Character Count config\n * @property {ErrorSummaryConfig} [errorSummary] - Error Summary config\n * @property {ExitThisPageConfig} [exitThisPage] - Exit This Page config\n * @property {FileUploadConfig} [fileUpload] - File Upload config\n * @property {NotificationBannerConfig} [notificationBanner] - Notification Banner config\n * @property {PasswordInputConfig} [passwordInput] - Password input config\n */\n\n/**\n * Config for individual components\n *\n * @import { AccordionConfig } from './components/accordion/accordion.mjs'\n * @import { ButtonConfig } from './components/button/button.mjs'\n * @import { CharacterCountConfig } from './components/character-count/character-count.mjs'\n * @import { ErrorSummaryConfig } from './components/error-summary/error-summary.mjs'\n * @import { ExitThisPageConfig } from './components/exit-this-page/exit-this-page.mjs'\n * @import { NotificationBannerConfig } from './components/notification-banner/notification-banner.mjs'\n * @import { PasswordInputConfig } from './components/password-input/password-input.mjs'\n * @import { FileUploadConfig } from './components/file-upload/file-upload.mjs'\n */\n\n/**\n * Component config keys, e.g. `accordion` and `characterCount`\n *\n * @typedef {keyof Omit<Config, 'scope' | 'onError'>} ConfigKey\n */\n\n/**\n * @template {CompatibleClass} ComponentClass\n * @typedef {ConstructorParameters<ComponentClass>[1]} ComponentConfig\n */\n\n/**\n * @template {CompatibleClass} ComponentClass\n * @typedef {object} ErrorContext\n * @property {Element} [element] - Element used for component module initialisation\n * @property {ComponentClass} [component] - Class of component\n * @property {Config | ComponentConfig<ComponentClass>} [config] - Config supplied to components\n */\n\n/**\n * @template {CompatibleClass} ComponentClass\n * @callback OnErrorCallback\n * @param {unknown} error - Thrown error\n * @param {ErrorContext<ComponentClass>} context - Object containing the element, component class and configuration\n */\n\n/**\n * @template {CompatibleClass} ComponentClass\n * @typedef {object} CreateAllOptions\n * @property {Element | Document | null} [scope] - scope of the document to search within\n * @property {OnErrorCallback<ComponentClass>} [onError] - callback function if error throw by component on init\n */\n","import { ConfigurableComponent } from '../../common/configuration.mjs'\n\nconst DEBOUNCE_TIMEOUT_IN_SECONDS = 1\n\n/**\n * JavaScript enhancements for the Button component\n *\n * @preserve\n * @augments ConfigurableComponent<ButtonConfig>\n */\nexport class Button extends ConfigurableComponent {\n /**\n * @private\n * @type {number | null}\n */\n debounceFormSubmitTimer = null\n\n /**\n * @param {Element | null} $root - HTML element to use for button\n * @param {ButtonConfig} [config] - Button config\n */\n constructor($root, config = {}) {\n super($root, config)\n\n this.$root.addEventListener('keydown', (event) => this.handleKeyDown(event))\n this.$root.addEventListener('click', (event) => this.debounce(event))\n }\n\n /**\n * Trigger a click event when the space key is pressed\n *\n * Some screen readers tell users they can use the space bar to activate\n * things with the 'button' role, so we need to match the functionality of\n * native HTML buttons.\n *\n * See https://github.com/alphagov/govuk_elements/pull/272#issuecomment-233028270\n *\n * @private\n * @param {KeyboardEvent} event - Keydown event\n */\n handleKeyDown(event) {\n const $target = event.target\n\n // Handle space bar only\n if (event.key !== ' ') {\n return\n }\n\n // Handle elements with [role=\"button\"] only\n if (\n $target instanceof HTMLElement &&\n $target.getAttribute('role') === 'button'\n ) {\n event.preventDefault() // prevent the page from scrolling\n $target.click()\n }\n }\n\n /**\n * Debounce double-clicks\n *\n * If the click quickly succeeds a previous click then nothing will happen.\n * This stops people accidentally causing multiple form submissions by double\n * clicking buttons.\n *\n * @private\n * @param {MouseEvent} event - Mouse click event\n * @returns {undefined | false} Returns undefined, or false when debounced\n */\n debounce(event) {\n // Check the button that was clicked has preventDoubleClick enabled\n if (!this.config.preventDoubleClick) {\n return\n }\n\n // If the timer is still running, prevent the click from submitting the form\n if (this.debounceFormSubmitTimer) {\n event.preventDefault()\n return false\n }\n\n this.debounceFormSubmitTimer = window.setTimeout(() => {\n this.debounceFormSubmitTimer = null\n }, DEBOUNCE_TIMEOUT_IN_SECONDS * 1000)\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-button'\n\n /**\n * Button default config\n *\n * @see {@link ButtonConfig}\n * @constant\n * @type {ButtonConfig}\n */\n static defaults = Object.freeze({\n preventDoubleClick: false\n })\n\n /**\n * Button config schema\n *\n * @constant\n * @satisfies {Schema<ButtonConfig>}\n */\n static schema = Object.freeze({\n properties: {\n preventDoubleClick: { type: 'boolean' }\n }\n })\n}\n\n/**\n * Button config\n *\n * @typedef {object} ButtonConfig\n * @property {boolean} [preventDoubleClick=false] - Prevent accidental double\n * clicks on submit buttons from submitting forms multiple times.\n */\n\n/**\n * @import { Schema } from '../../common/configuration.mjs'\n */\n","/**\n * Returns the value of the given attribute closest to the given element (including itself)\n *\n * @internal\n * @param {Element} $element - The element to start walking the DOM tree up\n * @param {string} attributeName - The name of the attribute\n * @returns {string | null} Attribute value\n */\nexport function closestAttributeValue($element, attributeName) {\n const $closestElementWithAttribute = $element.closest(`[${attributeName}]`)\n return $closestElementWithAttribute\n ? $closestElementWithAttribute.getAttribute(attributeName)\n : null\n}\n","import { isObject } from './common/index.mjs'\n\n/**\n * Internal support for selecting messages to render, with placeholder\n * interpolation and locale-aware number formatting and pluralisation\n *\n * @internal\n */\nexport class I18n {\n translations\n locale\n\n /**\n * @internal\n * @param {{ [key: string]: string | TranslationPluralForms }} translations - Key-value pairs of the translation strings to use.\n * @param {object} [config] - Configuration options for the function.\n * @param {string | null} [config.locale] - An overriding locale for the PluralRules functionality.\n */\n constructor(translations = {}, config = {}) {\n // Make list of translations available throughout function\n this.translations = translations\n\n // The locale to use for PluralRules and NumberFormat\n this.locale = config.locale ?? (document.documentElement.lang || 'en')\n }\n\n /**\n * The most used function - takes the key for a given piece of UI text and\n * returns the appropriate string.\n *\n * @internal\n * @param {string} lookupKey - The lookup key of the string to use.\n * @param {{ [key: string]: unknown }} [options] - Any options passed with the translation string, e.g: for string interpolation.\n * @returns {string} The appropriate translation string.\n * @throws {Error} Lookup key required\n * @throws {Error} Options required for `${}` placeholders\n */\n t(lookupKey, options) {\n if (!lookupKey) {\n // Print a console error if no lookup key has been provided\n throw new Error('i18n: lookup key missing')\n }\n\n // Fetch the translation for that lookup key\n let translation = this.translations[lookupKey]\n\n // If the `count` option is set, determine which plural suffix is needed and\n // change the lookupKey to match. We check to see if it's numeric instead of\n // falsy, as this could legitimately be 0.\n if (typeof options?.count === 'number' && isObject(translation)) {\n const translationPluralForm =\n translation[this.getPluralSuffix(lookupKey, options.count)]\n\n // Update translation with plural suffix\n if (translationPluralForm) {\n translation = translationPluralForm\n }\n }\n\n if (typeof translation === 'string') {\n // Check for ${} placeholders in the translation string\n // eslint-disable-next-line @typescript-eslint/prefer-regexp-exec\n if (translation.match(/%{(.\\S+)}/)) {\n if (!options) {\n throw new Error(\n 'i18n: cannot replace placeholders in string if no option data provided'\n )\n }\n\n return this.replacePlaceholders(translation, options)\n }\n\n return translation\n }\n\n // If the key wasn't found in our translations object,\n // return the lookup key itself as the fallback\n return lookupKey\n }\n\n /**\n * Takes a translation string with placeholders, and replaces the placeholders\n * with the provided data\n *\n * @internal\n * @param {string} translationString - The translation string\n * @param {{ [key: string]: unknown }} options - Any options passed with the translation string, e.g: for string interpolation.\n * @returns {string} The translation string to output, with $\\{\\} placeholders replaced\n */\n replacePlaceholders(translationString, options) {\n const formatter = Intl.NumberFormat.supportedLocalesOf(this.locale).length\n ? new Intl.NumberFormat(this.locale)\n : undefined\n\n return translationString.replace(\n /%{(.\\S+)}/g,\n\n /**\n * Replace translation string placeholders\n *\n * @internal\n * @param {string} placeholderWithBraces - Placeholder with braces\n * @param {string} placeholderKey - Placeholder key\n * @returns {string} Placeholder value\n */\n function (placeholderWithBraces, placeholderKey) {\n if (Object.prototype.hasOwnProperty.call(options, placeholderKey)) {\n const placeholderValue = options[placeholderKey]\n\n // If a user has passed `false` as the value for the placeholder\n // treat it as though the value should not be displayed\n if (\n placeholderValue === false ||\n (typeof placeholderValue !== 'number' &&\n typeof placeholderValue !== 'string')\n ) {\n return ''\n }\n\n // If the placeholder's value is a number, localise the number formatting\n if (typeof placeholderValue === 'number') {\n return formatter\n ? formatter.format(placeholderValue)\n : `${placeholderValue}`\n }\n\n return placeholderValue\n }\n\n throw new Error(\n `i18n: no data found to replace ${placeholderWithBraces} placeholder in string`\n )\n }\n )\n }\n\n /**\n * Check to see if the browser supports Intl.PluralRules\n *\n * It requires all conditions to be met in order to be supported:\n * - The implementation of Intl supports PluralRules (NOT true in Safari 10–12)\n * - The browser/OS has plural rules for the current locale (browser dependent)\n *\n * {@link https://browsersl.ist/#q=supports+es6-module+and+not+supports+intl-pluralrules}\n *\n * @internal\n * @returns {boolean} Returns true if all conditions are met. Returns false otherwise.\n */\n hasIntlPluralRulesSupport() {\n return Boolean(\n 'PluralRules' in window.Intl &&\n Intl.PluralRules.supportedLocalesOf(this.locale).length\n )\n }\n\n /**\n * Get the appropriate suffix for the plural form.\n *\n * Uses Intl.PluralRules (or our own fallback implementation) to get the\n * 'preferred' form to use for the given count.\n *\n * Checks that a translation has been provided for that plural form – if it\n * hasn't, it'll fall back to the 'other' plural form (unless that doesn't exist\n * either, in which case an error will be thrown)\n *\n * @internal\n * @param {string} lookupKey - The lookup key of the string to use.\n * @param {number} count - Number used to determine which pluralisation to use.\n * @returns {PluralRule} The suffix associated with the correct pluralisation for this locale.\n * @throws {Error} Plural form `.other` required when preferred plural form is missing\n */\n getPluralSuffix(lookupKey, count) {\n // Validate that the number is actually a number.\n //\n // Number(count) will turn anything that can't be converted to a Number type\n // into 'NaN'. isFinite filters out NaN, as it isn't a finite number.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion\n count = Number(count)\n if (!isFinite(count)) {\n return 'other'\n }\n\n // Fetch the translation for that lookup key\n const translation = this.translations[lookupKey]\n\n // Check to verify that all the requirements for Intl.PluralRules are met.\n // If so, we can use that instead of our custom implementation. Otherwise,\n // use the hardcoded fallback.\n const preferredForm = this.hasIntlPluralRulesSupport()\n ? new Intl.PluralRules(this.locale).select(count)\n : 'other'\n\n // Use the correct plural form if provided\n if (isObject(translation)) {\n if (preferredForm in translation) {\n return preferredForm\n // Fall back to `other` if the plural form is missing, but log a warning\n // to the console\n } else if ('other' in translation) {\n console.warn(\n `i18n: Missing plural form \".${preferredForm}\" for \"${this.locale}\" locale. Falling back to \".other\".`\n )\n\n return 'other'\n }\n }\n\n // If the required `other` plural form is missing, all we can do is error\n throw new Error(\n `i18n: Plural form \".other\" is required for \"${this.locale}\" locale`\n )\n }\n}\n\n/**\n * Plural rule category mnemonic tags\n *\n * @internal\n * @typedef {'zero' | 'one' | 'two' | 'few' | 'many' | 'other'} PluralRule\n */\n\n/**\n * Translated message by plural rule they correspond to.\n *\n * Allows to group pluralised messages under a single key when passing\n * translations to a component's constructor\n *\n * @internal\n * @typedef {object} TranslationPluralForms\n * @property {string} [other] - General plural form\n * @property {string} [zero] - Plural form used with 0\n * @property {string} [one] - Plural form used with 1\n * @property {string} [two] - Plural form used with 2\n * @property {string} [few] - Plural form used for a few\n * @property {string} [many] - Plural form used for many\n */\n","import { closestAttributeValue } from '../../common/closest-attribute-value.mjs'\nimport {\n validateConfig,\n ConfigurableComponent,\n configOverride\n} from '../../common/configuration.mjs'\nimport { formatErrorMessage } from '../../common/index.mjs'\nimport { ConfigError, ElementError } from '../../errors/index.mjs'\nimport { I18n } from '../../i18n.mjs'\n\n/**\n * Character count component\n *\n * Tracks the number of characters or words in the `.govuk-js-character-count`\n * `<textarea>` inside the element. Displays a message with the remaining number\n * of characters/words available, or the number of characters/words in excess.\n *\n * You can configure the message to only appear after a certain percentage\n * of the available characters/words has been entered.\n *\n * @preserve\n * @augments ConfigurableComponent<CharacterCountConfig>\n */\nexport class CharacterCount extends ConfigurableComponent {\n /** @private */\n $textarea\n\n /** @private */\n $visibleCountMessage\n\n /** @private */\n $screenReaderCountMessage\n\n /**\n * @private\n * @type {number | null}\n */\n lastInputTimestamp = null\n\n /** @private */\n lastInputValue = ''\n\n /**\n * @private\n * @type {number | null}\n */\n valueChecker = null\n\n /** @private */\n i18n\n\n /** @private */\n maxLength;\n\n /**\n * Character count config override\n *\n * To ensure data-attributes take complete precedence, even if they change\n * the type of count, we need to reset the `maxlength` and `maxwords` from\n * the JavaScript config.\n *\n * @internal\n * @param {CharacterCountConfig} datasetConfig - configuration specified by dataset\n * @returns {CharacterCountConfig} - configuration to override by dataset\n */\n [configOverride](datasetConfig) {\n let configOverrides = {}\n if ('maxwords' in datasetConfig || 'maxlength' in datasetConfig) {\n configOverrides = {\n maxlength: undefined,\n maxwords: undefined\n }\n }\n\n return configOverrides\n }\n\n /**\n * @param {Element | null} $root - HTML element to use for character count\n * @param {CharacterCountConfig} [config] - Character count config\n */\n constructor($root, config = {}) {\n super($root, config)\n\n const $textarea = this.$root.querySelector('.govuk-js-character-count')\n if (\n !(\n $textarea instanceof HTMLTextAreaElement ||\n $textarea instanceof HTMLInputElement\n )\n ) {\n throw new ElementError({\n component: CharacterCount,\n element: $textarea,\n expectedType: 'HTMLTextareaElement or HTMLInputElement',\n identifier: 'Form field (`.govuk-js-character-count`)'\n })\n }\n\n // Check for valid config\n const errors = validateConfig(CharacterCount.schema, this.config)\n if (errors[0]) {\n throw new ConfigError(formatErrorMessage(CharacterCount, errors[0]))\n }\n\n this.i18n = new I18n(this.config.i18n, {\n // Read the fallback if necessary rather than have it set in the defaults\n locale: closestAttributeValue(this.$root, 'lang')\n })\n\n // Determine the limit attribute (characters or words)\n this.maxLength = this.config.maxwords ?? this.config.maxlength ?? Infinity\n\n this.$textarea = $textarea\n\n const textareaDescriptionId = `${this.$textarea.id}-info`\n const $textareaDescription = document.getElementById(textareaDescriptionId)\n if (!$textareaDescription) {\n throw new ElementError({\n component: CharacterCount,\n element: $textareaDescription,\n identifier: `Count message (\\`id=\"${textareaDescriptionId}\"\\`)`\n })\n }\n\n // Pre-existing validation error rendered from server\n this.$errorMessage = this.$root.querySelector('.govuk-error-message')\n\n // Inject a description for the textarea if none is present already\n // for when the component was rendered with no maxlength, maxwords\n // nor custom textareaDescriptionText\n // eslint-disable-next-line @typescript-eslint/prefer-regexp-exec\n if ($textareaDescription.textContent.match(/^\\s*$/)) {\n $textareaDescription.textContent = this.i18n.t('textareaDescription', {\n count: this.maxLength\n })\n }\n\n // Move the textarea description to be immediately after the textarea\n // Kept for backwards compatibility\n this.$textarea.insertAdjacentElement('afterend', $textareaDescription)\n\n // Create the *screen reader* specific live-updating counter\n // This doesn't need any styling classes, as it is never visible\n const $screenReaderCountMessage = document.createElement('div')\n $screenReaderCountMessage.className =\n 'govuk-character-count__sr-status govuk-visually-hidden'\n $screenReaderCountMessage.setAttribute('aria-live', 'polite')\n this.$screenReaderCountMessage = $screenReaderCountMessage\n $textareaDescription.insertAdjacentElement(\n 'afterend',\n $screenReaderCountMessage\n )\n\n // Create our live-updating counter element, copying the classes from the\n // textarea description for backwards compatibility as these may have been\n // configured\n const $visibleCountMessage = document.createElement('div')\n $visibleCountMessage.className = $textareaDescription.className\n $visibleCountMessage.classList.add('govuk-character-count__status')\n $visibleCountMessage.setAttribute('aria-hidden', 'true')\n this.$visibleCountMessage = $visibleCountMessage\n $textareaDescription.insertAdjacentElement('afterend', $visibleCountMessage)\n\n // Hide the textarea description\n $textareaDescription.classList.add('govuk-visually-hidden')\n\n // Remove hard limit if set\n this.$textarea.removeAttribute('maxlength')\n\n this.bindChangeEvents()\n\n // When the page is restored after navigating 'back' in some browsers the\n // state of form controls is not restored until *after* the DOMContentLoaded\n // event is fired, so we need to sync after the pageshow event.\n window.addEventListener('pageshow', () => this.updateCountMessage())\n\n // Although we've set up handlers to sync state on the pageshow event, init\n // could be called after those events have fired, for example if they are\n // added to the page dynamically, so update now too.\n this.updateCountMessage()\n }\n\n /**\n * Bind change events\n *\n * Set up event listeners on the $textarea so that the count messages update\n * when the user types.\n *\n * @private\n */\n bindChangeEvents() {\n this.$textarea.addEventListener('keyup', () => this.handleKeyUp())\n\n // Bind focus/blur events to start/stop polling\n this.$textarea.addEventListener('focus', () => this.handleFocus())\n this.$textarea.addEventListener('blur', () => this.handleBlur())\n }\n\n /**\n * Handle key up event\n *\n * Update the visible character counter and keep track of when the last update\n * happened for each keypress\n *\n * @private\n */\n handleKeyUp() {\n this.updateVisibleCountMessage()\n this.lastInputTimestamp = Date.now()\n }\n\n /**\n * Handle focus event\n *\n * Speech recognition software such as Dragon NaturallySpeaking will modify\n * the fields by directly changing its `value`. These changes don't trigger\n * events in JavaScript, so we need to poll to handle when and if they occur.\n *\n * Once the keyup event hasn't been detected for at least 1000 ms (1s), check\n * if the textarea value has changed and update the count message if it has.\n *\n * This is so that the update triggered by the manual comparison doesn't\n * conflict with debounced KeyboardEvent updates.\n *\n * @private\n */\n handleFocus() {\n this.valueChecker = window.setInterval(() => {\n if (\n !this.lastInputTimestamp ||\n Date.now() - 500 >= this.lastInputTimestamp\n ) {\n this.updateIfValueChanged()\n }\n }, 1000)\n }\n\n /**\n * Handle blur event\n *\n * Stop checking the textarea value once the textarea no longer has focus\n *\n * @private\n */\n handleBlur() {\n // Cancel value checking on blur\n if (this.valueChecker) {\n window.clearInterval(this.valueChecker)\n }\n }\n\n /**\n * Update count message if textarea value has changed\n *\n * @private\n */\n updateIfValueChanged() {\n if (this.$textarea.value !== this.lastInputValue) {\n this.lastInputValue = this.$textarea.value\n this.updateCountMessage()\n }\n }\n\n /**\n * Update count message\n *\n * Helper function to update both the visible and screen reader-specific\n * counters simultaneously (e.g. on init)\n *\n * @private\n */\n updateCountMessage() {\n this.updateVisibleCountMessage()\n this.updateScreenReaderCountMessage()\n }\n\n /**\n * Update visible count message\n *\n * @private\n */\n updateVisibleCountMessage() {\n const remainingNumber = this.maxLength - this.count(this.$textarea.value)\n const isError = remainingNumber < 0\n\n // If input is over the threshold, remove the disabled class which renders\n // the counter invisible.\n this.$visibleCountMessage.classList.toggle(\n 'govuk-character-count__message--disabled',\n !this.isOverThreshold()\n )\n\n // Update styles\n if (!this.$errorMessage) {\n // Only toggle the textarea error class if there isn't an error message\n // already, as it may be unrelated to the limit (eg: allowed characters)\n // and would set the border colour back to black.\n this.$textarea.classList.toggle('govuk-textarea--error', isError)\n }\n this.$visibleCountMessage.classList.toggle('govuk-error-message', isError)\n this.$visibleCountMessage.classList.toggle('govuk-hint', !isError)\n\n // Update message\n this.$visibleCountMessage.textContent = this.getCountMessage()\n }\n\n /**\n * Update screen reader count message\n *\n * @private\n */\n updateScreenReaderCountMessage() {\n // If over the threshold, remove the aria-hidden attribute, allowing screen\n // readers to announce the content of the element.\n if (this.isOverThreshold()) {\n this.$screenReaderCountMessage.removeAttribute('aria-hidden')\n } else {\n this.$screenReaderCountMessage.setAttribute('aria-hidden', 'true')\n }\n\n // Update message\n this.$screenReaderCountMessage.textContent = this.getCountMessage()\n }\n\n /**\n * Count the number of characters (or words, if `config.maxwords` is set)\n * in the given text\n *\n * @private\n * @param {string} text - The text to count the characters of\n * @returns {number} the number of characters (or words) in the text\n */\n count(text) {\n if (this.config.maxwords) {\n const tokens = text.match(/\\S+/g) ?? [] // Matches consecutive non-whitespace chars\n return tokens.length\n }\n\n return text.length\n }\n\n /**\n * Get count message\n *\n * @private\n * @returns {string} Status message\n */\n getCountMessage() {\n const remainingNumber = this.maxLength - this.count(this.$textarea.value)\n const countType = this.config.maxwords ? 'words' : 'characters'\n return this.formatCountMessage(remainingNumber, countType)\n }\n\n /**\n * Formats the message shown to users according to what's counted\n * and how many remain\n *\n * @private\n * @param {number} remainingNumber - The number of words/characaters remaining\n * @param {string} countType - \"words\" or \"characters\"\n * @returns {string} Status message\n */\n formatCountMessage(remainingNumber, countType) {\n if (remainingNumber === 0) {\n return this.i18n.t(`${countType}AtLimit`)\n }\n\n const translationKeySuffix =\n remainingNumber < 0 ? 'OverLimit' : 'UnderLimit'\n\n return this.i18n.t(`${countType}${translationKeySuffix}`, {\n count: Math.abs(remainingNumber)\n })\n }\n\n /**\n * Check if count is over threshold\n *\n * Checks whether the value is over the configured threshold for the input.\n * If there is no configured threshold, it is set to 0 and this function will\n * always return true.\n *\n * @private\n * @returns {boolean} true if the current count is over the config.threshold\n * (or no threshold is set)\n */\n isOverThreshold() {\n // No threshold means we're always above threshold so save some computation\n if (!this.config.threshold) {\n return true\n }\n\n // Determine the remaining number of characters/words\n const currentLength = this.count(this.$textarea.value)\n const maxLength = this.maxLength\n\n const thresholdValue = (maxLength * this.config.threshold) / 100\n\n return thresholdValue <= currentLength\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-character-count'\n\n /**\n * Character count default config\n *\n * @see {@link CharacterCountConfig}\n * @constant\n * @type {CharacterCountConfig}\n */\n static defaults = Object.freeze({\n threshold: 0,\n i18n: {\n // Characters\n charactersUnderLimit: {\n one: 'You have %{count} character remaining',\n other: 'You have %{count} characters remaining'\n },\n charactersAtLimit: 'You have 0 characters remaining',\n charactersOverLimit: {\n one: 'You have %{count} character too many',\n other: 'You have %{count} characters too many'\n },\n // Words\n wordsUnderLimit: {\n one: 'You have %{count} word remaining',\n other: 'You have %{count} words remaining'\n },\n wordsAtLimit: 'You have 0 words remaining',\n wordsOverLimit: {\n one: 'You have %{count} word too many',\n other: 'You have %{count} words too many'\n },\n textareaDescription: {\n other: ''\n }\n }\n })\n\n /**\n * Character count config schema\n *\n * @constant\n * @satisfies {Schema<CharacterCountConfig>}\n */\n static schema = Object.freeze({\n properties: {\n i18n: { type: 'object' },\n maxwords: { type: 'number' },\n maxlength: { type: 'number' },\n threshold: { type: 'number' }\n },\n anyOf: [\n {\n required: ['maxwords'],\n errorMessage: 'Either \"maxlength\" or \"maxwords\" must be provided'\n },\n {\n required: ['maxlength'],\n errorMessage: 'Either \"maxlength\" or \"maxwords\" must be provided'\n }\n ]\n })\n}\n\n/**\n * Character count config\n *\n * @see {@link CharacterCount.defaults}\n * @typedef {object} CharacterCountConfig\n * @property {number} [maxlength] - The maximum number of characters.\n * If maxwords is provided, the maxlength option will be ignored.\n * @property {number} [maxwords] - The maximum number of words. If maxwords is\n * provided, the maxlength option will be ignored.\n * @property {number} [threshold=0] - The percentage value of the limit at\n * which point the count message is displayed. If this attribute is set, the\n * count message will be hidden by default.\n * @property {CharacterCountTranslations} [i18n=CharacterCount.defaults.i18n] - Character count translations\n */\n\n/**\n * Character count translations\n *\n * @see {@link CharacterCount.defaults.i18n}\n * @typedef {object} CharacterCountTranslations\n *\n * Messages shown to users as they type. It provides feedback on how many words\n * or characters they have remaining or if they are over the limit. This also\n * includes a message used as an accessible description for the textarea.\n * @property {TranslationPluralForms} [charactersUnderLimit] - Message displayed\n * when the number of characters is under the configured maximum, `maxlength`.\n * This message is displayed visually and through assistive technologies. The\n * component will replace the `%{count}` placeholder with the number of\n * remaining characters. This is a [pluralised list of\n * messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).\n * @property {string} [charactersAtLimit] - Message displayed when the number of\n * characters reaches the configured maximum, `maxlength`. This message is\n * displayed visually and through assistive technologies.\n * @property {TranslationPluralForms} [charactersOverLimit] - Message displayed\n * when the number of characters is over the configured maximum, `maxlength`.\n * This message is displayed visually and through assistive technologies. The\n * component will replace the `%{count}` placeholder with the number of\n * remaining characters. This is a [pluralised list of\n * messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).\n * @property {TranslationPluralForms} [wordsUnderLimit] - Message displayed when\n * the number of words is under the configured maximum, `maxlength`. This\n * message is displayed visually and through assistive technologies. The\n * component will replace the `%{count}` placeholder with the number of\n * remaining words. This is a [pluralised list of\n * messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).\n * @property {string} [wordsAtLimit] - Message displayed when the number of\n * words reaches the configured maximum, `maxlength`. This message is\n * displayed visually and through assistive technologies.\n * @property {TranslationPluralForms} [wordsOverLimit] - Message displayed when\n * the number of words is over the configured maximum, `maxlength`. This\n * message is displayed visually and through assistive technologies. The\n * component will replace the `%{count}` placeholder with the number of\n * remaining words. This is a [pluralised list of\n * messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).\n * @property {TranslationPluralForms} [textareaDescription] - Message made\n * available to assistive technologies, if none is already present in the\n * HTML, to describe that the component accepts only a limited amount of\n * content. It is visible on the page when JavaScript is unavailable. The\n * component will replace the `%{count}` placeholder with the value of the\n * `maxlength` or `maxwords` parameter.\n */\n\n/**\n * @import { Schema } from '../../common/configuration.mjs'\n * @import { TranslationPluralForms } from '../../i18n.mjs'\n */\n","import { Component } from '../../component.mjs'\nimport { ElementError } from '../../errors/index.mjs'\n\n/**\n * Checkboxes component\n *\n * @preserve\n */\nexport class Checkboxes extends Component {\n /** @private */\n $inputs\n\n /**\n * Checkboxes can be associated with a 'conditionally revealed' content block\n * – for example, a checkbox for 'Phone' could reveal an additional form field\n * for the user to enter their phone number.\n *\n * These associations are made using a `data-aria-controls` attribute, which\n * is promoted to an aria-controls attribute during initialisation.\n *\n * We also need to restore the state of any conditional reveals on the page\n * (for example if the user has navigated back), and set up event handlers to\n * keep the reveal in sync with the checkbox state.\n *\n * @param {Element | null} $root - HTML element to use for checkboxes\n */\n constructor($root) {\n super($root)\n\n const $inputs = this.$root.querySelectorAll('input[type=\"checkbox\"]')\n if (!$inputs.length) {\n throw new ElementError({\n component: Checkboxes,\n identifier: 'Form inputs (`<input type=\"checkbox\">`)'\n })\n }\n\n this.$inputs = $inputs\n\n this.$inputs.forEach(($input) => {\n const targetId = $input.getAttribute('data-aria-controls')\n\n // Skip radios without data-aria-controls attributes\n if (!targetId) {\n return\n }\n\n // Throw if target conditional element does not exist.\n if (!document.getElementById(targetId)) {\n throw new ElementError({\n component: Checkboxes,\n identifier: `Conditional reveal (\\`id=\"${targetId}\"\\`)`\n })\n }\n\n // Promote the data-aria-controls attribute to a aria-controls attribute\n // so that the relationship is exposed in the AOM\n $input.setAttribute('aria-controls', targetId)\n $input.removeAttribute('data-aria-controls')\n })\n\n // When the page is restored after navigating 'back' in some browsers the\n // state of form controls is not restored until *after* the DOMContentLoaded\n // event is fired, so we need to sync after the pageshow event.\n window.addEventListener('pageshow', () => this.syncAllConditionalReveals())\n\n // Although we've set up handlers to sync state on the pageshow event, init\n // could be called after those events have fired, for example if they are\n // added to the page dynamically, so sync now too.\n this.syncAllConditionalReveals()\n\n // Handle events\n this.$root.addEventListener('click', (event) => this.handleClick(event))\n }\n\n /**\n * Sync the conditional reveal states for all checkboxes in this component.\n *\n * @private\n */\n syncAllConditionalReveals() {\n this.$inputs.forEach(($input) =>\n this.syncConditionalRevealWithInputState($input)\n )\n }\n\n /**\n * Sync conditional reveal with the input state\n *\n * Synchronise the visibility of the conditional reveal, and its accessible\n * state, with the input's checked state.\n *\n * @private\n * @param {HTMLInputElement} $input - Checkbox input\n */\n syncConditionalRevealWithInputState($input) {\n const targetId = $input.getAttribute('aria-controls')\n if (!targetId) {\n return\n }\n\n const $target = document.getElementById(targetId)\n if ($target?.classList.contains('govuk-checkboxes__conditional')) {\n const inputIsChecked = $input.checked\n\n $input.setAttribute('aria-expanded', inputIsChecked.toString())\n $target.classList.toggle(\n 'govuk-checkboxes__conditional--hidden',\n !inputIsChecked\n )\n }\n }\n\n /**\n * Uncheck other checkboxes\n *\n * Find any other checkbox inputs with the same name value, and uncheck them.\n * This is useful for when a “None of these\" checkbox is checked.\n *\n * @private\n * @param {HTMLInputElement} $input - Checkbox input\n */\n unCheckAllInputsExcept($input) {\n const allInputsWithSameName = document.querySelectorAll(\n `input[type=\"checkbox\"][name=\"${$input.name}\"]`\n )\n\n allInputsWithSameName.forEach(($inputWithSameName) => {\n const hasSameFormOwner = $input.form === $inputWithSameName.form\n if (hasSameFormOwner && $inputWithSameName !== $input) {\n $inputWithSameName.checked = false\n this.syncConditionalRevealWithInputState($inputWithSameName)\n }\n })\n }\n\n /**\n * Uncheck exclusive checkboxes\n *\n * Find any checkbox inputs with the same name value and the 'exclusive'\n * behaviour, and uncheck them. This helps prevent someone checking both a\n * regular checkbox and a \"None of these\" checkbox in the same fieldset.\n *\n * @private\n * @param {HTMLInputElement} $input - Checkbox input\n */\n unCheckExclusiveInputs($input) {\n const allInputsWithSameNameAndExclusiveBehaviour =\n document.querySelectorAll(\n `input[data-behaviour=\"exclusive\"][type=\"checkbox\"][name=\"${$input.name}\"]`\n )\n\n allInputsWithSameNameAndExclusiveBehaviour.forEach(($exclusiveInput) => {\n const hasSameFormOwner = $input.form === $exclusiveInput.form\n if (hasSameFormOwner) {\n $exclusiveInput.checked = false\n this.syncConditionalRevealWithInputState($exclusiveInput)\n }\n })\n }\n\n /**\n * Click event handler\n *\n * Handle a click within the component root – if the click occurred on a checkbox,\n * sync the state of any associated conditional reveal with the checkbox\n * state.\n *\n * @private\n * @param {MouseEvent} event - Click event\n */\n handleClick(event) {\n const $clickedInput = event.target\n\n // Ignore clicks on things that aren't checkbox inputs\n if (\n !($clickedInput instanceof HTMLInputElement) ||\n $clickedInput.type !== 'checkbox'\n ) {\n return\n }\n\n // If the checkbox conditionally-reveals some content, sync the state\n const hasAriaControls = $clickedInput.getAttribute('aria-controls')\n if (hasAriaControls) {\n this.syncConditionalRevealWithInputState($clickedInput)\n }\n\n // No further behaviour needed for unchecking\n if (!$clickedInput.checked) {\n return\n }\n\n // Handle 'exclusive' checkbox behaviour (ie \"None of these\")\n const hasBehaviourExclusive =\n $clickedInput.getAttribute('data-behaviour') === 'exclusive'\n if (hasBehaviourExclusive) {\n this.unCheckAllInputsExcept($clickedInput)\n } else {\n this.unCheckExclusiveInputs($clickedInput)\n }\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-checkboxes'\n}\n","import { ConfigurableComponent } from '../../common/configuration.mjs'\nimport { setFocus } from '../../common/index.mjs'\n\n/**\n * Error summary component\n *\n * Takes focus on initialisation for accessible announcement, unless disabled in\n * configuration.\n *\n * @preserve\n * @augments ConfigurableComponent<ErrorSummaryConfig>\n */\nexport class ErrorSummary extends ConfigurableComponent {\n /**\n * @param {Element | null} $root - HTML element to use for error summary\n * @param {ErrorSummaryConfig} [config] - Error summary config\n */\n constructor($root, config = {}) {\n super($root, config)\n\n /**\n * Focus the error summary\n */\n if (!this.config.disableAutoFocus) {\n setFocus(this.$root)\n }\n\n this.$root.addEventListener('click', (event) => this.handleClick(event))\n }\n\n /**\n * Click event handler\n *\n * @private\n * @param {MouseEvent} event - Click event\n */\n handleClick(event) {\n const $target = event.target\n if ($target && this.focusTarget($target)) {\n event.preventDefault()\n }\n }\n\n /**\n * Focus the target element\n *\n * By default, the browser will scroll the target into view. Because our\n * labels or legends appear above the input, this means the user will be\n * presented with an input without any context, as the label or legend will be\n * off the top of the screen.\n *\n * Manually handling the click event, scrolling the question into view and\n * then focussing the element solves this.\n *\n * This also results in the label and/or legend being announced correctly in\n * NVDA (as tested in 2018.3.2) - without this only the field type is\n * announced (e.g. \"Edit, has autocomplete\").\n *\n * @private\n * @param {EventTarget} $target - Event target\n * @returns {boolean} True if the target was able to be focussed\n */\n focusTarget($target) {\n // If the element that was clicked was not a link, return early\n if (!($target instanceof HTMLAnchorElement)) {\n return false\n }\n\n const inputId = $target.hash.replace('#', '')\n if (!inputId) {\n return false\n }\n\n const $input = document.getElementById(inputId)\n if (!$input) {\n return false\n }\n\n const $legendOrLabel = this.getAssociatedLegendOrLabel($input)\n if (!$legendOrLabel) {\n return false\n }\n\n // Scroll the legend or label into view *before* calling focus on the input\n // to avoid extra scrolling in browsers that don't support `preventScroll`\n // (which at time of writing is most of them...)\n $legendOrLabel.scrollIntoView()\n $input.focus({ preventScroll: true })\n\n return true\n }\n\n /**\n * Get associated legend or label\n *\n * Returns the first element that exists from this list:\n *\n * - The `<legend>` associated with the closest `<fieldset>` ancestor, as long\n * as the top of it is no more than half a viewport height away from the\n * bottom of the input\n * - The first `<label>` that is associated with the input using for=\"inputId\"\n * - The closest parent `<label>`\n *\n * @private\n * @param {Element} $input - The input\n * @returns {Element | null} Associated legend or label, or null if no\n * associated legend or label can be found\n */\n getAssociatedLegendOrLabel($input) {\n const $fieldset = $input.closest('fieldset')\n\n if ($fieldset) {\n const $legends = $fieldset.getElementsByTagName('legend')\n\n if ($legends.length) {\n const $candidateLegend = $legends[0]\n\n // If the input type is radio or checkbox, always use the legend if\n // there is one.\n if (\n $input instanceof HTMLInputElement &&\n ($input.type === 'checkbox' || $input.type === 'radio')\n ) {\n return $candidateLegend\n }\n\n // For other input types, only scroll to the fieldset’s legend (instead\n // of the label associated with the input) if the input would end up in\n // the top half of the screen.\n //\n // This should avoid situations where the input either ends up off the\n // screen, or obscured by a software keyboard.\n const legendTop = $candidateLegend.getBoundingClientRect().top\n const inputRect = $input.getBoundingClientRect()\n\n // If the browser doesn't support Element.getBoundingClientRect().height\n // or window.innerHeight (like IE8), bail and just link to the label.\n if (inputRect.height && window.innerHeight) {\n const inputBottom = inputRect.top + inputRect.height\n\n if (inputBottom - legendTop < window.innerHeight / 2) {\n return $candidateLegend\n }\n }\n }\n }\n\n return (\n document.querySelector(`label[for='${$input.getAttribute('id')}']`) ??\n $input.closest('label')\n )\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-error-summary'\n\n /**\n * Error summary default config\n *\n * @see {@link ErrorSummaryConfig}\n * @constant\n * @type {ErrorSummaryConfig}\n */\n static defaults = Object.freeze({\n disableAutoFocus: false\n })\n\n /**\n * Error summary config schema\n *\n * @constant\n * @satisfies {Schema<ErrorSummaryConfig>}\n */\n static schema = Object.freeze({\n properties: {\n disableAutoFocus: { type: 'boolean' }\n }\n })\n}\n\n/**\n * Error summary config\n *\n * @typedef {object} ErrorSummaryConfig\n * @property {boolean} [disableAutoFocus=false] - If set to `true` the error\n * summary will not be focussed when the page loads.\n */\n\n/**\n * @import { Schema } from '../../common/configuration.mjs'\n */\n","import { getBreakpoint } from '../../common/index.mjs'\nimport { Component } from '../../component.mjs'\nimport { ElementError } from '../../errors/index.mjs'\n\n/**\n * Header component\n *\n * @preserve\n */\nexport class Header extends Component {\n /** @private */\n $menuButton\n\n /** @private */\n $menu\n\n /**\n * Save the opened/closed state for the nav in memory so that we can\n * accurately maintain state when the screen is changed from small to big and\n * back to small\n *\n * @private\n */\n menuIsOpen = false\n\n /**\n * A global const for storing a matchMedia instance which we'll use to detect\n * when a screen size change happens. We rely on it being null if the feature\n * isn't available to initially apply hidden attributes\n *\n * @private\n * @type {MediaQueryList | null}\n */\n mql = null\n\n /**\n * Apply a matchMedia for desktop which will trigger a state sync if the\n * browser viewport moves between states.\n *\n * @param {Element | null} $root - HTML element to use for header\n */\n constructor($root) {\n super($root)\n\n const $menuButton = this.$root.querySelector('.govuk-js-header-toggle')\n\n // Headers don't necessarily have a navigation. When they don't, the menu\n // toggle won't be rendered by our macro (or may be omitted when writing\n // plain HTML)\n if (!$menuButton) {\n return this\n }\n\n // Pad the header logo so it doesn't overlap the menu button\n this.$root.classList.add('govuk-header--with-js-navigation')\n\n const menuId = $menuButton.getAttribute('aria-controls')\n if (!menuId) {\n throw new ElementError({\n component: Header,\n identifier:\n 'Navigation button (`<button class=\"govuk-js-header-toggle\">`) attribute (`aria-controls`)'\n })\n }\n\n const $menu = document.getElementById(menuId)\n if (!$menu) {\n throw new ElementError({\n component: Header,\n element: $menu,\n identifier: `Navigation (\\`<ul id=\"${menuId}\">\\`)`\n })\n }\n\n this.$menu = $menu\n this.$menuButton = $menuButton\n\n this.setupResponsiveChecks()\n\n this.$menuButton.addEventListener('click', () =>\n this.handleMenuButtonClick()\n )\n }\n\n /**\n * Setup viewport resize check\n *\n * @private\n */\n setupResponsiveChecks() {\n const breakpoint = getBreakpoint('desktop')\n\n if (!breakpoint.value) {\n throw new ElementError({\n component: Header,\n identifier: `CSS custom property (\\`${breakpoint.property}\\`) on pseudo-class \\`:root\\``\n })\n }\n\n // Media query list for GOV.UK Frontend desktop breakpoint\n this.mql = window.matchMedia(`(min-width: ${breakpoint.value})`)\n\n // MediaQueryList.addEventListener isn't supported by Safari < 14 so we need\n // to be able to fall back to the deprecated MediaQueryList.addListener\n if ('addEventListener' in this.mql) {\n this.mql.addEventListener('change', () => this.checkMode())\n } else {\n // @ts-expect-error Property 'addListener' does not exist\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n this.mql.addListener(() => this.checkMode())\n }\n\n this.checkMode()\n }\n\n /**\n * Sync menu state\n *\n * Uses the global variable menuIsOpen to correctly set the accessible and\n * visual states of the menu and the menu button.\n * Additionally will force the menu to be visible and the menu button to be\n * hidden if the matchMedia is triggered to desktop.\n *\n * @private\n */\n checkMode() {\n if (!this.mql || !this.$menu || !this.$menuButton) {\n return\n }\n\n if (this.mql.matches) {\n this.$menu.removeAttribute('hidden')\n this.$menuButton.setAttribute('hidden', '')\n } else {\n this.$menuButton.removeAttribute('hidden')\n this.$menuButton.setAttribute('aria-expanded', this.menuIsOpen.toString())\n\n if (this.menuIsOpen) {\n this.$menu.removeAttribute('hidden')\n } else {\n this.$menu.setAttribute('hidden', '')\n }\n }\n }\n\n /**\n * Handle menu button click\n *\n * When the menu button is clicked, change the visibility of the menu and then\n * sync the accessibility state and menu button state\n *\n * @private\n */\n handleMenuButtonClick() {\n this.menuIsOpen = !this.menuIsOpen\n this.checkMode()\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-header'\n}\n","import { ConfigurableComponent } from '../../common/configuration.mjs'\nimport { setFocus } from '../../common/index.mjs'\n\n/**\n * Notification Banner component\n *\n * @preserve\n * @augments ConfigurableComponent<NotificationBannerConfig>\n */\nexport class NotificationBanner extends ConfigurableComponent {\n /**\n * @param {Element | null} $root - HTML element to use for notification banner\n * @param {NotificationBannerConfig} [config] - Notification banner config\n */\n constructor($root, config = {}) {\n super($root, config)\n\n /**\n * Focus the notification banner\n *\n * If `role=\"alert\"` is set, focus the element to help some assistive\n * technologies prioritise announcing it.\n *\n * You can turn off the auto-focus functionality by setting\n * `data-disable-auto-focus=\"true\"` in the component HTML. You might wish to\n * do this based on user research findings, or to avoid a clash with another\n * element which should be focused when the page loads.\n */\n if (\n this.$root.getAttribute('role') === 'alert' &&\n !this.config.disableAutoFocus\n ) {\n setFocus(this.$root)\n }\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-notification-banner'\n\n /**\n * Notification banner default config\n *\n * @see {@link NotificationBannerConfig}\n * @constant\n * @type {NotificationBannerConfig}\n */\n static defaults = Object.freeze({\n disableAutoFocus: false\n })\n\n /**\n * Notification banner config schema\n *\n * @constant\n * @satisfies {Schema<NotificationBannerConfig>}\n */\n static schema = Object.freeze({\n properties: {\n disableAutoFocus: { type: 'boolean' }\n }\n })\n}\n\n/**\n * Notification banner config\n *\n * @typedef {object} NotificationBannerConfig\n * @property {boolean} [disableAutoFocus=false] - If set to `true` the\n * notification banner will not be focussed when the page loads. This only\n * applies if the component has a `role` of `alert` – in other cases the\n * component will not be focused on page load, regardless of this option.\n */\n\n/**\n * @import { Schema } from '../../common/configuration.mjs'\n */\n","import { Component } from '../../component.mjs'\nimport { ElementError } from '../../errors/index.mjs'\n\n/**\n * Radios component\n *\n * @preserve\n */\nexport class Radios extends Component {\n /** @private */\n $inputs\n\n /**\n * Radios can be associated with a 'conditionally revealed' content block –\n * for example, a radio for 'Phone' could reveal an additional form field for\n * the user to enter their phone number.\n *\n * These associations are made using a `data-aria-controls` attribute, which\n * is promoted to an aria-controls attribute during initialisation.\n *\n * We also need to restore the state of any conditional reveals on the page\n * (for example if the user has navigated back), and set up event handlers to\n * keep the reveal in sync with the radio state.\n *\n * @param {Element | null} $root - HTML element to use for radios\n */\n constructor($root) {\n super($root)\n\n const $inputs = this.$root.querySelectorAll('input[type=\"radio\"]')\n if (!$inputs.length) {\n throw new ElementError({\n component: Radios,\n identifier: 'Form inputs (`<input type=\"radio\">`)'\n })\n }\n\n this.$inputs = $inputs\n\n this.$inputs.forEach(($input) => {\n const targetId = $input.getAttribute('data-aria-controls')\n\n // Skip radios without data-aria-controls attributes\n if (!targetId) {\n return\n }\n\n // Throw if target conditional element does not exist.\n if (!document.getElementById(targetId)) {\n throw new ElementError({\n component: Radios,\n identifier: `Conditional reveal (\\`id=\"${targetId}\"\\`)`\n })\n }\n\n // Promote the data-aria-controls attribute to a aria-controls attribute\n // so that the relationship is exposed in the AOM\n $input.setAttribute('aria-controls', targetId)\n $input.removeAttribute('data-aria-controls')\n })\n\n // When the page is restored after navigating 'back' in some browsers the\n // state of form controls is not restored until *after* the DOMContentLoaded\n // event is fired, so we need to sync after the pageshow event.\n window.addEventListener('pageshow', () => this.syncAllConditionalReveals())\n\n // Although we've set up handlers to sync state on the pageshow event, init\n // could be called after those events have fired, for example if they are\n // added to the page dynamically, so sync now too.\n this.syncAllConditionalReveals()\n\n // Handle events\n this.$root.addEventListener('click', (event) => this.handleClick(event))\n }\n\n /**\n * Sync the conditional reveal states for all radio buttons in this component.\n *\n * @private\n */\n syncAllConditionalReveals() {\n this.$inputs.forEach(($input) =>\n this.syncConditionalRevealWithInputState($input)\n )\n }\n\n /**\n * Sync conditional reveal with the input state\n *\n * Synchronise the visibility of the conditional reveal, and its accessible\n * state, with the input's checked state.\n *\n * @private\n * @param {HTMLInputElement} $input - Radio input\n */\n syncConditionalRevealWithInputState($input) {\n const targetId = $input.getAttribute('aria-controls')\n if (!targetId) {\n return\n }\n\n const $target = document.getElementById(targetId)\n if ($target?.classList.contains('govuk-radios__conditional')) {\n const inputIsChecked = $input.checked\n\n $input.setAttribute('aria-expanded', inputIsChecked.toString())\n $target.classList.toggle(\n 'govuk-radios__conditional--hidden',\n !inputIsChecked\n )\n }\n }\n\n /**\n * Click event handler\n *\n * Handle a click within the component root – if the click occurred on a radio, sync\n * the state of the conditional reveal for all radio buttons in the same form\n * with the same name (because checking one radio could have un-checked a\n * radio under the root of another Radio component)\n *\n * @private\n * @param {MouseEvent} event - Click event\n */\n handleClick(event) {\n const $clickedInput = event.target\n\n // Ignore clicks on things that aren't radio buttons\n if (\n !($clickedInput instanceof HTMLInputElement) ||\n $clickedInput.type !== 'radio'\n ) {\n return\n }\n\n // We only need to consider radios with conditional reveals, which will have\n // aria-controls attributes.\n const $allInputs = document.querySelectorAll(\n 'input[type=\"radio\"][aria-controls]'\n )\n\n const $clickedInputForm = $clickedInput.form\n const $clickedInputName = $clickedInput.name\n\n $allInputs.forEach(($input) => {\n const hasSameFormOwner = $input.form === $clickedInputForm\n const hasSameName = $input.name === $clickedInputName\n\n if (hasSameName && hasSameFormOwner) {\n this.syncConditionalRevealWithInputState($input)\n }\n })\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-radios'\n}\n","import { setFocus } from '../../common/index.mjs'\nimport { Component } from '../../component.mjs'\nimport { ElementError } from '../../errors/index.mjs'\n\n/**\n * Skip link component\n *\n * @preserve\n * @augments Component<HTMLAnchorElement>\n */\nexport class SkipLink extends Component {\n static elementType = HTMLAnchorElement\n\n /**\n * @param {Element | null} $root - HTML element to use for skip link\n * @throws {ElementError} when $root is not set or the wrong type\n * @throws {ElementError} when $root.hash does not contain a hash\n * @throws {ElementError} when the linked element is missing or the wrong type\n */\n constructor($root) {\n super($root)\n\n const hash = this.$root.hash\n const href = this.$root.getAttribute('href') ?? ''\n\n // Return early for external URLs or links to other pages\n if (\n this.$root.origin !== window.location.origin ||\n this.$root.pathname !== window.location.pathname\n ) {\n return\n }\n\n const linkedElementId = hash.replace('#', '')\n\n // Check link path matching current page\n if (!linkedElementId) {\n throw new ElementError(\n `Skip link: Target link (\\`href=\"${href}\"\\`) has no hash fragment`\n )\n }\n\n const $linkedElement = document.getElementById(linkedElementId)\n\n // Check for link target element\n if (!$linkedElement) {\n throw new ElementError({\n component: SkipLink,\n element: $linkedElement,\n identifier: `Target content (\\`id=\"${linkedElementId}\"\\`)`\n })\n }\n\n /**\n * Focus the linked element on click\n *\n * Adds a helper CSS class to hide native focus styles,\n * but removes it on blur to restore native focus styles\n */\n this.$root.addEventListener('click', () =>\n setFocus($linkedElement, {\n onBeforeFocus() {\n $linkedElement.classList.add('govuk-skip-link-focused-element')\n },\n onBlur() {\n $linkedElement.classList.remove('govuk-skip-link-focused-element')\n }\n })\n )\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-skip-link'\n}\n","import { initAllAutocomplete as initAllAutocompleteImp } from '~/src/client/javascripts/autocomplete.js'\nimport { initFileUpload as initFileUploadImp } from '~/src/client/javascripts/file-upload.js'\nimport { initAllGovuk as initAllGovukImp } from '~/src/client/javascripts/govuk.js'\nimport { initPreviewCloseLink as initPreviewCloseLinkImp } from '~/src/client/javascripts/preview-close-link.js'\nexport { initMaps } from '~/src/client/javascripts/location-map.js'\n\nexport const initAllGovuk = initAllGovukImp\nexport const initAllAutocomplete = initAllAutocompleteImp\nexport const initFileUpload = initFileUploadImp\nexport const initPreviewCloseLink = initPreviewCloseLinkImp\n\n/**\n * Initialise all clientside components (but not maps as this will be an opt-in for now given the additional UMD assets that are required)\n */\nexport function initAll() {\n initAllGovuk()\n initAllAutocomplete()\n initFileUpload()\n initPreviewCloseLink()\n}\n","import {\n Button,\n CharacterCount,\n Checkboxes,\n ErrorSummary,\n Header,\n NotificationBanner,\n Radios,\n SkipLink,\n createAll\n} from 'govuk-frontend'\n\nexport function initAllGovuk() {\n createAll(Button)\n createAll(CharacterCount)\n createAll(Checkboxes)\n createAll(ErrorSummary)\n createAll(Header)\n createAll(NotificationBanner)\n createAll(Radios)\n createAll(SkipLink)\n}\n","/**\n * Initialise autocomplete\n * @param {HTMLSelectElement | null} $select\n * @param {(config: object) => void} init\n */\nfunction initAutocomplete($select, init) {\n if (!$select) {\n return\n }\n\n const config = {\n id: $select.id,\n selectElement: $select\n }\n\n init(config)\n\n /** @type {HTMLInputElement | null} */\n const $input = document.querySelector(`#${config.id}`)\n\n // Allowed values for input\n const inputValues = [...$select.options].map((option) => option.text)\n\n // Reset select when input value is not allowed\n $input?.addEventListener('blur', () => {\n if (!$input.value || !inputValues.includes($input.value)) {\n $select.value = ''\n }\n })\n}\n\nexport function initAllAutocomplete() {\n // Find all autocompletes\n const $autocompletes = document.querySelectorAll(\n `[data-module=\"govuk-accessible-autocomplete\"]`\n )\n\n // Lazy load autocomplete component\n if ($autocompletes.length) {\n // @ts-expect-error -- No types available\n import('accessible-autocomplete')\n .then((component) => {\n const { default: accessibleAutocomplete } = component\n\n // Initialise each autocomplete\n $autocompletes.forEach(($module) =>\n initAutocomplete(\n $module.querySelector('select'),\n accessibleAutocomplete.enhanceSelectElement\n )\n )\n })\n\n // eslint-disable-next-line no-console\n .catch(console.error)\n }\n}\n","export function initPreviewCloseLink() {\n // Show preview close link via `rel=\"opener\"`\n if (window.opener) {\n const $closeLink = document.querySelector('.js-preview-banner-close')\n\n $closeLink?.removeAttribute('hidden')\n $closeLink?.addEventListener('click', (event) => {\n event.preventDefault()\n window.close()\n })\n }\n}\n"],"names":["leafPrototypes","getProto","inProgress","dmsSeparator","Dms","separator","char","parse","dms","isNaN","parseFloat","isFinite","Number","dmsParts","String","trim","replace","split","length","splice","NaN","deg","test","toDms","format","dp","undefined","Infinity","Math","abs","d","m","s","toFixed","floor","slice","toLat","lat","wrap90","toLon","lon","wrap180","toBrng","brng","wrap360","fromLocale","str","locale","toLocaleString","thousands","decimal","toLocale","compassPoint","bearing","precision","includes","RangeError","n","round","degrees","x","p","prototype","toRadians","this","PI","toDegrees","Vector3d","constructor","y","z","TypeError","sqrt","plus","v","minus","times","dividedBy","dot","cross","negate","unit","norm","angleTo","sign","atan2","rotateAround","axis","angle","a","sin","c","cos","t","r","rp","toString","ellipsoids","WGS84","b","f","datums","ellipsoid","Object","freeze","LatLonEllipsoidal","height","_lat","_lon","_height","latitude","lng","longitude","datum","_datum","args","ll","type","Array","isArray","coordinates","JSON","stringify","toCartesian","referenceFrame","h","eSq","Cartesian","equals","point","EPSILON","epoch","dpHeight","super","toLatLon","e2","Airy1830","AiryModified","Bessel1841","Clarke1866","Clarke1880IGN","GRS80","Intl1924","WGS72","ED50","transform","ETRS89","Irl1975","NAD27","NAD83","NTF","OSGB36","Potsdam","TokyoJapan","keys","forEach","e","LatLonEllipsoidal_Datum","pop","convertDatum","toDatum","cartesian","Cartesian_Datum","deprecatedDatum","console","info","latLon","oldCartesian","map","newCartesian","applyTransform","x1","y1","z1","tx","ty","tz","rx","ry","rz","nationalGrid","trueOrigin","falseOrigin","easting","northing","scaleFactor","OsGridRef","E","N","E0","N0","F0","n2","n3","M","pow","tan","dE","dE2","dE3","dE4","dE5","LatLon_OsGridRef","gridref","match","Error","l1","toUpperCase","charCodeAt","l2","e100km","n100km","en","padEnd","digits","useGrouping","minimumIntegerDigits","maximumFractionDigits","letterPair","fromCharCode","padStart","toOsGrid","message","osgbED","eastingNorthingToLatLong","latLong","long","osGridRefToLatLong","osGridRef","defaultConfig","zoom","center","COMPANY_SYMBOL_CODE","LOCATION_FIELD_SELECTOR","EVENTS","interactMarkerChange","defaultData","VTS_OUTDOOR_URL","VTS_DARK_URL","VTS_BLACK_AND_WHITE_URL","initMaps","config","assetPath","apiPath","data","locations","document","querySelectorAll","form","querySelector","buttons","from","addEventListener","submitter","HTMLButtonElement","preventDefault","formSubmitFactory","location","index","_getInitMapConfig","HTMLDivElement","locationInputs","locationType","dataset","locationtype","mapContainer","createElement","mapId","setAttribute","initConfig","locationField","latInput","longInput","getLatLongInputs","result","validateLatLong","value","valid","getInitMapCenterConfig","getInitLatLongMapConfig","eastingInput","northingInput","getEastingNorthingInputs","validateEastingNorthing","latlong","getInitEastingNorthingMapConfig","validateOsGridRef","getOsGridRefInput","getInitOsGridRefMapConfig","getInitMapConfig","after","interactPlugin","mapsConfig","logoAltText","defra","window","dataLayers","markerColor","outdoor","dark","interactionMode","multiSelect","InteractiveMap","_extends","mapProvider","maplibreProvider","reverseGeocodeProvider","openNamesProvider","url","behaviour","minZoom","maxZoom","containerHeight","enableZoomControls","transformRequest","makeTileRequestTransformer","plugins","mapStylesPlugin","mapStyles","id","label","thumbnail","logo","attribution","fromCodePoint","Date","getFullYear","backgroundColor","mapColorScheme","appColorScheme","searchPlugin","osNamesURL","width","showMarker","scaleBarPlugin","units","createMap","on","onUpdateInputs","centerMap","coords","bindLatLongField","LatLon","latLongToEastingNorthing","bindEastingNorthingField","osGridRefInput","latLongToOsGridRef","bindOsGridRefField","addPanel","showLabel","mobile","slot","initiallyOpen","dismissable","modal","tablet","desktop","html","enable","processLocation","resourceType","startsWith","URL","searchParams","has","encodeURIComponent","headers","path","substring","spritesPath","strLat","strLong","strEasting","strNorthing","exec","inputs","input","markers","addMarker","flyTo","essential","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","exports","module","__webpack_modules__","getPrototypeOf","obj","mode","__esModule","then","ns","create","def","current","indexOf","getOwnPropertyNames","key","definition","o","defineProperty","enumerable","get","chunkId","Promise","all","reduce","promises","u","prop","hasOwnProperty","call","l","done","push","script","needAttach","scripts","getElementsByTagName","i","getAttribute","charset","nc","src","onScriptComplete","prev","event","onerror","onload","clearTimeout","timeout","doneFns","parentNode","removeChild","fn","setTimeout","bind","target","head","appendChild","Symbol","toStringTag","scriptUrl","installedChunks","j","installedChunkData","promise","resolve","reject","error","errorType","realSrc","name","request","webpackJsonpCallback","parentChunkLoadingFunction","chunkIds","moreModules","runtime","some","chunkLoadingGlobal","self","ARIA_DESCRIBEDBY","ERROR_SUMMARY_TITLE_ID","showError","errorSummary","fileInput","getElementById","removeAttribute","innerHTML","formGroup","closest","classList","add","inputId","errorMessage","className","insertBefore","reloadPage","history","replaceState","href","pathname","initUpload","_Array$from$find","uploadButton","continueButton","find","button","textContent","formElement","selectedFile","isSubmitting","uploadId","files","statusText","_selectedFile$name3","container","uploadForm","HTMLFormElement","fileCountP","statusAnnouncer","_fileCountP$parentNod","nextSibling","addStatusAnnouncerToDOM","_unused","_unused2","body","createOrUpdateStatusAnnouncer","summaryList","buttonGroup","findOrCreateSummaryList","existingRow","remove","row","_selectedFile$name","_selectedFile$name2","createFileRow","firstChild","renderSummary","focus","disabled","handleStandardFormSubmission","_formElement$dataset$","action","formData","FormData","isLocalDev","proxyUrl","uploadUrl","fetchOptions","method","redirect","fetch","attempts","interval","setInterval","clearInterval","uploadStatusUrl","segments","filter","Boolean","join","buildUploadStatusUrl","Accept","response","ok","json","uploadStatus","catch","pollUploadStatus","handleAjaxFormSubmission","setFocus","$element","options","_options$onBeforeFocu","isFocusable","onBlur","_options$onBlur","once","onBeforeFocus","isSupported","$scope","contains","isObject","option","isScope","Element","Document","formatErrorMessage","Component","moduleName","GOVUKFrontendError","SupportError","supportMessage","HTMLScriptElement","ConfigError","ElementError","messageOrOptions","component","identifier","element","expectedType","InitError","componentOrMessage","$root","_$root","childConstructor","elementType","checkSupport","checkInitialised","HTMLElement","hasAttribute","isInitialised","configOverride","for","ConfigurableComponent","param","_config","defaults","datasetConfig","schema","out","entries","properties","entry","namespace","property","field","normaliseString","extractConfigByNamespace","normaliseDataset","mergeConfigs","trimmedValue","output","outputType","configObjects","formattedConfigObject","configObject","override","newObject","keyParts","createAll","scopeOrOptions","$elements","onError","scope","normaliseOptions","_options$scope","log","Button","debounceFormSubmitTimer","handleKeyDown","debounce","$target","click","preventDoubleClick","DEBOUNCE_TIMEOUT_IN_SECONDS","closestAttributeValue","attributeName","$closestElementWithAttribute","I18n","translations","_config$locale","documentElement","lang","lookupKey","translation","count","translationPluralForm","getPluralSuffix","replacePlaceholders","translationString","formatter","Intl","NumberFormat","supportedLocalesOf","placeholderWithBraces","placeholderKey","placeholderValue","hasIntlPluralRulesSupport","PluralRules","preferredForm","select","warn","CharacterCount","configOverrides","maxlength","maxwords","_ref","_this$config$maxwords","$textarea","$visibleCountMessage","$screenReaderCountMessage","lastInputTimestamp","lastInputValue","valueChecker","i18n","maxLength","HTMLTextAreaElement","HTMLInputElement","errors","validationErrors","conditions","required","every","validateConfig","textareaDescriptionId","$textareaDescription","$errorMessage","insertAdjacentElement","bindChangeEvents","updateCountMessage","handleKeyUp","handleFocus","handleBlur","updateVisibleCountMessage","now","updateIfValueChanged","updateScreenReaderCountMessage","isError","toggle","isOverThreshold","getCountMessage","text","_text$match","remainingNumber","countType","formatCountMessage","translationKeySuffix","threshold","currentLength","charactersUnderLimit","one","other","charactersAtLimit","charactersOverLimit","wordsUnderLimit","wordsAtLimit","wordsOverLimit","textareaDescription","anyOf","Checkboxes","$inputs","$input","targetId","syncAllConditionalReveals","handleClick","syncConditionalRevealWithInputState","inputIsChecked","checked","unCheckAllInputsExcept","$inputWithSameName","unCheckExclusiveInputs","$exclusiveInput","$clickedInput","ErrorSummary","disableAutoFocus","focusTarget","HTMLAnchorElement","hash","$legendOrLabel","getAssociatedLegendOrLabel","scrollIntoView","preventScroll","_document$querySelect","$fieldset","$legends","$candidateLegend","legendTop","getBoundingClientRect","top","inputRect","innerHeight","Header","$menuButton","$menu","menuIsOpen","mql","menuId","setupResponsiveChecks","handleMenuButtonClick","breakpoint","getComputedStyle","getPropertyValue","getBreakpoint","matchMedia","checkMode","addListener","matches","NotificationBanner","Radios","$allInputs","$clickedInputForm","$clickedInputName","hasSameFormOwner","SkipLink","_this$$root$getAttrib","origin","linkedElementId","$linkedElement","initAllGovuk","initAllAutocomplete","$autocompletes","default","accessibleAutocomplete","$module","$select","init","selectElement","inputValues","initAutocomplete","enhanceSelectElement","initFileUpload","readyState","initPreviewCloseLink","opener","$closeLink","close","initAll"],"sourceRoot":""}
1
+ {"version":3,"file":"javascripts/shared.min.js","mappings":"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","sources":["webpack:///webpack/runtime/create fake namespace object","webpack:///webpack/runtime/load script","webpack:///../../node_modules/geodesy/dms.js","webpack:///../../node_modules/geodesy/vector3d.js","webpack:///../../node_modules/geodesy/latlon-ellipsoidal.js","webpack:///../../node_modules/geodesy/latlon-ellipsoidal-datum.js","webpack:///../../node_modules/geodesy/osgridref.js","webpack:///./javascripts/location-map.js","webpack:///webpack/bootstrap","webpack:///webpack/runtime/define property getters","webpack:///webpack/runtime/ensure chunk","webpack:///webpack/runtime/get javascript chunk filename","webpack:///webpack/runtime/hasOwnProperty shorthand","webpack:///webpack/runtime/make namespace object","webpack:///webpack/runtime/publicPath","webpack:///webpack/runtime/jsonp chunk loading","webpack:///./javascripts/file-upload.js","webpack:///../../node_modules/govuk-frontend/src/govuk/common/index.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/errors/index.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/component.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/common/configuration.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/init.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/button/button.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/common/closest-attribute-value.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/i18n.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/character-count/character-count.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/checkboxes/checkboxes.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/error-summary/error-summary.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/header/header.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/notification-banner/notification-banner.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/radios/radios.mjs","webpack:///../../node_modules/govuk-frontend/src/govuk/components/skip-link/skip-link.mjs","webpack:///./javascripts/shared.js","webpack:///./javascripts/govuk.js","webpack:///./javascripts/autocomplete.js","webpack:///./javascripts/preview-close-link.js"],"sourcesContent":["var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__);\nvar leafPrototypes;\n// create a fake namespace object\n// mode & 1: value is a module id, require it\n// mode & 2: merge all properties of value into the ns\n// mode & 4: return value when already ns object\n// mode & 16: return value when it's Promise-like\n// mode & 8|1: behave like require\n__webpack_require__.t = function(value, mode) {\n\tif(mode & 1) value = this(value);\n\tif(mode & 8) return value;\n\tif(typeof value === 'object' && value) {\n\t\tif((mode & 4) && value.__esModule) return value;\n\t\tif((mode & 16) && typeof value.then === 'function') return value;\n\t}\n\tvar ns = Object.create(null);\n\t__webpack_require__.r(ns);\n\tvar def = {};\n\tleafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)];\n\tfor(var current = mode & 2 && value; (typeof current == 'object' || typeof current == 'function') && !~leafPrototypes.indexOf(current); current = getProto(current)) {\n\t\tObject.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key])));\n\t}\n\tdef['default'] = () => (value);\n\t__webpack_require__.d(ns, def);\n\treturn ns;\n};","var inProgress = {};\n// data-webpack is not used as build has no uniqueName\n// loadScript function to load a script via script tag\n__webpack_require__.l = (url, done, key, chunkId) => {\n\tif(inProgress[url]) { inProgress[url].push(done); return; }\n\tvar script, needAttach;\n\tif(key !== undefined) {\n\t\tvar scripts = document.getElementsByTagName(\"script\");\n\t\tfor(var i = 0; i < scripts.length; i++) {\n\t\t\tvar s = scripts[i];\n\t\t\tif(s.getAttribute(\"src\") == url) { script = s; break; }\n\t\t}\n\t}\n\tif(!script) {\n\t\tneedAttach = true;\n\t\tscript = document.createElement('script');\n\t\tscript.type = \"module\";\n\t\tscript.charset = 'utf-8';\n\t\tif (__webpack_require__.nc) {\n\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n\t\t}\n\n\n\t\tscript.src = url;\n\t}\n\tinProgress[url] = [done];\n\tvar onScriptComplete = (prev, event) => {\n\t\t// avoid mem leaks in IE.\n\t\tscript.onerror = script.onload = null;\n\t\tclearTimeout(timeout);\n\t\tvar doneFns = inProgress[url];\n\t\tdelete inProgress[url];\n\t\tscript.parentNode && script.parentNode.removeChild(script);\n\t\tdoneFns && doneFns.forEach((fn) => (fn(event)));\n\t\tif(prev) return prev(event);\n\t}\n\tvar timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000);\n\tscript.onerror = onScriptComplete.bind(null, script.onerror);\n\tscript.onload = onScriptComplete.bind(null, script.onload);\n\tneedAttach && document.head.appendChild(script);\n};","/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n/* Geodesy representation conversion functions (c) Chris Veness 2002-2020 */\n/* MIT Licence */\n/* www.movable-type.co.uk/scripts/latlong.html */\n/* www.movable-type.co.uk/scripts/js/geodesy/geodesy-library.html#dms */\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n/* eslint no-irregular-whitespace: [2, { skipComments: true }] */\n\n\n/**\n * Latitude/longitude points may be represented as decimal degrees, or subdivided into sexagesimal\n * minutes and seconds. This module provides methods for parsing and representing degrees / minutes\n * / seconds.\n *\n * @module dms\n */\n\n\n/* Degree-minutes-seconds (& cardinal directions) separator character */\nlet dmsSeparator = '\\u202f'; // U+202F = 'narrow no-break space'\n\n\n/**\n * Functions for parsing and representing degrees / minutes / seconds.\n */\nclass Dms {\n\n // note Unicode Degree = U+00B0. Prime = U+2032, Double prime = U+2033\n\n /**\n * Separator character to be used to separate degrees, minutes, seconds, and cardinal directions.\n *\n * Default separator is U+202F ‘narrow no-break space’.\n *\n * To change this (e.g. to empty string or full space), set Dms.separator prior to invoking\n * formatting.\n *\n * @example\n * import LatLon, { Dms } from '/js/geodesy/latlon-spherical.js';\n * const p = new LatLon(51.2, 0.33).toString('dms'); // 51° 12′ 00″ N, 000° 19′ 48″ E\n * Dms.separator = ''; // no separator\n * const pʹ = new LatLon(51.2, 0.33).toString('dms'); // 51°12′00″N, 000°19′48″E\n */\n static get separator() { return dmsSeparator; }\n static set separator(char) { dmsSeparator = char; }\n\n\n /**\n * Parses string representing degrees/minutes/seconds into numeric degrees.\n *\n * This is very flexible on formats, allowing signed decimal degrees, or deg-min-sec optionally\n * suffixed by compass direction (NSEW); a variety of separators are accepted. Examples -3.62,\n * '3 37 12W', '3°37′12″W'.\n *\n * Thousands/decimal separators must be comma/dot; use Dms.fromLocale to convert locale-specific\n * thousands/decimal separators.\n *\n * @param {string|number} dms - Degrees or deg/min/sec in variety of formats.\n * @returns {number} Degrees as decimal number.\n *\n * @example\n * const lat = Dms.parse('51° 28′ 40.37″ N');\n * const lon = Dms.parse('000° 00′ 05.29″ W');\n * const p1 = new LatLon(lat, lon); // 51.4779°N, 000.0015°W\n */\n static parse(dms) {\n // check for signed decimal degrees without NSEW, if so return it directly\n if (!isNaN(parseFloat(dms)) && isFinite(dms)) return Number(dms);\n\n // strip off any sign or compass dir'n & split out separate d/m/s\n const dmsParts = String(dms).trim().replace(/^-/, '').replace(/[NSEW]$/i, '').split(/[^0-9.,]+/);\n if (dmsParts[dmsParts.length-1]=='') dmsParts.splice(dmsParts.length-1); // from trailing symbol\n\n if (dmsParts == '') return NaN;\n\n // and convert to decimal degrees...\n let deg = null;\n switch (dmsParts.length) {\n case 3: // interpret 3-part result as d/m/s\n deg = dmsParts[0]/1 + dmsParts[1]/60 + dmsParts[2]/3600;\n break;\n case 2: // interpret 2-part result as d/m\n deg = dmsParts[0]/1 + dmsParts[1]/60;\n break;\n case 1: // just d (possibly decimal) or non-separated dddmmss\n deg = dmsParts[0];\n // check for fixed-width unseparated format eg 0033709W\n //if (/[NS]/i.test(dmsParts)) deg = '0' + deg; // - normalise N/S to 3-digit degrees\n //if (/[0-9]{7}/.test(deg)) deg = deg.slice(0,3)/1 + deg.slice(3,5)/60 + deg.slice(5)/3600;\n break;\n default:\n return NaN;\n }\n if (/^-|[WS]$/i.test(dms.trim())) deg = -deg; // take '-', west and south as -ve\n\n return Number(deg);\n }\n\n\n /**\n * Converts decimal degrees to deg/min/sec format\n * - degree, prime, double-prime symbols are added, but sign is discarded, though no compass\n * direction is added.\n * - degrees are zero-padded to 3 digits; for degrees latitude, use .slice(1) to remove leading\n * zero.\n *\n * @private\n * @param {number} deg - Degrees to be formatted as specified.\n * @param {string} [format=d] - Return value as 'd', 'dm', 'dms' for deg, deg+min, deg+min+sec.\n * @param {number} [dp=4|2|0] - Number of decimal places to use – default 4 for d, 2 for dm, 0 for dms.\n * @returns {string} Degrees formatted as deg/min/secs according to specified format.\n */\n static toDms(deg, format='d', dp=undefined) {\n if (isNaN(deg)) return null; // give up here if we can't make a number from deg\n if (typeof deg == 'string' && deg.trim() == '') return null;\n if (typeof deg == 'boolean') return null;\n if (deg == Infinity) return null;\n if (deg == null) return null;\n\n // default values\n if (dp === undefined) {\n switch (format) {\n case 'd': case 'deg': dp = 4; break;\n case 'dm': case 'deg+min': dp = 2; break;\n case 'dms': case 'deg+min+sec': dp = 0; break;\n default: format = 'd'; dp = 4; break; // be forgiving on invalid format\n }\n }\n\n deg = Math.abs(deg); // (unsigned result ready for appending compass dir'n)\n\n let dms = null, d = null, m = null, s = null;\n switch (format) {\n default: // invalid format spec!\n case 'd': case 'deg':\n d = deg.toFixed(dp); // round/right-pad degrees\n if (d<100) d = '0' + d; // left-pad with leading zeros (note may include decimals)\n if (d<10) d = '0' + d;\n dms = d + '°';\n break;\n case 'dm': case 'deg+min':\n d = Math.floor(deg); // get component deg\n m = ((deg*60) % 60).toFixed(dp); // get component min & round/right-pad\n if (m == 60) { m = (0).toFixed(dp); d++; } // check for rounding up\n d = ('000'+d).slice(-3); // left-pad with leading zeros\n if (m<10) m = '0' + m; // left-pad with leading zeros (note may include decimals)\n dms = d + '°'+Dms.separator + m + '′';\n break;\n case 'dms': case 'deg+min+sec':\n d = Math.floor(deg); // get component deg\n m = Math.floor((deg*3600)/60) % 60; // get component min\n s = (deg*3600 % 60).toFixed(dp); // get component sec & round/right-pad\n if (s == 60) { s = (0).toFixed(dp); m++; } // check for rounding up\n if (m == 60) { m = 0; d++; } // check for rounding up\n d = ('000'+d).slice(-3); // left-pad with leading zeros\n m = ('00'+m).slice(-2); // left-pad with leading zeros\n if (s<10) s = '0' + s; // left-pad with leading zeros (note may include decimals)\n dms = d + '°'+Dms.separator + m + '′'+Dms.separator + s + '″';\n break;\n }\n\n return dms;\n }\n\n\n /**\n * Converts numeric degrees to deg/min/sec latitude (2-digit degrees, suffixed with N/S).\n *\n * @param {number} deg - Degrees to be formatted as specified.\n * @param {string} [format=d] - Return value as 'd', 'dm', 'dms' for deg, deg+min, deg+min+sec.\n * @param {number} [dp=4|2|0] - Number of decimal places to use – default 4 for d, 2 for dm, 0 for dms.\n * @returns {string} Degrees formatted as deg/min/secs according to specified format.\n *\n * @example\n * const lat = Dms.toLat(-3.62, 'dms'); // 3°37′12″S\n */\n static toLat(deg, format, dp) {\n const lat = Dms.toDms(Dms.wrap90(deg), format, dp);\n return lat===null ? '–' : lat.slice(1) + Dms.separator + (deg<0 ? 'S' : 'N'); // knock off initial '0' for lat!\n }\n\n\n /**\n * Convert numeric degrees to deg/min/sec longitude (3-digit degrees, suffixed with E/W).\n *\n * @param {number} deg - Degrees to be formatted as specified.\n * @param {string} [format=d] - Return value as 'd', 'dm', 'dms' for deg, deg+min, deg+min+sec.\n * @param {number} [dp=4|2|0] - Number of decimal places to use – default 4 for d, 2 for dm, 0 for dms.\n * @returns {string} Degrees formatted as deg/min/secs according to specified format.\n *\n * @example\n * const lon = Dms.toLon(-3.62, 'dms'); // 3°37′12″W\n */\n static toLon(deg, format, dp) {\n const lon = Dms.toDms(Dms.wrap180(deg), format, dp);\n return lon===null ? '–' : lon + Dms.separator + (deg<0 ? 'W' : 'E');\n }\n\n\n /**\n * Converts numeric degrees to deg/min/sec as a bearing (0°..360°).\n *\n * @param {number} deg - Degrees to be formatted as specified.\n * @param {string} [format=d] - Return value as 'd', 'dm', 'dms' for deg, deg+min, deg+min+sec.\n * @param {number} [dp=4|2|0] - Number of decimal places to use – default 4 for d, 2 for dm, 0 for dms.\n * @returns {string} Degrees formatted as deg/min/secs according to specified format.\n *\n * @example\n * const lon = Dms.toBrng(-3.62, 'dms'); // 356°22′48″\n */\n static toBrng(deg, format, dp) {\n const brng = Dms.toDms(Dms.wrap360(deg), format, dp);\n return brng===null ? '–' : brng.replace('360', '0'); // just in case rounding took us up to 360°!\n }\n\n\n /**\n * Converts DMS string from locale thousands/decimal separators to JavaScript comma/dot separators\n * for subsequent parsing.\n *\n * Both thousands and decimal separators must be followed by a numeric character, to facilitate\n * parsing of single lat/long string (in which whitespace must be left after the comma separator).\n *\n * @param {string} str - Degrees/minutes/seconds formatted with locale separators.\n * @returns {string} Degrees/minutes/seconds formatted with standard Javascript separators.\n *\n * @example\n * const lat = Dms.fromLocale('51°28′40,12″N'); // '51°28′40.12″N' in France\n * const p = new LatLon(Dms.fromLocale('51°28′40,37″N, 000°00′05,29″W'); // '51.4779°N, 000.0015°W' in France\n */\n static fromLocale(str) {\n const locale = (123456.789).toLocaleString();\n const separator = { thousands: locale.slice(3, 4), decimal: locale.slice(7, 8) };\n return str.replace(separator.thousands, '⁜').replace(separator.decimal, '.').replace('⁜', ',');\n }\n\n\n /**\n * Converts DMS string from JavaScript comma/dot thousands/decimal separators to locale separators.\n *\n * Can also be used to format standard numbers such as distances.\n *\n * @param {string} str - Degrees/minutes/seconds formatted with standard Javascript separators.\n * @returns {string} Degrees/minutes/seconds formatted with locale separators.\n *\n * @example\n * const Dms.toLocale('123,456.789'); // '123.456,789' in France\n * const Dms.toLocale('51°28′40.12″N, 000°00′05.31″W'); // '51°28′40,12″N, 000°00′05,31″W' in France\n */\n static toLocale(str) {\n const locale = (123456.789).toLocaleString();\n const separator = { thousands: locale.slice(3, 4), decimal: locale.slice(7, 8) };\n return str.replace(/,([0-9])/, '⁜$1').replace('.', separator.decimal).replace('⁜', separator.thousands);\n }\n\n\n /**\n * Returns compass point (to given precision) for supplied bearing.\n *\n * @param {number} bearing - Bearing in degrees from north.\n * @param {number} [precision=3] - Precision (1:cardinal / 2:intercardinal / 3:secondary-intercardinal).\n * @returns {string} Compass point for supplied bearing.\n *\n * @example\n * const point = Dms.compassPoint(24); // point = 'NNE'\n * const point = Dms.compassPoint(24, 1); // point = 'N'\n */\n static compassPoint(bearing, precision=3) {\n if (![ 1, 2, 3 ].includes(Number(precision))) throw new RangeError(`invalid precision ‘${precision}’`);\n // note precision could be extended to 4 for quarter-winds (eg NbNW), but I think they are little used\n\n bearing = Dms.wrap360(bearing); // normalise to range 0..360°\n\n const cardinals = [\n 'N', 'NNE', 'NE', 'ENE',\n 'E', 'ESE', 'SE', 'SSE',\n 'S', 'SSW', 'SW', 'WSW',\n 'W', 'WNW', 'NW', 'NNW' ];\n const n = 4 * 2**(precision-1); // no of compass points at req’d precision (1=>4, 2=>8, 3=>16)\n const cardinal = cardinals[Math.round(bearing*n/360)%n * 16/n];\n\n return cardinal;\n }\n\n\n /**\n * Constrain degrees to range -90..+90 (for latitude); e.g. -91 => -89, 91 => 89.\n *\n * @private\n * @param {number} degrees\n * @returns degrees within range -90..+90.\n */\n static wrap90(degrees) {\n if (-90<=degrees && degrees<=90) return degrees; // avoid rounding due to arithmetic ops if within range\n\n // latitude wrapping requires a triangle wave function; a general triangle wave is\n // f(x) = 4a/p ⋅ | (x-p/4)%p - p/2 | - a\n // where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator\n // not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n'\n const x = degrees, a = 90, p = 360;\n return 4*a/p * Math.abs((((x-p/4)%p)+p)%p - p/2) - a;\n }\n\n /**\n * Constrain degrees to range -180..+180 (for longitude); e.g. -181 => 179, 181 => -179.\n *\n * @private\n * @param {number} degrees\n * @returns degrees within range -180..+180.\n */\n static wrap180(degrees) {\n if (-180<=degrees && degrees<=180) return degrees; // avoid rounding due to arithmetic ops if within range\n\n // longitude wrapping requires a sawtooth wave function; a general sawtooth wave is\n // f(x) = (2ax/p - p/2) % p - a\n // where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator\n // not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n'\n const x = degrees, a = 180, p = 360;\n return (((2*a*x/p - p/2)%p)+p)%p - a;\n }\n\n /**\n * Constrain degrees to range 0..360 (for bearings); e.g. -1 => 359, 361 => 1.\n *\n * @private\n * @param {number} degrees\n * @returns degrees within range 0..360.\n */\n static wrap360(degrees) {\n if (0<=degrees && degrees<360) return degrees; // avoid rounding due to arithmetic ops if within range\n\n // bearing wrapping requires a sawtooth wave function with a vertical offset equal to the\n // amplitude and a corresponding phase shift; this changes the general sawtooth wave function from\n // f(x) = (2ax/p - p/2) % p - a\n // to\n // f(x) = (2ax/p) % p\n // where a = amplitude, p = period, % = modulo; however, JavaScript '%' is a remainder operator\n // not a modulo operator - for modulo, replace 'x%n' with '((x%n)+n)%n'\n const x = degrees, a = 180, p = 360;\n return (((2*a*x/p)%p)+p)%p;\n }\n\n}\n\n\n// Extend Number object with methods to convert between degrees & radians\nNumber.prototype.toRadians = function() { return this * Math.PI / 180; };\nNumber.prototype.toDegrees = function() { return this * 180 / Math.PI; };\n\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nexport default Dms;\n","/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n/* Vector handling functions (c) Chris Veness 2011-2019 */\n/* MIT Licence */\n/* www.movable-type.co.uk/scripts/geodesy-library.html#vector3d */\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * Library of 3-d vector manipulation routines.\n *\n * @module vector3d\n */\n\n\n/* Vector3d - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * Functions for manipulating generic 3-d vectors.\n *\n * Functions return vectors as return results, so that operations can be chained.\n *\n * @example\n * const v = v1.cross(v2).dot(v3) // ≡ v1×v2⋅v3\n */\nclass Vector3d {\n\n /**\n * Creates a 3-d vector.\n *\n * @param {number} x - X component of vector.\n * @param {number} y - Y component of vector.\n * @param {number} z - Z component of vector.\n *\n * @example\n * import Vector3d from '/js/geodesy/vector3d.js';\n * const v = new Vector3d(0.267, 0.535, 0.802);\n */\n constructor(x, y, z) {\n if (isNaN(x) || isNaN(y) || isNaN(z)) throw new TypeError(`invalid vector [${x},${y},${z}]`);\n\n this.x = Number(x);\n this.y = Number(y);\n this.z = Number(z);\n }\n\n\n /**\n * Length (magnitude or norm) of ‘this’ vector.\n *\n * @returns {number} Magnitude of this vector.\n */\n get length() {\n return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);\n }\n\n\n /**\n * Adds supplied vector to ‘this’ vector.\n *\n * @param {Vector3d} v - Vector to be added to this vector.\n * @returns {Vector3d} Vector representing sum of this and v.\n */\n plus(v) {\n if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');\n\n return new Vector3d(this.x + v.x, this.y + v.y, this.z + v.z);\n }\n\n\n /**\n * Subtracts supplied vector from ‘this’ vector.\n *\n * @param {Vector3d} v - Vector to be subtracted from this vector.\n * @returns {Vector3d} Vector representing difference between this and v.\n */\n minus(v) {\n if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');\n\n return new Vector3d(this.x - v.x, this.y - v.y, this.z - v.z);\n }\n\n\n /**\n * Multiplies ‘this’ vector by a scalar value.\n *\n * @param {number} x - Factor to multiply this vector by.\n * @returns {Vector3d} Vector scaled by x.\n */\n times(x) {\n if (isNaN(x)) throw new TypeError(`invalid scalar value ‘${x}’`);\n\n return new Vector3d(this.x * x, this.y * x, this.z * x);\n }\n\n\n /**\n * Divides ‘this’ vector by a scalar value.\n *\n * @param {number} x - Factor to divide this vector by.\n * @returns {Vector3d} Vector divided by x.\n */\n dividedBy(x) {\n if (isNaN(x)) throw new TypeError(`invalid scalar value ‘${x}’`);\n\n return new Vector3d(this.x / x, this.y / x, this.z / x);\n }\n\n\n /**\n * Multiplies ‘this’ vector by the supplied vector using dot (scalar) product.\n *\n * @param {Vector3d} v - Vector to be dotted with this vector.\n * @returns {number} Dot product of ‘this’ and v.\n */\n dot(v) {\n if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');\n\n return this.x * v.x + this.y * v.y + this.z * v.z;\n }\n\n\n /**\n * Multiplies ‘this’ vector by the supplied vector using cross (vector) product.\n *\n * @param {Vector3d} v - Vector to be crossed with this vector.\n * @returns {Vector3d} Cross product of ‘this’ and v.\n */\n cross(v) {\n if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');\n\n const x = this.y * v.z - this.z * v.y;\n const y = this.z * v.x - this.x * v.z;\n const z = this.x * v.y - this.y * v.x;\n\n return new Vector3d(x, y, z);\n }\n\n\n /**\n * Negates a vector to point in the opposite direction.\n *\n * @returns {Vector3d} Negated vector.\n */\n negate() {\n return new Vector3d(-this.x, -this.y, -this.z);\n }\n\n\n /**\n * Normalizes a vector to its unit vector\n * – if the vector is already unit or is zero magnitude, this is a no-op.\n *\n * @returns {Vector3d} Normalised version of this vector.\n */\n unit() {\n const norm = this.length;\n if (norm == 1) return this;\n if (norm == 0) return this;\n\n const x = this.x / norm;\n const y = this.y / norm;\n const z = this.z / norm;\n\n return new Vector3d(x, y, z);\n }\n\n\n /**\n * Calculates the angle between ‘this’ vector and supplied vector atan2(|p₁×p₂|, p₁·p₂) (or if\n * (extra-planar) ‘n’ supplied then atan2(n·p₁×p₂, p₁·p₂).\n *\n * @param {Vector3d} v - Vector whose angle is to be determined from ‘this’ vector.\n * @param {Vector3d} [n] - Plane normal: if supplied, angle is signed +ve if this->v is\n * clockwise looking along n, -ve in opposite direction.\n * @returns {number} Angle (in radians) between this vector and supplied vector (in range 0..π\n * if n not supplied, range -π..+π if n supplied).\n */\n angleTo(v, n=undefined) {\n if (!(v instanceof Vector3d)) throw new TypeError('v is not Vector3d object');\n if (!(n instanceof Vector3d || n == undefined)) throw new TypeError('n is not Vector3d object');\n\n // q.v. stackoverflow.com/questions/14066933#answer-16544330, but n·p₁×p₂ is numerically\n // ill-conditioned, so just calculate sign to apply to |p₁×p₂|\n\n // if n·p₁×p₂ is -ve, negate |p₁×p₂|\n const sign = n==undefined || this.cross(v).dot(n)>=0 ? 1 : -1;\n\n const sinθ = this.cross(v).length * sign;\n const cosθ = this.dot(v);\n\n return Math.atan2(sinθ, cosθ);\n }\n\n\n /**\n * Rotates ‘this’ point around an axis by a specified angle.\n *\n * @param {Vector3d} axis - The axis being rotated around.\n * @param {number} angle - The angle of rotation (in degrees).\n * @returns {Vector3d} The rotated point.\n */\n rotateAround(axis, angle) {\n if (!(axis instanceof Vector3d)) throw new TypeError('axis is not Vector3d object');\n\n const θ = angle.toRadians();\n\n // en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle\n // en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Quaternion-derived_rotation_matrix\n const p = this.unit();\n const a = axis.unit();\n\n const s = Math.sin(θ);\n const c = Math.cos(θ);\n const t = 1-c;\n const x = a.x, y = a.y, z = a.z;\n\n const r = [ // rotation matrix for rotation about supplied axis\n [ t*x*x + c, t*x*y - s*z, t*x*z + s*y ],\n [ t*x*y + s*z, t*y*y + c, t*y*z - s*x ],\n [ t*x*z - s*y, t*y*z + s*x, t*z*z + c ],\n ];\n\n // multiply r × p\n const rp = [\n r[0][0]*p.x + r[0][1]*p.y + r[0][2]*p.z,\n r[1][0]*p.x + r[1][1]*p.y + r[1][2]*p.z,\n r[2][0]*p.x + r[2][1]*p.y + r[2][2]*p.z,\n ];\n const p2 = new Vector3d(rp[0], rp[1], rp[2]);\n\n return p2;\n // qv en.wikipedia.org/wiki/Rodrigues'_rotation_formula...\n }\n\n\n /**\n * String representation of vector.\n *\n * @param {number} [dp=3] - Number of decimal places to be used.\n * @returns {string} Vector represented as [x,y,z].\n */\n toString(dp=3) {\n return `[${this.x.toFixed(dp)},${this.y.toFixed(dp)},${this.z.toFixed(dp)}]`;\n }\n\n}\n\n\n// Extend Number object with methods to convert between degrees & radians\nNumber.prototype.toRadians = function() { return this * Math.PI / 180; };\nNumber.prototype.toDegrees = function() { return this * 180 / Math.PI; };\n\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nexport default Vector3d;\n","/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n/* Geodesy tools for an ellipsoidal earth model (c) Chris Veness 2005-2022 */\n/* MIT Licence */\n/* Core class for latlon-ellipsoidal-datum & latlon-ellipsoidal-referenceframe. */\n/* */\n/* www.movable-type.co.uk/scripts/latlong-convert-coords.html */\n/* www.movable-type.co.uk/scripts/geodesy-library.html#latlon-ellipsoidal */\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nimport Dms from './dms.js';\nimport Vector3d from './vector3d.js';\n\n\n/**\n * A latitude/longitude point defines a geographic location on or above/below the earth’s surface,\n * measured in degrees from the equator & the International Reference Meridian and in metres above\n * the ellipsoid, and based on a given datum.\n *\n * As so much modern geodesy is based on WGS-84 (as used by GPS), this module includes WGS-84\n * ellipsoid parameters, and it has methods for converting geodetic (latitude/longitude) points to/from\n * geocentric cartesian points; the latlon-ellipsoidal-datum and latlon-ellipsoidal-referenceframe\n * modules provide transformation parameters for converting between historical datums and between\n * modern reference frames.\n *\n * This module is used for both trigonometric geodesy (eg latlon-ellipsoidal-vincenty) and n-vector\n * geodesy (eg latlon-nvector-ellipsoidal), and also for UTM/MGRS mapping.\n *\n * @module latlon-ellipsoidal\n */\n\n\n/*\n * Ellipsoid parameters; exposed through static getter below.\n *\n * The only ellipsoid defined is WGS84, for use in utm/mgrs, vincenty, nvector.\n */\nconst ellipsoids = {\n WGS84: { a: 6378137, b: 6356752.314245, f: 1/298.257223563 },\n};\n\n\n/*\n * Datums; exposed through static getter below.\n *\n * The only datum defined is WGS84, for use in utm/mgrs, vincenty, nvector.\n */\nconst datums = {\n WGS84: { ellipsoid: ellipsoids.WGS84 },\n};\n\n\n// freeze static properties\nObject.freeze(ellipsoids.WGS84);\nObject.freeze(datums.WGS84);\n\n\n/* LatLonEllipsoidal - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * Latitude/longitude points on an ellipsoidal model earth, with ellipsoid parameters and methods\n * for converting points to/from cartesian (ECEF) coordinates.\n *\n * This is the core class, which will usually be used via LatLonEllipsoidal_Datum or\n * LatLonEllipsoidal_ReferenceFrame.\n */\nclass LatLonEllipsoidal {\n\n /**\n * Creates a geodetic latitude/longitude point on a (WGS84) ellipsoidal model earth.\n *\n * @param {number} lat - Latitude (in degrees).\n * @param {number} lon - Longitude (in degrees).\n * @param {number} [height=0] - Height above ellipsoid in metres.\n * @throws {TypeError} Invalid lat/lon/height.\n *\n * @example\n * import LatLon from '/js/geodesy/latlon-ellipsoidal.js';\n * const p = new LatLon(51.47788, -0.00147, 17);\n */\n constructor(lat, lon, height=0) {\n if (isNaN(lat) || lat == null) throw new TypeError(`invalid lat ‘${lat}’`);\n if (isNaN(lon) || lon == null) throw new TypeError(`invalid lon ‘${lon}’`);\n if (isNaN(height) || height == null) throw new TypeError(`invalid height ‘${height}’`);\n\n this._lat = Dms.wrap90(Number(lat));\n this._lon = Dms.wrap180(Number(lon));\n this._height = Number(height);\n }\n\n\n /**\n * Latitude in degrees north from equator (including aliases lat, latitude): can be set as\n * numeric or hexagesimal (deg-min-sec); returned as numeric.\n */\n get lat() { return this._lat; }\n get latitude() { return this._lat; }\n set lat(lat) {\n this._lat = isNaN(lat) ? Dms.wrap90(Dms.parse(lat)) : Dms.wrap90(Number(lat));\n if (isNaN(this._lat)) throw new TypeError(`invalid lat ‘${lat}’`);\n }\n set latitude(lat) {\n this._lat = isNaN(lat) ? Dms.wrap90(Dms.parse(lat)) : Dms.wrap90(Number(lat));\n if (isNaN(this._lat)) throw new TypeError(`invalid latitude ‘${lat}’`);\n }\n\n /**\n * Longitude in degrees east from international reference meridian (including aliases lon, lng,\n * longitude): can be set as numeric or hexagesimal (deg-min-sec); returned as numeric.\n */\n get lon() { return this._lon; }\n get lng() { return this._lon; }\n get longitude() { return this._lon; }\n set lon(lon) {\n this._lon = isNaN(lon) ? Dms.wrap180(Dms.parse(lon)) : Dms.wrap180(Number(lon));\n if (isNaN(this._lon)) throw new TypeError(`invalid lon ‘${lon}’`);\n }\n set lng(lon) {\n this._lon = isNaN(lon) ? Dms.wrap180(Dms.parse(lon)) : Dms.wrap180(Number(lon));\n if (isNaN(this._lon)) throw new TypeError(`invalid lng ‘${lon}’`);\n }\n set longitude(lon) {\n this._lon = isNaN(lon) ? Dms.wrap180(Dms.parse(lon)) : Dms.wrap180(Number(lon));\n if (isNaN(this._lon)) throw new TypeError(`invalid longitude ‘${lon}’`);\n }\n\n /**\n * Height in metres above ellipsoid.\n */\n get height() { return this._height; }\n set height(height) { this._height = Number(height); if (isNaN(this._height)) throw new TypeError(`invalid height ‘${height}’`); }\n\n\n /**\n * Datum.\n *\n * Note this is replicated within LatLonEllipsoidal in order that a LatLonEllipsoidal object can\n * be monkey-patched to look like a LatLonEllipsoidal_Datum, for Vincenty calculations on\n * different ellipsoids.\n *\n * @private\n */\n get datum() { return this._datum; }\n set datum(datum) { this._datum = datum; }\n\n\n /**\n * Ellipsoids with their parameters; this module only defines WGS84 parameters a = 6378137, b =\n * 6356752.314245, f = 1/298.257223563.\n *\n * @example\n * const a = LatLon.ellipsoids.WGS84.a; // 6378137\n */\n static get ellipsoids() {\n return ellipsoids;\n }\n\n /**\n * Datums; this module only defines WGS84 datum, hence no datum transformations.\n *\n * @example\n * const a = LatLon.datums.WGS84.ellipsoid.a; // 6377563.396\n */\n static get datums() {\n return datums;\n }\n\n\n /**\n * Parses a latitude/longitude point from a variety of formats.\n *\n * Latitude & longitude (in degrees) can be supplied as two separate parameters, as a single\n * comma-separated lat/lon string, or as a single object with { lat, lon } or GeoJSON properties.\n *\n * The latitude/longitude values may be numeric or strings; they may be signed decimal or\n * deg-min-sec (hexagesimal) suffixed by compass direction (NSEW); a variety of separators are\n * accepted. Examples -3.62, '3 37 12W', '3°37′12″W'.\n *\n * Thousands/decimal separators must be comma/dot; use Dms.fromLocale to convert locale-specific\n * thousands/decimal separators.\n *\n * @param {number|string|Object} lat|latlon - Latitude (in degrees), or comma-separated lat/lon, or lat/lon object.\n * @param {number} [lon] - Longitude (in degrees).\n * @param {number} [height=0] - Height above ellipsoid in metres.\n * @returns {LatLon} Latitude/longitude point on WGS84 ellipsoidal model earth.\n * @throws {TypeError} Invalid coordinate.\n *\n * @example\n * const p1 = LatLon.parse(51.47788, -0.00147); // numeric pair\n * const p2 = LatLon.parse('51°28′40″N, 000°00′05″W', 17); // dms string + height\n * const p3 = LatLon.parse({ lat: 52.205, lon: 0.119 }, 17); // { lat, lon } object numeric + height\n */\n static parse(...args) {\n if (args.length == 0) throw new TypeError('invalid (empty) point');\n\n let lat=undefined, lon=undefined, height=undefined;\n\n // single { lat, lon } object\n if (typeof args[0]=='object' && (args.length==1 || !isNaN(parseFloat(args[1])))) {\n const ll = args[0];\n if (ll.type == 'Point' && Array.isArray(ll.coordinates)) { // GeoJSON\n [ lon, lat, height ] = ll.coordinates;\n height = height || 0;\n } else { // regular { lat, lon } object\n if (ll.latitude != undefined) lat = ll.latitude;\n if (ll.lat != undefined) lat = ll.lat;\n if (ll.longitude != undefined) lon = ll.longitude;\n if (ll.lng != undefined) lon = ll.lng;\n if (ll.lon != undefined) lon = ll.lon;\n if (ll.height != undefined) height = ll.height;\n lat = Dms.wrap90(Dms.parse(lat));\n lon = Dms.wrap180(Dms.parse(lon));\n }\n if (args[1] != undefined) height = args[1];\n if (isNaN(lat) || isNaN(lon)) throw new TypeError(`invalid point ‘${JSON.stringify(args[0])}’`);\n }\n\n // single comma-separated lat/lon\n if (typeof args[0] == 'string' && args[0].split(',').length == 2) {\n [ lat, lon ] = args[0].split(',');\n lat = Dms.wrap90(Dms.parse(lat));\n lon = Dms.wrap180(Dms.parse(lon));\n height = args[1] || 0;\n if (isNaN(lat) || isNaN(lon)) throw new TypeError(`invalid point ‘${args[0]}’`);\n }\n\n // regular (lat, lon) arguments\n if (lat==undefined && lon==undefined) {\n [ lat, lon ] = args;\n lat = Dms.wrap90(Dms.parse(lat));\n lon = Dms.wrap180(Dms.parse(lon));\n height = args[2] || 0;\n if (isNaN(lat) || isNaN(lon)) throw new TypeError(`invalid point ‘${args.toString()}’`);\n }\n\n return new this(lat, lon, height); // 'new this' as may return subclassed types\n }\n\n\n /**\n * Converts ‘this’ point from (geodetic) latitude/longitude coordinates to (geocentric)\n * cartesian (x/y/z) coordinates.\n *\n * @returns {Cartesian} Cartesian point equivalent to lat/lon point, with x, y, z in metres from\n * earth centre.\n */\n toCartesian() {\n // x = (ν+h)⋅cosφ⋅cosλ, y = (ν+h)⋅cosφ⋅sinλ, z = (ν⋅(1-e²)+h)⋅sinφ\n // where ν = a/√(1−e²⋅sinφ⋅sinφ), e² = (a²-b²)/a² or (better conditioned) 2⋅f-f²\n const ellipsoid = this.datum\n ? this.datum.ellipsoid\n : this.referenceFrame ? this.referenceFrame.ellipsoid : ellipsoids.WGS84;\n\n const φ = this.lat.toRadians();\n const λ = this.lon.toRadians();\n const h = this.height;\n const { a, f } = ellipsoid;\n\n const sinφ = Math.sin(φ), cosφ = Math.cos(φ);\n const sinλ = Math.sin(λ), cosλ = Math.cos(λ);\n\n const eSq = 2*f - f*f; // 1st eccentricity squared ≡ (a²-b²)/a²\n const ν = a / Math.sqrt(1 - eSq*sinφ*sinφ); // radius of curvature in prime vertical\n\n const x = (ν+h) * cosφ * cosλ;\n const y = (ν+h) * cosφ * sinλ;\n const z = (ν*(1-eSq)+h) * sinφ;\n\n return new Cartesian(x, y, z);\n }\n\n\n /**\n * Checks if another point is equal to ‘this’ point.\n *\n * @param {LatLon} point - Point to be compared against this point.\n * @returns {bool} True if points have identical latitude, longitude, height, and datum/referenceFrame.\n * @throws {TypeError} Invalid point.\n *\n * @example\n * const p1 = new LatLon(52.205, 0.119);\n * const p2 = new LatLon(52.205, 0.119);\n * const equal = p1.equals(p2); // true\n */\n equals(point) {\n if (!(point instanceof LatLonEllipsoidal)) throw new TypeError(`invalid point ‘${point}’`);\n\n if (Math.abs(this.lat - point.lat) > Number.EPSILON) return false;\n if (Math.abs(this.lon - point.lon) > Number.EPSILON) return false;\n if (Math.abs(this.height - point.height) > Number.EPSILON) return false;\n if (this.datum != point.datum) return false;\n if (this.referenceFrame != point.referenceFrame) return false;\n if (this.epoch != point.epoch) return false;\n\n return true;\n }\n\n\n /**\n * Returns a string representation of ‘this’ point, formatted as degrees, degrees+minutes, or\n * degrees+minutes+seconds.\n *\n * @param {string} [format=d] - Format point as 'd', 'dm', 'dms', or 'n' for signed numeric.\n * @param {number} [dp=4|2|0] - Number of decimal places to use: default 4 for d, 2 for dm, 0 for dms.\n * @param {number} [dpHeight=null] - Number of decimal places to use for height; default is no height display.\n * @returns {string} Comma-separated formatted latitude/longitude.\n * @throws {RangeError} Invalid format.\n *\n * @example\n * const greenwich = new LatLon(51.47788, -0.00147, 46);\n * const d = greenwich.toString(); // 51.4779°N, 000.0015°W\n * const dms = greenwich.toString('dms', 2); // 51°28′40″N, 000°00′05″W\n * const [lat, lon] = greenwich.toString('n').split(','); // 51.4779, -0.0015\n * const dmsh = greenwich.toString('dms', 0, 0); // 51°28′40″N, 000°00′06″W +46m\n */\n toString(format='d', dp=undefined, dpHeight=null) {\n // note: explicitly set dp to undefined for passing through to toLat/toLon\n if (![ 'd', 'dm', 'dms', 'n' ].includes(format)) throw new RangeError(`invalid format ‘${format}’`);\n\n const height = (this.height>=0 ? ' +' : ' ') + this.height.toFixed(dpHeight) + 'm';\n if (format == 'n') { // signed numeric degrees\n if (dp == undefined) dp = 4;\n const lat = this.lat.toFixed(dp);\n const lon = this.lon.toFixed(dp);\n return `${lat}, ${lon}${dpHeight==null ? '' : height}`;\n }\n\n const lat = Dms.toLat(this.lat, format, dp);\n const lon = Dms.toLon(this.lon, format, dp);\n\n return `${lat}, ${lon}${dpHeight==null ? '' : height}`;\n }\n\n}\n\n\n/* Cartesian - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * ECEF (earth-centered earth-fixed) geocentric cartesian coordinates.\n *\n * @extends Vector3d\n */\nclass Cartesian extends Vector3d {\n\n /**\n * Creates cartesian coordinate representing ECEF (earth-centric earth-fixed) point.\n *\n * @param {number} x - X coordinate in metres (=> 0°N,0°E).\n * @param {number} y - Y coordinate in metres (=> 0°N,90°E).\n * @param {number} z - Z coordinate in metres (=> 90°N).\n *\n * @example\n * import { Cartesian } from '/js/geodesy/latlon-ellipsoidal.js';\n * const coord = new Cartesian(3980581.210, -111.159, 4966824.522);\n */\n constructor(x, y, z) {\n super(x, y, z); // arguably redundant constructor, but specifies units & axes\n }\n\n\n /**\n * Converts ‘this’ (geocentric) cartesian (x/y/z) coordinate to (geodetic) latitude/longitude\n * point on specified ellipsoid.\n *\n * Uses Bowring’s (1985) formulation for μm precision in concise form; ‘The accuracy of geodetic\n * latitude and height equations’, B R Bowring, Survey Review vol 28, 218, Oct 1985.\n *\n * @param {LatLon.ellipsoids} [ellipsoid=WGS84] - Ellipsoid to use when converting point.\n * @returns {LatLon} Latitude/longitude point defined by cartesian coordinates, on given ellipsoid.\n * @throws {TypeError} Invalid ellipsoid.\n *\n * @example\n * const c = new Cartesian(4027893.924, 307041.993, 4919474.294);\n * const p = c.toLatLon(); // 50.7978°N, 004.3592°E\n */\n toLatLon(ellipsoid=ellipsoids.WGS84) {\n // note ellipsoid is available as a parameter for when toLatLon gets subclassed to\n // Ellipsoidal_Datum / Ellipsoidal_Referenceframe.\n if (!ellipsoid || !ellipsoid.a) throw new TypeError(`invalid ellipsoid ‘${ellipsoid}’`);\n\n const { x, y, z } = this;\n const { a, b, f } = ellipsoid;\n\n const e2 = 2*f - f*f; // 1st eccentricity squared ≡ (a²−b²)/a²\n const ε2 = e2 / (1-e2); // 2nd eccentricity squared ≡ (a²−b²)/b²\n const p = Math.sqrt(x*x + y*y); // distance from minor axis\n const R = Math.sqrt(p*p + z*z); // polar radius\n\n // parametric latitude (Bowring eqn.17, replacing tanβ = z·a / p·b)\n const tanβ = (b*z)/(a*p) * (1+ε2*b/R);\n const sinβ = tanβ / Math.sqrt(1+tanβ*tanβ);\n const cosβ = sinβ / tanβ;\n\n // geodetic latitude (Bowring eqn.18: tanφ = z+ε²⋅b⋅sin³β / p−e²⋅cos³β)\n const φ = isNaN(cosβ) ? 0 : Math.atan2(z + ε2*b*sinβ*sinβ*sinβ, p - e2*a*cosβ*cosβ*cosβ);\n\n // longitude\n const λ = Math.atan2(y, x);\n\n // height above ellipsoid (Bowring eqn.7)\n const sinφ = Math.sin(φ), cosφ = Math.cos(φ);\n const ν = a / Math.sqrt(1-e2*sinφ*sinφ); // length of the normal terminated by the minor axis\n const h = p*cosφ + z*sinφ - (a*a/ν);\n\n const point = new LatLonEllipsoidal(φ.toDegrees(), λ.toDegrees(), h);\n\n return point;\n }\n\n\n /**\n * Returns a string representation of ‘this’ cartesian point.\n *\n * @param {number} [dp=0] - Number of decimal places to use.\n * @returns {string} Comma-separated latitude/longitude.\n */\n toString(dp=0) {\n const x = this.x.toFixed(dp), y = this.y.toFixed(dp), z = this.z.toFixed(dp);\n return `[${x},${y},${z}]`;\n }\n\n}\n\n\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nexport { LatLonEllipsoidal as default, Cartesian, Vector3d, Dms };\n","/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n/* Geodesy tools for conversions between (historical) datums (c) Chris Veness 2005-2019 */\n/* MIT Licence */\n/* www.movable-type.co.uk/scripts/latlong-convert-coords.html */\n/* www.movable-type.co.uk/scripts/geodesy-library.html#latlon-ellipsoidal-datum */\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nimport LatLonEllipsoidal, { Cartesian, Dms } from './latlon-ellipsoidal.js';\n\n\n/**\n * Historical geodetic datums: a latitude/longitude point defines a geographic location on or\n * above/below the earth’s surface, measured in degrees from the equator & the International\n * Reference Meridian and metres above the ellipsoid, and based on a given datum. The datum is\n * based on a reference ellipsoid and tied to geodetic survey reference points.\n *\n * Modern geodesy is generally based on the WGS84 datum (as used for instance by GPS systems), but\n * previously various reference ellipsoids and datum references were used.\n *\n * This module extends the core latlon-ellipsoidal module to include ellipsoid parameters and datum\n * transformation parameters, and methods for converting between different (generally historical)\n * datums.\n *\n * It can be used for UK Ordnance Survey mapping (OS National Grid References are still based on the\n * otherwise historical OSGB36 datum), as well as for historical purposes.\n *\n * q.v. Ordnance Survey ‘A guide to coordinate systems in Great Britain’ Section 6,\n * www.ordnancesurvey.co.uk/docs/support/guide-coordinate-systems-great-britain.pdf, and also\n * www.ordnancesurvey.co.uk/blog/2014/12/2.\n *\n * @module latlon-ellipsoidal-datum\n */\n\n\n/*\n * Ellipsoid parameters; exposed through static getter below.\n */\nconst ellipsoids = {\n WGS84: { a: 6378137, b: 6356752.314245, f: 1/298.257223563 },\n Airy1830: { a: 6377563.396, b: 6356256.909, f: 1/299.3249646 },\n AiryModified: { a: 6377340.189, b: 6356034.448, f: 1/299.3249646 },\n Bessel1841: { a: 6377397.155, b: 6356078.962818, f: 1/299.1528128 },\n Clarke1866: { a: 6378206.4, b: 6356583.8, f: 1/294.978698214 },\n Clarke1880IGN: { a: 6378249.2, b: 6356515.0, f: 1/293.466021294 },\n GRS80: { a: 6378137, b: 6356752.314140, f: 1/298.257222101 },\n Intl1924: { a: 6378388, b: 6356911.946, f: 1/297 }, // aka Hayford\n WGS72: { a: 6378135, b: 6356750.5, f: 1/298.26 },\n};\n\n\n/*\n * Datums; exposed through static getter below.\n */\nconst datums = {\n // transforms: t in metres, s in ppm, r in arcseconds tx ty tz s rx ry rz\n ED50: { ellipsoid: ellipsoids.Intl1924, transform: [ 89.5, 93.8, 123.1, -1.2, 0.0, 0.0, 0.156 ] }, // epsg.io/1311\n ETRS89: { ellipsoid: ellipsoids.GRS80, transform: [ 0, 0, 0, 0, 0, 0, 0 ] }, // epsg.io/1149; @ 1-metre level\n Irl1975: { ellipsoid: ellipsoids.AiryModified, transform: [ -482.530, 130.596, -564.557, -8.150, 1.042, 0.214, 0.631 ] }, // epsg.io/1954\n NAD27: { ellipsoid: ellipsoids.Clarke1866, transform: [ 8, -160, -176, 0, 0, 0, 0 ] },\n NAD83: { ellipsoid: ellipsoids.GRS80, transform: [ 0.9956, -1.9103, -0.5215, -0.00062, 0.025915, 0.009426, 0.011599 ] },\n NTF: { ellipsoid: ellipsoids.Clarke1880IGN, transform: [ 168, 60, -320, 0, 0, 0, 0 ] },\n OSGB36: { ellipsoid: ellipsoids.Airy1830, transform: [ -446.448, 125.157, -542.060, 20.4894, -0.1502, -0.2470, -0.8421 ] }, // epsg.io/1314\n Potsdam: { ellipsoid: ellipsoids.Bessel1841, transform: [ -582, -105, -414, -8.3, 1.04, 0.35, -3.08 ] },\n TokyoJapan: { ellipsoid: ellipsoids.Bessel1841, transform: [ 148, -507, -685, 0, 0, 0, 0 ] },\n WGS72: { ellipsoid: ellipsoids.WGS72, transform: [ 0, 0, -4.5, -0.22, 0, 0, 0.554 ] },\n WGS84: { ellipsoid: ellipsoids.WGS84, transform: [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] },\n};\n/* sources:\n * - ED50: www.gov.uk/guidance/oil-and-gas-petroleum-operations-notices#pon-4\n * - Irl1975: www.osi.ie/wp-content/uploads/2015/05/transformations_booklet.pdf\n * - NAD27: en.wikipedia.org/wiki/Helmert_transformation\n * - NAD83: www.uvm.edu/giv/resources/WGS84_NAD83.pdf [strictly, WGS84(G1150) -> NAD83(CORS96) @ epoch 1997.0]\n * (note NAD83(1986) ≡ WGS84(Original); confluence.qps.nl/pages/viewpage.action?pageId=29855173)\n * - NTF: Nouvelle Triangulation Francaise geodesie.ign.fr/contenu/fichiers/Changement_systeme_geodesique.pdf\n * - OSGB36: www.ordnancesurvey.co.uk/docs/support/guide-coordinate-systems-great-britain.pdf\n * - Potsdam: kartoweb.itc.nl/geometrics/Coordinate%20transformations/coordtrans.html\n * - TokyoJapan: www.geocachingtoolbox.com?page=datumEllipsoidDetails\n * - WGS72: www.icao.int/safety/pbn/documentation/eurocontrol/eurocontrol wgs 84 implementation manual.pdf\n *\n * more transform parameters are available from earth-info.nga.mil/GandG/coordsys/datums/NATO_DT.pdf,\n * www.fieldenmaps.info/cconv/web/cconv_params.js\n */\n/* note:\n * - ETRS89 reference frames are coincident with WGS-84 at epoch 1989.0 (ie null transform) at the one metre level.\n */\n\n\n// freeze static properties\nObject.keys(ellipsoids).forEach(e => Object.freeze(ellipsoids[e]));\nObject.keys(datums).forEach(d => { Object.freeze(datums[d]); Object.freeze(datums[d].transform); });\n\n\n/* LatLon - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * Latitude/longitude points on an ellipsoidal model earth, with ellipsoid parameters and methods\n * for converting between datums and to geocentric (ECEF) cartesian coordinates.\n *\n * @extends LatLonEllipsoidal\n */\nclass LatLonEllipsoidal_Datum extends LatLonEllipsoidal {\n\n /**\n * Creates a geodetic latitude/longitude point on an ellipsoidal model earth using given datum.\n *\n * @param {number} lat - Latitude (in degrees).\n * @param {number} lon - Longitude (in degrees).\n * @param {number} [height=0] - Height above ellipsoid in metres.\n * @param {LatLon.datums} datum - Datum this point is defined within.\n *\n * @example\n * import LatLon from '/js/geodesy/latlon-ellipsoidal-datum.js';\n * const p = new LatLon(53.3444, -6.2577, 17, LatLon.datums.Irl1975);\n */\n constructor(lat, lon, height=0, datum=datums.WGS84) {\n if (!datum || datum.ellipsoid==undefined) throw new TypeError(`unrecognised datum ‘${datum}’`);\n\n super(lat, lon, height);\n\n this._datum = datum;\n }\n\n\n /**\n * Datum this point is defined within.\n */\n get datum() {\n return this._datum;\n }\n\n\n /**\n * Ellipsoids with their parameters; semi-major axis (a), semi-minor axis (b), and flattening (f).\n *\n * Flattening f = (a−b)/a; at least one of these parameters is derived from defining constants.\n *\n * @example\n * const a = LatLon.ellipsoids.Airy1830.a; // 6377563.396\n */\n static get ellipsoids() {\n return ellipsoids;\n }\n\n\n /**\n * Datums; with associated ellipsoid, and Helmert transform parameters to convert from WGS-84\n * into given datum.\n *\n * Note that precision of various datums will vary, and WGS-84 (original) is not defined to be\n * accurate to better than ±1 metre. No transformation should be assumed to be accurate to\n * better than a metre, for many datums somewhat less.\n *\n * This is a small sample of commoner datums from a large set of historical datums. I will add\n * new datums on request.\n *\n * @example\n * const a = LatLon.datums.OSGB36.ellipsoid.a; // 6377563.396\n * const tx = LatLon.datums.OSGB36.transform; // [ tx, ty, tz, s, rx, ry, rz ]\n * const availableDatums = Object.keys(LatLon.datums).join(', '); // ED50, Irl1975, NAD27, ...\n */\n static get datums() {\n return datums;\n }\n\n\n // note instance datum getter/setters are in LatLonEllipsoidal\n\n\n /**\n * Parses a latitude/longitude point from a variety of formats.\n *\n * Latitude & longitude (in degrees) can be supplied as two separate parameters, as a single\n * comma-separated lat/lon string, or as a single object with { lat, lon } or GeoJSON properties.\n *\n * The latitude/longitude values may be numeric or strings; they may be signed decimal or\n * deg-min-sec (hexagesimal) suffixed by compass direction (NSEW); a variety of separators are\n * accepted. Examples -3.62, '3 37 12W', '3°37′12″W'.\n *\n * Thousands/decimal separators must be comma/dot; use Dms.fromLocale to convert locale-specific\n * thousands/decimal separators.\n *\n * @param {number|string|Object} lat|latlon - Geodetic Latitude (in degrees) or comma-separated lat/lon or lat/lon object.\n * @param {number} [lon] - Longitude in degrees.\n * @param {number} [height=0] - Height above ellipsoid in metres.\n * @param {LatLon.datums} [datum=WGS84] - Datum this point is defined within.\n * @returns {LatLon} Latitude/longitude point on ellipsoidal model earth using given datum.\n * @throws {TypeError} Unrecognised datum.\n *\n * @example\n * const p = LatLon.parse('51.47736, 0.0000', 0, LatLon.datums.OSGB36);\n */\n static parse(...args) {\n let datum = datums.WGS84;\n\n // if the last argument is a datum, use that, otherwise use default WGS-84\n if (args.length==4 || (args.length==3 && typeof args[2] == 'object')) datum = args.pop();\n\n if (!datum || datum.ellipsoid==undefined) throw new TypeError(`unrecognised datum ‘${datum}’`);\n\n const point = super.parse(...args);\n\n point._datum = datum;\n\n return point;\n }\n\n\n /**\n * Converts ‘this’ lat/lon coordinate to new coordinate system.\n *\n * @param {LatLon.datums} toDatum - Datum this coordinate is to be converted to.\n * @returns {LatLon} This point converted to new datum.\n * @throws {TypeError} Unrecognised datum.\n *\n * @example\n * const pWGS84 = new LatLon(51.47788, -0.00147, 0, LatLon.datums.WGS84);\n * const pOSGB = pWGS84.convertDatum(LatLon.datums.OSGB36); // 51.4773°N, 000.0001°E\n */\n convertDatum(toDatum) {\n if (!toDatum || toDatum.ellipsoid==undefined) throw new TypeError(`unrecognised datum ‘${toDatum}’`);\n\n const oldCartesian = this.toCartesian(); // convert geodetic to cartesian\n const newCartesian = oldCartesian.convertDatum(toDatum); // convert datum\n const newLatLon = newCartesian.toLatLon(); // convert cartesian back to geodetic\n\n return newLatLon;\n }\n\n\n /**\n * Converts ‘this’ point from (geodetic) latitude/longitude coordinates to (geocentric) cartesian\n * (x/y/z) coordinates, based on the same datum.\n *\n * Shadow of LatLonEllipsoidal.toCartesian(), returning Cartesian augmented with\n * LatLonEllipsoidal_Datum methods/properties.\n *\n * @returns {Cartesian} Cartesian point equivalent to lat/lon point, with x, y, z in metres from\n * earth centre, augmented with reference frame conversion methods and properties.\n */\n toCartesian() {\n const cartesian = super.toCartesian();\n const cartesianDatum = new Cartesian_Datum(cartesian.x, cartesian.y, cartesian.z, this.datum);\n return cartesianDatum;\n }\n\n}\n\n\n/* Cartesian - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * Augments Cartesian with datum the cooordinate is based on, and methods to convert between datums\n * (using Helmert 7-parameter transforms) and to convert cartesian to geodetic latitude/longitude\n * point.\n *\n * @extends Cartesian\n */\nclass Cartesian_Datum extends Cartesian {\n\n /**\n * Creates cartesian coordinate representing ECEF (earth-centric earth-fixed) point, on a given\n * datum. The datum will identify the primary meridian (for the x-coordinate), and is also\n * useful in transforming to/from geodetic (lat/lon) coordinates.\n *\n * @param {number} x - X coordinate in metres (=> 0°N,0°E).\n * @param {number} y - Y coordinate in metres (=> 0°N,90°E).\n * @param {number} z - Z coordinate in metres (=> 90°N).\n * @param {LatLon.datums} [datum] - Datum this coordinate is defined within.\n * @throws {TypeError} Unrecognised datum.\n *\n * @example\n * import { Cartesian } from '/js/geodesy/latlon-ellipsoidal-datum.js';\n * const coord = new Cartesian(3980581.210, -111.159, 4966824.522);\n */\n constructor(x, y, z, datum=undefined) {\n if (datum && datum.ellipsoid==undefined) throw new TypeError(`unrecognised datum ‘${datum}’`);\n\n super(x, y, z);\n\n if (datum) this._datum = datum;\n }\n\n\n /**\n * Datum this point is defined within.\n */\n get datum() {\n return this._datum;\n }\n set datum(datum) {\n if (!datum || datum.ellipsoid==undefined) throw new TypeError(`unrecognised datum ‘${datum}’`);\n this._datum = datum;\n }\n\n\n /**\n * Converts ‘this’ (geocentric) cartesian (x/y/z) coordinate to (geodetic) latitude/longitude\n * point (based on the same datum, or WGS84 if unset).\n *\n * Shadow of Cartesian.toLatLon(), returning LatLon augmented with LatLonEllipsoidal_Datum\n * methods convertDatum, toCartesian, etc.\n *\n * @returns {LatLon} Latitude/longitude point defined by cartesian coordinates.\n * @throws {TypeError} Unrecognised datum\n *\n * @example\n * const c = new Cartesian(4027893.924, 307041.993, 4919474.294);\n * const p = c.toLatLon(); // 50.7978°N, 004.3592°E\n */\n toLatLon(deprecatedDatum=undefined) {\n if (deprecatedDatum) {\n console.info('datum parameter to Cartesian_Datum.toLatLon is deprecated: set datum before calling toLatLon()');\n this.datum = deprecatedDatum;\n }\n const datum = this.datum || datums.WGS84;\n if (!datum || datum.ellipsoid==undefined) throw new TypeError(`unrecognised datum ‘${datum}’`);\n\n const latLon = super.toLatLon(datum.ellipsoid); // TODO: what if datum is not geocentric?\n const point = new LatLonEllipsoidal_Datum(latLon.lat, latLon.lon, latLon.height, this.datum);\n return point;\n }\n\n\n /**\n * Converts ‘this’ cartesian coordinate to new datum using Helmert 7-parameter transformation.\n *\n * @param {LatLon.datums} toDatum - Datum this coordinate is to be converted to.\n * @returns {Cartesian} This point converted to new datum.\n * @throws {Error} Undefined datum.\n *\n * @example\n * const c = new Cartesian(3980574.247, -102.127, 4966830.065, LatLon.datums.OSGB36);\n * c.convertDatum(LatLon.datums.Irl1975); // [??,??,??]\n */\n convertDatum(toDatum) {\n // TODO: what if datum is not geocentric?\n if (!toDatum || toDatum.ellipsoid == undefined) throw new TypeError(`unrecognised datum ‘${toDatum}’`);\n if (!this.datum) throw new TypeError('cartesian coordinate has no datum');\n\n let oldCartesian = null;\n let transform = null;\n\n if (this.datum == undefined || this.datum == datums.WGS84) {\n // converting from WGS 84\n oldCartesian = this;\n transform = toDatum.transform;\n }\n if (toDatum == datums.WGS84) {\n // converting to WGS 84; use inverse transform\n oldCartesian = this;\n transform = this.datum.transform.map(p => -p);\n }\n if (transform == null) {\n // neither this.datum nor toDatum are WGS84: convert this to WGS84 first\n oldCartesian = this.convertDatum(datums.WGS84);\n transform = toDatum.transform;\n }\n\n const newCartesian = oldCartesian.applyTransform(transform);\n newCartesian.datum = toDatum;\n\n return newCartesian;\n }\n\n\n /**\n * Applies Helmert 7-parameter transformation to ‘this’ coordinate using transform parameters t.\n *\n * This is used in converting datums (geodetic->cartesian, apply transform, cartesian->geodetic).\n *\n * @private\n * @param {number[]} t - Transformation to apply to this coordinate.\n * @returns {Cartesian} Transformed point.\n */\n applyTransform(t) {\n // this point\n const { x: x1, y: y1, z: z1 } = this;\n\n // transform parameters\n const tx = t[0]; // x-shift in metres\n const ty = t[1]; // y-shift in metres\n const tz = t[2]; // z-shift in metres\n const s = t[3]/1e6 + 1; // scale: normalise parts-per-million to (s+1)\n const rx = (t[4]/3600).toRadians(); // x-rotation: normalise arcseconds to radians\n const ry = (t[5]/3600).toRadians(); // y-rotation: normalise arcseconds to radians\n const rz = (t[6]/3600).toRadians(); // z-rotation: normalise arcseconds to radians\n\n // apply transform\n const x2 = tx + x1*s - y1*rz + z1*ry;\n const y2 = ty + x1*rz + y1*s - z1*rx;\n const z2 = tz - x1*ry + y1*rx + z1*s;\n\n return new Cartesian_Datum(x2, y2, z2);\n }\n}\n\n\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nexport { LatLonEllipsoidal_Datum as default, Cartesian_Datum as Cartesian, datums, Dms };\n","/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n/* Ordnance Survey Grid Reference functions (c) Chris Veness 2005-2021 */\n/* MIT Licence */\n/* www.movable-type.co.uk/scripts/latlong-gridref.html */\n/* www.movable-type.co.uk/scripts/geodesy-library.html#osgridref */\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nimport LatLonEllipsoidal, { Dms } from './latlon-ellipsoidal-datum.js';\n\n\n/**\n * Ordnance Survey OSGB grid references provide geocoordinate references for UK mapping purposes.\n *\n * Formulation implemented here due to Thomas, Redfearn, etc is as published by OS, but is inferior\n * to Krüger as used by e.g. Karney 2011.\n *\n * www.ordnancesurvey.co.uk/documents/resources/guide-coordinate-systems-great-britain.pdf.\n *\n * Note OSGB grid references cover Great Britain only; Ireland and the Channel Islands have their\n * own references.\n *\n * Note that these formulae are based on ellipsoidal calculations, and according to the OS are\n * accurate to about 4–5 metres – for greater accuracy, a geoid-based transformation (OSTN15) must\n * be used.\n */\n\n/*\n * Converted 2015 to work with WGS84 by default, OSGB36 as option;\n * www.ordnancesurvey.co.uk/blog/2014/12/confirmation-on-changes-to-latitude-and-longitude\n */\n\n\n/* OsGridRef - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\nconst nationalGrid = {\n trueOrigin: { lat: 49, lon: -2 }, // true origin of grid 49°N,2°W on OSGB36 datum\n falseOrigin: { easting: -400e3, northing: 100e3 }, // easting & northing of false origin, metres from true origin\n scaleFactor: 0.9996012717, // scale factor on central meridian\n ellipsoid: LatLonEllipsoidal.ellipsoids.Airy1830,\n};\n// note Irish National Grid uses t/o 53°30′N, 8°W, f/o 200kmW, 250kmS, scale factor 1.000035, on Airy 1830 Modified ellipsoid\n\n\n/**\n * OS Grid References with methods to parse and convert them to latitude/longitude points.\n */\nclass OsGridRef {\n\n /**\n * Creates an OsGridRef object.\n *\n * @param {number} easting - Easting in metres from OS Grid false origin.\n * @param {number} northing - Northing in metres from OS Grid false origin.\n *\n * @example\n * import OsGridRef from '/js/geodesy/osgridref.js';\n * const gridref = new OsGridRef(651409, 313177);\n */\n constructor(easting, northing) {\n this.easting = Number(easting);\n this.northing = Number(northing);\n\n if (isNaN(easting) || this.easting<0 || this.easting>700e3) throw new RangeError(`invalid easting ‘${easting}’`);\n if (isNaN(northing) || this.northing<0 || this.northing>1300e3) throw new RangeError(`invalid northing ‘${northing}’`);\n }\n\n\n /**\n * Converts ‘this’ Ordnance Survey Grid Reference easting/northing coordinate to latitude/longitude\n * (SW corner of grid square).\n *\n * While OS Grid References are based on OSGB-36, the Ordnance Survey have deprecated the use of\n * OSGB-36 for latitude/longitude coordinates (in favour of WGS-84), hence this function returns\n * WGS-84 by default, with OSGB-36 as an option. See www.ordnancesurvey.co.uk/blog/2014/12/2.\n *\n * Note formulation implemented here due to Thomas, Redfearn, etc is as published by OS, but is\n * inferior to Krüger as used by e.g. Karney 2011.\n *\n * @param {LatLon.datum} [datum=WGS84] - Datum to convert grid reference into.\n * @returns {LatLon} Latitude/longitude of supplied grid reference.\n *\n * @example\n * const gridref = new OsGridRef(651409.903, 313177.270);\n * const pWgs84 = gridref.toLatLon(); // 52°39′28.723″N, 001°42′57.787″E\n * // to obtain (historical) OSGB36 lat/lon point:\n * const pOsgb = gridref.toLatLon(LatLon.datums.OSGB36); // 52°39′27.253″N, 001°43′04.518″E\n */\n toLatLon(datum=LatLonEllipsoidal.datums.WGS84) {\n const { easting: E, northing: N } = this;\n\n const { a, b } = nationalGrid.ellipsoid; // a = 6377563.396, b = 6356256.909\n const φ0 = nationalGrid.trueOrigin.lat.toRadians(); // latitude of true origin, 49°N\n const λ0 = nationalGrid.trueOrigin.lon.toRadians(); // longitude of true origin, 2°W\n const E0 = -nationalGrid.falseOrigin.easting; // easting of true origin, 400km\n const N0 = -nationalGrid.falseOrigin.northing; // northing of true origin, -100km\n const F0 = nationalGrid.scaleFactor; // 0.9996012717\n\n const e2 = 1 - (b*b)/(a*a); // eccentricity squared\n const n = (a-b)/(a+b), n2 = n*n, n3 = n*n*n; // n, n², n³\n\n let φ=φ0, M=0;\n do {\n φ = (N-N0-M)/(a*F0) + φ;\n\n const Ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (φ-φ0);\n const Mb = (3*n + 3*n*n + (21/8)*n3) * Math.sin(φ-φ0) * Math.cos(φ+φ0);\n const Mc = ((15/8)*n2 + (15/8)*n3) * Math.sin(2*(φ-φ0)) * Math.cos(2*(φ+φ0));\n const Md = (35/24)*n3 * Math.sin(3*(φ-φ0)) * Math.cos(3*(φ+φ0));\n M = b * F0 * (Ma - Mb + Mc - Md); // meridional arc\n\n } while (Math.abs(N-N0-M) >= 0.00001); // ie until < 0.01mm\n\n const cosφ = Math.cos(φ), sinφ = Math.sin(φ);\n const ν = a*F0/Math.sqrt(1-e2*sinφ*sinφ); // nu = transverse radius of curvature\n const ρ = a*F0*(1-e2)/Math.pow(1-e2*sinφ*sinφ, 1.5); // rho = meridional radius of curvature\n const η2 = ν/ρ-1; // eta = ?\n\n const tanφ = Math.tan(φ);\n const tan2φ = tanφ*tanφ, tan4φ = tan2φ*tan2φ, tan6φ = tan4φ*tan2φ;\n const secφ = 1/cosφ;\n const ν3 = ν*ν*ν, ν5 = ν3*ν*ν, ν7 = ν5*ν*ν;\n const VII = tanφ/(2*ρ*ν);\n const VIII = tanφ/(24*ρ*ν3)*(5+3*tan2φ+η2-9*tan2φ*η2);\n const IX = tanφ/(720*ρ*ν5)*(61+90*tan2φ+45*tan4φ);\n const X = secφ/ν;\n const XI = secφ/(6*ν3)*(ν/ρ+2*tan2φ);\n const XII = secφ/(120*ν5)*(5+28*tan2φ+24*tan4φ);\n const XIIA = secφ/(5040*ν7)*(61+662*tan2φ+1320*tan4φ+720*tan6φ);\n\n const dE = (E-E0), dE2 = dE*dE, dE3 = dE2*dE, dE4 = dE2*dE2, dE5 = dE3*dE2, dE6 = dE4*dE2, dE7 = dE5*dE2;\n φ = φ - VII*dE2 + VIII*dE4 - IX*dE6;\n const λ = λ0 + X*dE - XI*dE3 + XII*dE5 - XIIA*dE7;\n\n let point = new LatLon_OsGridRef(φ.toDegrees(), λ.toDegrees(), 0, LatLonEllipsoidal.datums.OSGB36);\n\n if (datum != LatLonEllipsoidal.datums.OSGB36) {\n // if point is required in datum other than OSGB36, convert it\n point = point.convertDatum(datum);\n // convertDatum() gives us a LatLon: convert to LatLon_OsGridRef which includes toOsGrid()\n point = new LatLon_OsGridRef(point.lat, point.lon, point.height, point.datum);\n }\n\n return point;\n }\n\n\n /**\n * Parses grid reference to OsGridRef object.\n *\n * Accepts standard grid references (eg 'SU 387 148'), with or without whitespace separators, from\n * two-digit references up to 10-digit references (1m × 1m square), or fully numeric comma-separated\n * references in metres (eg '438700,114800').\n *\n * @param {string} gridref - Standard format OS Grid Reference.\n * @returns {OsGridRef} Numeric version of grid reference in metres from false origin (SW corner of\n * supplied grid square).\n * @throws {Error} Invalid grid reference.\n *\n * @example\n * const grid = OsGridRef.parse('TG 51409 13177'); // grid: { easting: 651409, northing: 313177 }\n */\n static parse(gridref) {\n gridref = String(gridref).trim();\n\n // check for fully numeric comma-separated gridref format\n let match = gridref.match(/^(\\d+),\\s*(\\d+)$/);\n if (match) return new OsGridRef(match[1], match[2]);\n\n // validate format\n match = gridref.match(/^[HNST][ABCDEFGHJKLMNOPQRSTUVWXYZ]\\s*[0-9]+\\s*[0-9]+$/i);\n if (!match) throw new Error(`invalid grid reference ‘${gridref}’`);\n\n // get numeric values of letter references, mapping A->0, B->1, C->2, etc:\n let l1 = gridref.toUpperCase().charCodeAt(0) - 'A'.charCodeAt(0); // 500km square\n let l2 = gridref.toUpperCase().charCodeAt(1) - 'A'.charCodeAt(0); // 100km square\n // shuffle down letters after 'I' since 'I' is not used in grid:\n if (l1 > 7) l1--;\n if (l2 > 7) l2--;\n\n // convert grid letters into 100km-square indexes from false origin (grid square SV):\n const e100km = ((l1 - 2) % 5) * 5 + (l2 % 5);\n const n100km = (19 - Math.floor(l1 / 5) * 5) - Math.floor(l2 / 5);\n\n // skip grid letters to get numeric (easting/northing) part of ref\n let en = gridref.slice(2).trim().split(/\\s+/);\n // if e/n not whitespace separated, split half way\n if (en.length == 1) en = [ en[0].slice(0, en[0].length / 2), en[0].slice(en[0].length / 2) ];\n\n // validation\n if (en[0].length != en[1].length) throw new Error(`invalid grid reference ‘${gridref}’`);\n\n // standardise to 10-digit refs (metres)\n en[0] = en[0].padEnd(5, '0');\n en[1] = en[1].padEnd(5, '0');\n\n const e = e100km + en[0];\n const n = n100km + en[1];\n\n return new OsGridRef(e, n);\n }\n\n\n /**\n * Converts ‘this’ numeric grid reference to standard OS Grid Reference.\n *\n * @param {number} [digits=10] - Precision of returned grid reference (10 digits = metres);\n * digits=0 will return grid reference in numeric format.\n * @returns {string} This grid reference in standard format.\n *\n * @example\n * const gridref = new OsGridRef(651409, 313177).toString(8); // 'TG 5140 1317'\n * const gridref = new OsGridRef(651409, 313177).toString(0); // '651409,313177'\n */\n toString(digits=10) {\n if (![ 0,2,4,6,8,10,12,14,16 ].includes(Number(digits))) throw new RangeError(`invalid precision ‘${digits}’`); // eslint-disable-line comma-spacing\n\n let { easting: e, northing: n } = this;\n\n // use digits = 0 to return numeric format (in metres) - note northing may be >= 1e7\n if (digits == 0) {\n const format = { useGrouping: false, minimumIntegerDigits: 6, maximumFractionDigits: 3 };\n const ePad = e.toLocaleString('en', format);\n const nPad = n.toLocaleString('en', format);\n return `${ePad},${nPad}`;\n }\n\n // get the 100km-grid indices\n const e100km = Math.floor(e / 100000), n100km = Math.floor(n / 100000);\n\n // translate those into numeric equivalents of the grid letters\n let l1 = (19 - n100km) - (19 - n100km) % 5 + Math.floor((e100km + 10) / 5);\n let l2 = (19 - n100km) * 5 % 25 + e100km % 5;\n\n // compensate for skipped 'I' and build grid letter-pairs\n if (l1 > 7) l1++;\n if (l2 > 7) l2++;\n const letterPair = String.fromCharCode(l1 + 'A'.charCodeAt(0), l2 + 'A'.charCodeAt(0));\n\n // strip 100km-grid indices from easting & northing, and reduce precision\n e = Math.floor((e % 100000) / Math.pow(10, 5 - digits / 2));\n n = Math.floor((n % 100000) / Math.pow(10, 5 - digits / 2));\n\n // pad eastings & northings with leading zeros\n e = e.toString().padStart(digits/2, '0');\n n = n.toString().padStart(digits/2, '0');\n\n return `${letterPair} ${e} ${n}`;\n }\n\n}\n\n\n/* LatLon_OsGridRef - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\n\n/**\n * Extends LatLon class with method to convert LatLon point to OS Grid Reference.\n *\n * @extends LatLonEllipsoidal\n */\nclass LatLon_OsGridRef extends LatLonEllipsoidal {\n\n /**\n * Converts latitude/longitude to Ordnance Survey grid reference easting/northing coordinate.\n *\n * @returns {OsGridRef} OS Grid Reference easting/northing.\n *\n * @example\n * const grid = new LatLon(52.65798, 1.71605).toOsGrid(); // TG 51409 13177\n * // for conversion of (historical) OSGB36 latitude/longitude point:\n * const grid = new LatLon(52.65798, 1.71605).toOsGrid(LatLon.datums.OSGB36);\n */\n toOsGrid() {\n // if necessary convert to OSGB36 first\n const point = this.datum == LatLonEllipsoidal.datums.OSGB36\n ? this\n : this.convertDatum(LatLonEllipsoidal.datums.OSGB36);\n\n const φ = point.lat.toRadians();\n const λ = point.lon.toRadians();\n\n const { a, b } = nationalGrid.ellipsoid; // a = 6377563.396, b = 6356256.909\n const φ0 = nationalGrid.trueOrigin.lat.toRadians(); // latitude of true origin, 49°N\n const λ0 = nationalGrid.trueOrigin.lon.toRadians(); // longitude of true origin, 2°W\n const E0 = -nationalGrid.falseOrigin.easting; // easting of true origin, 400km\n const N0 = -nationalGrid.falseOrigin.northing; // northing of true origin, -100km\n const F0 = nationalGrid.scaleFactor; // 0.9996012717\n\n const e2 = 1 - (b*b)/(a*a); // eccentricity squared\n const n = (a-b)/(a+b), n2 = n*n, n3 = n*n*n; // n, n², n³\n\n const cosφ = Math.cos(φ), sinφ = Math.sin(φ);\n const ν = a*F0/Math.sqrt(1-e2*sinφ*sinφ); // nu = transverse radius of curvature\n const ρ = a*F0*(1-e2)/Math.pow(1-e2*sinφ*sinφ, 1.5); // rho = meridional radius of curvature\n const η2 = ν/ρ-1; // eta = ?\n\n const Ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (φ-φ0);\n const Mb = (3*n + 3*n*n + (21/8)*n3) * Math.sin(φ-φ0) * Math.cos(φ+φ0);\n const Mc = ((15/8)*n2 + (15/8)*n3) * Math.sin(2*(φ-φ0)) * Math.cos(2*(φ+φ0));\n const Md = (35/24)*n3 * Math.sin(3*(φ-φ0)) * Math.cos(3*(φ+φ0));\n const M = b * F0 * (Ma - Mb + Mc - Md); // meridional arc\n\n const cos3φ = cosφ*cosφ*cosφ;\n const cos5φ = cos3φ*cosφ*cosφ;\n const tan2φ = Math.tan(φ)*Math.tan(φ);\n const tan4φ = tan2φ*tan2φ;\n\n const I = M + N0;\n const II = (ν/2)*sinφ*cosφ;\n const III = (ν/24)*sinφ*cos3φ*(5-tan2φ+9*η2);\n const IIIA = (ν/720)*sinφ*cos5φ*(61-58*tan2φ+tan4φ);\n const IV = ν*cosφ;\n const V = (ν/6)*cos3φ*(ν/ρ-tan2φ);\n const VI = (ν/120) * cos5φ * (5 - 18*tan2φ + tan4φ + 14*η2 - 58*tan2φ*η2);\n\n const Δλ = λ-λ0;\n const Δλ2 = Δλ*Δλ, Δλ3 = Δλ2*Δλ, Δλ4 = Δλ3*Δλ, Δλ5 = Δλ4*Δλ, Δλ6 = Δλ5*Δλ;\n\n let N = I + II*Δλ2 + III*Δλ4 + IIIA*Δλ6;\n let E = E0 + IV*Δλ + V*Δλ3 + VI*Δλ5;\n\n N = Number(N.toFixed(3)); // round to mm precision\n E = Number(E.toFixed(3));\n\n try {\n return new OsGridRef(E, N); // note: gets truncated to SW corner of 1m grid square\n } catch (e) {\n throw new Error(`${e.message} from (${point.lat.toFixed(6)},${point.lon.toFixed(6)}).toOsGrid()`);\n }\n }\n\n\n /**\n * Override LatLonEllipsoidal.convertDatum() with version which returns LatLon_OsGridRef.\n */\n convertDatum(toDatum) {\n const osgbED = super.convertDatum(toDatum); // returns LatLonEllipsoidal_Datum\n const osgbOSGR = new LatLon_OsGridRef(osgbED.lat, osgbED.lon, osgbED.height, osgbED.datum);\n return osgbOSGR;\n }\n\n}\n\n\n/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\n\nexport { OsGridRef as default, LatLon_OsGridRef as LatLon, Dms };\n","// @ts-expect-error - no types\nimport OsGridRef, { LatLon } from 'geodesy/osgridref.js'\n\n/**\n * Converts lat long to easting and northing\n * @param {object} param\n * @param {number} param.lat\n * @param {number} param.long\n * @returns {{ easting: number, northing: number }}\n */\nfunction latLongToEastingNorthing({ lat, long }) {\n const point = new LatLon(lat, long)\n\n return point.toOsGrid()\n}\n\n/**\n * Converts easting and northing to lat long\n * @param {object} param\n * @param {number} param.easting\n * @param {number} param.northing\n * @returns {{ lat: number, long: number }}\n */\nfunction eastingNorthingToLatLong({ easting, northing }) {\n const point = new OsGridRef(easting, northing)\n const latLong = point.toLatLon()\n\n return { lat: latLong.latitude, long: latLong.longitude }\n}\n\n/**\n * Converts lat long to an ordnance survey grid reference\n * @param {object} param\n * @param {number} param.lat\n * @param {number} param.long\n * @returns {string}\n */\nfunction latLongToOsGridRef({ lat, long }) {\n const point = new LatLon(lat, long)\n\n return point.toOsGrid().toString()\n}\n\n/**\n * Converts an ordnance survey grid reference to lat long\n * @param {string} osGridRef\n * @returns {{ lat: number, long: number }}\n */\nfunction osGridRefToLatLong(osGridRef) {\n const point = OsGridRef.parse(osGridRef)\n const latLong = point.toLatLon()\n\n return { lat: latLong.latitude, long: latLong.longitude }\n}\n\n// Center of UK\nconst DEFAULT_LAT = 53.825564\nconst DEFAULT_LONG = -2.421975\n\n/** @type {InteractiveMapInitConfig} */\nconst defaultConfig = {\n zoom: '6',\n center: [DEFAULT_LONG, DEFAULT_LAT]\n}\n\nconst COMPANY_SYMBOL_CODE = 169\nconst LOCATION_FIELD_SELECTOR = 'input.govuk-input'\nconst EVENTS = {\n interactMarkerChange: 'interact:markerchange'\n}\n\nconst defaultData = {\n VTS_OUTDOOR_URL: '/api/maps/vts/OS_VTS_3857_Outdoor.json',\n VTS_DARK_URL: '/api/maps/vts/OS_VTS_3857_Dark.json',\n VTS_BLACK_AND_WHITE_URL: '/api/maps/vts/OS_VTS_3857_Black_and_White.json'\n}\n\n/**\n * Make a form submit handler that only allows submissions from allowed buttons\n * @param {HTMLButtonElement[]} buttons - the form buttons to allow submissions\n */\nexport function formSubmitFactory(buttons) {\n /**\n * The submit handler\n * @param {SubmitEvent} e\n */\n const onFormSubmit = function (e) {\n if (\n !(e.submitter instanceof HTMLButtonElement) ||\n !buttons.includes(e.submitter)\n ) {\n e.preventDefault()\n }\n }\n\n return onFormSubmit\n}\n\n/**\n * Initialise location maps\n * @param {Partial<MapsEnvironmentConfig>} config - the map configuration\n */\nexport function initMaps(config = {}) {\n const {\n assetPath = '/assets',\n apiPath = '/form/api',\n data = defaultData\n } = config\n const locations = document.querySelectorAll('.app-location-field')\n\n // TODO: Fix this in `interactive-map`\n // If there are location components on the page fix up the main form submit\n // handler so it doesn't fire when using the integrated map search feature\n if (locations.length) {\n const form = document.querySelector('form')\n\n if (form === null) {\n return\n }\n\n const buttons = Array.from(form.querySelectorAll('button'))\n form.addEventListener('submit', formSubmitFactory(buttons), false)\n }\n\n locations.forEach((location, index) => {\n processLocation({ assetPath, apiPath, data }, location, index)\n })\n}\n\n/**\n * OS API request proxy factory\n * @param {string} apiPath - the root API path\n */\nexport function makeTileRequestTransformer(apiPath) {\n /**\n * Proxy OS API requests via our server\n * @param {string} url - the request URL\n * @param {string} resourceType - the resource type\n */\n return function transformTileRequest(url, resourceType) {\n if (url.startsWith('https://api.os.uk')) {\n if (resourceType === 'Tile') {\n return {\n url: url.replace(\n 'https://api.os.uk/maps/vector/v1/vts',\n `${window.location.origin}${apiPath}`\n ),\n headers: {}\n }\n }\n\n if (resourceType !== 'Style') {\n return {\n url: `${apiPath}/map-proxy?url=${encodeURIComponent(url)}`,\n headers: {}\n }\n }\n }\n\n const spritesPath =\n 'https://raw.githubusercontent.com/OrdnanceSurvey/OS-Vector-Tile-API-Stylesheets/main'\n\n // Proxy sprite requests\n if (url.startsWith(spritesPath)) {\n const path = url.substring(spritesPath.length)\n return {\n url: `${apiPath}/maps/vts${path}`,\n headers: {}\n }\n }\n\n return { url, headers: {} }\n }\n}\n\n/**\n * Processes a location field to add map capability\n * @param {MapsEnvironmentConfig} config - the location field element\n * @param {Element} location - the location field element\n * @param {*} index - the 0-based index\n */\nfunction processLocation(config, location, index) {\n if (!(location instanceof HTMLDivElement)) {\n return\n }\n\n const locationInputs = location.querySelector('.app-location-field-inputs')\n if (!(locationInputs instanceof HTMLDivElement)) {\n return\n }\n const locationType = location.dataset.locationtype\n\n // Check for support\n const supportedLocations = [\n 'latlongfield',\n 'eastingnorthingfield',\n 'osgridreffield'\n ]\n if (!locationType || !supportedLocations.includes(locationType)) {\n return\n }\n\n const mapContainer = document.createElement('div')\n const mapId = `map_${index}`\n\n mapContainer.setAttribute('id', mapId)\n mapContainer.setAttribute('class', 'map-container')\n\n const initConfig = getInitMapConfig(location) ?? defaultConfig\n\n locationInputs.after(mapContainer)\n\n const { map, interactPlugin } = createMap(mapId, initConfig, config)\n\n map.on(\n 'map:ready',\n /**\n * Callback function which fires when the map is ready\n * @param {object} e - the event\n * @param {MapLibreMap} e.map - the map provider instance\n */\n function onMapReady(e) {\n switch (locationType) {\n case 'latlongfield':\n bindLatLongField(location, map, e.map)\n break\n case 'eastingnorthingfield':\n bindEastingNorthingField(location, map, e.map)\n break\n case 'osgridreffield':\n bindOsGridRefField(location, map, e.map)\n break\n default:\n throw new Error('Not implemented')\n }\n\n // Add info panel\n map.addPanel('info', {\n showLabel: true,\n label: 'How to use the map',\n mobile: {\n slot: 'bottom',\n initiallyOpen: true,\n dismissable: true,\n modal: false\n },\n tablet: {\n slot: 'bottom',\n initiallyOpen: true,\n dismissable: true,\n modal: false\n },\n desktop: {\n slot: 'bottom',\n initiallyOpen: true,\n dismissable: true,\n modal: false\n },\n html: 'If using a map click on a point to update the location.<br><br>If using a keyboard, navigate to the point, centering the crosshair at the location and press enter.'\n })\n\n // Enable the interact plugin\n interactPlugin.enable()\n }\n )\n}\n\n/**\n * Create a Defra map instance\n * @param {string} mapId - the map id\n * @param {InteractiveMapInitConfig} initConfig - the map initial configuration\n * @param {MapsEnvironmentConfig} mapsConfig - the map environment params\n */\nfunction createMap(mapId, initConfig, mapsConfig) {\n const { assetPath, apiPath, data = defaultData } = mapsConfig\n const logoAltText = 'Ordnance survey logo'\n\n // @ts-expect-error - Defra namespace currently comes from UMD support files\n const defra = window.defra\n\n const interactPlugin = defra.interactPlugin({\n dataLayers: [],\n markerColor: { outdoor: '#ff0000', dark: '#00ff00' },\n interactionMode: 'marker',\n multiSelect: false\n })\n\n /** @type {InteractiveMap} */\n const map = new defra.InteractiveMap(mapId, {\n ...initConfig,\n mapProvider: defra.maplibreProvider(),\n reverseGeocodeProvider: defra.openNamesProvider({\n url: `${apiPath}/reverse-geocode-proxy?easting={easting}&northing={northing}`\n }),\n behaviour: 'inline',\n minZoom: 6,\n maxZoom: 18,\n containerHeight: '400px',\n enableZoomControls: true,\n transformRequest: makeTileRequestTransformer(apiPath),\n plugins: [\n defra.mapStylesPlugin({\n mapStyles: [\n {\n id: 'outdoor',\n label: 'Outdoor',\n url: data.VTS_OUTDOOR_URL,\n thumbnail: `${assetPath}/interactive-map/assets/images/outdoor-map-thumb.jpg`,\n logo: `${assetPath}/interactive-map/assets/images/os-logo.svg`,\n logoAltText,\n attribution: `Contains OS data ${String.fromCodePoint(COMPANY_SYMBOL_CODE)} Crown copyright and database rights ${new Date().getFullYear()}`,\n backgroundColor: '#f5f5f0'\n },\n {\n id: 'dark',\n label: 'Dark',\n url: data.VTS_DARK_URL,\n mapColorScheme: 'dark',\n appColorScheme: 'dark',\n thumbnail: `${assetPath}/interactive-map/assets/images/dark-map-thumb.jpg`,\n logo: `${assetPath}/interactive-map/assets/images/os-logo-white.svg`,\n logoAltText,\n attribution: `Contains OS data ${String.fromCodePoint(COMPANY_SYMBOL_CODE)} Crown copyright and database rights ${new Date().getFullYear()}`\n },\n {\n id: 'black-and-white',\n label: 'Black/White',\n url: data.VTS_BLACK_AND_WHITE_URL,\n thumbnail: `${assetPath}/interactive-map/assets/images/black-and-white-map-thumb.jpg`,\n logo: `${assetPath}/interactive-map/assets/images/os-logo-black.svg`,\n logoAltText,\n attribution: `Contains OS data ${String.fromCodePoint(COMPANY_SYMBOL_CODE)} Crown copyright and database rights ${new Date().getFullYear()}`\n }\n ]\n }),\n interactPlugin,\n defra.searchPlugin({\n osNamesURL: `${apiPath}/geocode-proxy?query={query}`,\n width: '300px',\n showMarker: false\n }),\n defra.scaleBarPlugin({\n units: 'metric'\n })\n ]\n })\n\n return { map, interactPlugin }\n}\n\n/**\n * Gets initial map config for a location field\n * @param {HTMLDivElement} locationField - the location field element\n */\nfunction getInitMapConfig(locationField) {\n const locationType = locationField.dataset.locationtype\n\n switch (locationType) {\n case 'latlongfield':\n return getInitLatLongMapConfig(locationField)\n case 'eastingnorthingfield':\n return getInitEastingNorthingMapConfig(locationField)\n case 'osgridreffield':\n return getInitOsGridRefMapConfig(locationField)\n default:\n throw new Error('Not implemented')\n }\n}\n\n/**\n * Validates lat and long is numeric and within UK bounds\n * @param {string} strLat - the latitude string\n * @param {string} strLong - the longitude string\n * @returns {{ valid: false } | { valid: true, value: { lat: number, long: number } }}\n */\nfunction validateLatLong(strLat, strLong) {\n const lat = strLat.trim() && Number(strLat.trim())\n const long = strLong.trim() && Number(strLong.trim())\n\n if (!lat || !long) {\n return { valid: false }\n }\n\n const latMin = 49.85\n const latMax = 60.859\n const longMin = -13.687\n const longMax = 1.767\n\n const latInBounds = lat >= latMin && lat <= latMax\n const longInBounds = long >= longMin && long <= longMax\n\n if (!latInBounds || !longInBounds) {\n return { valid: false }\n }\n\n return { valid: true, value: { lat, long } }\n}\n\n/**\n * Validates easting and northing is numeric and within UK bounds\n * @param {string} strEasting - the easting string\n * @param {string} strNorthing - the northing string\n * @returns {{ valid: false } | { valid: true, value: { easting: number, northing: number } }}\n */\nfunction validateEastingNorthing(strEasting, strNorthing) {\n const easting = strEasting.trim() && Number(strEasting.trim())\n const northing = strNorthing.trim() && Number(strNorthing.trim())\n\n if (!easting || !northing) {\n return { valid: false }\n }\n\n const eastingMin = 0\n const eastingMax = 700000\n const northingMin = 0\n const northingMax = 1300000\n\n const latInBounds = easting >= eastingMin && easting <= eastingMax\n const longInBounds = northing >= northingMin && northing <= northingMax\n\n if (!latInBounds || !longInBounds) {\n return { valid: false }\n }\n\n return { valid: true, value: { easting, northing } }\n}\n\n/**\n * Validates OS grid reference is correct\n * @param {string} osGridRef - the OsGridRef\n * @returns {{ valid: false } | { valid: true, value: string }}\n */\nfunction validateOsGridRef(osGridRef) {\n if (!osGridRef) {\n return { valid: false }\n }\n\n const pattern =\n /^((([sS]|[nN])[a-hA-Hj-zJ-Z])|(([tT]|[oO])[abfglmqrvwABFGLMQRVW])|([hH][l-zL-Z])|([jJ][lmqrvwLMQRVW]))\\s?(([0-9]{3})\\s?([0-9]{3})|([0-9]{4})\\s?([0-9]{4})|([0-9]{5})\\s?([0-9]{5}))$/\n\n const match = pattern.exec(osGridRef)\n\n if (match === null) {\n return { valid: false }\n }\n\n return { valid: true, value: match[0] }\n}\n\n/**\n * Gets the inputs for a latlong location field\n * @param {HTMLDivElement} locationField - the latlong location field element\n */\nfunction getLatLongInputs(locationField) {\n const inputs = locationField.querySelectorAll(LOCATION_FIELD_SELECTOR)\n\n if (inputs.length !== 2) {\n throw new Error('Expected 2 inputs for lat and long')\n }\n\n const latInput = /** @type {HTMLInputElement} */ (inputs[0])\n const longInput = /** @type {HTMLInputElement} */ (inputs[1])\n\n return { latInput, longInput }\n}\n\n/**\n * Gets the inputs for a easting/northing location field\n * @param {HTMLDivElement} locationField - the eastingnorthing location field element\n */\nfunction getEastingNorthingInputs(locationField) {\n const inputs = locationField.querySelectorAll(LOCATION_FIELD_SELECTOR)\n\n if (inputs.length !== 2) {\n throw new Error('Expected 2 inputs for easting and northing')\n }\n\n const eastingInput = /** @type {HTMLInputElement} */ (inputs[0])\n const northingInput = /** @type {HTMLInputElement} */ (inputs[1])\n\n return { eastingInput, northingInput }\n}\n\n/**\n * Gets the input for a OS grid reference location field\n * @param {HTMLDivElement} locationField - the osgridref location field element\n */\nfunction getOsGridRefInput(locationField) {\n const input = locationField.querySelector(LOCATION_FIELD_SELECTOR)\n\n if (input === null) {\n throw new Error('Expected 1 input for osgridref')\n }\n\n return /** @type {HTMLInputElement} */ (input)\n}\n\n/**\n * Get the initial map config for a center point\n * @param {MapCenter} center - the point\n */\nfunction getInitMapCenterConfig(center) {\n return {\n zoom: '16',\n center,\n markers: [\n {\n id: 'location',\n coords: center\n }\n ]\n }\n}\n\n/**\n * Gets initial map config for a latlong location field\n * @param {HTMLDivElement} locationField - the latlong location field element\n * @returns {InteractiveMapInitConfig | undefined}\n */\nfunction getInitLatLongMapConfig(locationField) {\n const { latInput, longInput } = getLatLongInputs(locationField)\n const result = validateLatLong(latInput.value, longInput.value)\n\n if (!result.valid) {\n return undefined\n }\n\n /** @type {MapCenter} */\n const center = [result.value.long, result.value.lat]\n\n return getInitMapCenterConfig(center)\n}\n\n/**\n * Gets initial map config for a easting/northing location field\n * @param {HTMLDivElement} locationField - the eastingnorthing location field element\n * @returns {InteractiveMapInitConfig | undefined}\n */\nfunction getInitEastingNorthingMapConfig(locationField) {\n const { eastingInput, northingInput } =\n getEastingNorthingInputs(locationField)\n const result = validateEastingNorthing(\n eastingInput.value,\n northingInput.value\n )\n\n if (!result.valid) {\n return undefined\n }\n\n const latlong = eastingNorthingToLatLong(result.value)\n\n /** @type {MapCenter} */\n const center = [latlong.long, latlong.lat]\n\n return getInitMapCenterConfig(center)\n}\n\n/**\n * Gets initial map config for an OS grid reference location field\n * @param {HTMLDivElement} locationField - the osgridref location field element\n * @returns {InteractiveMapInitConfig | undefined}\n */\nfunction getInitOsGridRefMapConfig(locationField) {\n const osGridRefInput = getOsGridRefInput(locationField)\n const result = validateOsGridRef(osGridRefInput.value)\n\n if (!result.valid) {\n return undefined\n }\n\n const latlong = osGridRefToLatLong(result.value)\n\n /** @type {MapCenter} */\n const center = [latlong.long, latlong.lat]\n\n return getInitMapCenterConfig(center)\n}\n\n/**\n * Bind a latlong field to the map\n * @param {HTMLDivElement} locationField - the latlong location field\n * @param {InteractiveMap} map - the map component instance (of InteractiveMap)\n * @param {MapLibreMap} mapProvider - the map provider instance (of MapLibreMap)\n */\nfunction bindLatLongField(locationField, map, mapProvider) {\n const { latInput, longInput } = getLatLongInputs(locationField)\n\n map.on(\n EVENTS.interactMarkerChange,\n /**\n * Callback function which fires when the map marker changes\n * @param {object} e - the event\n * @param {[number, number]} e.coords - the map marker coordinates\n */\n function onInteractMarkerChange(e) {\n const maxPrecision = 7\n latInput.value = e.coords[1].toFixed(maxPrecision)\n longInput.value = e.coords[0].toFixed(maxPrecision)\n }\n )\n\n /**\n * Lat & long input change event listener\n * Update the map view location when the inputs are changed\n */\n function onUpdateInputs() {\n const result = validateLatLong(latInput.value, longInput.value)\n\n if (result.valid) {\n /** @type {MapCenter} */\n const center = [result.value.long, result.value.lat]\n\n centerMap(map, mapProvider, center)\n }\n }\n\n latInput.addEventListener('change', onUpdateInputs, false)\n longInput.addEventListener('change', onUpdateInputs, false)\n}\n\n/**\n * Bind an eastingnorthing field to the map\n * @param {HTMLDivElement} locationField - the eastingnorthing location field\n * @param {InteractiveMap} map - the map component instance (of InteractiveMap)\n * @param {MapLibreMap} mapProvider - the map provider instance (of MapLibreMap)\n */\nfunction bindEastingNorthingField(locationField, map, mapProvider) {\n const { eastingInput, northingInput } =\n getEastingNorthingInputs(locationField)\n\n map.on(\n EVENTS.interactMarkerChange,\n /**\n * Callback function which fires when the map marker changes\n * @param {object} e - the event\n * @param {[number, number]} e.coords - the map marker coordinates\n */\n function onInteractMarkerChange(e) {\n const maxPrecision = 0\n const point = latLongToEastingNorthing({\n lat: e.coords[1],\n long: e.coords[0]\n })\n\n eastingInput.value = point.easting.toFixed(maxPrecision)\n northingInput.value = point.northing.toFixed(maxPrecision)\n }\n )\n\n /**\n * Easting & northing input change event listener\n * Update the map view location when the inputs are changed\n */\n function onUpdateInputs() {\n const result = validateEastingNorthing(\n eastingInput.value,\n northingInput.value\n )\n\n if (result.valid) {\n const latlong = eastingNorthingToLatLong(result.value)\n\n /** @type {MapCenter} */\n const center = [latlong.long, latlong.lat]\n\n centerMap(map, mapProvider, center)\n }\n }\n\n eastingInput.addEventListener('change', onUpdateInputs, false)\n northingInput.addEventListener('change', onUpdateInputs, false)\n}\n\n/**\n * Bind an OS grid reference field to the map\n * @param {HTMLDivElement} locationField - the osgridref location field\n * @param {InteractiveMap} map - the map component instance (of InteractiveMap)\n * @param {MapLibreMap} mapProvider - the map provider instance (of MapLibreMap)\n */\nfunction bindOsGridRefField(locationField, map, mapProvider) {\n const osGridRefInput = getOsGridRefInput(locationField)\n\n map.on(\n EVENTS.interactMarkerChange,\n /**\n * Callback function which fires when the map marker changes\n * @param {object} e - the event\n * @param {[number, number]} e.coords - the map marker coordinates\n */\n function onInteractMarkerChange(e) {\n const point = latLongToOsGridRef({\n lat: e.coords[1],\n long: e.coords[0]\n })\n\n osGridRefInput.value = point\n }\n )\n\n /**\n * OS grid reference input change event listener\n * Update the map view location when the input is changed\n */\n function onUpdateInput() {\n const result = validateOsGridRef(osGridRefInput.value)\n\n if (result.valid) {\n const latlong = osGridRefToLatLong(result.value)\n\n /** @type {MapCenter} */\n const center = [latlong.long, latlong.lat]\n\n centerMap(map, mapProvider, center)\n }\n }\n\n osGridRefInput.addEventListener('change', onUpdateInput, false)\n}\n\n/**\n * Updates the marker position and moves the map view port the new location\n * @param {InteractiveMap} map - the map component instance (of InteractiveMap)\n * @param {MapLibreMap} mapProvider - the map provider instance (of MapLibreMap)\n * @param {MapCenter} center - the point\n */\nfunction centerMap(map, mapProvider, center) {\n // Move the 'location' marker to the new point\n map.addMarker('location', center)\n\n // Pan & zoom the map to the new valid location\n mapProvider.flyTo({\n center,\n zoom: 14,\n essential: true\n })\n}\n\n/**\n * @typedef {object} InteractiveMap - an instance of a InteractiveMap\n * @property {Function} on - register callback listeners to map events\n * @property {Function} addPanel - adds a new panel to the map\n * @property {Function} addMarker - adds/updates a marker\n */\n\n/**\n * @typedef {object} MapLibreMap\n * @property {Function} flyTo - pans/zooms to a new location\n */\n\n/**\n * @typedef {[number, number]} MapCenter - Map center point as [long, lat]\n */\n\n/**\n * @typedef {object} InteractiveMapInitConfig - additional config that can be provided to InteractiveMap\n * @property {string} zoom - the zoom level of the map\n * @property {MapCenter} center - the center point of the map\n * @property {{ id: string, coords: MapCenter}[]} [markers] - the markers to add to the map\n */\n\n/**\n * @typedef {object} TileData\n * @property {string} VTS_OUTDOOR_URL - the outdoor tile URL\n * @property {string} VTS_DARK_URL - the dark tile URL\n * @property {string} VTS_BLACK_AND_WHITE_URL - the black and white tile URL\n */\n\n/**\n * @typedef {object} MapsEnvironmentConfig\n * @property {string} assetPath - the root asset path\n * @property {string} apiPath - the root API path\n * @property {TileData} data - the tile data config\n */\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n// expose the modules object (__webpack_modules__)\n__webpack_require__.m = __webpack_modules__;\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.f = {};\n// This file contains only the entry chunk.\n// The chunk loading function for additional chunks\n__webpack_require__.e = (chunkId) => {\n\treturn Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {\n\t\t__webpack_require__.f[key](chunkId, promises);\n\t\treturn promises;\n\t}, []));\n};","// This function allow to reference async chunks\n__webpack_require__.u = (chunkId) => {\n\t// return url for filenames based on template\n\treturn \"javascripts/\" + \"vendor/accessible-autocomplete\" + \".min.js\";\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var scriptUrl;\nif (typeof import.meta.url === \"string\") scriptUrl = import.meta.url\n// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration\n// or pass an empty string (\"\") and set the __webpack_public_path__ variable from your code to use your own logic.\nif (!scriptUrl) throw new Error(\"Automatic publicPath is not supported in this browser\");\nscriptUrl = scriptUrl.replace(/^blob:/, \"\").replace(/#.*$/, \"\").replace(/\\?.*$/, \"\").replace(/\\/[^\\/]+$/, \"/\");\n__webpack_require__.p = scriptUrl + \"../\";","// no baseURI\n\n// object to store loaded and loading chunks\n// undefined = chunk not loaded, null = chunk preloaded/prefetched\n// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded\nvar installedChunks = {\n\t804: 0\n};\n\n__webpack_require__.f.j = (chunkId, promises) => {\n\t\t// JSONP chunk loading for javascript\n\t\tvar installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;\n\t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n\t\t\t// a Promise means \"currently loading\".\n\t\t\tif(installedChunkData) {\n\t\t\t\tpromises.push(installedChunkData[2]);\n\t\t\t} else {\n\t\t\t\tif(true) { // all chunks have JS\n\t\t\t\t\t// setup Promise in chunk cache\n\t\t\t\t\tvar promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject]));\n\t\t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n\t\t\t\t\t// start chunk loading\n\t\t\t\t\tvar url = __webpack_require__.p + __webpack_require__.u(chunkId);\n\t\t\t\t\t// create error before stack unwound to get useful stacktrace later\n\t\t\t\t\tvar error = new Error();\n\t\t\t\t\tvar loadingEnded = (event) => {\n\t\t\t\t\t\tif(__webpack_require__.o(installedChunks, chunkId)) {\n\t\t\t\t\t\t\tinstalledChunkData = installedChunks[chunkId];\n\t\t\t\t\t\t\tif(installedChunkData !== 0) installedChunks[chunkId] = undefined;\n\t\t\t\t\t\t\tif(installedChunkData) {\n\t\t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n\t\t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n\t\t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n\t\t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n\t\t\t\t\t\t\t\terror.type = errorType;\n\t\t\t\t\t\t\t\terror.request = realSrc;\n\t\t\t\t\t\t\t\tinstalledChunkData[1](error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\t__webpack_require__.l(url, loadingEnded, \"chunk-\" + chunkId, chunkId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n};\n\n// no prefetching\n\n// no preloaded\n\n// no HMR\n\n// no HMR manifest\n\n// no on chunks loaded\n\n// install a JSONP callback for chunk loading\nvar webpackJsonpCallback = (parentChunkLoadingFunction, data) => {\n\tvar chunkIds = data[0];\n\tvar moreModules = data[1];\n\tvar runtime = data[2];\n\t// add \"moreModules\" to the modules object,\n\t// then flag all \"chunkIds\" as loaded and fire callback\n\tvar moduleId, chunkId, i = 0;\n\tif(chunkIds.some((id) => (installedChunks[id] !== 0))) {\n\t\tfor(moduleId in moreModules) {\n\t\t\tif(__webpack_require__.o(moreModules, moduleId)) {\n\t\t\t\t__webpack_require__.m[moduleId] = moreModules[moduleId];\n\t\t\t}\n\t\t}\n\t\tif(runtime) var result = runtime(__webpack_require__);\n\t}\n\tif(parentChunkLoadingFunction) parentChunkLoadingFunction(data);\n\tfor(;i < chunkIds.length; i++) {\n\t\tchunkId = chunkIds[i];\n\t\tif(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) {\n\t\t\tinstalledChunks[chunkId][0]();\n\t\t}\n\t\tinstalledChunks[chunkId] = 0;\n\t}\n\n}\n\nvar chunkLoadingGlobal = self[\"webpackChunk\"] = self[\"webpackChunk\"] || [];\nchunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));\nchunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));","export const MAX_POLLING_DURATION = 300 // 5 minutes\nconst ARIA_DESCRIBEDBY = 'aria-describedby'\nconst ERROR_SUMMARY_TITLE_ID = 'error-summary-title'\n\n/**\n * Creates or updates status announcer for screen readers\n * @param {HTMLElement | null} form - The form element\n * @param {HTMLElement | null} fileCountP - The file count paragraph element\n * @returns {HTMLElement} The status announcer element\n */\nfunction createOrUpdateStatusAnnouncer(form, fileCountP) {\n let statusAnnouncer = form?.querySelector('#statusInformation')\n\n if (!statusAnnouncer) {\n statusAnnouncer = document.createElement('div')\n statusAnnouncer.id = 'statusInformation'\n statusAnnouncer.className = 'govuk-visually-hidden'\n statusAnnouncer.setAttribute('aria-live', 'polite')\n\n // multiple fallbacks to ensure the status announcer is always added to the DOM\n // this helps with cross-browser compatibility and unexpected DOM structures encountered during QA\n try {\n addStatusAnnouncerToDOM(\n asHTMLElement(form),\n asHTMLElement(fileCountP),\n asHTMLElement(statusAnnouncer)\n )\n } catch {\n try {\n form?.appendChild(statusAnnouncer)\n } catch {\n document.body.appendChild(statusAnnouncer)\n }\n }\n }\n\n return /** @type {HTMLElement} */ (statusAnnouncer)\n}\n\n/**\n * Helper function to add the status announcer to the DOM\n * @param {HTMLElement} form - The form element\n * @param {HTMLElement | null} fileCountP - The file count paragraph element\n * @param {HTMLElement} statusAnnouncer - The status announcer element to add\n */\nfunction addStatusAnnouncerToDOM(form, fileCountP, statusAnnouncer) {\n if (fileCountP?.nextSibling && fileCountP.parentNode === form) {\n form.insertBefore(statusAnnouncer, fileCountP.nextSibling)\n return\n }\n\n const parentElement = fileCountP?.parentNode ?? form\n parentElement.appendChild(statusAnnouncer)\n}\n\n/**\n * Finds an existing summary list or creates a new one\n * @param {HTMLFormElement} form - The form element\n * @param {HTMLElement} fileCountP - The file count paragraph element\n * @returns {HTMLElement} The summary list element\n */\nfunction findOrCreateSummaryList(form, fileCountP) {\n let summaryList = form.querySelector('dl.govuk-summary-list')\n\n if (!summaryList) {\n summaryList = document.createElement('dl')\n summaryList.className = 'govuk-summary-list govuk-summary-list--long-key'\n\n const buttonGroup = form.querySelector('.govuk-button-group')\n\n if (buttonGroup) {\n form.insertBefore(summaryList, buttonGroup)\n } else {\n form.insertBefore(summaryList, fileCountP.nextSibling)\n }\n }\n\n return /** @type {HTMLElement} */ (summaryList)\n}\n\n/**\n * Creates a file row element for the summary list\n * @param {File | null} selectedFile - The selected file\n * @param {string} statusText - The status to display\n * @returns {HTMLElement} The created row element\n */\nfunction createFileRow(selectedFile, statusText) {\n const row = document.createElement('div')\n row.className = 'govuk-summary-list__row'\n row.setAttribute('data-filename', selectedFile?.name ?? '')\n row.innerHTML = `\n <dt class=\"govuk-summary-list__key\">\n ${selectedFile?.name ?? ''}\n </dt>\n <dd class=\"govuk-summary-list__value\">\n <strong class=\"govuk-tag govuk-tag--yellow\">${statusText}</strong>\n </dd>\n <dd class=\"govuk-summary-list__actions\">\n </dd>\n `\n return row\n}\n\n/**\n * Renders or updates the file summary box for the selected file\n * @param {File | null} selectedFile - The selected file\n * @param {string} statusText - The status to display\n * @param {HTMLElement} form - The form element\n */\nfunction renderSummary(selectedFile, statusText, form) {\n const container = document.getElementById('uploadedFilesContainer')\n const uploadForm = container ? container.closest('form') : null\n\n if (!uploadForm || !(uploadForm instanceof HTMLFormElement)) {\n return\n }\n\n const fileCountP = uploadForm.querySelector('p.govuk-body')\n\n if (!fileCountP) {\n return\n }\n\n const statusAnnouncer = createOrUpdateStatusAnnouncer(\n /** @type {HTMLElement} */ (uploadForm),\n /** @type {HTMLElement | null} */ (fileCountP)\n )\n\n const fileInput = form.querySelector('input[type=\"file\"]')\n\n if (fileInput) {\n fileInput.setAttribute(ARIA_DESCRIBEDBY, 'statusInformation')\n }\n\n const summaryList = findOrCreateSummaryList(\n /** @type {HTMLFormElement} */ (uploadForm),\n /** @type {HTMLElement} */ (fileCountP)\n )\n\n const existingRow = document.querySelector(\n `[data-filename=\"${selectedFile?.name}\"]`\n )\n\n if (existingRow) {\n existingRow.remove()\n }\n\n const row = createFileRow(selectedFile, statusText)\n summaryList.insertBefore(row, summaryList.firstChild)\n statusAnnouncer.textContent = `${selectedFile?.name ?? ''} ${statusText}`\n}\n\n/**\n * Shows an error message using the GOV.UK error summary component\n * and adds inline error styling to the file input\n * @param {string} message - The error message to display\n * @param {HTMLElement | null} errorSummary - The error summary container\n * @param {HTMLInputElement} fileInput - The file input element\n * @returns {void}\n */\nfunction showError(message, errorSummary, fileInput) {\n const topErrorSummary = document.querySelector('.govuk-error-summary')\n\n if (topErrorSummary) {\n const titleElement = document.getElementById(ERROR_SUMMARY_TITLE_ID)\n if (titleElement) {\n fileInput.setAttribute(ARIA_DESCRIBEDBY, ERROR_SUMMARY_TITLE_ID)\n } else {\n fileInput.removeAttribute(ARIA_DESCRIBEDBY)\n }\n return\n }\n\n if (errorSummary) {\n errorSummary.innerHTML = `\n <div class=\"govuk-error-summary\" data-module=\"govuk-error-summary\">\n <div role=\"alert\">\n <h2 class=\"govuk-error-summary__title\" id=\"${ERROR_SUMMARY_TITLE_ID}\">\n There is a problem\n </h2>\n <div class=\"govuk-error-summary__body\">\n <ul class=\"govuk-list govuk-error-summary__list\">\n <li>\n <a href=\"#file-upload\">${message}</a>\n </li>\n </ul>\n </div>\n </div>\n </div>\n `\n\n fileInput.setAttribute(ARIA_DESCRIBEDBY, ERROR_SUMMARY_TITLE_ID)\n }\n\n const formGroup = fileInput.closest('.govuk-form-group')\n if (formGroup) {\n formGroup.classList.add('govuk-form-group--error')\n fileInput.classList.add('govuk-file-upload--error')\n\n const inputId = fileInput.id\n let errorMessage = document.getElementById(`${inputId}-error`)\n\n if (!errorMessage) {\n errorMessage = document.createElement('p')\n errorMessage.id = `${inputId}-error`\n errorMessage.className = 'govuk-error-message'\n errorMessage.innerHTML = `<span class=\"govuk-visually-hidden\">Error:</span> ${message}`\n formGroup.insertBefore(errorMessage, fileInput)\n }\n\n fileInput.setAttribute(\n ARIA_DESCRIBEDBY,\n `error-summary-title ${inputId}-error`\n )\n }\n}\n\n/**\n * Helper to safely convert an Element to HTMLElement\n * @param {Element | null} element - The element to convert\n */\nfunction asHTMLElement(element) {\n return /** @type {HTMLElement} */ (element)\n}\n\nfunction reloadPage() {\n window.history.replaceState(null, '', window.location.href)\n window.location.href = window.location.pathname\n}\n\n/**\n * Build the upload status URL given the current pathname and the upload ID.\n * This only works when called on a file upload page that has a maximum depth of 1 URL segments following the slug.\n * @param {string} pathname – e.g. window.location.pathname\n * @param {string} uploadId\n * @returns {string} e.g. \"/form/upload-status/abc123\"\n */\nexport function buildUploadStatusUrl(pathname, uploadId) {\n // Remove preview markers and duplicate slashes\n const normalisedPath = pathname\n .replace(/\\/preview\\/(draft|live)/g, '')\n .replace(/\\/{2,}/g, '/')\n .replace(/\\/$/, '')\n\n const segments = normalisedPath.split('/').filter(Boolean)\n\n // The slug is always the second to last segment\n // The prefix is everything before the slug\n const prefix =\n segments.length > 2\n ? `/${segments.slice(0, segments.length - 2).join('/')}`\n : ''\n\n return `${prefix}/upload-status/${uploadId}`\n}\n\n/**\n * Polls the upload status endpoint until the file is ready or timeout occurs\n * @param {string} uploadId - The upload ID to check\n */\nfunction pollUploadStatus(uploadId) {\n let attempts = 0\n const interval = setInterval(() => {\n attempts++\n\n if (attempts >= MAX_POLLING_DURATION) {\n clearInterval(interval)\n reloadPage()\n return\n }\n\n const uploadStatusUrl = buildUploadStatusUrl(\n window.location.pathname,\n uploadId\n )\n\n fetch(uploadStatusUrl, {\n headers: {\n Accept: 'application/json'\n }\n })\n .then((response) => {\n if (!response.ok) {\n throw new Error('Network response was not ok')\n }\n return response.json()\n })\n .then((data) => {\n if (data.uploadStatus === 'ready') {\n clearInterval(interval)\n reloadPage()\n }\n })\n .catch(() => {\n clearInterval(interval)\n reloadPage()\n })\n }, 1000)\n}\n\n/**\n * Handle standard form submission for file upload\n * @param {HTMLFormElement} formElement - The form element\n * @param {HTMLInputElement} fileInput - The file input element\n * @param {HTMLButtonElement} uploadButton - The upload button\n * @param {HTMLButtonElement} continueButton - The continue button\n * @param {File | null} selectedFile - The selected file\n */\nfunction handleStandardFormSubmission(\n formElement,\n fileInput,\n uploadButton,\n continueButton,\n selectedFile\n) {\n renderSummary(selectedFile, 'Uploading…', formElement)\n\n fileInput.focus()\n\n setTimeout(() => {\n fileInput.disabled = true\n uploadButton.disabled = true\n continueButton.disabled = true\n }, 100)\n}\n\n/**\n * Handle AJAX form submission with upload ID\n * @param {Event} event - The click event\n * @param {HTMLFormElement} formElement - The form element\n * @param {HTMLInputElement} fileInput - The file input element\n * @param {HTMLButtonElement} uploadButton - The upload button\n * @param {HTMLElement | null} errorSummary - The error summary container\n * @param {string | undefined} uploadId - The upload ID\n * @returns {boolean} Whether the event was handled\n */\nfunction handleAjaxFormSubmission(\n event,\n formElement,\n fileInput,\n uploadButton,\n errorSummary,\n uploadId\n) {\n if (!formElement.action || !uploadId) {\n return false\n }\n\n event.preventDefault()\n\n const formData = new FormData(formElement)\n const isLocalDev = !!formElement.dataset.proxyUrl\n const uploadUrl = formElement.dataset.proxyUrl ?? formElement.action\n\n const fetchOptions = /** @type {RequestInit} */ ({\n method: 'POST',\n body: formData,\n redirect: isLocalDev ? 'follow' : 'manual' // follow mode if local development with the proxy\n })\n\n // no-cors mode if needed local development with the proxy\n if (isLocalDev) {\n fetchOptions.mode = 'no-cors'\n }\n\n fetch(uploadUrl, fetchOptions)\n .then(() => {\n pollUploadStatus(uploadId)\n })\n .catch(() => {\n fileInput.disabled = false\n uploadButton.disabled = false\n\n showError(\n 'There was a problem uploading the file',\n errorSummary,\n fileInput\n )\n\n return null\n })\n\n return true\n}\n\nfunction initUpload() {\n const form = document.querySelector('form:has(input[type=\"file\"])')\n /** @type {HTMLInputElement | null} */\n const fileInput = form ? form.querySelector('input[type=\"file\"]') : null\n /** @type {HTMLButtonElement | null} */\n const uploadButton = form ? form.querySelector('.upload-file-button') : null\n const continueButton =\n /** @type {HTMLButtonElement} */ (\n Array.from(document.querySelectorAll('button.govuk-button')).find(\n (button) => button.textContent.trim() === 'Continue'\n )\n ) ?? null\n\n const errorSummary = document.querySelector('.govuk-error-summary-container')\n\n if (!form || !fileInput || !uploadButton) {\n return\n }\n\n const formElement = /** @type {HTMLFormElement} */ (form)\n /** @type {File | null} */\n let selectedFile = null\n let isSubmitting = false\n const uploadId = formElement.dataset.uploadId\n\n fileInput.addEventListener('change', () => {\n if (errorSummary) {\n errorSummary.innerHTML = ''\n }\n\n if (fileInput.files && fileInput.files.length > 0) {\n selectedFile = fileInput.files[0]\n }\n })\n\n uploadButton.addEventListener('click', (event) => {\n if (!selectedFile) {\n event.preventDefault()\n showError(\n 'Select a file',\n /** @type {HTMLElement | null} */ (errorSummary),\n fileInput\n )\n return\n }\n\n if (isSubmitting) {\n event.preventDefault()\n return\n }\n\n isSubmitting = true\n\n handleStandardFormSubmission(\n formElement,\n fileInput,\n uploadButton,\n continueButton,\n selectedFile\n )\n\n handleAjaxFormSubmission(\n event,\n formElement,\n fileInput,\n uploadButton,\n /** @type {HTMLElement | null} */ (errorSummary),\n uploadId\n )\n })\n}\n\nexport function initFileUpload() {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', initUpload)\n } else {\n initUpload()\n }\n}\n","/**\n * Common helpers which do not require polyfill.\n *\n * IMPORTANT: If a helper require a polyfill, please isolate it in its own module\n * so that the polyfill can be properly tree-shaken and does not burden\n * the components that do not need that helper\n */\n\n/**\n * Get GOV.UK Frontend breakpoint value from CSS custom property\n *\n * @private\n * @param {string} name - Breakpoint name\n * @returns {{ property: string, value?: string }} Breakpoint object\n */\nexport function getBreakpoint(name) {\n const property = `--govuk-breakpoint-${name}`\n\n // Get value from `<html>` with breakpoints on CSS :root\n const value = window\n .getComputedStyle(document.documentElement)\n .getPropertyValue(property)\n\n return {\n property,\n value: value || undefined\n }\n}\n\n/**\n * Move focus to element\n *\n * Sets tabindex to -1 to make the element programmatically focusable,\n * but removes it on blur as the element doesn't need to be focused again.\n *\n * @private\n * @template {HTMLElement} FocusElement\n * @param {FocusElement} $element - HTML element\n * @param {object} [options] - Handler options\n * @param {function(this: FocusElement): void} [options.onBeforeFocus] - Callback before focus\n * @param {function(this: FocusElement): void} [options.onBlur] - Callback on blur\n */\nexport function setFocus($element, options = {}) {\n const isFocusable = $element.getAttribute('tabindex')\n\n if (!isFocusable) {\n $element.setAttribute('tabindex', '-1')\n }\n\n /**\n * Handle element focus\n */\n function onFocus() {\n $element.addEventListener('blur', onBlur, { once: true })\n }\n\n /**\n * Handle element blur\n */\n function onBlur() {\n options.onBlur?.call($element)\n\n if (!isFocusable) {\n $element.removeAttribute('tabindex')\n }\n }\n\n // Add listener to reset element on blur, after focus\n $element.addEventListener('focus', onFocus, { once: true })\n\n // Focus element\n options.onBeforeFocus?.call($element)\n $element.focus()\n}\n\n/**\n * Checks if component is already initialised\n *\n * @internal\n * @param {Element} $root - HTML element to be checked\n * @param {string} moduleName - name of component module\n * @returns {boolean} Whether component is already initialised\n */\nexport function isInitialised($root, moduleName) {\n return (\n $root instanceof HTMLElement &&\n $root.hasAttribute(`data-${moduleName}-init`)\n )\n}\n\n/**\n * Checks if GOV.UK Frontend is supported on this page\n *\n * Some browsers will load and run our JavaScript but GOV.UK Frontend\n * won't be supported.\n *\n * @param {HTMLElement | null} [$scope] - (internal) `<body>` HTML element checked for browser support\n * @returns {boolean} Whether GOV.UK Frontend is supported on this page\n */\nexport function isSupported($scope = document.body) {\n if (!$scope) {\n return false\n }\n\n return $scope.classList.contains('govuk-frontend-supported')\n}\n\n/**\n * Check for an array\n *\n * @internal\n * @param {unknown} option - Option to check\n * @returns {boolean} Whether the option is an array\n */\nfunction isArray(option) {\n return Array.isArray(option)\n}\n\n/**\n * Check for an object\n *\n * @internal\n * @template {Partial<Record<keyof ObjectType, unknown>>} ObjectType\n * @param {unknown | ObjectType} option - Option to check\n * @returns {option is ObjectType} Whether the option is an object\n */\nexport function isObject(option) {\n return !!option && typeof option === 'object' && !isArray(option)\n}\n\n/**\n * Check for valid scope\n *\n * @internal\n * @template {Element | Document} ScopeType\n * @param {unknown | ScopeType} $scope - Scope of the document to search within\n * @returns {$scope is ScopeType} Whether the scope can be queried\n */\nexport function isScope($scope) {\n return !!$scope && ($scope instanceof Element || $scope instanceof Document)\n}\n\n/**\n * Format error message\n *\n * @internal\n * @param {ComponentWithModuleName} Component - Component that threw the error\n * @param {string} message - Error message\n * @returns {string} - Formatted error message\n */\nexport function formatErrorMessage(Component, message) {\n return `${Component.moduleName}: ${message}`\n}\n\n/* eslint-disable jsdoc/valid-types --\n * `{new(...args: any[] ): object}` is not recognised as valid\n * https://github.com/gajus/eslint-plugin-jsdoc/issues/145#issuecomment-1308722878\n * https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/131\n **/\n\n/**\n * @typedef ComponentWithModuleName\n * @property {string} moduleName - Name of the component\n */\n\n/* eslint-enable jsdoc/valid-types */\n","import { formatErrorMessage, isObject } from '../common/index.mjs'\n\n/**\n * GOV.UK Frontend error\n *\n * A base class for `Error`s thrown by GOV.UK Frontend.\n *\n * It is meant to be extended into specific types of errors\n * to be thrown by our code.\n *\n * @example\n * ```js\n * class MissingRootError extends GOVUKFrontendError {\n * // Setting an explicit name is important as extending the class will not\n * // set a new `name` on the subclass. The `name` property is important\n * // to ensure intelligible error names even if the class name gets\n * // mangled by a minifier\n * name = \"MissingRootError\"\n * }\n * ```\n * @virtual\n */\nexport class GOVUKFrontendError extends Error {\n name = 'GOVUKFrontendError'\n}\n\n/**\n * Indicates that GOV.UK Frontend is not supported\n */\nexport class SupportError extends GOVUKFrontendError {\n name = 'SupportError'\n\n /**\n * Checks if GOV.UK Frontend is supported on this page\n *\n * @param {HTMLElement | null} [$scope] - HTML element `<body>` checked for browser support\n */\n constructor($scope = document.body) {\n const supportMessage =\n 'noModule' in HTMLScriptElement.prototype\n ? 'GOV.UK Frontend initialised without `<body class=\"govuk-frontend-supported\">` from template `<script>` snippet'\n : 'GOV.UK Frontend is not supported in this browser'\n\n super(\n $scope\n ? supportMessage\n : 'GOV.UK Frontend initialised without `<script type=\"module\">`'\n )\n }\n}\n\n/**\n * Indicates that a component has received an illegal configuration\n */\nexport class ConfigError extends GOVUKFrontendError {\n name = 'ConfigError'\n}\n\n/**\n * Indicates an issue with an element (possibly `null` or `undefined`)\n */\nexport class ElementError extends GOVUKFrontendError {\n name = 'ElementError'\n\n /**\n * @internal\n * @overload\n * @param {string} message - Element error message\n */\n\n /**\n * @internal\n * @overload\n * @param {ElementErrorOptions} options - Element error options\n */\n\n /**\n * @internal\n * @param {string | ElementErrorOptions} messageOrOptions - Element error message or options\n */\n constructor(messageOrOptions) {\n let message = typeof messageOrOptions === 'string' ? messageOrOptions : ''\n\n // Build message from options\n if (isObject(messageOrOptions)) {\n const { component, identifier, element, expectedType } = messageOrOptions\n\n message = identifier\n\n // Append reason\n message += element\n ? ` is not of type ${expectedType ?? 'HTMLElement'}`\n : ' not found'\n\n // Prepend with module name (optional)\n if (component) {\n message = formatErrorMessage(component, message)\n }\n }\n\n super(message)\n }\n}\n\n/**\n * Indicates that a component is already initialised\n */\nexport class InitError extends GOVUKFrontendError {\n name = 'InitError'\n\n /**\n * @internal\n * @param {ComponentWithModuleName | string} componentOrMessage - name of the component module\n */\n constructor(componentOrMessage) {\n const message =\n typeof componentOrMessage === 'string'\n ? componentOrMessage\n : formatErrorMessage(\n componentOrMessage,\n `Root element (\\`$root\\`) already initialised`\n )\n\n super(message)\n }\n}\n\n/**\n * Element error options\n *\n * @internal\n * @typedef {object} ElementErrorOptions\n * @property {Element | Document | null} [element] - The element in error (optional)\n * @property {ComponentWithModuleName} [component] - Component throwing the error (optional)\n * @property {string} identifier - An identifier that'll let the user understand which element has an error. This is whatever makes the most sense\n * @property {string} [expectedType] - The type that was expected for the identifier (optional)\n */\n\n/**\n * @import { ComponentWithModuleName } from '../common/index.mjs'\n */\n","import { isInitialised, isSupported } from './common/index.mjs'\nimport { ElementError, InitError, SupportError } from './errors/index.mjs'\n\n/**\n * Base Component class\n *\n * Centralises the behaviours shared by our components\n *\n * @virtual\n * @template {Element} [RootElementType=HTMLElement]\n */\nexport class Component {\n /**\n * @type {typeof Element}\n */\n static elementType = HTMLElement\n\n // allows Typescript user to work around the lack of types\n // in GOVUKFrontend package, Typescript is not aware of $root\n // in components that extend GOVUKFrontendComponent\n /**\n * Returns the root element of the component\n *\n * @protected\n * @returns {RootElementType} - the root element of component\n */\n get $root() {\n return this._$root\n }\n\n /**\n * @protected\n * @type {RootElementType}\n */\n _$root\n\n /**\n * Constructs a new component, validating that GOV.UK Frontend is supported\n *\n * @internal\n * @param {Element | null} [$root] - HTML element to use for component\n */\n constructor($root) {\n const childConstructor = /** @type {ChildClassConstructor} */ (\n this.constructor\n )\n\n // TypeScript does not enforce that inheriting classes will define a `moduleName`\n // (even if we add a `@virtual` `static moduleName` property to this class).\n // While we trust users to do this correctly, we do a little check to provide them\n // a helpful error message.\n //\n // After this, we'll be sure that `childConstructor` has a `moduleName`\n // as expected of the `ChildClassConstructor` we've cast `this.constructor` to.\n if (typeof childConstructor.moduleName !== 'string') {\n throw new InitError(`\\`moduleName\\` not defined in component`)\n }\n\n if (!($root instanceof childConstructor.elementType)) {\n throw new ElementError({\n element: $root,\n component: childConstructor,\n identifier: 'Root element (`$root`)',\n expectedType: childConstructor.elementType.name\n })\n } else {\n this._$root = /** @type {RootElementType} */ ($root)\n }\n\n childConstructor.checkSupport()\n\n this.checkInitialised()\n\n const moduleName = childConstructor.moduleName\n\n this.$root.setAttribute(`data-${moduleName}-init`, '')\n }\n\n /**\n * Validates whether component is already initialised\n *\n * @private\n * @throws {InitError} when component is already initialised\n */\n checkInitialised() {\n const constructor = /** @type {ChildClassConstructor} */ (this.constructor)\n const moduleName = constructor.moduleName\n\n if (moduleName && isInitialised(this.$root, moduleName)) {\n throw new InitError(constructor)\n }\n }\n\n /**\n * Validates whether components are supported\n *\n * @throws {SupportError} when the components are not supported\n */\n static checkSupport() {\n if (!isSupported()) {\n throw new SupportError()\n }\n }\n}\n\n/**\n * @typedef ChildClass\n * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component\n */\n\n/**\n * @typedef {typeof Component & ChildClass} ChildClassConstructor\n */\n","import { Component } from '../component.mjs'\nimport { ConfigError } from '../errors/index.mjs'\n\nimport { isObject, isScope, formatErrorMessage } from './index.mjs'\n\nexport const configOverride = Symbol.for('configOverride')\n\n/**\n * Base Component class\n *\n * Centralises the behaviours shared by our components\n *\n * @virtual\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]\n * @template {Element & { dataset: DOMStringMap }} [RootElementType=HTMLElement]\n * @augments Component<RootElementType>\n */\nexport class ConfigurableComponent extends Component {\n /**\n * configOverride\n *\n * Function which defines configuration overrides to prioritize\n * properties from the root element's dataset.\n *\n * It should take a subset of configuration as input and return\n * a new configuration object with properties that should be\n * overridden based on the root element's dataset. A Symbol\n * is used for indexing to prevent conflicts.\n *\n * @internal\n * @virtual\n * @param {Partial<ConfigurationType>} [param] - Configuration object\n * @returns {Partial<ConfigurationType>} return - Configuration object\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n [configOverride](param) {\n return {}\n }\n\n /**\n * Returns the root element of the component\n *\n * @protected\n * @returns {ConfigurationType} - the root element of component\n */\n get config() {\n return this._config\n }\n\n /**\n *\n * @type {ConfigurationType}\n */\n _config\n\n /**\n * Constructs a new component, validating that GOV.UK Frontend is supported\n *\n * @internal\n * @param {Element | null} [$root] - HTML element to use for component\n * @param {ConfigurationType} [config] - HTML element to use for component\n */\n constructor($root, config) {\n super($root)\n\n const childConstructor =\n /** @type {ChildClassConstructor<ConfigurationType>} */ (this.constructor)\n\n if (!isObject(childConstructor.defaults)) {\n throw new ConfigError(\n formatErrorMessage(\n childConstructor,\n 'Config passed as parameter into constructor but no defaults defined'\n )\n )\n }\n\n const datasetConfig = /** @type {ConfigurationType} */ (\n normaliseDataset(childConstructor, this._$root.dataset)\n )\n\n this._config = /** @type {ConfigurationType} */ (\n mergeConfigs(\n childConstructor.defaults,\n config ?? {},\n this[configOverride](datasetConfig),\n datasetConfig\n )\n )\n }\n}\n\n/**\n * Normalise string\n *\n * 'If it looks like a duck, and it quacks like a duck…' 🦆\n *\n * If the passed value looks like a boolean or a number, convert it to a boolean\n * or number.\n *\n * Designed to be used to convert config passed via data attributes (which are\n * always strings) into something sensible.\n *\n * @internal\n * @param {DOMStringMap[string]} value - The value to normalise\n * @param {SchemaProperty} [property] - Component schema property\n * @returns {string | boolean | number | undefined} Normalised data\n */\nexport function normaliseString(value, property) {\n const trimmedValue = value ? value.trim() : ''\n\n let output\n let outputType = property?.type\n\n // No schema type set? Determine automatically\n if (!outputType) {\n if (['true', 'false'].includes(trimmedValue)) {\n outputType = 'boolean'\n }\n\n // Empty / whitespace-only strings are considered finite so we need to check\n // the length of the trimmed string as well\n if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {\n outputType = 'number'\n }\n }\n\n switch (outputType) {\n case 'boolean':\n output = trimmedValue === 'true'\n break\n\n case 'number':\n output = Number(trimmedValue)\n break\n\n default:\n output = value\n }\n\n return output\n}\n\n/**\n * Normalise dataset\n *\n * Loop over an object and normalise each value using {@link normaliseString},\n * optionally expanding nested `i18n.field`\n *\n * @internal\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType\n * @template {[keyof ConfigurationType, SchemaProperty | undefined][]} SchemaEntryType\n * @param {{ schema?: Schema<ConfigurationType>, moduleName: string }} Component - Component class\n * @param {DOMStringMap} dataset - HTML element dataset\n * @returns {ObjectNested} Normalised dataset\n */\nexport function normaliseDataset(Component, dataset) {\n if (!isObject(Component.schema)) {\n throw new ConfigError(\n formatErrorMessage(\n Component,\n 'Config passed as parameter into constructor but no schema defined'\n )\n )\n }\n\n const out = /** @type {ObjectNested} */ ({})\n const entries = /** @type {SchemaEntryType} */ (\n Object.entries(Component.schema.properties)\n )\n\n // Normalise top-level dataset ('data-*') values using schema types\n for (const entry of entries) {\n const [namespace, property] = entry\n\n // Cast the `namespace` to string so it can be used to access the dataset\n const field = namespace.toString()\n\n if (field in dataset) {\n out[field] = normaliseString(dataset[field], property)\n }\n\n /**\n * Extract and normalise nested object values automatically using\n * {@link normaliseString} but only schema object types are allowed\n */\n if (property?.type === 'object') {\n out[field] = extractConfigByNamespace(\n Component.schema,\n dataset,\n namespace\n )\n }\n }\n\n return out\n}\n\n/**\n * Normalise options passed to `initAll` or `createAll`\n *\n * @internal\n * @template {CompatibleClass} ComponentClass\n * @param {Config | CreateAllOptions<ComponentClass> | OnErrorCallback<ComponentClass> | Element | Document | null} [scopeOrOptions] - Scope of the document to search within, initialisation options or error callback function\n * @returns {CreateAllOptions<ComponentClass>} Normalised options\n */\nexport function normaliseOptions(scopeOrOptions) {\n let /** @type {Element | Document | null} */ $scope = document\n let /** @type {OnErrorCallback<ComponentClass> | undefined} */ onError\n\n // Handle options object\n if (isObject(scopeOrOptions)) {\n const options = scopeOrOptions\n\n // Scope must be valid or null\n if (isScope(options.scope) || options.scope === null) {\n $scope = options.scope\n }\n\n // Error handler must be a function\n if (typeof options.onError === 'function') {\n onError = options.onError\n }\n }\n\n if (isScope(scopeOrOptions)) {\n $scope = scopeOrOptions\n } else if (scopeOrOptions === null) {\n $scope = null\n } else if (typeof scopeOrOptions === 'function') {\n onError = scopeOrOptions\n }\n\n return {\n scope: $scope,\n onError\n }\n}\n\n/**\n * Config merging function\n *\n * Takes any number of objects and combines them together, with\n * greatest priority on the LAST item passed in.\n *\n * @internal\n * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge\n * @returns {{ [key: string]: unknown }} A merged config object\n */\nexport function mergeConfigs(...configObjects) {\n // Start with an empty object as our base\n /** @type {{ [key: string]: unknown }} */\n const formattedConfigObject = {}\n\n // Loop through each of the passed objects\n for (const configObject of configObjects) {\n for (const key of Object.keys(configObject)) {\n const option = formattedConfigObject[key]\n const override = configObject[key]\n\n // Push their keys one-by-one into formattedConfigObject. Any duplicate\n // keys with object values will be merged, otherwise the new value will\n // override the existing value.\n if (isObject(option) && isObject(override)) {\n formattedConfigObject[key] = mergeConfigs(option, override)\n } else {\n // Apply override\n formattedConfigObject[key] = override\n }\n }\n }\n\n return formattedConfigObject\n}\n\n/**\n * Validate component config by schema\n *\n * Follows limited examples in JSON schema for wider support in future\n *\n * {@link https://ajv.js.org/json-schema.html#compound-keywords}\n * {@link https://ajv.js.org/packages/ajv-errors.html#single-message}\n *\n * @internal\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType\n * @param {Schema<ConfigurationType>} schema - The schema of a component\n * @param {ConfigurationType} config - Component config\n * @returns {string[]} List of validation errors\n */\nexport function validateConfig(schema, config) {\n const validationErrors = []\n\n // Check errors for each schema\n for (const [name, conditions] of Object.entries(schema)) {\n const errors = []\n\n // Check errors for each schema condition\n if (Array.isArray(conditions)) {\n for (const { required, errorMessage } of conditions) {\n if (!required.every((key) => !!config[key])) {\n errors.push(errorMessage) // Missing config key value\n }\n }\n\n // Check one condition passes or add errors\n if (name === 'anyOf' && !(conditions.length - errors.length >= 1)) {\n validationErrors.push(...errors)\n }\n }\n }\n\n return validationErrors\n}\n\n/**\n * Extracts keys starting with a particular namespace from dataset ('data-*')\n * object, removing the namespace in the process, normalising all values\n *\n * @internal\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType\n * @param {Schema<ConfigurationType>} schema - The schema of a component\n * @param {DOMStringMap} dataset - The object to extract key-value pairs from\n * @param {keyof ConfigurationType} namespace - The namespace to filter keys with\n * @returns {ObjectNested | undefined} Nested object with dot-separated key namespace removed\n */\nexport function extractConfigByNamespace(schema, dataset, namespace) {\n const property = schema.properties[namespace]\n\n // Only extract configs for object schema properties\n if (property?.type !== 'object') {\n return\n }\n\n // Add default empty config\n const newObject = /** @type {Record<typeof namespace, ObjectNested>} */ ({\n [namespace]: {}\n })\n\n for (const [key, value] of Object.entries(dataset)) {\n /** @type {ObjectNested | ObjectNested[NestedKey]} */\n let current = newObject\n\n // Split the key into parts, using . as our namespace separator\n const keyParts = key.split('.')\n\n /**\n * Create new level per part\n *\n * e.g. 'i18n.textareaDescription.other' becomes\n * `{ i18n: { textareaDescription: { other } } }`\n */\n for (const [index, name] of keyParts.entries()) {\n if (isObject(current)) {\n // Drop down to nested object until the last part\n if (index < keyParts.length - 1) {\n // New nested object (optionally) replaces existing value\n if (!isObject(current[name])) {\n current[name] = {}\n }\n\n // Drop down into new or existing nested object\n current = current[name]\n } else if (key !== namespace) {\n // Normalised value (optionally) replaces existing value\n current[name] = normaliseString(value)\n }\n }\n }\n }\n\n return newObject[namespace]\n}\n\n/**\n * @internal\n * @typedef {keyof ObjectNested} NestedKey\n * @typedef {{ [key: string]: string | boolean | number | ObjectNested | undefined }} ObjectNested\n */\n\n/**\n * Schema for component config\n *\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType\n * @typedef {object} Schema\n * @property {Record<keyof ConfigurationType, SchemaProperty | undefined>} properties - Schema properties\n * @property {SchemaCondition<ConfigurationType>[]} [anyOf] - List of schema conditions\n */\n\n/**\n * Schema property for component config\n *\n * @typedef {object} SchemaProperty\n * @property {'string' | 'boolean' | 'number' | 'object'} type - Property type\n */\n\n/**\n * Schema condition for component config\n *\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType\n * @typedef {object} SchemaCondition\n * @property {(keyof ConfigurationType)[]} required - List of required config fields\n * @property {string} errorMessage - Error message when required config fields not provided\n */\n\n/**\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]\n * @typedef ChildClass\n * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component\n * @property {Schema<ConfigurationType>} [schema] - The schema of the component configuration\n * @property {ConfigurationType} [defaults] - The default values of the configuration of the component\n */\n\n/**\n * @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]\n * @typedef {typeof Component & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>\n */\n\n/**\n * @import { CompatibleClass, Config, CreateAllOptions, OnErrorCallback } from '../init.mjs'\n */\n","import { normaliseOptions } from './common/configuration.mjs'\nimport { isObject, isSupported } from './common/index.mjs'\nimport { Accordion } from './components/accordion/accordion.mjs'\nimport { Button } from './components/button/button.mjs'\nimport { CharacterCount } from './components/character-count/character-count.mjs'\nimport { Checkboxes } from './components/checkboxes/checkboxes.mjs'\nimport { ErrorSummary } from './components/error-summary/error-summary.mjs'\nimport { ExitThisPage } from './components/exit-this-page/exit-this-page.mjs'\nimport { FileUpload } from './components/file-upload/file-upload.mjs'\nimport { Header } from './components/header/header.mjs'\nimport { NotificationBanner } from './components/notification-banner/notification-banner.mjs'\nimport { PasswordInput } from './components/password-input/password-input.mjs'\nimport { Radios } from './components/radios/radios.mjs'\nimport { ServiceNavigation } from './components/service-navigation/service-navigation.mjs'\nimport { SkipLink } from './components/skip-link/skip-link.mjs'\nimport { Tabs } from './components/tabs/tabs.mjs'\nimport { ElementError, SupportError } from './errors/index.mjs'\n\n/**\n * Initialise all components\n *\n * Use the `data-module` attributes to find, instantiate and init all of the\n * components provided as part of GOV.UK Frontend.\n *\n * @param {Config | Element | Document | null} [scopeOrConfig] - Scope of the document to search within or config for all components (with optional scope)\n */\nfunction initAll(scopeOrConfig = {}) {\n const config = isObject(scopeOrConfig) ? scopeOrConfig : {}\n\n // Extract initialisation options\n const options = normaliseOptions(scopeOrConfig)\n\n try {\n // Skip initialisation when GOV.UK Frontend is not supported\n if (!isSupported()) {\n throw new SupportError()\n }\n\n // Users can initialise GOV.UK Frontend in certain sections of the page\n // unless the scope is null (for example, query selector not found)\n if (options.scope === null) {\n throw new ElementError({\n element: options.scope,\n identifier: 'GOV.UK Frontend scope element (`$scope`)'\n })\n }\n } catch (error) {\n if (options.onError) {\n options.onError(error, {\n config\n })\n } else {\n console.log(error)\n }\n\n return\n }\n\n const components = /** @type {const} */ ([\n [Accordion, config.accordion],\n [Button, config.button],\n [CharacterCount, config.characterCount],\n [Checkboxes],\n [ErrorSummary, config.errorSummary],\n [ExitThisPage, config.exitThisPage],\n [FileUpload, config.fileUpload],\n [Header],\n [NotificationBanner, config.notificationBanner],\n [PasswordInput, config.passwordInput],\n [Radios],\n [ServiceNavigation],\n [SkipLink],\n [Tabs]\n ])\n\n components.forEach(([Component, componentConfig]) => {\n createAll(Component, componentConfig, options)\n })\n}\n\n/**\n * Create all instances of a specific component on the page\n *\n * Uses the `data-module` attribute to find all elements matching the specified\n * component on the page, creating instances of the component object for each\n * of them.\n *\n * Any component errors will be caught and logged to the console.\n *\n * @template {CompatibleClass} ComponentClass\n * @param {ComponentClass} Component - class of the component to create\n * @param {ComponentConfig<ComponentClass>} [config] - Config supplied to component\n * @param {OnErrorCallback<ComponentClass> | Element | Document | null | CreateAllOptions<ComponentClass>} [scopeOrOptions] - options for createAll including scope of the document to search within and callback function if error throw by component on init\n * @returns {Array<InstanceType<ComponentClass>>} - array of instantiated components\n */\nfunction createAll(Component, config, scopeOrOptions) {\n let /** @type {NodeListOf<Element> | undefined} */ $elements\n\n // Extract initialisation options\n const options = normaliseOptions(scopeOrOptions)\n\n try {\n // Skip initialisation when GOV.UK Frontend is not supported\n if (!isSupported()) {\n throw new SupportError()\n }\n\n // Users can initialise GOV.UK Frontend in certain sections of the page\n // unless the scope is null (for example, query selector not found)\n if (options.scope === null) {\n throw new ElementError({\n element: options.scope,\n component: Component,\n identifier: 'Scope element (`$scope`)'\n })\n }\n\n $elements = options.scope?.querySelectorAll(\n `[data-module=\"${Component.moduleName}\"]`\n )\n } catch (error) {\n if (options.onError) {\n options.onError(error, {\n component: Component,\n config\n })\n } else {\n console.log(error)\n }\n\n return []\n }\n\n /* eslint-disable-next-line @typescript-eslint/no-unsafe-return --\n * We can't define CompatibleClass as `{new(): CompatibleClass, moduleName: string}`,\n * as when doing `typeof Accordion` (or any component), TypeScript doesn't seem\n * to acknowledge the static `moduleName` that's set in our component classes.\n * This means we have to set the constructor of `CompatibleClass` as `{new(): any}`,\n * leading to ESLint frowning that we're returning `any[]`.\n */\n return Array.from($elements ?? [])\n .map(($element) => {\n try {\n // Only pass config to components that accept it\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return typeof config !== 'undefined'\n ? new Component($element, config)\n : new Component($element)\n } catch (error) {\n if (options.onError) {\n options.onError(error, {\n element: $element,\n component: Component,\n config\n })\n } else {\n console.log(error)\n }\n\n return null\n }\n })\n .filter(Boolean) // Exclude components that errored\n}\n\nexport { initAll, createAll }\n\n/* eslint-disable jsdoc/valid-types --\n * `{new(...args: any[] ): object}` is not recognised as valid\n * https://github.com/gajus/eslint-plugin-jsdoc/issues/145#issuecomment-1308722878\n * https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/131\n **/\n\n/**\n * @typedef {{new (...args: any[]): any, moduleName: string}} CompatibleClass\n */\n\n/* eslint-enable jsdoc/valid-types */\n\n/**\n * Config for all components via `initAll()`\n *\n * @typedef {object} Config\n * @property {Element | Document | null} [scope] - Scope of the document to search within\n * @property {OnErrorCallback<CompatibleClass>} [onError] - Initialisation error callback\n * @property {AccordionConfig} [accordion] - Accordion config\n * @property {ButtonConfig} [button] - Button config\n * @property {CharacterCountConfig} [characterCount] - Character Count config\n * @property {ErrorSummaryConfig} [errorSummary] - Error Summary config\n * @property {ExitThisPageConfig} [exitThisPage] - Exit This Page config\n * @property {FileUploadConfig} [fileUpload] - File Upload config\n * @property {NotificationBannerConfig} [notificationBanner] - Notification Banner config\n * @property {PasswordInputConfig} [passwordInput] - Password input config\n */\n\n/**\n * Config for individual components\n *\n * @import { AccordionConfig } from './components/accordion/accordion.mjs'\n * @import { ButtonConfig } from './components/button/button.mjs'\n * @import { CharacterCountConfig } from './components/character-count/character-count.mjs'\n * @import { ErrorSummaryConfig } from './components/error-summary/error-summary.mjs'\n * @import { ExitThisPageConfig } from './components/exit-this-page/exit-this-page.mjs'\n * @import { NotificationBannerConfig } from './components/notification-banner/notification-banner.mjs'\n * @import { PasswordInputConfig } from './components/password-input/password-input.mjs'\n * @import { FileUploadConfig } from './components/file-upload/file-upload.mjs'\n */\n\n/**\n * Component config keys, e.g. `accordion` and `characterCount`\n *\n * @typedef {keyof Omit<Config, 'scope' | 'onError'>} ConfigKey\n */\n\n/**\n * @template {CompatibleClass} ComponentClass\n * @typedef {ConstructorParameters<ComponentClass>[1]} ComponentConfig\n */\n\n/**\n * @template {CompatibleClass} ComponentClass\n * @typedef {object} ErrorContext\n * @property {Element} [element] - Element used for component module initialisation\n * @property {ComponentClass} [component] - Class of component\n * @property {Config | ComponentConfig<ComponentClass>} [config] - Config supplied to components\n */\n\n/**\n * @template {CompatibleClass} ComponentClass\n * @callback OnErrorCallback\n * @param {unknown} error - Thrown error\n * @param {ErrorContext<ComponentClass>} context - Object containing the element, component class and configuration\n */\n\n/**\n * @template {CompatibleClass} ComponentClass\n * @typedef {object} CreateAllOptions\n * @property {Element | Document | null} [scope] - scope of the document to search within\n * @property {OnErrorCallback<ComponentClass>} [onError] - callback function if error throw by component on init\n */\n","import { ConfigurableComponent } from '../../common/configuration.mjs'\n\nconst DEBOUNCE_TIMEOUT_IN_SECONDS = 1\n\n/**\n * JavaScript enhancements for the Button component\n *\n * @preserve\n * @augments ConfigurableComponent<ButtonConfig>\n */\nexport class Button extends ConfigurableComponent {\n /**\n * @private\n * @type {number | null}\n */\n debounceFormSubmitTimer = null\n\n /**\n * @param {Element | null} $root - HTML element to use for button\n * @param {ButtonConfig} [config] - Button config\n */\n constructor($root, config = {}) {\n super($root, config)\n\n this.$root.addEventListener('keydown', (event) => this.handleKeyDown(event))\n this.$root.addEventListener('click', (event) => this.debounce(event))\n }\n\n /**\n * Trigger a click event when the space key is pressed\n *\n * Some screen readers tell users they can use the space bar to activate\n * things with the 'button' role, so we need to match the functionality of\n * native HTML buttons.\n *\n * See https://github.com/alphagov/govuk_elements/pull/272#issuecomment-233028270\n *\n * @private\n * @param {KeyboardEvent} event - Keydown event\n */\n handleKeyDown(event) {\n const $target = event.target\n\n // Handle space bar only\n if (event.key !== ' ') {\n return\n }\n\n // Handle elements with [role=\"button\"] only\n if (\n $target instanceof HTMLElement &&\n $target.getAttribute('role') === 'button'\n ) {\n event.preventDefault() // prevent the page from scrolling\n $target.click()\n }\n }\n\n /**\n * Debounce double-clicks\n *\n * If the click quickly succeeds a previous click then nothing will happen.\n * This stops people accidentally causing multiple form submissions by double\n * clicking buttons.\n *\n * @private\n * @param {MouseEvent} event - Mouse click event\n * @returns {undefined | false} Returns undefined, or false when debounced\n */\n debounce(event) {\n // Check the button that was clicked has preventDoubleClick enabled\n if (!this.config.preventDoubleClick) {\n return\n }\n\n // If the timer is still running, prevent the click from submitting the form\n if (this.debounceFormSubmitTimer) {\n event.preventDefault()\n return false\n }\n\n this.debounceFormSubmitTimer = window.setTimeout(() => {\n this.debounceFormSubmitTimer = null\n }, DEBOUNCE_TIMEOUT_IN_SECONDS * 1000)\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-button'\n\n /**\n * Button default config\n *\n * @see {@link ButtonConfig}\n * @constant\n * @type {ButtonConfig}\n */\n static defaults = Object.freeze({\n preventDoubleClick: false\n })\n\n /**\n * Button config schema\n *\n * @constant\n * @satisfies {Schema<ButtonConfig>}\n */\n static schema = Object.freeze({\n properties: {\n preventDoubleClick: { type: 'boolean' }\n }\n })\n}\n\n/**\n * Button config\n *\n * @typedef {object} ButtonConfig\n * @property {boolean} [preventDoubleClick=false] - Prevent accidental double\n * clicks on submit buttons from submitting forms multiple times.\n */\n\n/**\n * @import { Schema } from '../../common/configuration.mjs'\n */\n","/**\n * Returns the value of the given attribute closest to the given element (including itself)\n *\n * @internal\n * @param {Element} $element - The element to start walking the DOM tree up\n * @param {string} attributeName - The name of the attribute\n * @returns {string | null} Attribute value\n */\nexport function closestAttributeValue($element, attributeName) {\n const $closestElementWithAttribute = $element.closest(`[${attributeName}]`)\n return $closestElementWithAttribute\n ? $closestElementWithAttribute.getAttribute(attributeName)\n : null\n}\n","import { isObject } from './common/index.mjs'\n\n/**\n * Internal support for selecting messages to render, with placeholder\n * interpolation and locale-aware number formatting and pluralisation\n *\n * @internal\n */\nexport class I18n {\n translations\n locale\n\n /**\n * @internal\n * @param {{ [key: string]: string | TranslationPluralForms }} translations - Key-value pairs of the translation strings to use.\n * @param {object} [config] - Configuration options for the function.\n * @param {string | null} [config.locale] - An overriding locale for the PluralRules functionality.\n */\n constructor(translations = {}, config = {}) {\n // Make list of translations available throughout function\n this.translations = translations\n\n // The locale to use for PluralRules and NumberFormat\n this.locale = config.locale ?? (document.documentElement.lang || 'en')\n }\n\n /**\n * The most used function - takes the key for a given piece of UI text and\n * returns the appropriate string.\n *\n * @internal\n * @param {string} lookupKey - The lookup key of the string to use.\n * @param {{ [key: string]: unknown }} [options] - Any options passed with the translation string, e.g: for string interpolation.\n * @returns {string} The appropriate translation string.\n * @throws {Error} Lookup key required\n * @throws {Error} Options required for `${}` placeholders\n */\n t(lookupKey, options) {\n if (!lookupKey) {\n // Print a console error if no lookup key has been provided\n throw new Error('i18n: lookup key missing')\n }\n\n // Fetch the translation for that lookup key\n let translation = this.translations[lookupKey]\n\n // If the `count` option is set, determine which plural suffix is needed and\n // change the lookupKey to match. We check to see if it's numeric instead of\n // falsy, as this could legitimately be 0.\n if (typeof options?.count === 'number' && isObject(translation)) {\n const translationPluralForm =\n translation[this.getPluralSuffix(lookupKey, options.count)]\n\n // Update translation with plural suffix\n if (translationPluralForm) {\n translation = translationPluralForm\n }\n }\n\n if (typeof translation === 'string') {\n // Check for ${} placeholders in the translation string\n // eslint-disable-next-line @typescript-eslint/prefer-regexp-exec\n if (translation.match(/%{(.\\S+)}/)) {\n if (!options) {\n throw new Error(\n 'i18n: cannot replace placeholders in string if no option data provided'\n )\n }\n\n return this.replacePlaceholders(translation, options)\n }\n\n return translation\n }\n\n // If the key wasn't found in our translations object,\n // return the lookup key itself as the fallback\n return lookupKey\n }\n\n /**\n * Takes a translation string with placeholders, and replaces the placeholders\n * with the provided data\n *\n * @internal\n * @param {string} translationString - The translation string\n * @param {{ [key: string]: unknown }} options - Any options passed with the translation string, e.g: for string interpolation.\n * @returns {string} The translation string to output, with $\\{\\} placeholders replaced\n */\n replacePlaceholders(translationString, options) {\n const formatter = Intl.NumberFormat.supportedLocalesOf(this.locale).length\n ? new Intl.NumberFormat(this.locale)\n : undefined\n\n return translationString.replace(\n /%{(.\\S+)}/g,\n\n /**\n * Replace translation string placeholders\n *\n * @internal\n * @param {string} placeholderWithBraces - Placeholder with braces\n * @param {string} placeholderKey - Placeholder key\n * @returns {string} Placeholder value\n */\n function (placeholderWithBraces, placeholderKey) {\n if (Object.prototype.hasOwnProperty.call(options, placeholderKey)) {\n const placeholderValue = options[placeholderKey]\n\n // If a user has passed `false` as the value for the placeholder\n // treat it as though the value should not be displayed\n if (\n placeholderValue === false ||\n (typeof placeholderValue !== 'number' &&\n typeof placeholderValue !== 'string')\n ) {\n return ''\n }\n\n // If the placeholder's value is a number, localise the number formatting\n if (typeof placeholderValue === 'number') {\n return formatter\n ? formatter.format(placeholderValue)\n : `${placeholderValue}`\n }\n\n return placeholderValue\n }\n\n throw new Error(\n `i18n: no data found to replace ${placeholderWithBraces} placeholder in string`\n )\n }\n )\n }\n\n /**\n * Check to see if the browser supports Intl.PluralRules\n *\n * It requires all conditions to be met in order to be supported:\n * - The implementation of Intl supports PluralRules (NOT true in Safari 10–12)\n * - The browser/OS has plural rules for the current locale (browser dependent)\n *\n * {@link https://browsersl.ist/#q=supports+es6-module+and+not+supports+intl-pluralrules}\n *\n * @internal\n * @returns {boolean} Returns true if all conditions are met. Returns false otherwise.\n */\n hasIntlPluralRulesSupport() {\n return Boolean(\n 'PluralRules' in window.Intl &&\n Intl.PluralRules.supportedLocalesOf(this.locale).length\n )\n }\n\n /**\n * Get the appropriate suffix for the plural form.\n *\n * Uses Intl.PluralRules (or our own fallback implementation) to get the\n * 'preferred' form to use for the given count.\n *\n * Checks that a translation has been provided for that plural form – if it\n * hasn't, it'll fall back to the 'other' plural form (unless that doesn't exist\n * either, in which case an error will be thrown)\n *\n * @internal\n * @param {string} lookupKey - The lookup key of the string to use.\n * @param {number} count - Number used to determine which pluralisation to use.\n * @returns {PluralRule} The suffix associated with the correct pluralisation for this locale.\n * @throws {Error} Plural form `.other` required when preferred plural form is missing\n */\n getPluralSuffix(lookupKey, count) {\n // Validate that the number is actually a number.\n //\n // Number(count) will turn anything that can't be converted to a Number type\n // into 'NaN'. isFinite filters out NaN, as it isn't a finite number.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-conversion\n count = Number(count)\n if (!isFinite(count)) {\n return 'other'\n }\n\n // Fetch the translation for that lookup key\n const translation = this.translations[lookupKey]\n\n // Check to verify that all the requirements for Intl.PluralRules are met.\n // If so, we can use that instead of our custom implementation. Otherwise,\n // use the hardcoded fallback.\n const preferredForm = this.hasIntlPluralRulesSupport()\n ? new Intl.PluralRules(this.locale).select(count)\n : 'other'\n\n // Use the correct plural form if provided\n if (isObject(translation)) {\n if (preferredForm in translation) {\n return preferredForm\n // Fall back to `other` if the plural form is missing, but log a warning\n // to the console\n } else if ('other' in translation) {\n console.warn(\n `i18n: Missing plural form \".${preferredForm}\" for \"${this.locale}\" locale. Falling back to \".other\".`\n )\n\n return 'other'\n }\n }\n\n // If the required `other` plural form is missing, all we can do is error\n throw new Error(\n `i18n: Plural form \".other\" is required for \"${this.locale}\" locale`\n )\n }\n}\n\n/**\n * Plural rule category mnemonic tags\n *\n * @internal\n * @typedef {'zero' | 'one' | 'two' | 'few' | 'many' | 'other'} PluralRule\n */\n\n/**\n * Translated message by plural rule they correspond to.\n *\n * Allows to group pluralised messages under a single key when passing\n * translations to a component's constructor\n *\n * @internal\n * @typedef {object} TranslationPluralForms\n * @property {string} [other] - General plural form\n * @property {string} [zero] - Plural form used with 0\n * @property {string} [one] - Plural form used with 1\n * @property {string} [two] - Plural form used with 2\n * @property {string} [few] - Plural form used for a few\n * @property {string} [many] - Plural form used for many\n */\n","import { closestAttributeValue } from '../../common/closest-attribute-value.mjs'\nimport {\n validateConfig,\n ConfigurableComponent,\n configOverride\n} from '../../common/configuration.mjs'\nimport { formatErrorMessage } from '../../common/index.mjs'\nimport { ConfigError, ElementError } from '../../errors/index.mjs'\nimport { I18n } from '../../i18n.mjs'\n\n/**\n * Character count component\n *\n * Tracks the number of characters or words in the `.govuk-js-character-count`\n * `<textarea>` inside the element. Displays a message with the remaining number\n * of characters/words available, or the number of characters/words in excess.\n *\n * You can configure the message to only appear after a certain percentage\n * of the available characters/words has been entered.\n *\n * @preserve\n * @augments ConfigurableComponent<CharacterCountConfig>\n */\nexport class CharacterCount extends ConfigurableComponent {\n /** @private */\n $textarea\n\n /** @private */\n $visibleCountMessage\n\n /** @private */\n $screenReaderCountMessage\n\n /**\n * @private\n * @type {number | null}\n */\n lastInputTimestamp = null\n\n /** @private */\n lastInputValue = ''\n\n /**\n * @private\n * @type {number | null}\n */\n valueChecker = null\n\n /** @private */\n i18n\n\n /** @private */\n maxLength;\n\n /**\n * Character count config override\n *\n * To ensure data-attributes take complete precedence, even if they change\n * the type of count, we need to reset the `maxlength` and `maxwords` from\n * the JavaScript config.\n *\n * @internal\n * @param {CharacterCountConfig} datasetConfig - configuration specified by dataset\n * @returns {CharacterCountConfig} - configuration to override by dataset\n */\n [configOverride](datasetConfig) {\n let configOverrides = {}\n if ('maxwords' in datasetConfig || 'maxlength' in datasetConfig) {\n configOverrides = {\n maxlength: undefined,\n maxwords: undefined\n }\n }\n\n return configOverrides\n }\n\n /**\n * @param {Element | null} $root - HTML element to use for character count\n * @param {CharacterCountConfig} [config] - Character count config\n */\n constructor($root, config = {}) {\n super($root, config)\n\n const $textarea = this.$root.querySelector('.govuk-js-character-count')\n if (\n !(\n $textarea instanceof HTMLTextAreaElement ||\n $textarea instanceof HTMLInputElement\n )\n ) {\n throw new ElementError({\n component: CharacterCount,\n element: $textarea,\n expectedType: 'HTMLTextareaElement or HTMLInputElement',\n identifier: 'Form field (`.govuk-js-character-count`)'\n })\n }\n\n // Check for valid config\n const errors = validateConfig(CharacterCount.schema, this.config)\n if (errors[0]) {\n throw new ConfigError(formatErrorMessage(CharacterCount, errors[0]))\n }\n\n this.i18n = new I18n(this.config.i18n, {\n // Read the fallback if necessary rather than have it set in the defaults\n locale: closestAttributeValue(this.$root, 'lang')\n })\n\n // Determine the limit attribute (characters or words)\n this.maxLength = this.config.maxwords ?? this.config.maxlength ?? Infinity\n\n this.$textarea = $textarea\n\n const textareaDescriptionId = `${this.$textarea.id}-info`\n const $textareaDescription = document.getElementById(textareaDescriptionId)\n if (!$textareaDescription) {\n throw new ElementError({\n component: CharacterCount,\n element: $textareaDescription,\n identifier: `Count message (\\`id=\"${textareaDescriptionId}\"\\`)`\n })\n }\n\n // Pre-existing validation error rendered from server\n this.$errorMessage = this.$root.querySelector('.govuk-error-message')\n\n // Inject a description for the textarea if none is present already\n // for when the component was rendered with no maxlength, maxwords\n // nor custom textareaDescriptionText\n // eslint-disable-next-line @typescript-eslint/prefer-regexp-exec\n if ($textareaDescription.textContent.match(/^\\s*$/)) {\n $textareaDescription.textContent = this.i18n.t('textareaDescription', {\n count: this.maxLength\n })\n }\n\n // Move the textarea description to be immediately after the textarea\n // Kept for backwards compatibility\n this.$textarea.insertAdjacentElement('afterend', $textareaDescription)\n\n // Create the *screen reader* specific live-updating counter\n // This doesn't need any styling classes, as it is never visible\n const $screenReaderCountMessage = document.createElement('div')\n $screenReaderCountMessage.className =\n 'govuk-character-count__sr-status govuk-visually-hidden'\n $screenReaderCountMessage.setAttribute('aria-live', 'polite')\n this.$screenReaderCountMessage = $screenReaderCountMessage\n $textareaDescription.insertAdjacentElement(\n 'afterend',\n $screenReaderCountMessage\n )\n\n // Create our live-updating counter element, copying the classes from the\n // textarea description for backwards compatibility as these may have been\n // configured\n const $visibleCountMessage = document.createElement('div')\n $visibleCountMessage.className = $textareaDescription.className\n $visibleCountMessage.classList.add('govuk-character-count__status')\n $visibleCountMessage.setAttribute('aria-hidden', 'true')\n this.$visibleCountMessage = $visibleCountMessage\n $textareaDescription.insertAdjacentElement('afterend', $visibleCountMessage)\n\n // Hide the textarea description\n $textareaDescription.classList.add('govuk-visually-hidden')\n\n // Remove hard limit if set\n this.$textarea.removeAttribute('maxlength')\n\n this.bindChangeEvents()\n\n // When the page is restored after navigating 'back' in some browsers the\n // state of form controls is not restored until *after* the DOMContentLoaded\n // event is fired, so we need to sync after the pageshow event.\n window.addEventListener('pageshow', () => this.updateCountMessage())\n\n // Although we've set up handlers to sync state on the pageshow event, init\n // could be called after those events have fired, for example if they are\n // added to the page dynamically, so update now too.\n this.updateCountMessage()\n }\n\n /**\n * Bind change events\n *\n * Set up event listeners on the $textarea so that the count messages update\n * when the user types.\n *\n * @private\n */\n bindChangeEvents() {\n this.$textarea.addEventListener('keyup', () => this.handleKeyUp())\n\n // Bind focus/blur events to start/stop polling\n this.$textarea.addEventListener('focus', () => this.handleFocus())\n this.$textarea.addEventListener('blur', () => this.handleBlur())\n }\n\n /**\n * Handle key up event\n *\n * Update the visible character counter and keep track of when the last update\n * happened for each keypress\n *\n * @private\n */\n handleKeyUp() {\n this.updateVisibleCountMessage()\n this.lastInputTimestamp = Date.now()\n }\n\n /**\n * Handle focus event\n *\n * Speech recognition software such as Dragon NaturallySpeaking will modify\n * the fields by directly changing its `value`. These changes don't trigger\n * events in JavaScript, so we need to poll to handle when and if they occur.\n *\n * Once the keyup event hasn't been detected for at least 1000 ms (1s), check\n * if the textarea value has changed and update the count message if it has.\n *\n * This is so that the update triggered by the manual comparison doesn't\n * conflict with debounced KeyboardEvent updates.\n *\n * @private\n */\n handleFocus() {\n this.valueChecker = window.setInterval(() => {\n if (\n !this.lastInputTimestamp ||\n Date.now() - 500 >= this.lastInputTimestamp\n ) {\n this.updateIfValueChanged()\n }\n }, 1000)\n }\n\n /**\n * Handle blur event\n *\n * Stop checking the textarea value once the textarea no longer has focus\n *\n * @private\n */\n handleBlur() {\n // Cancel value checking on blur\n if (this.valueChecker) {\n window.clearInterval(this.valueChecker)\n }\n }\n\n /**\n * Update count message if textarea value has changed\n *\n * @private\n */\n updateIfValueChanged() {\n if (this.$textarea.value !== this.lastInputValue) {\n this.lastInputValue = this.$textarea.value\n this.updateCountMessage()\n }\n }\n\n /**\n * Update count message\n *\n * Helper function to update both the visible and screen reader-specific\n * counters simultaneously (e.g. on init)\n *\n * @private\n */\n updateCountMessage() {\n this.updateVisibleCountMessage()\n this.updateScreenReaderCountMessage()\n }\n\n /**\n * Update visible count message\n *\n * @private\n */\n updateVisibleCountMessage() {\n const remainingNumber = this.maxLength - this.count(this.$textarea.value)\n const isError = remainingNumber < 0\n\n // If input is over the threshold, remove the disabled class which renders\n // the counter invisible.\n this.$visibleCountMessage.classList.toggle(\n 'govuk-character-count__message--disabled',\n !this.isOverThreshold()\n )\n\n // Update styles\n if (!this.$errorMessage) {\n // Only toggle the textarea error class if there isn't an error message\n // already, as it may be unrelated to the limit (eg: allowed characters)\n // and would set the border colour back to black.\n this.$textarea.classList.toggle('govuk-textarea--error', isError)\n }\n this.$visibleCountMessage.classList.toggle('govuk-error-message', isError)\n this.$visibleCountMessage.classList.toggle('govuk-hint', !isError)\n\n // Update message\n this.$visibleCountMessage.textContent = this.getCountMessage()\n }\n\n /**\n * Update screen reader count message\n *\n * @private\n */\n updateScreenReaderCountMessage() {\n // If over the threshold, remove the aria-hidden attribute, allowing screen\n // readers to announce the content of the element.\n if (this.isOverThreshold()) {\n this.$screenReaderCountMessage.removeAttribute('aria-hidden')\n } else {\n this.$screenReaderCountMessage.setAttribute('aria-hidden', 'true')\n }\n\n // Update message\n this.$screenReaderCountMessage.textContent = this.getCountMessage()\n }\n\n /**\n * Count the number of characters (or words, if `config.maxwords` is set)\n * in the given text\n *\n * @private\n * @param {string} text - The text to count the characters of\n * @returns {number} the number of characters (or words) in the text\n */\n count(text) {\n if (this.config.maxwords) {\n const tokens = text.match(/\\S+/g) ?? [] // Matches consecutive non-whitespace chars\n return tokens.length\n }\n\n return text.length\n }\n\n /**\n * Get count message\n *\n * @private\n * @returns {string} Status message\n */\n getCountMessage() {\n const remainingNumber = this.maxLength - this.count(this.$textarea.value)\n const countType = this.config.maxwords ? 'words' : 'characters'\n return this.formatCountMessage(remainingNumber, countType)\n }\n\n /**\n * Formats the message shown to users according to what's counted\n * and how many remain\n *\n * @private\n * @param {number} remainingNumber - The number of words/characaters remaining\n * @param {string} countType - \"words\" or \"characters\"\n * @returns {string} Status message\n */\n formatCountMessage(remainingNumber, countType) {\n if (remainingNumber === 0) {\n return this.i18n.t(`${countType}AtLimit`)\n }\n\n const translationKeySuffix =\n remainingNumber < 0 ? 'OverLimit' : 'UnderLimit'\n\n return this.i18n.t(`${countType}${translationKeySuffix}`, {\n count: Math.abs(remainingNumber)\n })\n }\n\n /**\n * Check if count is over threshold\n *\n * Checks whether the value is over the configured threshold for the input.\n * If there is no configured threshold, it is set to 0 and this function will\n * always return true.\n *\n * @private\n * @returns {boolean} true if the current count is over the config.threshold\n * (or no threshold is set)\n */\n isOverThreshold() {\n // No threshold means we're always above threshold so save some computation\n if (!this.config.threshold) {\n return true\n }\n\n // Determine the remaining number of characters/words\n const currentLength = this.count(this.$textarea.value)\n const maxLength = this.maxLength\n\n const thresholdValue = (maxLength * this.config.threshold) / 100\n\n return thresholdValue <= currentLength\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-character-count'\n\n /**\n * Character count default config\n *\n * @see {@link CharacterCountConfig}\n * @constant\n * @type {CharacterCountConfig}\n */\n static defaults = Object.freeze({\n threshold: 0,\n i18n: {\n // Characters\n charactersUnderLimit: {\n one: 'You have %{count} character remaining',\n other: 'You have %{count} characters remaining'\n },\n charactersAtLimit: 'You have 0 characters remaining',\n charactersOverLimit: {\n one: 'You have %{count} character too many',\n other: 'You have %{count} characters too many'\n },\n // Words\n wordsUnderLimit: {\n one: 'You have %{count} word remaining',\n other: 'You have %{count} words remaining'\n },\n wordsAtLimit: 'You have 0 words remaining',\n wordsOverLimit: {\n one: 'You have %{count} word too many',\n other: 'You have %{count} words too many'\n },\n textareaDescription: {\n other: ''\n }\n }\n })\n\n /**\n * Character count config schema\n *\n * @constant\n * @satisfies {Schema<CharacterCountConfig>}\n */\n static schema = Object.freeze({\n properties: {\n i18n: { type: 'object' },\n maxwords: { type: 'number' },\n maxlength: { type: 'number' },\n threshold: { type: 'number' }\n },\n anyOf: [\n {\n required: ['maxwords'],\n errorMessage: 'Either \"maxlength\" or \"maxwords\" must be provided'\n },\n {\n required: ['maxlength'],\n errorMessage: 'Either \"maxlength\" or \"maxwords\" must be provided'\n }\n ]\n })\n}\n\n/**\n * Character count config\n *\n * @see {@link CharacterCount.defaults}\n * @typedef {object} CharacterCountConfig\n * @property {number} [maxlength] - The maximum number of characters.\n * If maxwords is provided, the maxlength option will be ignored.\n * @property {number} [maxwords] - The maximum number of words. If maxwords is\n * provided, the maxlength option will be ignored.\n * @property {number} [threshold=0] - The percentage value of the limit at\n * which point the count message is displayed. If this attribute is set, the\n * count message will be hidden by default.\n * @property {CharacterCountTranslations} [i18n=CharacterCount.defaults.i18n] - Character count translations\n */\n\n/**\n * Character count translations\n *\n * @see {@link CharacterCount.defaults.i18n}\n * @typedef {object} CharacterCountTranslations\n *\n * Messages shown to users as they type. It provides feedback on how many words\n * or characters they have remaining or if they are over the limit. This also\n * includes a message used as an accessible description for the textarea.\n * @property {TranslationPluralForms} [charactersUnderLimit] - Message displayed\n * when the number of characters is under the configured maximum, `maxlength`.\n * This message is displayed visually and through assistive technologies. The\n * component will replace the `%{count}` placeholder with the number of\n * remaining characters. This is a [pluralised list of\n * messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).\n * @property {string} [charactersAtLimit] - Message displayed when the number of\n * characters reaches the configured maximum, `maxlength`. This message is\n * displayed visually and through assistive technologies.\n * @property {TranslationPluralForms} [charactersOverLimit] - Message displayed\n * when the number of characters is over the configured maximum, `maxlength`.\n * This message is displayed visually and through assistive technologies. The\n * component will replace the `%{count}` placeholder with the number of\n * remaining characters. This is a [pluralised list of\n * messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).\n * @property {TranslationPluralForms} [wordsUnderLimit] - Message displayed when\n * the number of words is under the configured maximum, `maxlength`. This\n * message is displayed visually and through assistive technologies. The\n * component will replace the `%{count}` placeholder with the number of\n * remaining words. This is a [pluralised list of\n * messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).\n * @property {string} [wordsAtLimit] - Message displayed when the number of\n * words reaches the configured maximum, `maxlength`. This message is\n * displayed visually and through assistive technologies.\n * @property {TranslationPluralForms} [wordsOverLimit] - Message displayed when\n * the number of words is over the configured maximum, `maxlength`. This\n * message is displayed visually and through assistive technologies. The\n * component will replace the `%{count}` placeholder with the number of\n * remaining words. This is a [pluralised list of\n * messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).\n * @property {TranslationPluralForms} [textareaDescription] - Message made\n * available to assistive technologies, if none is already present in the\n * HTML, to describe that the component accepts only a limited amount of\n * content. It is visible on the page when JavaScript is unavailable. The\n * component will replace the `%{count}` placeholder with the value of the\n * `maxlength` or `maxwords` parameter.\n */\n\n/**\n * @import { Schema } from '../../common/configuration.mjs'\n * @import { TranslationPluralForms } from '../../i18n.mjs'\n */\n","import { Component } from '../../component.mjs'\nimport { ElementError } from '../../errors/index.mjs'\n\n/**\n * Checkboxes component\n *\n * @preserve\n */\nexport class Checkboxes extends Component {\n /** @private */\n $inputs\n\n /**\n * Checkboxes can be associated with a 'conditionally revealed' content block\n * – for example, a checkbox for 'Phone' could reveal an additional form field\n * for the user to enter their phone number.\n *\n * These associations are made using a `data-aria-controls` attribute, which\n * is promoted to an aria-controls attribute during initialisation.\n *\n * We also need to restore the state of any conditional reveals on the page\n * (for example if the user has navigated back), and set up event handlers to\n * keep the reveal in sync with the checkbox state.\n *\n * @param {Element | null} $root - HTML element to use for checkboxes\n */\n constructor($root) {\n super($root)\n\n const $inputs = this.$root.querySelectorAll('input[type=\"checkbox\"]')\n if (!$inputs.length) {\n throw new ElementError({\n component: Checkboxes,\n identifier: 'Form inputs (`<input type=\"checkbox\">`)'\n })\n }\n\n this.$inputs = $inputs\n\n this.$inputs.forEach(($input) => {\n const targetId = $input.getAttribute('data-aria-controls')\n\n // Skip radios without data-aria-controls attributes\n if (!targetId) {\n return\n }\n\n // Throw if target conditional element does not exist.\n if (!document.getElementById(targetId)) {\n throw new ElementError({\n component: Checkboxes,\n identifier: `Conditional reveal (\\`id=\"${targetId}\"\\`)`\n })\n }\n\n // Promote the data-aria-controls attribute to a aria-controls attribute\n // so that the relationship is exposed in the AOM\n $input.setAttribute('aria-controls', targetId)\n $input.removeAttribute('data-aria-controls')\n })\n\n // When the page is restored after navigating 'back' in some browsers the\n // state of form controls is not restored until *after* the DOMContentLoaded\n // event is fired, so we need to sync after the pageshow event.\n window.addEventListener('pageshow', () => this.syncAllConditionalReveals())\n\n // Although we've set up handlers to sync state on the pageshow event, init\n // could be called after those events have fired, for example if they are\n // added to the page dynamically, so sync now too.\n this.syncAllConditionalReveals()\n\n // Handle events\n this.$root.addEventListener('click', (event) => this.handleClick(event))\n }\n\n /**\n * Sync the conditional reveal states for all checkboxes in this component.\n *\n * @private\n */\n syncAllConditionalReveals() {\n this.$inputs.forEach(($input) =>\n this.syncConditionalRevealWithInputState($input)\n )\n }\n\n /**\n * Sync conditional reveal with the input state\n *\n * Synchronise the visibility of the conditional reveal, and its accessible\n * state, with the input's checked state.\n *\n * @private\n * @param {HTMLInputElement} $input - Checkbox input\n */\n syncConditionalRevealWithInputState($input) {\n const targetId = $input.getAttribute('aria-controls')\n if (!targetId) {\n return\n }\n\n const $target = document.getElementById(targetId)\n if ($target?.classList.contains('govuk-checkboxes__conditional')) {\n const inputIsChecked = $input.checked\n\n $input.setAttribute('aria-expanded', inputIsChecked.toString())\n $target.classList.toggle(\n 'govuk-checkboxes__conditional--hidden',\n !inputIsChecked\n )\n }\n }\n\n /**\n * Uncheck other checkboxes\n *\n * Find any other checkbox inputs with the same name value, and uncheck them.\n * This is useful for when a “None of these\" checkbox is checked.\n *\n * @private\n * @param {HTMLInputElement} $input - Checkbox input\n */\n unCheckAllInputsExcept($input) {\n const allInputsWithSameName = document.querySelectorAll(\n `input[type=\"checkbox\"][name=\"${$input.name}\"]`\n )\n\n allInputsWithSameName.forEach(($inputWithSameName) => {\n const hasSameFormOwner = $input.form === $inputWithSameName.form\n if (hasSameFormOwner && $inputWithSameName !== $input) {\n $inputWithSameName.checked = false\n this.syncConditionalRevealWithInputState($inputWithSameName)\n }\n })\n }\n\n /**\n * Uncheck exclusive checkboxes\n *\n * Find any checkbox inputs with the same name value and the 'exclusive'\n * behaviour, and uncheck them. This helps prevent someone checking both a\n * regular checkbox and a \"None of these\" checkbox in the same fieldset.\n *\n * @private\n * @param {HTMLInputElement} $input - Checkbox input\n */\n unCheckExclusiveInputs($input) {\n const allInputsWithSameNameAndExclusiveBehaviour =\n document.querySelectorAll(\n `input[data-behaviour=\"exclusive\"][type=\"checkbox\"][name=\"${$input.name}\"]`\n )\n\n allInputsWithSameNameAndExclusiveBehaviour.forEach(($exclusiveInput) => {\n const hasSameFormOwner = $input.form === $exclusiveInput.form\n if (hasSameFormOwner) {\n $exclusiveInput.checked = false\n this.syncConditionalRevealWithInputState($exclusiveInput)\n }\n })\n }\n\n /**\n * Click event handler\n *\n * Handle a click within the component root – if the click occurred on a checkbox,\n * sync the state of any associated conditional reveal with the checkbox\n * state.\n *\n * @private\n * @param {MouseEvent} event - Click event\n */\n handleClick(event) {\n const $clickedInput = event.target\n\n // Ignore clicks on things that aren't checkbox inputs\n if (\n !($clickedInput instanceof HTMLInputElement) ||\n $clickedInput.type !== 'checkbox'\n ) {\n return\n }\n\n // If the checkbox conditionally-reveals some content, sync the state\n const hasAriaControls = $clickedInput.getAttribute('aria-controls')\n if (hasAriaControls) {\n this.syncConditionalRevealWithInputState($clickedInput)\n }\n\n // No further behaviour needed for unchecking\n if (!$clickedInput.checked) {\n return\n }\n\n // Handle 'exclusive' checkbox behaviour (ie \"None of these\")\n const hasBehaviourExclusive =\n $clickedInput.getAttribute('data-behaviour') === 'exclusive'\n if (hasBehaviourExclusive) {\n this.unCheckAllInputsExcept($clickedInput)\n } else {\n this.unCheckExclusiveInputs($clickedInput)\n }\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-checkboxes'\n}\n","import { ConfigurableComponent } from '../../common/configuration.mjs'\nimport { setFocus } from '../../common/index.mjs'\n\n/**\n * Error summary component\n *\n * Takes focus on initialisation for accessible announcement, unless disabled in\n * configuration.\n *\n * @preserve\n * @augments ConfigurableComponent<ErrorSummaryConfig>\n */\nexport class ErrorSummary extends ConfigurableComponent {\n /**\n * @param {Element | null} $root - HTML element to use for error summary\n * @param {ErrorSummaryConfig} [config] - Error summary config\n */\n constructor($root, config = {}) {\n super($root, config)\n\n /**\n * Focus the error summary\n */\n if (!this.config.disableAutoFocus) {\n setFocus(this.$root)\n }\n\n this.$root.addEventListener('click', (event) => this.handleClick(event))\n }\n\n /**\n * Click event handler\n *\n * @private\n * @param {MouseEvent} event - Click event\n */\n handleClick(event) {\n const $target = event.target\n if ($target && this.focusTarget($target)) {\n event.preventDefault()\n }\n }\n\n /**\n * Focus the target element\n *\n * By default, the browser will scroll the target into view. Because our\n * labels or legends appear above the input, this means the user will be\n * presented with an input without any context, as the label or legend will be\n * off the top of the screen.\n *\n * Manually handling the click event, scrolling the question into view and\n * then focussing the element solves this.\n *\n * This also results in the label and/or legend being announced correctly in\n * NVDA (as tested in 2018.3.2) - without this only the field type is\n * announced (e.g. \"Edit, has autocomplete\").\n *\n * @private\n * @param {EventTarget} $target - Event target\n * @returns {boolean} True if the target was able to be focussed\n */\n focusTarget($target) {\n // If the element that was clicked was not a link, return early\n if (!($target instanceof HTMLAnchorElement)) {\n return false\n }\n\n const inputId = $target.hash.replace('#', '')\n if (!inputId) {\n return false\n }\n\n const $input = document.getElementById(inputId)\n if (!$input) {\n return false\n }\n\n const $legendOrLabel = this.getAssociatedLegendOrLabel($input)\n if (!$legendOrLabel) {\n return false\n }\n\n // Scroll the legend or label into view *before* calling focus on the input\n // to avoid extra scrolling in browsers that don't support `preventScroll`\n // (which at time of writing is most of them...)\n $legendOrLabel.scrollIntoView()\n $input.focus({ preventScroll: true })\n\n return true\n }\n\n /**\n * Get associated legend or label\n *\n * Returns the first element that exists from this list:\n *\n * - The `<legend>` associated with the closest `<fieldset>` ancestor, as long\n * as the top of it is no more than half a viewport height away from the\n * bottom of the input\n * - The first `<label>` that is associated with the input using for=\"inputId\"\n * - The closest parent `<label>`\n *\n * @private\n * @param {Element} $input - The input\n * @returns {Element | null} Associated legend or label, or null if no\n * associated legend or label can be found\n */\n getAssociatedLegendOrLabel($input) {\n const $fieldset = $input.closest('fieldset')\n\n if ($fieldset) {\n const $legends = $fieldset.getElementsByTagName('legend')\n\n if ($legends.length) {\n const $candidateLegend = $legends[0]\n\n // If the input type is radio or checkbox, always use the legend if\n // there is one.\n if (\n $input instanceof HTMLInputElement &&\n ($input.type === 'checkbox' || $input.type === 'radio')\n ) {\n return $candidateLegend\n }\n\n // For other input types, only scroll to the fieldset’s legend (instead\n // of the label associated with the input) if the input would end up in\n // the top half of the screen.\n //\n // This should avoid situations where the input either ends up off the\n // screen, or obscured by a software keyboard.\n const legendTop = $candidateLegend.getBoundingClientRect().top\n const inputRect = $input.getBoundingClientRect()\n\n // If the browser doesn't support Element.getBoundingClientRect().height\n // or window.innerHeight (like IE8), bail and just link to the label.\n if (inputRect.height && window.innerHeight) {\n const inputBottom = inputRect.top + inputRect.height\n\n if (inputBottom - legendTop < window.innerHeight / 2) {\n return $candidateLegend\n }\n }\n }\n }\n\n return (\n document.querySelector(`label[for='${$input.getAttribute('id')}']`) ??\n $input.closest('label')\n )\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-error-summary'\n\n /**\n * Error summary default config\n *\n * @see {@link ErrorSummaryConfig}\n * @constant\n * @type {ErrorSummaryConfig}\n */\n static defaults = Object.freeze({\n disableAutoFocus: false\n })\n\n /**\n * Error summary config schema\n *\n * @constant\n * @satisfies {Schema<ErrorSummaryConfig>}\n */\n static schema = Object.freeze({\n properties: {\n disableAutoFocus: { type: 'boolean' }\n }\n })\n}\n\n/**\n * Error summary config\n *\n * @typedef {object} ErrorSummaryConfig\n * @property {boolean} [disableAutoFocus=false] - If set to `true` the error\n * summary will not be focussed when the page loads.\n */\n\n/**\n * @import { Schema } from '../../common/configuration.mjs'\n */\n","import { getBreakpoint } from '../../common/index.mjs'\nimport { Component } from '../../component.mjs'\nimport { ElementError } from '../../errors/index.mjs'\n\n/**\n * Header component\n *\n * @preserve\n */\nexport class Header extends Component {\n /** @private */\n $menuButton\n\n /** @private */\n $menu\n\n /**\n * Save the opened/closed state for the nav in memory so that we can\n * accurately maintain state when the screen is changed from small to big and\n * back to small\n *\n * @private\n */\n menuIsOpen = false\n\n /**\n * A global const for storing a matchMedia instance which we'll use to detect\n * when a screen size change happens. We rely on it being null if the feature\n * isn't available to initially apply hidden attributes\n *\n * @private\n * @type {MediaQueryList | null}\n */\n mql = null\n\n /**\n * Apply a matchMedia for desktop which will trigger a state sync if the\n * browser viewport moves between states.\n *\n * @param {Element | null} $root - HTML element to use for header\n */\n constructor($root) {\n super($root)\n\n const $menuButton = this.$root.querySelector('.govuk-js-header-toggle')\n\n // Headers don't necessarily have a navigation. When they don't, the menu\n // toggle won't be rendered by our macro (or may be omitted when writing\n // plain HTML)\n if (!$menuButton) {\n return this\n }\n\n // Pad the header logo so it doesn't overlap the menu button\n this.$root.classList.add('govuk-header--with-js-navigation')\n\n const menuId = $menuButton.getAttribute('aria-controls')\n if (!menuId) {\n throw new ElementError({\n component: Header,\n identifier:\n 'Navigation button (`<button class=\"govuk-js-header-toggle\">`) attribute (`aria-controls`)'\n })\n }\n\n const $menu = document.getElementById(menuId)\n if (!$menu) {\n throw new ElementError({\n component: Header,\n element: $menu,\n identifier: `Navigation (\\`<ul id=\"${menuId}\">\\`)`\n })\n }\n\n this.$menu = $menu\n this.$menuButton = $menuButton\n\n this.setupResponsiveChecks()\n\n this.$menuButton.addEventListener('click', () =>\n this.handleMenuButtonClick()\n )\n }\n\n /**\n * Setup viewport resize check\n *\n * @private\n */\n setupResponsiveChecks() {\n const breakpoint = getBreakpoint('desktop')\n\n if (!breakpoint.value) {\n throw new ElementError({\n component: Header,\n identifier: `CSS custom property (\\`${breakpoint.property}\\`) on pseudo-class \\`:root\\``\n })\n }\n\n // Media query list for GOV.UK Frontend desktop breakpoint\n this.mql = window.matchMedia(`(min-width: ${breakpoint.value})`)\n\n // MediaQueryList.addEventListener isn't supported by Safari < 14 so we need\n // to be able to fall back to the deprecated MediaQueryList.addListener\n if ('addEventListener' in this.mql) {\n this.mql.addEventListener('change', () => this.checkMode())\n } else {\n // @ts-expect-error Property 'addListener' does not exist\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n this.mql.addListener(() => this.checkMode())\n }\n\n this.checkMode()\n }\n\n /**\n * Sync menu state\n *\n * Uses the global variable menuIsOpen to correctly set the accessible and\n * visual states of the menu and the menu button.\n * Additionally will force the menu to be visible and the menu button to be\n * hidden if the matchMedia is triggered to desktop.\n *\n * @private\n */\n checkMode() {\n if (!this.mql || !this.$menu || !this.$menuButton) {\n return\n }\n\n if (this.mql.matches) {\n this.$menu.removeAttribute('hidden')\n this.$menuButton.setAttribute('hidden', '')\n } else {\n this.$menuButton.removeAttribute('hidden')\n this.$menuButton.setAttribute('aria-expanded', this.menuIsOpen.toString())\n\n if (this.menuIsOpen) {\n this.$menu.removeAttribute('hidden')\n } else {\n this.$menu.setAttribute('hidden', '')\n }\n }\n }\n\n /**\n * Handle menu button click\n *\n * When the menu button is clicked, change the visibility of the menu and then\n * sync the accessibility state and menu button state\n *\n * @private\n */\n handleMenuButtonClick() {\n this.menuIsOpen = !this.menuIsOpen\n this.checkMode()\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-header'\n}\n","import { ConfigurableComponent } from '../../common/configuration.mjs'\nimport { setFocus } from '../../common/index.mjs'\n\n/**\n * Notification Banner component\n *\n * @preserve\n * @augments ConfigurableComponent<NotificationBannerConfig>\n */\nexport class NotificationBanner extends ConfigurableComponent {\n /**\n * @param {Element | null} $root - HTML element to use for notification banner\n * @param {NotificationBannerConfig} [config] - Notification banner config\n */\n constructor($root, config = {}) {\n super($root, config)\n\n /**\n * Focus the notification banner\n *\n * If `role=\"alert\"` is set, focus the element to help some assistive\n * technologies prioritise announcing it.\n *\n * You can turn off the auto-focus functionality by setting\n * `data-disable-auto-focus=\"true\"` in the component HTML. You might wish to\n * do this based on user research findings, or to avoid a clash with another\n * element which should be focused when the page loads.\n */\n if (\n this.$root.getAttribute('role') === 'alert' &&\n !this.config.disableAutoFocus\n ) {\n setFocus(this.$root)\n }\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-notification-banner'\n\n /**\n * Notification banner default config\n *\n * @see {@link NotificationBannerConfig}\n * @constant\n * @type {NotificationBannerConfig}\n */\n static defaults = Object.freeze({\n disableAutoFocus: false\n })\n\n /**\n * Notification banner config schema\n *\n * @constant\n * @satisfies {Schema<NotificationBannerConfig>}\n */\n static schema = Object.freeze({\n properties: {\n disableAutoFocus: { type: 'boolean' }\n }\n })\n}\n\n/**\n * Notification banner config\n *\n * @typedef {object} NotificationBannerConfig\n * @property {boolean} [disableAutoFocus=false] - If set to `true` the\n * notification banner will not be focussed when the page loads. This only\n * applies if the component has a `role` of `alert` – in other cases the\n * component will not be focused on page load, regardless of this option.\n */\n\n/**\n * @import { Schema } from '../../common/configuration.mjs'\n */\n","import { Component } from '../../component.mjs'\nimport { ElementError } from '../../errors/index.mjs'\n\n/**\n * Radios component\n *\n * @preserve\n */\nexport class Radios extends Component {\n /** @private */\n $inputs\n\n /**\n * Radios can be associated with a 'conditionally revealed' content block –\n * for example, a radio for 'Phone' could reveal an additional form field for\n * the user to enter their phone number.\n *\n * These associations are made using a `data-aria-controls` attribute, which\n * is promoted to an aria-controls attribute during initialisation.\n *\n * We also need to restore the state of any conditional reveals on the page\n * (for example if the user has navigated back), and set up event handlers to\n * keep the reveal in sync with the radio state.\n *\n * @param {Element | null} $root - HTML element to use for radios\n */\n constructor($root) {\n super($root)\n\n const $inputs = this.$root.querySelectorAll('input[type=\"radio\"]')\n if (!$inputs.length) {\n throw new ElementError({\n component: Radios,\n identifier: 'Form inputs (`<input type=\"radio\">`)'\n })\n }\n\n this.$inputs = $inputs\n\n this.$inputs.forEach(($input) => {\n const targetId = $input.getAttribute('data-aria-controls')\n\n // Skip radios without data-aria-controls attributes\n if (!targetId) {\n return\n }\n\n // Throw if target conditional element does not exist.\n if (!document.getElementById(targetId)) {\n throw new ElementError({\n component: Radios,\n identifier: `Conditional reveal (\\`id=\"${targetId}\"\\`)`\n })\n }\n\n // Promote the data-aria-controls attribute to a aria-controls attribute\n // so that the relationship is exposed in the AOM\n $input.setAttribute('aria-controls', targetId)\n $input.removeAttribute('data-aria-controls')\n })\n\n // When the page is restored after navigating 'back' in some browsers the\n // state of form controls is not restored until *after* the DOMContentLoaded\n // event is fired, so we need to sync after the pageshow event.\n window.addEventListener('pageshow', () => this.syncAllConditionalReveals())\n\n // Although we've set up handlers to sync state on the pageshow event, init\n // could be called after those events have fired, for example if they are\n // added to the page dynamically, so sync now too.\n this.syncAllConditionalReveals()\n\n // Handle events\n this.$root.addEventListener('click', (event) => this.handleClick(event))\n }\n\n /**\n * Sync the conditional reveal states for all radio buttons in this component.\n *\n * @private\n */\n syncAllConditionalReveals() {\n this.$inputs.forEach(($input) =>\n this.syncConditionalRevealWithInputState($input)\n )\n }\n\n /**\n * Sync conditional reveal with the input state\n *\n * Synchronise the visibility of the conditional reveal, and its accessible\n * state, with the input's checked state.\n *\n * @private\n * @param {HTMLInputElement} $input - Radio input\n */\n syncConditionalRevealWithInputState($input) {\n const targetId = $input.getAttribute('aria-controls')\n if (!targetId) {\n return\n }\n\n const $target = document.getElementById(targetId)\n if ($target?.classList.contains('govuk-radios__conditional')) {\n const inputIsChecked = $input.checked\n\n $input.setAttribute('aria-expanded', inputIsChecked.toString())\n $target.classList.toggle(\n 'govuk-radios__conditional--hidden',\n !inputIsChecked\n )\n }\n }\n\n /**\n * Click event handler\n *\n * Handle a click within the component root – if the click occurred on a radio, sync\n * the state of the conditional reveal for all radio buttons in the same form\n * with the same name (because checking one radio could have un-checked a\n * radio under the root of another Radio component)\n *\n * @private\n * @param {MouseEvent} event - Click event\n */\n handleClick(event) {\n const $clickedInput = event.target\n\n // Ignore clicks on things that aren't radio buttons\n if (\n !($clickedInput instanceof HTMLInputElement) ||\n $clickedInput.type !== 'radio'\n ) {\n return\n }\n\n // We only need to consider radios with conditional reveals, which will have\n // aria-controls attributes.\n const $allInputs = document.querySelectorAll(\n 'input[type=\"radio\"][aria-controls]'\n )\n\n const $clickedInputForm = $clickedInput.form\n const $clickedInputName = $clickedInput.name\n\n $allInputs.forEach(($input) => {\n const hasSameFormOwner = $input.form === $clickedInputForm\n const hasSameName = $input.name === $clickedInputName\n\n if (hasSameName && hasSameFormOwner) {\n this.syncConditionalRevealWithInputState($input)\n }\n })\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-radios'\n}\n","import { setFocus } from '../../common/index.mjs'\nimport { Component } from '../../component.mjs'\nimport { ElementError } from '../../errors/index.mjs'\n\n/**\n * Skip link component\n *\n * @preserve\n * @augments Component<HTMLAnchorElement>\n */\nexport class SkipLink extends Component {\n static elementType = HTMLAnchorElement\n\n /**\n * @param {Element | null} $root - HTML element to use for skip link\n * @throws {ElementError} when $root is not set or the wrong type\n * @throws {ElementError} when $root.hash does not contain a hash\n * @throws {ElementError} when the linked element is missing or the wrong type\n */\n constructor($root) {\n super($root)\n\n const hash = this.$root.hash\n const href = this.$root.getAttribute('href') ?? ''\n\n // Return early for external URLs or links to other pages\n if (\n this.$root.origin !== window.location.origin ||\n this.$root.pathname !== window.location.pathname\n ) {\n return\n }\n\n const linkedElementId = hash.replace('#', '')\n\n // Check link path matching current page\n if (!linkedElementId) {\n throw new ElementError(\n `Skip link: Target link (\\`href=\"${href}\"\\`) has no hash fragment`\n )\n }\n\n const $linkedElement = document.getElementById(linkedElementId)\n\n // Check for link target element\n if (!$linkedElement) {\n throw new ElementError({\n component: SkipLink,\n element: $linkedElement,\n identifier: `Target content (\\`id=\"${linkedElementId}\"\\`)`\n })\n }\n\n /**\n * Focus the linked element on click\n *\n * Adds a helper CSS class to hide native focus styles,\n * but removes it on blur to restore native focus styles\n */\n this.$root.addEventListener('click', () =>\n setFocus($linkedElement, {\n onBeforeFocus() {\n $linkedElement.classList.add('govuk-skip-link-focused-element')\n },\n onBlur() {\n $linkedElement.classList.remove('govuk-skip-link-focused-element')\n }\n })\n )\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-skip-link'\n}\n","import { initAllAutocomplete as initAllAutocompleteImp } from '~/src/client/javascripts/autocomplete.js'\nimport { initFileUpload as initFileUploadImp } from '~/src/client/javascripts/file-upload.js'\nimport { initAllGovuk as initAllGovukImp } from '~/src/client/javascripts/govuk.js'\nimport { initPreviewCloseLink as initPreviewCloseLinkImp } from '~/src/client/javascripts/preview-close-link.js'\nexport { initMaps } from '~/src/client/javascripts/location-map.js'\n\nexport const initAllGovuk = initAllGovukImp\nexport const initAllAutocomplete = initAllAutocompleteImp\nexport const initFileUpload = initFileUploadImp\nexport const initPreviewCloseLink = initPreviewCloseLinkImp\n\n/**\n * Initialise all clientside components (but not maps as this will be an opt-in for now given the additional UMD assets that are required)\n */\nexport function initAll() {\n initAllGovuk()\n initAllAutocomplete()\n initFileUpload()\n initPreviewCloseLink()\n}\n","import {\n Button,\n CharacterCount,\n Checkboxes,\n ErrorSummary,\n Header,\n NotificationBanner,\n Radios,\n SkipLink,\n createAll\n} from 'govuk-frontend'\n\nexport function initAllGovuk() {\n createAll(Button)\n createAll(CharacterCount)\n createAll(Checkboxes)\n createAll(ErrorSummary)\n createAll(Header)\n createAll(NotificationBanner)\n createAll(Radios)\n createAll(SkipLink)\n}\n","/**\n * Initialise autocomplete\n * @param {HTMLSelectElement | null} $select\n * @param {(config: object) => void} init\n */\nfunction initAutocomplete($select, init) {\n if (!$select) {\n return\n }\n\n const config = {\n id: $select.id,\n selectElement: $select\n }\n\n init(config)\n\n /** @type {HTMLInputElement | null} */\n const $input = document.querySelector(`#${config.id}`)\n\n // Allowed values for input\n const inputValues = [...$select.options].map((option) => option.text)\n\n // Reset select when input value is not allowed\n $input?.addEventListener('blur', () => {\n if (!$input.value || !inputValues.includes($input.value)) {\n $select.value = ''\n }\n })\n}\n\nexport function initAllAutocomplete() {\n // Find all autocompletes\n const $autocompletes = document.querySelectorAll(\n `[data-module=\"govuk-accessible-autocomplete\"]`\n )\n\n // Lazy load autocomplete component\n if ($autocompletes.length) {\n // @ts-expect-error -- No types available\n import('accessible-autocomplete')\n .then((component) => {\n const { default: accessibleAutocomplete } = component\n\n // Initialise each autocomplete\n $autocompletes.forEach(($module) =>\n initAutocomplete(\n $module.querySelector('select'),\n accessibleAutocomplete.enhanceSelectElement\n )\n )\n })\n\n // eslint-disable-next-line no-console\n .catch(console.error)\n }\n}\n","export function initPreviewCloseLink() {\n // Show preview close link via `rel=\"opener\"`\n if (window.opener) {\n const $closeLink = document.querySelector('.js-preview-banner-close')\n\n $closeLink?.removeAttribute('hidden')\n $closeLink?.addEventListener('click', (event) => {\n event.preventDefault()\n window.close()\n })\n }\n}\n"],"names":["leafPrototypes","getProto","inProgress","dmsSeparator","Dms","separator","char","parse","dms","isNaN","parseFloat","isFinite","Number","dmsParts","String","trim","replace","split","length","splice","NaN","deg","test","toDms","format","dp","undefined","Infinity","Math","abs","d","m","s","toFixed","floor","slice","toLat","lat","wrap90","toLon","lon","wrap180","toBrng","brng","wrap360","fromLocale","str","locale","toLocaleString","thousands","decimal","toLocale","compassPoint","bearing","precision","includes","RangeError","n","round","degrees","x","p","prototype","toRadians","this","PI","toDegrees","Vector3d","constructor","y","z","TypeError","sqrt","plus","v","minus","times","dividedBy","dot","cross","negate","unit","norm","angleTo","sign","atan2","rotateAround","axis","angle","a","sin","c","cos","t","r","rp","toString","ellipsoids","WGS84","b","f","datums","ellipsoid","Object","freeze","LatLonEllipsoidal","height","_lat","_lon","_height","latitude","lng","longitude","datum","_datum","args","ll","type","Array","isArray","coordinates","JSON","stringify","toCartesian","referenceFrame","h","eSq","Cartesian","equals","point","EPSILON","epoch","dpHeight","super","toLatLon","e2","Airy1830","AiryModified","Bessel1841","Clarke1866","Clarke1880IGN","GRS80","Intl1924","WGS72","ED50","transform","ETRS89","Irl1975","NAD27","NAD83","NTF","OSGB36","Potsdam","TokyoJapan","keys","forEach","e","LatLonEllipsoidal_Datum","pop","convertDatum","toDatum","cartesian","Cartesian_Datum","deprecatedDatum","console","info","latLon","oldCartesian","map","newCartesian","applyTransform","x1","y1","z1","tx","ty","tz","rx","ry","rz","nationalGrid","trueOrigin","falseOrigin","easting","northing","scaleFactor","OsGridRef","E","N","E0","N0","F0","n2","n3","M","pow","tan","dE","dE2","dE3","dE4","dE5","LatLon_OsGridRef","gridref","match","Error","l1","toUpperCase","charCodeAt","l2","e100km","n100km","en","padEnd","digits","useGrouping","minimumIntegerDigits","maximumFractionDigits","letterPair","fromCharCode","padStart","toOsGrid","message","osgbED","eastingNorthingToLatLong","latLong","long","osGridRefToLatLong","osGridRef","defaultConfig","zoom","center","COMPANY_SYMBOL_CODE","LOCATION_FIELD_SELECTOR","EVENTS","interactMarkerChange","defaultData","VTS_OUTDOOR_URL","VTS_DARK_URL","VTS_BLACK_AND_WHITE_URL","initMaps","config","assetPath","apiPath","data","locations","document","querySelectorAll","form","querySelector","buttons","from","addEventListener","submitter","HTMLButtonElement","preventDefault","formSubmitFactory","location","index","_getInitMapConfig","HTMLDivElement","locationInputs","locationType","dataset","locationtype","mapContainer","createElement","mapId","setAttribute","initConfig","locationField","latInput","longInput","getLatLongInputs","result","validateLatLong","value","valid","getInitMapCenterConfig","getInitLatLongMapConfig","eastingInput","northingInput","getEastingNorthingInputs","validateEastingNorthing","latlong","getInitEastingNorthingMapConfig","validateOsGridRef","getOsGridRefInput","getInitOsGridRefMapConfig","getInitMapConfig","after","interactPlugin","mapsConfig","logoAltText","defra","window","dataLayers","markerColor","outdoor","dark","interactionMode","multiSelect","InteractiveMap","_extends","mapProvider","maplibreProvider","reverseGeocodeProvider","openNamesProvider","url","behaviour","minZoom","maxZoom","containerHeight","enableZoomControls","transformRequest","makeTileRequestTransformer","plugins","mapStylesPlugin","mapStyles","id","label","thumbnail","logo","attribution","fromCodePoint","Date","getFullYear","backgroundColor","mapColorScheme","appColorScheme","searchPlugin","osNamesURL","width","showMarker","scaleBarPlugin","units","createMap","on","onUpdateInputs","centerMap","coords","bindLatLongField","LatLon","latLongToEastingNorthing","bindEastingNorthingField","osGridRefInput","latLongToOsGridRef","bindOsGridRefField","addPanel","showLabel","mobile","slot","initiallyOpen","dismissable","modal","tablet","desktop","html","enable","processLocation","resourceType","startsWith","origin","headers","encodeURIComponent","path","substring","spritesPath","strLat","strLong","strEasting","strNorthing","exec","inputs","input","markers","addMarker","flyTo","essential","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","exports","module","__webpack_modules__","getPrototypeOf","obj","mode","__esModule","then","ns","create","def","current","indexOf","getOwnPropertyNames","key","definition","o","defineProperty","enumerable","get","chunkId","Promise","all","reduce","promises","u","prop","hasOwnProperty","call","l","done","push","script","needAttach","scripts","getElementsByTagName","i","getAttribute","charset","nc","src","onScriptComplete","prev","event","onerror","onload","clearTimeout","timeout","doneFns","parentNode","removeChild","fn","setTimeout","bind","target","head","appendChild","Symbol","toStringTag","scriptUrl","installedChunks","j","installedChunkData","promise","resolve","reject","error","errorType","realSrc","name","request","webpackJsonpCallback","parentChunkLoadingFunction","chunkIds","moreModules","runtime","some","chunkLoadingGlobal","self","ARIA_DESCRIBEDBY","ERROR_SUMMARY_TITLE_ID","showError","errorSummary","fileInput","getElementById","removeAttribute","innerHTML","formGroup","closest","classList","add","inputId","errorMessage","className","insertBefore","reloadPage","history","replaceState","href","pathname","initUpload","_Array$from$find","uploadButton","continueButton","find","button","textContent","formElement","selectedFile","isSubmitting","uploadId","files","statusText","_selectedFile$name3","container","uploadForm","HTMLFormElement","fileCountP","statusAnnouncer","_fileCountP$parentNod","nextSibling","addStatusAnnouncerToDOM","_unused","_unused2","body","createOrUpdateStatusAnnouncer","summaryList","buttonGroup","findOrCreateSummaryList","existingRow","remove","row","_selectedFile$name","_selectedFile$name2","createFileRow","firstChild","renderSummary","focus","disabled","handleStandardFormSubmission","_formElement$dataset$","action","formData","FormData","isLocalDev","proxyUrl","uploadUrl","fetchOptions","method","redirect","fetch","attempts","interval","setInterval","clearInterval","uploadStatusUrl","segments","filter","Boolean","join","buildUploadStatusUrl","Accept","response","ok","json","uploadStatus","catch","pollUploadStatus","handleAjaxFormSubmission","setFocus","$element","options","_options$onBeforeFocu","isFocusable","onBlur","_options$onBlur","once","onBeforeFocus","isSupported","$scope","contains","isObject","option","isScope","Element","Document","formatErrorMessage","Component","moduleName","GOVUKFrontendError","SupportError","supportMessage","HTMLScriptElement","ConfigError","ElementError","messageOrOptions","component","identifier","element","expectedType","InitError","componentOrMessage","$root","_$root","childConstructor","elementType","checkSupport","checkInitialised","HTMLElement","hasAttribute","isInitialised","configOverride","for","ConfigurableComponent","param","_config","defaults","datasetConfig","schema","out","entries","properties","entry","namespace","property","field","normaliseString","extractConfigByNamespace","normaliseDataset","mergeConfigs","trimmedValue","output","outputType","configObjects","formattedConfigObject","configObject","override","newObject","keyParts","createAll","scopeOrOptions","$elements","onError","scope","normaliseOptions","_options$scope","log","Button","debounceFormSubmitTimer","handleKeyDown","debounce","$target","click","preventDoubleClick","DEBOUNCE_TIMEOUT_IN_SECONDS","closestAttributeValue","attributeName","$closestElementWithAttribute","I18n","translations","_config$locale","documentElement","lang","lookupKey","translation","count","translationPluralForm","getPluralSuffix","replacePlaceholders","translationString","formatter","Intl","NumberFormat","supportedLocalesOf","placeholderWithBraces","placeholderKey","placeholderValue","hasIntlPluralRulesSupport","PluralRules","preferredForm","select","warn","CharacterCount","configOverrides","maxlength","maxwords","_ref","_this$config$maxwords","$textarea","$visibleCountMessage","$screenReaderCountMessage","lastInputTimestamp","lastInputValue","valueChecker","i18n","maxLength","HTMLTextAreaElement","HTMLInputElement","errors","validationErrors","conditions","required","every","validateConfig","textareaDescriptionId","$textareaDescription","$errorMessage","insertAdjacentElement","bindChangeEvents","updateCountMessage","handleKeyUp","handleFocus","handleBlur","updateVisibleCountMessage","now","updateIfValueChanged","updateScreenReaderCountMessage","isError","toggle","isOverThreshold","getCountMessage","text","_text$match","remainingNumber","countType","formatCountMessage","translationKeySuffix","threshold","currentLength","charactersUnderLimit","one","other","charactersAtLimit","charactersOverLimit","wordsUnderLimit","wordsAtLimit","wordsOverLimit","textareaDescription","anyOf","Checkboxes","$inputs","$input","targetId","syncAllConditionalReveals","handleClick","syncConditionalRevealWithInputState","inputIsChecked","checked","unCheckAllInputsExcept","$inputWithSameName","unCheckExclusiveInputs","$exclusiveInput","$clickedInput","ErrorSummary","disableAutoFocus","focusTarget","HTMLAnchorElement","hash","$legendOrLabel","getAssociatedLegendOrLabel","scrollIntoView","preventScroll","_document$querySelect","$fieldset","$legends","$candidateLegend","legendTop","getBoundingClientRect","top","inputRect","innerHeight","Header","$menuButton","$menu","menuIsOpen","mql","menuId","setupResponsiveChecks","handleMenuButtonClick","breakpoint","getComputedStyle","getPropertyValue","getBreakpoint","matchMedia","checkMode","addListener","matches","NotificationBanner","Radios","$allInputs","$clickedInputForm","$clickedInputName","hasSameFormOwner","SkipLink","_this$$root$getAttrib","linkedElementId","$linkedElement","initAllGovuk","initAllAutocomplete","$autocompletes","default","accessibleAutocomplete","$module","$select","init","selectElement","inputValues","initAutocomplete","enhanceSelectElement","initFileUpload","readyState","initPreviewCloseLink","opener","$closeLink","close","initAll"],"sourceRoot":""}