katalyst-govuk-formbuilder 1.4.0 → 1.5.0

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -0
  3. data/app/assets/builds/katalyst/govuk/formbuilder.css +58 -5
  4. data/app/assets/builds/katalyst/govuk/formbuilder.js +98 -64
  5. data/app/assets/builds/katalyst/govuk/formbuilder.min.js +1 -1
  6. data/app/assets/stylesheets/katalyst/govuk/components/richtextarea/_index.scss +7 -6
  7. data/lib/katalyst/govuk/formbuilder/version.rb +1 -1
  8. data/vendor/assets/stylesheets/govuk-frontend/govuk/all-ie8.scss +8 -0
  9. data/vendor/assets/stylesheets/govuk-frontend/govuk/components/accordion/_index.scss +3 -3
  10. data/vendor/assets/stylesheets/govuk-frontend/govuk/components/back-link/_index.scss +1 -1
  11. data/vendor/assets/stylesheets/govuk-frontend/govuk/components/breadcrumbs/_index.scss +1 -1
  12. data/vendor/assets/stylesheets/govuk-frontend/govuk/components/button/_index.scss +8 -6
  13. data/vendor/assets/stylesheets/govuk-frontend/govuk/components/checkboxes/_index.scss +6 -6
  14. data/vendor/assets/stylesheets/govuk-frontend/govuk/components/file-upload/_index.scss +6 -1
  15. data/vendor/assets/stylesheets/govuk-frontend/govuk/components/footer/_index.scss +0 -7
  16. data/vendor/assets/stylesheets/govuk-frontend/govuk/components/input/_index.scss +15 -3
  17. data/vendor/assets/stylesheets/govuk-frontend/govuk/components/radios/_index.scss +5 -5
  18. data/vendor/assets/stylesheets/govuk-frontend/govuk/components/select/_index.scss +7 -1
  19. data/vendor/assets/stylesheets/govuk-frontend/govuk/components/tag/_index.scss +18 -18
  20. data/vendor/assets/stylesheets/govuk-frontend/govuk/components/textarea/_index.scss +8 -1
  21. data/vendor/assets/stylesheets/govuk-frontend/govuk/core/_all.scss +1 -0
  22. data/vendor/assets/stylesheets/govuk-frontend/govuk/core/_govuk-frontend-version.scss +5 -0
  23. data/vendor/assets/stylesheets/govuk-frontend/govuk/helpers/_colour.scss +5 -2
  24. data/vendor/assets/stylesheets/govuk-frontend/govuk/helpers/_focused.scss +1 -1
  25. data/vendor/assets/stylesheets/govuk-frontend/govuk/helpers/_font-faces.scss +1 -1
  26. data/vendor/assets/stylesheets/govuk-frontend/govuk/objects/_width-container.scss +1 -1
  27. data/vendor/assets/stylesheets/govuk-frontend/govuk/settings/_colours-organisations.scss +4 -0
  28. data/vendor/assets/stylesheets/govuk-frontend/govuk/settings/_ie8.scss +16 -0
  29. data/vendor/assets/stylesheets/govuk-frontend/govuk/settings/_links.scss +5 -1
  30. data/vendor/assets/stylesheets/govuk-frontend/govuk/tools/_ie8.scss +38 -2
  31. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c9df88d9d5abc58da448ed9fd96a45ef1924a27dcda9939ef0596b9e21ff3d06
4
- data.tar.gz: c824d95a58018d8376b8cc9de329ef21b1e8d06d9fca460f9683072a625a0fb9
3
+ metadata.gz: 58e68fe3ca009403a6a3b6f979919eb10a4841e2d67da05ce35bd76f4c711a9b
4
+ data.tar.gz: ec8757dab48258b437ed24d3393fec87e51d956658262fce3d80113f51d64303
5
5
  SHA512:
6
- metadata.gz: 9c7bf61b527e1092fb8327b2ba88d9832d45e99d46a445a8f1a6d1428d81ddc154aa79af57c5e9fb0892ea2da77482bdb0df396b50bc1ef0bd3e8cab6e9b7213
7
- data.tar.gz: 02c92b8b8822995c73bff3851ce55b418b36e11ff5fd25bbf59021a664602802338104a19c752744edcf2f77e1c79471ce7b6ade424d07f7bf2664135e6cf5dc
6
+ metadata.gz: a2dc00bb4778ee15bdfd8a964cb60b107a48ee41dff48bcab840fdb59185b4d6fddb2406d45af5e2b1ea0b86fc5d71811790ea98ecd62c38191c274411134e0f
7
+ data.tar.gz: f4b98623d425c1107f5e2688ad77482f03dfb803d05378f3f7b4e0d68cdb3fe3aa83df7134b18bbc50425f76e67a00af9a07ed4acfc811a96d205d0f534bede9
data/README.md CHANGED
@@ -47,6 +47,14 @@ helper Katalyst::GOVUK::Formbuilder::Frontend
47
47
 
48
48
  Bug reports and pull requests are welcome on GitHub at https://github.com/katalyst/govuk-formbuilder.
49
49
 
50
+ ## Release
51
+
52
+ Update the version number in `lib/katalyst/govuk/formbuilder/version.rb` and run:
53
+
54
+ ```bash
55
+ bundle exec rake release
56
+ ```
57
+
50
58
  ## License
51
59
 
52
60
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,3 +1,7 @@
1
+ :root {
2
+ --govuk-frontend-version: "4.6.0";
3
+ }
4
+
1
5
  .govuk-link {
2
6
  font-family: "Open Sans", sans-serif;
3
7
  -webkit-font-smoothing: antialiased;
@@ -1128,7 +1132,7 @@
1128
1132
  .govuk-button[disabled=disabled]:hover,
1129
1133
  .govuk-button[disabled]:hover {
1130
1134
  background-color: #00703c;
1131
- cursor: default;
1135
+ cursor: not-allowed;
1132
1136
  }
1133
1137
  .govuk-button--disabled:active,
1134
1138
  .govuk-button[disabled=disabled]:active,
@@ -1728,7 +1732,7 @@
1728
1732
 
1729
1733
  .govuk-checkboxes__input:disabled,
1730
1734
  .govuk-checkboxes__input:disabled + .govuk-checkboxes__label {
1731
- cursor: default;
1735
+ cursor: not-allowed;
1732
1736
  }
1733
1737
 
1734
1738
  .govuk-checkboxes__input:disabled + .govuk-checkboxes__label,
@@ -1893,6 +1897,12 @@
1893
1897
  outline-offset: 0;
1894
1898
  box-shadow: inset 0 0 0 2px;
1895
1899
  }
1900
+ .govuk-input:disabled {
1901
+ opacity: 0.5;
1902
+ color: inherit;
1903
+ background-color: transparent;
1904
+ cursor: not-allowed;
1905
+ }
1896
1906
 
1897
1907
  .govuk-input::-webkit-outer-spin-button,
1898
1908
  .govuk-input::-webkit-inner-spin-button {
@@ -1911,6 +1921,28 @@
1911
1921
  border-color: #0b0c0c;
1912
1922
  }
1913
1923
 
1924
+ .govuk-input--extra-letter-spacing {
1925
+ font-family: "Open Sans", sans-serif;
1926
+ -webkit-font-smoothing: antialiased;
1927
+ -moz-osx-font-smoothing: grayscale;
1928
+ -webkit-font-feature-settings: "tnum" 1;
1929
+ font-feature-settings: "tnum" 1;
1930
+ font-weight: 400;
1931
+ letter-spacing: 0.05em;
1932
+ }
1933
+ @media print {
1934
+ .govuk-input--extra-letter-spacing {
1935
+ font-family: sans-serif;
1936
+ }
1937
+ }
1938
+ @supports (font-variant-numeric: tabular-nums) {
1939
+ .govuk-input--extra-letter-spacing {
1940
+ -webkit-font-feature-settings: normal;
1941
+ font-feature-settings: normal;
1942
+ font-variant-numeric: tabular-nums;
1943
+ }
1944
+ }
1945
+
1914
1946
  .govuk-input--width-30 {
1915
1947
  max-width: 29.5em;
1916
1948
  }
@@ -2249,6 +2281,10 @@
2249
2281
  outline: 3px solid #ffdd00;
2250
2282
  box-shadow: inset 0 0 0 4px #0b0c0c;
2251
2283
  }
2284
+ .govuk-file-upload:disabled {
2285
+ opacity: 0.5;
2286
+ cursor: not-allowed;
2287
+ }
2252
2288
 
2253
2289
  .govuk-radios__item {
2254
2290
  font-family: "Open Sans", sans-serif;
@@ -2360,7 +2396,7 @@
2360
2396
 
2361
2397
  .govuk-radios__input:disabled,
2362
2398
  .govuk-radios__input:disabled + .govuk-radios__label {
2363
- cursor: default;
2399
+ cursor: not-allowed;
2364
2400
  }
2365
2401
 
2366
2402
  .govuk-radios__input:disabled + .govuk-radios__label,
@@ -2540,6 +2576,11 @@
2540
2576
  outline-offset: 0;
2541
2577
  box-shadow: inset 0 0 0 2px;
2542
2578
  }
2579
+ .govuk-select:disabled {
2580
+ opacity: 0.5;
2581
+ color: inherit;
2582
+ cursor: not-allowed;
2583
+ }
2543
2584
 
2544
2585
  .govuk-select option:active,
2545
2586
  .govuk-select option:checked,
@@ -2602,6 +2643,12 @@
2602
2643
  outline-offset: 0;
2603
2644
  box-shadow: inset 0 0 0 2px;
2604
2645
  }
2646
+ .govuk-textarea:disabled {
2647
+ opacity: 0.5;
2648
+ color: inherit;
2649
+ background-color: transparent;
2650
+ cursor: not-allowed;
2651
+ }
2605
2652
 
2606
2653
  .govuk-textarea--error {
2607
2654
  border-color: #d4351c;
@@ -4141,10 +4188,16 @@
4141
4188
  outline-offset: 0;
4142
4189
  box-shadow: inset 0 0 0 2px;
4143
4190
  }
4191
+ .govuk-richtextarea:disabled {
4192
+ opacity: 0.5;
4193
+ color: inherit;
4194
+ background-color: transparent;
4195
+ cursor: not-allowed;
4196
+ }
4144
4197
 
4145
- .govuk-richtextarea--error {
4198
+ .govuk-textarea--error {
4146
4199
  border-color: #d4351c;
4147
4200
  }
4148
- .govuk-richtextarea--error:focus {
4201
+ .govuk-textarea--error:focus {
4149
4202
  border-color: #0b0c0c;
4150
4203
  }
@@ -276,7 +276,7 @@ function normaliseString(value) {
276
276
  if (trimmedValue === "false") {
277
277
  return false;
278
278
  }
279
- if (trimmedValue.length > 0 && isFinite(trimmedValue)) {
279
+ if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
280
280
  return Number(trimmedValue);
281
281
  }
282
282
  return value;
@@ -551,7 +551,7 @@ var KEY_SPACE = 32;
551
551
  var DEBOUNCE_TIMEOUT_IN_SECONDS = 1;
552
552
 
553
553
  function Button($module, config) {
554
- if (!$module) {
554
+ if (!($module instanceof HTMLElement)) {
555
555
  return this;
556
556
  }
557
557
  this.$module = $module;
@@ -572,7 +572,10 @@ Button.prototype.init = function() {
572
572
 
573
573
  Button.prototype.handleKeyDown = function(event) {
574
574
  var $target = event.target;
575
- if ($target.getAttribute("role") === "button" && event.keyCode === KEY_SPACE) {
575
+ if (event.keyCode !== KEY_SPACE) {
576
+ return;
577
+ }
578
+ if ($target instanceof HTMLElement && $target.getAttribute("role") === "button") {
576
579
  event.preventDefault();
577
580
  $target.click();
578
581
  }
@@ -618,10 +621,8 @@ Button.prototype.debounce = function(event) {
618
621
  }).call("object" === typeof window && window || "object" === typeof self && self || "object" === typeof global && global || {});
619
622
 
620
623
  function closestAttributeValue($element, attributeName) {
621
- var closestElementWithAttribute = $element.closest("[" + attributeName + "]");
622
- if (closestElementWithAttribute) {
623
- return closestElementWithAttribute.getAttribute(attributeName);
624
- }
624
+ var $closestElementWithAttribute = $element.closest("[" + attributeName + "]");
625
+ return $closestElementWithAttribute ? $closestElementWithAttribute.getAttribute(attributeName) : null;
625
626
  }
626
627
 
627
628
  function I18n(translations, config) {
@@ -633,11 +634,11 @@ I18n.prototype.t = function(lookupKey, options) {
633
634
  if (!lookupKey) {
634
635
  throw new Error("i18n: lookup key missing");
635
636
  }
636
- if (options && typeof options.count !== "undefined") {
637
+ if (options && typeof options.count === "number") {
637
638
  lookupKey = lookupKey + "." + this.getPluralSuffix(lookupKey, options.count);
638
639
  }
639
- if (lookupKey in this.translations) {
640
- var translationString = this.translations[lookupKey];
640
+ var translationString = this.translations[lookupKey];
641
+ if (typeof translationString === "string") {
641
642
  if (translationString.match(/%{(.\S+)}/)) {
642
643
  if (!options) {
643
644
  throw new Error("i18n: cannot replace placeholders in string if no option data provided");
@@ -659,11 +660,11 @@ I18n.prototype.replacePlaceholders = function(translationString, options) {
659
660
  return translationString.replace(/%{(.\S+)}/g, (function(placeholderWithBraces, placeholderKey) {
660
661
  if (Object.prototype.hasOwnProperty.call(options, placeholderKey)) {
661
662
  var placeholderValue = options[placeholderKey];
662
- if (placeholderValue === false) {
663
+ if (placeholderValue === false || typeof placeholderValue !== "number" && typeof placeholderValue !== "string") {
663
664
  return "";
664
665
  }
665
- if (typeof placeholderValue === "number" && formatter) {
666
- return formatter.format(placeholderValue);
666
+ if (typeof placeholderValue === "number") {
667
+ return formatter ? formatter.format(placeholderValue) : placeholderValue.toString();
667
668
  }
668
669
  return placeholderValue;
669
670
  } else {
@@ -1108,7 +1109,11 @@ var CHARACTER_COUNT_TRANSLATIONS = {
1108
1109
  };
1109
1110
 
1110
1111
  function CharacterCount($module, config) {
1111
- if (!$module) {
1112
+ if (!($module instanceof HTMLElement)) {
1113
+ return this;
1114
+ }
1115
+ var $textarea = $module.querySelector(".govuk-js-character-count");
1116
+ if (!($textarea instanceof HTMLTextAreaElement || $textarea instanceof HTMLInputElement)) {
1112
1117
  return this;
1113
1118
  }
1114
1119
  var defaultConfig = {
@@ -1127,26 +1132,32 @@ function CharacterCount($module, config) {
1127
1132
  this.i18n = new I18n(extractConfigByNamespace(this.config, "i18n"), {
1128
1133
  locale: closestAttributeValue($module, "lang")
1129
1134
  });
1130
- if (this.config.maxwords) {
1135
+ this.maxLength = Infinity;
1136
+ if ("maxwords" in this.config && this.config.maxwords) {
1131
1137
  this.maxLength = this.config.maxwords;
1132
- } else if (this.config.maxlength) {
1138
+ } else if ("maxlength" in this.config && this.config.maxlength) {
1133
1139
  this.maxLength = this.config.maxlength;
1134
1140
  } else {
1135
1141
  return;
1136
1142
  }
1137
1143
  this.$module = $module;
1138
- this.$textarea = $module.querySelector(".govuk-js-character-count");
1144
+ this.$textarea = $textarea;
1139
1145
  this.$visibleCountMessage = null;
1140
1146
  this.$screenReaderCountMessage = null;
1141
1147
  this.lastInputTimestamp = null;
1148
+ this.lastInputValue = "";
1149
+ this.valueChecker = null;
1142
1150
  }
1143
1151
 
1144
1152
  CharacterCount.prototype.init = function() {
1145
- if (!this.$textarea) {
1153
+ if (!this.$module || !this.$textarea) {
1146
1154
  return;
1147
1155
  }
1148
1156
  var $textarea = this.$textarea;
1149
1157
  var $textareaDescription = document.getElementById($textarea.id + "-info");
1158
+ if (!$textareaDescription) {
1159
+ return;
1160
+ }
1150
1161
  if ($textareaDescription.innerText.match(/^\s*$/)) {
1151
1162
  $textareaDescription.innerText = this.i18n.t("textareaDescription", {
1152
1163
  count: this.maxLength
@@ -1167,11 +1178,7 @@ CharacterCount.prototype.init = function() {
1167
1178
  $textareaDescription.classList.add("govuk-visually-hidden");
1168
1179
  $textarea.removeAttribute("maxlength");
1169
1180
  this.bindChangeEvents();
1170
- if ("onpageshow" in window) {
1171
- window.addEventListener("pageshow", this.updateCountMessage.bind(this));
1172
- } else {
1173
- window.addEventListener("DOMContentLoaded", this.updateCountMessage.bind(this));
1174
- }
1181
+ window.addEventListener("onpageshow" in window ? "pageshow" : "DOMContentLoaded", this.updateCountMessage.bind(this));
1175
1182
  this.updateCountMessage();
1176
1183
  };
1177
1184
 
@@ -1200,9 +1207,8 @@ CharacterCount.prototype.handleBlur = function() {
1200
1207
  };
1201
1208
 
1202
1209
  CharacterCount.prototype.updateIfValueChanged = function() {
1203
- if (!this.$textarea.oldValue) this.$textarea.oldValue = "";
1204
- if (this.$textarea.value !== this.$textarea.oldValue) {
1205
- this.$textarea.oldValue = this.$textarea.value;
1210
+ if (this.$textarea.value !== this.lastInputValue) {
1211
+ this.lastInputValue = this.$textarea.value;
1206
1212
  this.updateCountMessage();
1207
1213
  }
1208
1214
  };
@@ -1238,13 +1244,13 @@ CharacterCount.prototype.updateScreenReaderCountMessage = function() {
1238
1244
  if (this.isOverThreshold()) {
1239
1245
  $screenReaderCountMessage.removeAttribute("aria-hidden");
1240
1246
  } else {
1241
- $screenReaderCountMessage.setAttribute("aria-hidden", true);
1247
+ $screenReaderCountMessage.setAttribute("aria-hidden", "true");
1242
1248
  }
1243
1249
  $screenReaderCountMessage.innerText = this.getCountMessage();
1244
1250
  };
1245
1251
 
1246
1252
  CharacterCount.prototype.count = function(text) {
1247
- if (this.config.maxwords) {
1253
+ if ("maxwords" in this.config && this.config.maxwords) {
1248
1254
  var tokens = text.match(/\S+/g) || [];
1249
1255
  return tokens.length;
1250
1256
  } else {
@@ -1254,7 +1260,7 @@ CharacterCount.prototype.count = function(text) {
1254
1260
 
1255
1261
  CharacterCount.prototype.getCountMessage = function() {
1256
1262
  var remainingNumber = this.maxLength - this.count(this.$textarea.value);
1257
- var countType = this.config.maxwords ? "words" : "characters";
1263
+ var countType = "maxwords" in this.config && this.config.maxwords ? "words" : "characters";
1258
1264
  return this.formatCountMessage(remainingNumber, countType);
1259
1265
  };
1260
1266
 
@@ -1280,11 +1286,21 @@ CharacterCount.prototype.isOverThreshold = function() {
1280
1286
  };
1281
1287
 
1282
1288
  function Checkboxes($module) {
1289
+ if (!($module instanceof HTMLElement)) {
1290
+ return this;
1291
+ }
1292
+ var $inputs = $module.querySelectorAll('input[type="checkbox"]');
1293
+ if (!$inputs.length) {
1294
+ return this;
1295
+ }
1283
1296
  this.$module = $module;
1284
- this.$inputs = $module.querySelectorAll('input[type="checkbox"]');
1297
+ this.$inputs = $inputs;
1285
1298
  }
1286
1299
 
1287
1300
  Checkboxes.prototype.init = function() {
1301
+ if (!this.$module || !this.$inputs) {
1302
+ return;
1303
+ }
1288
1304
  var $module = this.$module;
1289
1305
  var $inputs = this.$inputs;
1290
1306
  nodeListForEach($inputs, (function($input) {
@@ -1295,11 +1311,7 @@ Checkboxes.prototype.init = function() {
1295
1311
  $input.setAttribute("aria-controls", targetId);
1296
1312
  $input.removeAttribute("data-aria-controls");
1297
1313
  }));
1298
- if ("onpageshow" in window) {
1299
- window.addEventListener("pageshow", this.syncAllConditionalReveals.bind(this));
1300
- } else {
1301
- window.addEventListener("DOMContentLoaded", this.syncAllConditionalReveals.bind(this));
1302
- }
1314
+ window.addEventListener("onpageshow" in window ? "pageshow" : "DOMContentLoaded", this.syncAllConditionalReveals.bind(this));
1303
1315
  this.syncAllConditionalReveals();
1304
1316
  $module.addEventListener("click", this.handleClick.bind(this));
1305
1317
  };
@@ -1309,39 +1321,45 @@ Checkboxes.prototype.syncAllConditionalReveals = function() {
1309
1321
  };
1310
1322
 
1311
1323
  Checkboxes.prototype.syncConditionalRevealWithInputState = function($input) {
1312
- var $target = document.getElementById($input.getAttribute("aria-controls"));
1324
+ var targetId = $input.getAttribute("aria-controls");
1325
+ if (!targetId) {
1326
+ return;
1327
+ }
1328
+ var $target = document.getElementById(targetId);
1313
1329
  if ($target && $target.classList.contains("govuk-checkboxes__conditional")) {
1314
1330
  var inputIsChecked = $input.checked;
1315
- $input.setAttribute("aria-expanded", inputIsChecked);
1331
+ $input.setAttribute("aria-expanded", inputIsChecked.toString());
1316
1332
  $target.classList.toggle("govuk-checkboxes__conditional--hidden", !inputIsChecked);
1317
1333
  }
1318
1334
  };
1319
1335
 
1320
1336
  Checkboxes.prototype.unCheckAllInputsExcept = function($input) {
1337
+ var $component = this;
1321
1338
  var allInputsWithSameName = document.querySelectorAll('input[type="checkbox"][name="' + $input.name + '"]');
1322
- nodeListForEach(allInputsWithSameName, function($inputWithSameName) {
1339
+ nodeListForEach(allInputsWithSameName, (function($inputWithSameName) {
1323
1340
  var hasSameFormOwner = $input.form === $inputWithSameName.form;
1324
1341
  if (hasSameFormOwner && $inputWithSameName !== $input) {
1325
1342
  $inputWithSameName.checked = false;
1326
- this.syncConditionalRevealWithInputState($inputWithSameName);
1343
+ $component.syncConditionalRevealWithInputState($inputWithSameName);
1327
1344
  }
1328
- }.bind(this));
1345
+ }));
1329
1346
  };
1330
1347
 
1331
1348
  Checkboxes.prototype.unCheckExclusiveInputs = function($input) {
1349
+ var $component = this;
1332
1350
  var allInputsWithSameNameAndExclusiveBehaviour = document.querySelectorAll('input[data-behaviour="exclusive"][type="checkbox"][name="' + $input.name + '"]');
1333
- nodeListForEach(allInputsWithSameNameAndExclusiveBehaviour, function($exclusiveInput) {
1351
+ nodeListForEach(allInputsWithSameNameAndExclusiveBehaviour, (function($exclusiveInput) {
1334
1352
  var hasSameFormOwner = $input.form === $exclusiveInput.form;
1335
1353
  if (hasSameFormOwner) {
1336
1354
  $exclusiveInput.checked = false;
1337
- this.syncConditionalRevealWithInputState($exclusiveInput);
1355
+ $component.syncConditionalRevealWithInputState($exclusiveInput);
1338
1356
  }
1339
- }.bind(this));
1357
+ }));
1340
1358
  };
1341
1359
 
1342
1360
  Checkboxes.prototype.handleClick = function(event) {
1343
1361
  var $clickedInput = event.target;
1344
- if ($clickedInput.type !== "checkbox") {
1362
+ if (!($clickedInput instanceof HTMLInputElement) || $clickedInput.type !== "checkbox") {
1345
1363
  return;
1346
1364
  }
1347
1365
  var hasAriaControls = $clickedInput.getAttribute("aria-controls");
@@ -1360,7 +1378,7 @@ Checkboxes.prototype.handleClick = function(event) {
1360
1378
  };
1361
1379
 
1362
1380
  function ErrorSummary($module, config) {
1363
- if (!$module) {
1381
+ if (!($module instanceof HTMLElement)) {
1364
1382
  return this;
1365
1383
  }
1366
1384
  this.$module = $module;
@@ -1371,10 +1389,10 @@ function ErrorSummary($module, config) {
1371
1389
  }
1372
1390
 
1373
1391
  ErrorSummary.prototype.init = function() {
1374
- var $module = this.$module;
1375
- if (!$module) {
1392
+ if (!this.$module) {
1376
1393
  return;
1377
1394
  }
1395
+ var $module = this.$module;
1378
1396
  this.setFocus();
1379
1397
  $module.addEventListener("click", this.handleClick.bind(this));
1380
1398
  };
@@ -1399,10 +1417,13 @@ ErrorSummary.prototype.handleClick = function(event) {
1399
1417
  };
1400
1418
 
1401
1419
  ErrorSummary.prototype.focusTarget = function($target) {
1402
- if ($target.tagName !== "A" || $target.href === false) {
1420
+ if (!($target instanceof HTMLAnchorElement)) {
1403
1421
  return false;
1404
1422
  }
1405
1423
  var inputId = this.getFragmentFromUrl($target.href);
1424
+ if (!inputId) {
1425
+ return false;
1426
+ }
1406
1427
  var $input = document.getElementById(inputId);
1407
1428
  if (!$input) {
1408
1429
  return false;
@@ -1420,7 +1441,7 @@ ErrorSummary.prototype.focusTarget = function($target) {
1420
1441
 
1421
1442
  ErrorSummary.prototype.getFragmentFromUrl = function(url) {
1422
1443
  if (url.indexOf("#") === -1) {
1423
- return false;
1444
+ return undefined;
1424
1445
  }
1425
1446
  return url.split("#").pop();
1426
1447
  };
@@ -1431,7 +1452,7 @@ ErrorSummary.prototype.getAssociatedLegendOrLabel = function($input) {
1431
1452
  var $legends = $fieldset.getElementsByTagName("legend");
1432
1453
  if ($legends.length) {
1433
1454
  var $candidateLegend = $legends[0];
1434
- if ($input.type === "checkbox" || $input.type === "radio") {
1455
+ if ($input instanceof HTMLInputElement && ($input.type === "checkbox" || $input.type === "radio")) {
1435
1456
  return $candidateLegend;
1436
1457
  }
1437
1458
  var legendTop = $candidateLegend.getBoundingClientRect().top;
@@ -1448,11 +1469,21 @@ ErrorSummary.prototype.getAssociatedLegendOrLabel = function($input) {
1448
1469
  };
1449
1470
 
1450
1471
  function Radios($module) {
1472
+ if (!($module instanceof HTMLElement)) {
1473
+ return this;
1474
+ }
1475
+ var $inputs = $module.querySelectorAll('input[type="radio"]');
1476
+ if (!$inputs.length) {
1477
+ return this;
1478
+ }
1451
1479
  this.$module = $module;
1452
- this.$inputs = $module.querySelectorAll('input[type="radio"]');
1480
+ this.$inputs = $inputs;
1453
1481
  }
1454
1482
 
1455
1483
  Radios.prototype.init = function() {
1484
+ if (!this.$module || !this.$inputs) {
1485
+ return;
1486
+ }
1456
1487
  var $module = this.$module;
1457
1488
  var $inputs = this.$inputs;
1458
1489
  nodeListForEach($inputs, (function($input) {
@@ -1463,11 +1494,7 @@ Radios.prototype.init = function() {
1463
1494
  $input.setAttribute("aria-controls", targetId);
1464
1495
  $input.removeAttribute("data-aria-controls");
1465
1496
  }));
1466
- if ("onpageshow" in window) {
1467
- window.addEventListener("pageshow", this.syncAllConditionalReveals.bind(this));
1468
- } else {
1469
- window.addEventListener("DOMContentLoaded", this.syncAllConditionalReveals.bind(this));
1470
- }
1497
+ window.addEventListener("onpageshow" in window ? "pageshow" : "DOMContentLoaded", this.syncAllConditionalReveals.bind(this));
1471
1498
  this.syncAllConditionalReveals();
1472
1499
  $module.addEventListener("click", this.handleClick.bind(this));
1473
1500
  };
@@ -1477,27 +1504,34 @@ Radios.prototype.syncAllConditionalReveals = function() {
1477
1504
  };
1478
1505
 
1479
1506
  Radios.prototype.syncConditionalRevealWithInputState = function($input) {
1480
- var $target = document.getElementById($input.getAttribute("aria-controls"));
1507
+ var targetId = $input.getAttribute("aria-controls");
1508
+ if (!targetId) {
1509
+ return;
1510
+ }
1511
+ var $target = document.getElementById(targetId);
1481
1512
  if ($target && $target.classList.contains("govuk-radios__conditional")) {
1482
1513
  var inputIsChecked = $input.checked;
1483
- $input.setAttribute("aria-expanded", inputIsChecked);
1514
+ $input.setAttribute("aria-expanded", inputIsChecked.toString());
1484
1515
  $target.classList.toggle("govuk-radios__conditional--hidden", !inputIsChecked);
1485
1516
  }
1486
1517
  };
1487
1518
 
1488
1519
  Radios.prototype.handleClick = function(event) {
1520
+ var $component = this;
1489
1521
  var $clickedInput = event.target;
1490
- if ($clickedInput.type !== "radio") {
1522
+ if (!($clickedInput instanceof HTMLInputElement) || $clickedInput.type !== "radio") {
1491
1523
  return;
1492
1524
  }
1493
1525
  var $allInputs = document.querySelectorAll('input[type="radio"][aria-controls]');
1494
- nodeListForEach($allInputs, function($input) {
1495
- var hasSameFormOwner = $input.form === $clickedInput.form;
1496
- var hasSameName = $input.name === $clickedInput.name;
1526
+ var $clickedInputForm = $clickedInput.form;
1527
+ var $clickedInputName = $clickedInput.name;
1528
+ nodeListForEach($allInputs, (function($input) {
1529
+ var hasSameFormOwner = $input.form === $clickedInputForm;
1530
+ var hasSameName = $input.name === $clickedInputName;
1497
1531
  if (hasSameName && hasSameFormOwner) {
1498
- this.syncConditionalRevealWithInputState($input);
1532
+ $component.syncConditionalRevealWithInputState($input);
1499
1533
  }
1500
- }.bind(this));
1534
+ }));
1501
1535
  };
1502
1536
 
1503
1537
  function initAll(options) {