hof 24.4.0 → 24.5.0-jquery-removal-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +10 -0
- package/build/tasks/vite/index.js +7 -2
- package/build/tasks/vite/vite.config.js +0 -2
- package/config/builder-defaults.js +2 -1
- package/controller/validation/validators.js +9 -6
- package/frontend/govuk-template/govuk_template_generated.html +118 -0
- package/frontend/template-mixins/mixins/helpers.js +2 -2
- package/frontend/template-partials/translations/src/cy/cookies.json +14 -2
- package/frontend/themes/gov-uk/client-js/session-timeout-dialog.js +61 -37
- package/middleware/rate-limiter.js +6 -6
- package/package.json +26 -32
- package/sandbox/apps/sandbox/sections/summary-data-sections.js +2 -2
- package/sandbox/apps/sandbox/translations/en/default.json +278 -0
- package/sandbox/assets/js/index.js +11 -64
- package/sandbox/assets/scss/app.scss +1 -26
- package/sandbox/assets/scss/autocomplete.scss +143 -0
- package/sandbox/package.json +2 -6
- package/sandbox/public/css/app.css +11836 -0
- package/sandbox/public/images/govuk-logo.svg +25 -0
- package/sandbox/public/images/icons/icon-caret-left.png +0 -0
- package/sandbox/public/images/icons/icon-complete.png +0 -0
- package/sandbox/public/images/icons/icon-cross-remove-sign.png +0 -0
- package/sandbox/public/js/bundle.js +23 -0
- package/sandbox/yarn.lock +15 -22
- package/utilities/date.js +14 -0
- package/utilities/index.js +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## 2026-09-14 Version 24.5.1 (Stable), @nzorba @dk4g @dalglishdhandaHO
|
|
2
|
+
|
|
3
|
+
### Changed
|
|
4
|
+
- Removed jQuery from the session timeout dialog and replaced its DOM operations with native browser APIs.
|
|
5
|
+
- Removed the jQuery dependency and updated frontend tests to use native DOM APIs and `fetch`.
|
|
6
|
+
- Migrated from `momentjs` to `dayjs`
|
|
7
|
+
|
|
8
|
+
### Fixes
|
|
9
|
+
- Added missing Welsh cookie page translation entries to match the English cookie configuration ((#710)[https://github.com/UKHomeOfficeForms/hof/pull/710])
|
|
10
|
+
|
|
11
|
+
### Chore & Maintenance
|
|
12
|
+
- Removed the vulnerable PhantomJS-based functional test driver stack and migrated the functional test browser runner to Playwright ((#699)[https://github.com/UKHomeOfficeForms/hof/pull/699])
|
|
13
|
+
- Updated node and Redis versions in testing matrix ((#700)[https://github.com/UKHomeOfficeForms/hof/pull/700])
|
|
14
|
+
|
|
1
15
|
## 2026-07-01, Version 24.4.0 (Stable), @vivekkumar-ho
|
|
2
16
|
|
|
3
17
|
### Security
|
package/README.md
CHANGED
|
@@ -139,6 +139,16 @@ Each task (except Vite) has a common configuration format with the following opt
|
|
|
139
139
|
- `match` - defines the pattern for files to watch to trigger a rebuild of this task
|
|
140
140
|
- `restart` - defines if this task should result in a server restart
|
|
141
141
|
|
|
142
|
+
For Vite, the following configuration can be specified:
|
|
143
|
+
- `outDir` - Specifies the output directory (relative to project root). Only changes only the build output location, does not change runtime static mounting.
|
|
144
|
+
Example from hof settings:
|
|
145
|
+
```js
|
|
146
|
+
"js": {
|
|
147
|
+
"outDir": './dist/public'
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
|
|
142
152
|
Additionally the server instance created by `watch` can be configured by setting `server` config. Available options are:
|
|
143
153
|
|
|
144
154
|
- `cmd` - defines the command used to start the server
|
|
@@ -7,18 +7,23 @@ const hofDefaults = require('../../../config/hof-defaults');
|
|
|
7
7
|
|
|
8
8
|
module.exports = config => {
|
|
9
9
|
process.env.NODE_ENV = hofDefaults.env;
|
|
10
|
+
const publicDirectory = path.resolve(process.cwd(), config.js.outDir || 'public');
|
|
10
11
|
|
|
11
12
|
if(!config.production) {
|
|
12
13
|
return vite.build({
|
|
13
14
|
configFile: viteConfig,
|
|
14
15
|
mode: 'development',
|
|
15
16
|
build: {
|
|
16
|
-
sourcemap: config.js.sourceMaps
|
|
17
|
+
sourcemap: config.js.sourceMaps,
|
|
18
|
+
outDir: publicDirectory
|
|
17
19
|
}
|
|
18
20
|
});
|
|
19
21
|
}
|
|
20
22
|
return vite.build({
|
|
21
|
-
configFile: viteConfig
|
|
23
|
+
configFile: viteConfig,
|
|
24
|
+
build: {
|
|
25
|
+
outDir: publicDirectory
|
|
26
|
+
}
|
|
22
27
|
});
|
|
23
28
|
};
|
|
24
29
|
module.exports.task = 'vite';
|
|
@@ -7,7 +7,6 @@ import fs from 'fs';
|
|
|
7
7
|
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
|
8
8
|
import commonjs from '@rollup/plugin-commonjs';
|
|
9
9
|
|
|
10
|
-
const publicDirectory = resolve(process.cwd(), 'public');
|
|
11
10
|
const entryFile = (() => {
|
|
12
11
|
const src = resolve(process.cwd(), 'assets/js/index.js');
|
|
13
12
|
if (fs.existsSync(src)) return src;
|
|
@@ -49,7 +48,6 @@ export default defineConfig({
|
|
|
49
48
|
base: '/assets/',
|
|
50
49
|
publicDir: 'static', // static files copied as-is
|
|
51
50
|
build: {
|
|
52
|
-
outDir: publicDirectory,
|
|
53
51
|
emptyOutDir: false,
|
|
54
52
|
sourcemap: false,
|
|
55
53
|
rollupOptions: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const dayjs = require('dayjs');
|
|
4
|
+
const customParseFormat = require('dayjs/plugin/customParseFormat');
|
|
4
5
|
const _ = require('lodash');
|
|
5
6
|
const libPhoneNumber = require('libphonenumber-js/max');
|
|
6
7
|
const deprecate = require('deprecate');
|
|
@@ -11,6 +12,8 @@ const emailValidator = require('./email');
|
|
|
11
12
|
const dateFormat = 'YYYY-MM-DD';
|
|
12
13
|
let Validators;
|
|
13
14
|
|
|
15
|
+
dayjs.extend(customParseFormat);
|
|
16
|
+
|
|
14
17
|
module.exports = Validators = {
|
|
15
18
|
|
|
16
19
|
string(value) {
|
|
@@ -113,7 +116,7 @@ module.exports = Validators = {
|
|
|
113
116
|
},
|
|
114
117
|
|
|
115
118
|
date(value) {
|
|
116
|
-
return value === '' || Validators.regex(value, /\d{4}\-\d{2}\-\d{2}/) &&
|
|
119
|
+
return value === '' || Validators.regex(value, /\d{4}\-\d{2}\-\d{2}/) && dayjs(value, dateFormat, true).isValid();
|
|
117
120
|
},
|
|
118
121
|
|
|
119
122
|
'date-year'(value) {
|
|
@@ -131,12 +134,12 @@ module.exports = Validators = {
|
|
|
131
134
|
// eslint-disable-next-line no-inline-comments, spaced-comment
|
|
132
135
|
before(value, date) {
|
|
133
136
|
// validator can also do before(value, [diff, unit][, diff, unit])
|
|
134
|
-
let valueDate =
|
|
137
|
+
let valueDate = dayjs(value, dateFormat, true);
|
|
135
138
|
let comparator;
|
|
136
139
|
if (arguments.length === 2) {
|
|
137
140
|
comparator = date;
|
|
138
141
|
} else {
|
|
139
|
-
comparator =
|
|
142
|
+
comparator = dayjs();
|
|
140
143
|
const args = [].slice.call(arguments, 1);
|
|
141
144
|
let diff;
|
|
142
145
|
let unit;
|
|
@@ -151,12 +154,12 @@ module.exports = Validators = {
|
|
|
151
154
|
|
|
152
155
|
after(value, date) {
|
|
153
156
|
// validator can also do after(value, [diff, unit][, diff, unit])
|
|
154
|
-
let valueDate =
|
|
157
|
+
let valueDate = dayjs(value, dateFormat, true);
|
|
155
158
|
let comparator;
|
|
156
159
|
if (arguments.length === 2) {
|
|
157
160
|
comparator = date;
|
|
158
161
|
} else {
|
|
159
|
-
comparator =
|
|
162
|
+
comparator = dayjs();
|
|
160
163
|
const args = [].slice.call(arguments, 1);
|
|
161
164
|
let diff;
|
|
162
165
|
let unit;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<!--[if lt IE 9]><html class="lte-ie8" lang="{{htmlLang}}"><![endif]-->
|
|
4
|
+
<!--[if gt IE 8]><!--><html lang="{{htmlLang}}" class="govuk-template--rebranded"><!--<![endif]-->
|
|
5
|
+
<head>
|
|
6
|
+
<meta charset="utf-8" />
|
|
7
|
+
<title>{{$pageTitle}}{{/pageTitle}}</title>
|
|
8
|
+
{{$head}}{{/head}}
|
|
9
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
10
|
+
<meta name="theme-color" content="#1d70b8">
|
|
11
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
12
|
+
<link rel="icon" sizes="48x48" href="{{govukAssetPath}}rebrand/images/favicon.ico">
|
|
13
|
+
<link rel="icon" sizes="any" href="{{govukAssetPath}}rebrand/images/favicon.svg" type="image/svg+xml">
|
|
14
|
+
<link rel="mask-icon" href="{{govukAssetPath}}rebrand/images/govuk-icon-mask.svg" color="#1d70b8">
|
|
15
|
+
<link rel="apple-touch-icon" href="{{govukAssetPath}}rebrand/images/govuk-icon-180.png">
|
|
16
|
+
<link rel="manifest" href="{{govukAssetPath}}rebrand/manifest.json">
|
|
17
|
+
<meta property="og:image" content="{{govukAssetPath}}rebrand/images/govuk-opengraph-image.png">
|
|
18
|
+
</head>
|
|
19
|
+
|
|
20
|
+
<body class="{{$bodyClasses}}{{/bodyClasses}} govuk-template__body js-enabled" >
|
|
21
|
+
<script {{#nonce}}nonce="{{nonce}}"{{/nonce}}>document.body.className += ' js-enabled' + ('noModule' in HTMLScriptElement.prototype ? ' govuk-frontend-supported' : '');</script>
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
<div id="global-cookie-message" class="gem-c-cookie-banner govuk-clearfix" data-module="cookie-banner" role="region" aria-label="cookie banner" data-nosnippet="">
|
|
25
|
+
{{$cookieMessage}}{{/cookieMessage}}
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
{{$bodyStart}}{{/bodyStart}}
|
|
29
|
+
|
|
30
|
+
<header role="banner" id="govuk-header" class="{{$headerClass}}{{/headerClass}}">
|
|
31
|
+
<div class="govuk-header__container govuk-width-container">
|
|
32
|
+
|
|
33
|
+
<div class="govuk-header__logo">
|
|
34
|
+
<a href="{{$homepageUrl}}https://www.gov.uk{{/homepageUrl}}" title="{{$logoLinkTitle}}Go to the GOV.UK homepage{{/logoLinkTitle}}" id="logo" class="govuk-header__link govuk-header__link--homepage" target="_blank" data-module="track-click" data-track-category="homeLinkClicked" data-track-action="homeHeader">
|
|
35
|
+
<!--[if gt IE 8]><!-->
|
|
36
|
+
<div id="govuk-header__logo"></div>
|
|
37
|
+
<img src="/public/images/govuk-logo.svg" id="govuk-header__logo" alt="Logo" loading="lazy" />
|
|
38
|
+
<!--<![endif]-->
|
|
39
|
+
<!--[if IE 8]>
|
|
40
|
+
<img src="{{govukAssetPath}}rebrand/images/govuk-logotype-tudor-crown.png" class="govuk-header__logotype-crown-fallback-image" width="32" height="30" alt="">
|
|
41
|
+
<![endif]-->
|
|
42
|
+
</a>
|
|
43
|
+
</div>
|
|
44
|
+
{{$insideHeader}}{{/insideHeader}}
|
|
45
|
+
|
|
46
|
+
{{$propositionHeader}}{{/propositionHeader}}
|
|
47
|
+
</div>
|
|
48
|
+
</header>
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
{{$afterHeader}}{{/afterHeader}}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
{{$main}}{{/main}}
|
|
55
|
+
|
|
56
|
+
<footer class="govuk-footer">
|
|
57
|
+
<div class="govuk-width-container">
|
|
58
|
+
<svg
|
|
59
|
+
focusable="false"
|
|
60
|
+
role="presentation"
|
|
61
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
62
|
+
viewBox="0 0 64 60"
|
|
63
|
+
height="30"
|
|
64
|
+
width="32"
|
|
65
|
+
fill="currentcolor" class="govuk-footer__crown">
|
|
66
|
+
<g>
|
|
67
|
+
<circle cx="20" cy="17.6" r="3.7" />
|
|
68
|
+
<circle cx="10.2" cy="23.5" r="3.7" />
|
|
69
|
+
<circle cx="3.7" cy="33.2" r="3.7" />
|
|
70
|
+
<circle cx="31.7" cy="30.6" r="3.7" />
|
|
71
|
+
<circle cx="43.3" cy="17.6" r="3.7" />
|
|
72
|
+
<circle cx="53.2" cy="23.5" r="3.7" />
|
|
73
|
+
<circle cx="59.7" cy="33.2" r="3.7" />
|
|
74
|
+
<circle cx="31.7" cy="30.6" r="3.7" />
|
|
75
|
+
<path d="M33.1,9.8c.2-.1.3-.3.5-.5l4.6,2.4v-6.8l-4.6,1.5c-.1-.2-.3-.3-.5-.5l1.9-5.9h-6.7l1.9,5.9c-.2.1-.3.3-.5.5l-4.6-1.5v6.8l4.6-2.4c.1.2.3.3.5.5l-2.6,8c-.9,2.8,1.2,5.7,4.1,5.7h0c3,0,5.1-2.9,4.1-5.7l-2.6-8ZM37,37.9s-3.4,3.8-4.1,6.1c2.2,0,4.2-.5,6.4-2.8l-.7,8.5c-2-2.8-4.4-4.1-5.7-3.8.1,3.1.5,6.7,5.8,7.2,3.7.3,6.7-1.5,7-3.8.4-2.6-2-4.3-3.7-1.6-1.4-4.5,2.4-6.1,4.9-3.2-1.9-4.5-1.8-7.7,2.4-10.9,3,4,2.6,7.3-1.2,11.1,2.4-1.3,6.2,0,4,4.6-1.2-2.8-3.7-2.2-4.2.2-.3,1.7.7,3.7,3,4.2,1.9.3,4.7-.9,7-5.9-1.3,0-2.4.7-3.9,1.7l2.4-8c.6,2.3,1.4,3.7,2.2,4.5.6-1.6.5-2.8,0-5.3l5,1.8c-2.6,3.6-5.2,8.7-7.3,17.5-7.4-1.1-15.7-1.7-24.5-1.7h0c-8.8,0-17.1.6-24.5,1.7-2.1-8.9-4.7-13.9-7.3-17.5l5-1.8c-.5,2.5-.6,3.7,0,5.3.8-.8,1.6-2.3,2.2-4.5l2.4,8c-1.5-1-2.6-1.7-3.9-1.7,2.3,5,5.2,6.2,7,5.9,2.3-.4,3.3-2.4,3-4.2-.5-2.4-3-3.1-4.2-.2-2.2-4.6,1.6-6,4-4.6-3.7-3.7-4.2-7.1-1.2-11.1,4.2,3.2,4.3,6.4,2.4,10.9,2.5-2.8,6.3-1.3,4.9,3.2-1.8-2.7-4.1-1-3.7,1.6.3,2.3,3.3,4.1,7,3.8,5.4-.5,5.7-4.2,5.8-7.2-1.3-.2-3.7,1-5.7,3.8l-.7-8.5c2.2,2.3,4.2,2.7,6.4,2.8-.7-2.3-4.1-6.1-4.1-6.1h10.6,0Z" />
|
|
76
|
+
</g>
|
|
77
|
+
</svg>
|
|
78
|
+
<div class="govuk-footer__meta">
|
|
79
|
+
<div class="govuk-footer__meta-item govuk-footer__meta-item--grow">
|
|
80
|
+
<h2 class="govuk-visually-hidden">Support links</h2>
|
|
81
|
+
{{$footerSupportLinks}}{{/footerSupportLinks}}
|
|
82
|
+
<svg
|
|
83
|
+
aria-hidden="true"
|
|
84
|
+
focusable="false"
|
|
85
|
+
class="govuk-footer__licence-logo"
|
|
86
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
87
|
+
viewBox="0 0 483.2 195.7"
|
|
88
|
+
height="17"
|
|
89
|
+
width="41">
|
|
90
|
+
<path
|
|
91
|
+
fill="currentColor"
|
|
92
|
+
d="M421.5 142.8V.1l-50.7 32.3v161.1h112.4v-50.7zm-122.3-9.6A47.12 47.12 0 0 1 221 97.8c0-26 21.1-47.1 47.1-47.1 16.7 0 31.4 8.7 39.7 21.8l42.7-27.2A97.63 97.63 0 0 0 268.1 0c-36.5 0-68.3 20.1-85.1 49.7A98 98 0 0 0 97.8 0C43.9 0 0 43.9 0 97.8s43.9 97.8 97.8 97.8c36.5 0 68.3-20.1 85.1-49.7a97.76 97.76 0 0 0 149.6 25.4l19.4 22.2h3v-87.8h-80l24.3 27.5zM97.8 145c-26 0-47.1-21.1-47.1-47.1s21.1-47.1 47.1-47.1 47.2 21 47.2 47S123.8 145 97.8 145" />
|
|
93
|
+
</svg>
|
|
94
|
+
<span class="govuk-footer__licence-description">
|
|
95
|
+
{{$licenceMessage}}All content is available under the <a href="https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/" id="open-government-licence" class="govuk-footer__link" target="_blank" rel="license">Open Government Licence v3.0</a>, except where otherwise stated{{/licenceMessage}}
|
|
96
|
+
</span>
|
|
97
|
+
</div>
|
|
98
|
+
<div class="govuk-footer__meta-item">
|
|
99
|
+
<a
|
|
100
|
+
class="govuk-footer__link govuk-footer__copyright-logo"
|
|
101
|
+
href="https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/uk-government-licensing-framework/crown-copyright/">
|
|
102
|
+
{{$crownCopyrightMessage}}© Crown copyright{{/crownCopyrightMessage}}
|
|
103
|
+
</a>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</footer>
|
|
108
|
+
|
|
109
|
+
<div id="global-app-error" class="app-error hidden"></div>
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
{{$bodyEnd}}{{/bodyEnd}}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
<script {{#nonce}}nonce="{{nonce}}"{{/nonce}}>if (typeof window.GOVUK === 'undefined') document.body.className = document.body.className.replace('js-enabled', '');</script>
|
|
116
|
+
|
|
117
|
+
</body>
|
|
118
|
+
</html>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
const querystring = require('querystring');
|
|
5
5
|
const path = require('path');
|
|
6
|
-
const
|
|
6
|
+
const { formatDate } = require('../../../utilities/date');
|
|
7
7
|
|
|
8
8
|
const renderer = require('./render');
|
|
9
9
|
|
|
@@ -33,7 +33,7 @@ module.exports = options => (req, res, next) => {
|
|
|
33
33
|
return function (txt) {
|
|
34
34
|
txt = (txt || '').split('|');
|
|
35
35
|
const value = hoganRender(txt[0], this);
|
|
36
|
-
return
|
|
36
|
+
return formatDate(value, txt[1]);
|
|
37
37
|
};
|
|
38
38
|
};
|
|
39
39
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"banner": {
|
|
3
|
-
"message": "
|
|
4
|
-
"link": "
|
|
3
|
+
"message": "Rydym yn defnyddio rhai cwcis hanfodol i wneud i'r wefan hon weithio.",
|
|
4
|
+
"link": "Gweld cwcis"
|
|
5
5
|
},
|
|
6
6
|
"header": "Cwcis",
|
|
7
7
|
"intro": "Mae’r gwasanaeth hwn yn gosod ffeiliau bychan (a elwir yn ‘cwcis’) ar eich cyfrifiadur er mwyn:",
|
|
@@ -39,6 +39,11 @@
|
|
|
39
39
|
"seen_cookie_message",
|
|
40
40
|
"Yn gadael i ni wybod eich bod wedi gweld ein neges cwcis yn barod",
|
|
41
41
|
"1 mis"
|
|
42
|
+
],
|
|
43
|
+
[
|
|
44
|
+
"cookie_preferences",
|
|
45
|
+
"Rhowch wybod i ni eich bod wedi cadw eich gosodiadau caniatâd cwci",
|
|
46
|
+
"1 mis"
|
|
42
47
|
]
|
|
43
48
|
]
|
|
44
49
|
},
|
|
@@ -56,6 +61,8 @@
|
|
|
56
61
|
},
|
|
57
62
|
"no-identify": "Nid oes unrhyw fanylion personol yn cael eu storio gyda’r wybodaeth hon, felly ni ellir eich adnabod.",
|
|
58
63
|
"analytics-table": {
|
|
64
|
+
"containerIdExpires": "2 flynedd",
|
|
65
|
+
"containerIdPurpose": "Defnyddir er mwyn parhau'r sesiwn",
|
|
59
66
|
"headers": [
|
|
60
67
|
"Enw",
|
|
61
68
|
"Pwrpas",
|
|
@@ -71,6 +78,11 @@
|
|
|
71
78
|
"_gat",
|
|
72
79
|
"Defnyddir i wasgu’r gyfradd ceisiadau",
|
|
73
80
|
"10 munud"
|
|
81
|
+
],
|
|
82
|
+
[
|
|
83
|
+
"_gid",
|
|
84
|
+
"Defnyddir i gyfrif ac olrhain y tudalennau rydych yn eu gweld",
|
|
85
|
+
"24 awr"
|
|
74
86
|
]
|
|
75
87
|
]
|
|
76
88
|
},
|
|
@@ -1,40 +1,55 @@
|
|
|
1
1
|
/* eslint max-len: 0 */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const $ = require('jquery');
|
|
5
4
|
window.dialogPolyfill = require('dialog-polyfill');
|
|
6
5
|
|
|
6
|
+
function getTextContent(selector) {
|
|
7
|
+
const element = document.querySelector(selector);
|
|
8
|
+
return element ? element.textContent : '';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function dialogDatasetValue(key) {
|
|
12
|
+
const element = document.getElementById('js-modal-dialog');
|
|
13
|
+
return element ? element.dataset[key] : undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function parseTimeout(value, fallback) {
|
|
17
|
+
const parsedValue = parseInt(value, 10);
|
|
18
|
+
return Number.isNaN(parsedValue) ? fallback : parsedValue;
|
|
19
|
+
}
|
|
20
|
+
|
|
7
21
|
// Modal dialog prototype
|
|
8
22
|
window.GOVUK.sessionDialog = {
|
|
9
23
|
el: document.getElementById('js-modal-dialog'),
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
$fallBackElement: $('.govuk-timeout-warning-fallback'),
|
|
24
|
+
lastFocusedEl: null,
|
|
25
|
+
closeButton: document.querySelector('.modal-dialog .js-dialog-close'),
|
|
26
|
+
fallBackElement: document.querySelector('.govuk-timeout-warning-fallback'),
|
|
14
27
|
dialogIsOpenClass: 'dialog-is-open',
|
|
15
28
|
timers: [],
|
|
16
|
-
warningTextPrefix:
|
|
29
|
+
warningTextPrefix: getTextContent('.dialog-text-prefix'),
|
|
17
30
|
warningTextSuffix: '.',
|
|
18
|
-
warningText:
|
|
31
|
+
warningText: getTextContent('.dialog-text'),
|
|
19
32
|
warningTextExtra: '',
|
|
20
33
|
|
|
21
34
|
// Timer specific markup. If these are not present, timeout and redirection are disabled
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
timer: document.querySelector('#js-modal-dialog') ? document.querySelector('#js-modal-dialog .timer') : null,
|
|
36
|
+
accessibleTimer: document.querySelector('#js-modal-dialog') ? document.querySelector('#js-modal-dialog .at-timer') : null,
|
|
24
37
|
|
|
25
|
-
secondsSessionTimeout:
|
|
26
|
-
secondsTimeoutWarning:
|
|
27
|
-
timeoutRedirectUrl:
|
|
38
|
+
secondsSessionTimeout: parseTimeout(dialogDatasetValue('sessionTimeout'), 1800),
|
|
39
|
+
secondsTimeoutWarning: parseTimeout(dialogDatasetValue('sessionTimeoutWarning'), 300),
|
|
40
|
+
timeoutRedirectUrl: dialogDatasetValue('urlRedirect'),
|
|
28
41
|
timeSessionRefreshed: new Date(),
|
|
29
42
|
|
|
30
43
|
bindUIElements: function () {
|
|
31
|
-
window.GOVUK.sessionDialog
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
if (window.GOVUK.sessionDialog.closeButton) {
|
|
45
|
+
window.GOVUK.sessionDialog.closeButton.addEventListener('click', function (e) {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
window.GOVUK.sessionDialog.closeDialog();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
35
50
|
|
|
36
51
|
// Close modal when ESC pressed
|
|
37
|
-
|
|
52
|
+
document.addEventListener('keydown', function (e) {
|
|
38
53
|
if (window.GOVUK.sessionDialog.isDialogOpen() && e.keyCode === 27) {
|
|
39
54
|
window.GOVUK.sessionDialog.closeDialog();
|
|
40
55
|
}
|
|
@@ -46,8 +61,8 @@ window.GOVUK.sessionDialog = {
|
|
|
46
61
|
},
|
|
47
62
|
|
|
48
63
|
isConfigured: function () {
|
|
49
|
-
return window.GOVUK.sessionDialog
|
|
50
|
-
window.GOVUK.sessionDialog
|
|
64
|
+
return window.GOVUK.sessionDialog.timer &&
|
|
65
|
+
window.GOVUK.sessionDialog.accessibleTimer &&
|
|
51
66
|
window.GOVUK.sessionDialog.secondsSessionTimeout &&
|
|
52
67
|
window.GOVUK.sessionDialog.secondsTimeoutWarning &&
|
|
53
68
|
window.GOVUK.sessionDialog.timeoutRedirectUrl;
|
|
@@ -55,7 +70,8 @@ window.GOVUK.sessionDialog = {
|
|
|
55
70
|
|
|
56
71
|
openDialog: function () {
|
|
57
72
|
if (!window.GOVUK.sessionDialog.isDialogOpen()) {
|
|
58
|
-
|
|
73
|
+
document.documentElement.classList.add(window.GOVUK.sessionDialog.dialogIsOpenClass);
|
|
74
|
+
document.body.classList.add(window.GOVUK.sessionDialog.dialogIsOpenClass);
|
|
59
75
|
window.GOVUK.sessionDialog.saveLastFocusedEl();
|
|
60
76
|
window.GOVUK.sessionDialog.makePageContentInert();
|
|
61
77
|
window.GOVUK.sessionDialog.el.showModal();
|
|
@@ -65,7 +81,8 @@ window.GOVUK.sessionDialog = {
|
|
|
65
81
|
|
|
66
82
|
closeDialog: function () {
|
|
67
83
|
if (window.GOVUK.sessionDialog.isDialogOpen()) {
|
|
68
|
-
|
|
84
|
+
document.documentElement.classList.remove(window.GOVUK.sessionDialog.dialogIsOpenClass);
|
|
85
|
+
document.body.classList.remove(window.GOVUK.sessionDialog.dialogIsOpenClass);
|
|
69
86
|
window.GOVUK.sessionDialog.el.close();
|
|
70
87
|
window.GOVUK.sessionDialog.el.open = false;
|
|
71
88
|
window.GOVUK.sessionDialog.setFocusOnLastFocusedEl();
|
|
@@ -75,19 +92,19 @@ window.GOVUK.sessionDialog = {
|
|
|
75
92
|
},
|
|
76
93
|
|
|
77
94
|
saveLastFocusedEl: function () {
|
|
78
|
-
window.GOVUK.sessionDialog
|
|
79
|
-
if (!window.GOVUK.sessionDialog
|
|
80
|
-
window.GOVUK.sessionDialog
|
|
95
|
+
window.GOVUK.sessionDialog.lastFocusedEl = document.activeElement;
|
|
96
|
+
if (!window.GOVUK.sessionDialog.lastFocusedEl || window.GOVUK.sessionDialog.lastFocusedEl === document.body) {
|
|
97
|
+
window.GOVUK.sessionDialog.lastFocusedEl = null;
|
|
81
98
|
} else if (document.querySelector) {
|
|
82
|
-
window.GOVUK.sessionDialog
|
|
99
|
+
window.GOVUK.sessionDialog.lastFocusedEl = document.querySelector(':focus');
|
|
83
100
|
}
|
|
84
101
|
},
|
|
85
102
|
|
|
86
103
|
// Set focus back on last focused el when modal closed
|
|
87
104
|
setFocusOnLastFocusedEl: function () {
|
|
88
|
-
if (window.GOVUK.sessionDialog
|
|
105
|
+
if (window.GOVUK.sessionDialog.lastFocusedEl) {
|
|
89
106
|
window.setTimeout(function () {
|
|
90
|
-
window.GOVUK.sessionDialog
|
|
107
|
+
window.GOVUK.sessionDialog.lastFocusedEl.focus();
|
|
91
108
|
}, 0);
|
|
92
109
|
}
|
|
93
110
|
},
|
|
@@ -222,15 +239,15 @@ window.GOVUK.sessionDialog = {
|
|
|
222
239
|
},
|
|
223
240
|
|
|
224
241
|
startCountdown: function () {
|
|
225
|
-
const
|
|
226
|
-
const
|
|
242
|
+
const timer = window.GOVUK.sessionDialog.timer;
|
|
243
|
+
const accessibleTimer = window.GOVUK.sessionDialog.accessibleTimer;
|
|
227
244
|
let timerRunOnce = false;
|
|
228
245
|
const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
|
229
246
|
|
|
230
247
|
const seconds = window.GOVUK.sessionDialog.secondsUntilSessionTimeout();
|
|
231
248
|
const minutes = seconds / 60;
|
|
232
249
|
|
|
233
|
-
|
|
250
|
+
timer.textContent = minutes + ' minute' + (minutes > 1 ? 's' : '');
|
|
234
251
|
|
|
235
252
|
(function countdown() {
|
|
236
253
|
const secondsUntilSessionTimeout = window.GOVUK.sessionDialog.secondsUntilSessionTimeout();
|
|
@@ -250,17 +267,17 @@ window.GOVUK.sessionDialog = {
|
|
|
250
267
|
const atText = window.GOVUK.sessionDialog.warningTextPrefix + countdownAtText + window.GOVUK.sessionDialog.warningTextSuffix + ' ' + window.GOVUK.sessionDialog.warningText;
|
|
251
268
|
const extraText = '\n' + window.GOVUK.sessionDialog.warningTextExtra;
|
|
252
269
|
|
|
253
|
-
|
|
270
|
+
timer.innerHTML = text + ' ' + extraText;
|
|
254
271
|
|
|
255
272
|
// Update screen reader friendly content every 20 secs
|
|
256
273
|
if (secondsLeft % 20 === 0) {
|
|
257
274
|
// Read out the extra content only once.
|
|
258
275
|
// Don't read out on iOS VoiceOver which stalls on the longer text
|
|
259
276
|
if (!timerRunOnce && !iOS) {
|
|
260
|
-
|
|
277
|
+
accessibleTimer.textContent = atText + extraText;
|
|
261
278
|
timerRunOnce = true;
|
|
262
279
|
} else {
|
|
263
|
-
|
|
280
|
+
accessibleTimer.textContent = atText;
|
|
264
281
|
}
|
|
265
282
|
}
|
|
266
283
|
|
|
@@ -277,10 +294,17 @@ window.GOVUK.sessionDialog = {
|
|
|
277
294
|
},
|
|
278
295
|
|
|
279
296
|
refreshSession: function () {
|
|
280
|
-
|
|
281
|
-
.
|
|
297
|
+
return fetch('')
|
|
298
|
+
.then(function (response) {
|
|
299
|
+
if (!response.ok) {
|
|
300
|
+
throw new Error('Session refresh failed');
|
|
301
|
+
}
|
|
282
302
|
window.GOVUK.sessionDialog.timeSessionRefreshed = new Date();
|
|
283
303
|
window.GOVUK.sessionDialog.controller();
|
|
304
|
+
})
|
|
305
|
+
.catch(function (error) {
|
|
306
|
+
// eslint-disable-next-line no-console
|
|
307
|
+
console.error(error);
|
|
284
308
|
});
|
|
285
309
|
},
|
|
286
310
|
|
|
@@ -327,7 +351,7 @@ window.GOVUK.sessionDialog = {
|
|
|
327
351
|
},
|
|
328
352
|
|
|
329
353
|
init: function (options) {
|
|
330
|
-
|
|
354
|
+
Object.assign(window.GOVUK.sessionDialog, options);
|
|
331
355
|
if (window.GOVUK.sessionDialog.el && window.GOVUK.sessionDialog.isConfigured()) {
|
|
332
356
|
// Native dialog is not supported by some browsers so use polyfill
|
|
333
357
|
if (typeof HTMLDialogElement !== 'function') {
|
|
@@ -336,7 +360,7 @@ window.GOVUK.sessionDialog = {
|
|
|
336
360
|
return true;
|
|
337
361
|
} catch (error) {
|
|
338
362
|
// Doesn't support polyfill (IE8) - display fallback element
|
|
339
|
-
window.GOVUK.sessionDialog
|
|
363
|
+
window.GOVUK.sessionDialog.fallBackElement.classList.add('govuk-!-display-block');
|
|
340
364
|
return false;
|
|
341
365
|
}
|
|
342
366
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
const
|
|
2
|
+
const dayjs = require('dayjs');
|
|
3
3
|
const redis = require('redis');
|
|
4
4
|
const config = require('./../config/hof-defaults');
|
|
5
5
|
|
|
@@ -36,8 +36,8 @@ module.exports = (options, rateLimitType) => {
|
|
|
36
36
|
logger.log('error', `Error with requesting redis session for rate limiting: ${err}`);
|
|
37
37
|
return await closeConnection();
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
const windowStartTimestamp =
|
|
39
|
+
let currentRequestTime = dayjs();
|
|
40
|
+
const windowStartTimestamp = dayjs().subtract(WINDOW_SIZE_IN_MINUTES, 'minutes').unix();
|
|
41
41
|
let oldRecord = false;
|
|
42
42
|
let data;
|
|
43
43
|
// if no record is found , create a new record for user and store to redis
|
|
@@ -74,9 +74,9 @@ module.exports = (options, rateLimitType) => {
|
|
|
74
74
|
}
|
|
75
75
|
// if number of requests made is less than allowed maximum, log new entry
|
|
76
76
|
const lastRequestLog = data[data.length - 1];
|
|
77
|
-
|
|
78
|
-
.subtract(WINDOW_LOG_INTERVAL_IN_MINUTES, 'minutes')
|
|
79
|
-
|
|
77
|
+
currentRequestTime = currentRequestTime
|
|
78
|
+
.subtract(WINDOW_LOG_INTERVAL_IN_MINUTES, 'minutes');
|
|
79
|
+
const potentialCurrentWindowIntervalStartTimeStamp = currentRequestTime.unix();
|
|
80
80
|
// if interval has not passed since last request log, increment counter
|
|
81
81
|
if (lastRequestLog[timestampName] > potentialCurrentWindowIntervalStartTimeStamp) {
|
|
82
82
|
lastRequestLog[countName]++;
|