katalyst-govuk-formbuilder 1.2.2 → 1.3.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.
@@ -7,18 +7,122 @@ function nodeListForEach(nodes, callback) {
7
7
  }
8
8
  }
9
9
 
10
+ function mergeConfigs() {
11
+ var flattenObject = function(configObject) {
12
+ var flattenedObject = {};
13
+ var flattenLoop = function(obj, prefix) {
14
+ for (var key in obj) {
15
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) {
16
+ continue;
17
+ }
18
+ var value = obj[key];
19
+ var prefixedKey = prefix ? prefix + "." + key : key;
20
+ if (typeof value === "object") {
21
+ flattenLoop(value, prefixedKey);
22
+ } else {
23
+ flattenedObject[prefixedKey] = value;
24
+ }
25
+ }
26
+ };
27
+ flattenLoop(configObject);
28
+ return flattenedObject;
29
+ };
30
+ var formattedConfigObject = {};
31
+ for (var i = 0; i < arguments.length; i++) {
32
+ var obj = flattenObject(arguments[i]);
33
+ for (var key in obj) {
34
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
35
+ formattedConfigObject[key] = obj[key];
36
+ }
37
+ }
38
+ }
39
+ return formattedConfigObject;
40
+ }
41
+
42
+ function extractConfigByNamespace(configObject, namespace) {
43
+ if (!configObject || typeof configObject !== "object") {
44
+ throw new Error('Provide a `configObject` of type "object".');
45
+ }
46
+ if (!namespace || typeof namespace !== "string") {
47
+ throw new Error('Provide a `namespace` of type "string" to filter the `configObject` by.');
48
+ }
49
+ var newObject = {};
50
+ for (var key in configObject) {
51
+ var keyParts = key.split(".");
52
+ if (Object.prototype.hasOwnProperty.call(configObject, key) && keyParts[0] === namespace) {
53
+ if (keyParts.length > 1) {
54
+ keyParts.shift();
55
+ }
56
+ var newKey = keyParts.join(".");
57
+ newObject[newKey] = configObject[key];
58
+ }
59
+ }
60
+ return newObject;
61
+ }
62
+
10
63
  (function(undefined$1) {
11
- var detect = "Window" in this;
64
+ var detect = "defineProperty" in Object && function() {
65
+ try {
66
+ var a = {};
67
+ Object.defineProperty(a, "test", {
68
+ value: 42
69
+ });
70
+ return true;
71
+ } catch (e) {
72
+ return false;
73
+ }
74
+ }();
12
75
  if (detect) return;
13
- if (typeof WorkerGlobalScope === "undefined" && typeof importScripts !== "function") {
14
- (function(global) {
15
- if (global.constructor) {
16
- global.Window = global.constructor;
76
+ (function(nativeDefineProperty) {
77
+ var supportsAccessors = Object.prototype.hasOwnProperty("__defineGetter__");
78
+ var ERR_ACCESSORS_NOT_SUPPORTED = "Getters & setters cannot be defined on this javascript engine";
79
+ var ERR_VALUE_ACCESSORS = "A property cannot both have accessors and be writable or have a value";
80
+ Object.defineProperty = function defineProperty(object, property, descriptor) {
81
+ if (nativeDefineProperty && (object === window || object === document || object === Element.prototype || object instanceof Element)) {
82
+ return nativeDefineProperty(object, property, descriptor);
83
+ }
84
+ if (object === null || !(object instanceof Object || typeof object === "object")) {
85
+ throw new TypeError("Object.defineProperty called on non-object");
86
+ }
87
+ if (!(descriptor instanceof Object)) {
88
+ throw new TypeError("Property description must be an object");
89
+ }
90
+ var propertyString = String(property);
91
+ var hasValueOrWritable = "value" in descriptor || "writable" in descriptor;
92
+ var getterType = "get" in descriptor && typeof descriptor.get;
93
+ var setterType = "set" in descriptor && typeof descriptor.set;
94
+ if (getterType) {
95
+ if (getterType !== "function") {
96
+ throw new TypeError("Getter must be a function");
97
+ }
98
+ if (!supportsAccessors) {
99
+ throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
100
+ }
101
+ if (hasValueOrWritable) {
102
+ throw new TypeError(ERR_VALUE_ACCESSORS);
103
+ }
104
+ Object.__defineGetter__.call(object, propertyString, descriptor.get);
17
105
  } else {
18
- (global.Window = global.constructor = new Function("return function Window() {}")()).prototype = this;
106
+ object[propertyString] = descriptor.value;
19
107
  }
20
- })(this);
21
- }
108
+ if (setterType) {
109
+ if (setterType !== "function") {
110
+ throw new TypeError("Setter must be a function");
111
+ }
112
+ if (!supportsAccessors) {
113
+ throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
114
+ }
115
+ if (hasValueOrWritable) {
116
+ throw new TypeError(ERR_VALUE_ACCESSORS);
117
+ }
118
+ Object.__defineSetter__.call(object, propertyString, descriptor.set);
119
+ }
120
+ if ("value" in descriptor) {
121
+ object[propertyString] = descriptor.value;
122
+ }
123
+ return object;
124
+ };
125
+ })(Object.defineProperty);
22
126
  }).call("object" === typeof window && window || "object" === typeof self && self || "object" === typeof global && global || {});
23
127
 
24
128
  (function(undefined$1) {
@@ -105,68 +209,99 @@ function nodeListForEach(nodes, callback) {
105
209
  }).call("object" === typeof window && window || "object" === typeof self && self || "object" === typeof global && global || {});
106
210
 
107
211
  (function(undefined$1) {
108
- var detect = "defineProperty" in Object && function() {
109
- try {
110
- var a = {};
111
- Object.defineProperty(a, "test", {
112
- value: 42
113
- });
114
- return true;
115
- } catch (e) {
212
+ var detect = function() {
213
+ if (!document.documentElement.dataset) {
116
214
  return false;
117
215
  }
216
+ var el = document.createElement("div");
217
+ el.setAttribute("data-a-b", "c");
218
+ return el.dataset && el.dataset.aB == "c";
118
219
  }();
119
220
  if (detect) return;
120
- (function(nativeDefineProperty) {
121
- var supportsAccessors = Object.prototype.hasOwnProperty("__defineGetter__");
122
- var ERR_ACCESSORS_NOT_SUPPORTED = "Getters & setters cannot be defined on this javascript engine";
123
- var ERR_VALUE_ACCESSORS = "A property cannot both have accessors and be writable or have a value";
124
- Object.defineProperty = function defineProperty(object, property, descriptor) {
125
- if (nativeDefineProperty && (object === window || object === document || object === Element.prototype || object instanceof Element)) {
126
- return nativeDefineProperty(object, property, descriptor);
127
- }
128
- if (object === null || !(object instanceof Object || typeof object === "object")) {
129
- throw new TypeError("Object.defineProperty called on non-object");
130
- }
131
- if (!(descriptor instanceof Object)) {
132
- throw new TypeError("Property description must be an object");
133
- }
134
- var propertyString = String(property);
135
- var hasValueOrWritable = "value" in descriptor || "writable" in descriptor;
136
- var getterType = "get" in descriptor && typeof descriptor.get;
137
- var setterType = "set" in descriptor && typeof descriptor.set;
138
- if (getterType) {
139
- if (getterType !== "function") {
140
- throw new TypeError("Getter must be a function");
141
- }
142
- if (!supportsAccessors) {
143
- throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
144
- }
145
- if (hasValueOrWritable) {
146
- throw new TypeError(ERR_VALUE_ACCESSORS);
147
- }
148
- Object.__defineGetter__.call(object, propertyString, descriptor.get);
149
- } else {
150
- object[propertyString] = descriptor.value;
151
- }
152
- if (setterType) {
153
- if (setterType !== "function") {
154
- throw new TypeError("Setter must be a function");
155
- }
156
- if (!supportsAccessors) {
157
- throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
158
- }
159
- if (hasValueOrWritable) {
160
- throw new TypeError(ERR_VALUE_ACCESSORS);
221
+ Object.defineProperty(Element.prototype, "dataset", {
222
+ get: function() {
223
+ var element = this;
224
+ var attributes = this.attributes;
225
+ var map = {};
226
+ for (var i = 0; i < attributes.length; i++) {
227
+ var attribute = attributes[i];
228
+ if (attribute && attribute.name && /^data-\w[.\w-]*$/.test(attribute.name)) {
229
+ var name = attribute.name;
230
+ var value = attribute.value;
231
+ var propName = name.substr(5).replace(/-./g, (function(prop) {
232
+ return prop.charAt(1).toUpperCase();
233
+ }));
234
+ if ("__defineGetter__" in Object.prototype && "__defineSetter__" in Object.prototype) {
235
+ Object.defineProperty(map, propName, {
236
+ enumerable: true,
237
+ get: function() {
238
+ return this.value;
239
+ }.bind({
240
+ value: value || ""
241
+ }),
242
+ set: function setter(name, value) {
243
+ if (typeof value !== "undefined") {
244
+ this.setAttribute(name, value);
245
+ } else {
246
+ this.removeAttribute(name);
247
+ }
248
+ }.bind(element, name)
249
+ });
250
+ } else {
251
+ map[propName] = value;
252
+ }
161
253
  }
162
- Object.__defineSetter__.call(object, propertyString, descriptor.set);
163
254
  }
164
- if ("value" in descriptor) {
165
- object[propertyString] = descriptor.value;
255
+ return map;
256
+ }
257
+ });
258
+ }).call("object" === typeof window && window || "object" === typeof self && self || "object" === typeof global && global || {});
259
+
260
+ (function(undefined$1) {
261
+ var detect = "trim" in String.prototype;
262
+ if (detect) return;
263
+ String.prototype.trim = function() {
264
+ return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "");
265
+ };
266
+ }).call("object" === typeof window && window || "object" === typeof self && self || "object" === typeof global && global || {});
267
+
268
+ function normaliseString(value) {
269
+ if (typeof value !== "string") {
270
+ return value;
271
+ }
272
+ var trimmedValue = value.trim();
273
+ if (trimmedValue === "true") {
274
+ return true;
275
+ }
276
+ if (trimmedValue === "false") {
277
+ return false;
278
+ }
279
+ if (trimmedValue.length > 0 && isFinite(trimmedValue)) {
280
+ return Number(trimmedValue);
281
+ }
282
+ return value;
283
+ }
284
+
285
+ function normaliseDataset(dataset) {
286
+ var out = {};
287
+ for (var key in dataset) {
288
+ out[key] = normaliseString(dataset[key]);
289
+ }
290
+ return out;
291
+ }
292
+
293
+ (function(undefined$1) {
294
+ var detect = "Window" in this;
295
+ if (detect) return;
296
+ if (typeof WorkerGlobalScope === "undefined" && typeof importScripts !== "function") {
297
+ (function(global) {
298
+ if (global.constructor) {
299
+ global.Window = global.constructor;
300
+ } else {
301
+ (global.Window = global.constructor = new Function("return function Window() {}")()).prototype = this;
166
302
  }
167
- return object;
168
- };
169
- })(Object.defineProperty);
303
+ })(this);
304
+ }
170
305
  }).call("object" === typeof window && window || "object" === typeof self && self || "object" === typeof global && global || {});
171
306
 
172
307
  (function(undefined$1) {
@@ -415,22 +550,36 @@ var KEY_SPACE = 32;
415
550
 
416
551
  var DEBOUNCE_TIMEOUT_IN_SECONDS = 1;
417
552
 
418
- function Button($module) {
553
+ function Button($module, config) {
554
+ if (!$module) {
555
+ return this;
556
+ }
419
557
  this.$module = $module;
420
558
  this.debounceFormSubmitTimer = null;
559
+ var defaultConfig = {
560
+ preventDoubleClick: false
561
+ };
562
+ this.config = mergeConfigs(defaultConfig, config || {}, normaliseDataset($module.dataset));
421
563
  }
422
564
 
565
+ Button.prototype.init = function() {
566
+ if (!this.$module) {
567
+ return;
568
+ }
569
+ this.$module.addEventListener("keydown", this.handleKeyDown);
570
+ this.$module.addEventListener("click", this.debounce.bind(this));
571
+ };
572
+
423
573
  Button.prototype.handleKeyDown = function(event) {
424
- var target = event.target;
425
- if (target.getAttribute("role") === "button" && event.keyCode === KEY_SPACE) {
574
+ var $target = event.target;
575
+ if ($target.getAttribute("role") === "button" && event.keyCode === KEY_SPACE) {
426
576
  event.preventDefault();
427
- target.click();
577
+ $target.click();
428
578
  }
429
579
  };
430
580
 
431
581
  Button.prototype.debounce = function(event) {
432
- var target = event.target;
433
- if (target.getAttribute("data-prevent-double-click") !== "true") {
582
+ if (!this.config.preventDoubleClick) {
434
583
  return;
435
584
  }
436
585
  if (this.debounceFormSubmitTimer) {
@@ -442,11 +591,260 @@ Button.prototype.debounce = function(event) {
442
591
  }.bind(this), DEBOUNCE_TIMEOUT_IN_SECONDS * 1e3);
443
592
  };
444
593
 
445
- Button.prototype.init = function() {
446
- this.$module.addEventListener("keydown", this.handleKeyDown);
447
- this.$module.addEventListener("click", this.debounce);
594
+ (function(undefined$1) {
595
+ var detect = "document" in this && "matches" in document.documentElement;
596
+ if (detect) return;
597
+ Element.prototype.matches = Element.prototype.webkitMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.mozMatchesSelector || function matches(selector) {
598
+ var element = this;
599
+ var elements = (element.document || element.ownerDocument).querySelectorAll(selector);
600
+ var index = 0;
601
+ while (elements[index] && elements[index] !== element) {
602
+ ++index;
603
+ }
604
+ return !!elements[index];
605
+ };
606
+ }).call("object" === typeof window && window || "object" === typeof self && self || "object" === typeof global && global || {});
607
+
608
+ (function(undefined$1) {
609
+ var detect = "document" in this && "closest" in document.documentElement;
610
+ if (detect) return;
611
+ Element.prototype.closest = function closest(selector) {
612
+ var node = this;
613
+ while (node) {
614
+ if (node.matches(selector)) return node; else node = "SVGElement" in window && node instanceof SVGElement ? node.parentNode : node.parentElement;
615
+ }
616
+ return null;
617
+ };
618
+ }).call("object" === typeof window && window || "object" === typeof self && self || "object" === typeof global && global || {});
619
+
620
+ function closestAttributeValue($element, attributeName) {
621
+ var closestElementWithAttribute = $element.closest("[" + attributeName + "]");
622
+ if (closestElementWithAttribute) {
623
+ return closestElementWithAttribute.getAttribute(attributeName);
624
+ }
625
+ }
626
+
627
+ function I18n(translations, config) {
628
+ this.translations = translations || {};
629
+ this.locale = config && config.locale || document.documentElement.lang || "en";
630
+ }
631
+
632
+ I18n.prototype.t = function(lookupKey, options) {
633
+ if (!lookupKey) {
634
+ throw new Error("i18n: lookup key missing");
635
+ }
636
+ if (options && typeof options.count !== "undefined") {
637
+ lookupKey = lookupKey + "." + this.getPluralSuffix(lookupKey, options.count);
638
+ }
639
+ if (lookupKey in this.translations) {
640
+ var translationString = this.translations[lookupKey];
641
+ if (translationString.match(/%{(.\S+)}/)) {
642
+ if (!options) {
643
+ throw new Error("i18n: cannot replace placeholders in string if no option data provided");
644
+ }
645
+ return this.replacePlaceholders(translationString, options);
646
+ } else {
647
+ return translationString;
648
+ }
649
+ } else {
650
+ return lookupKey;
651
+ }
652
+ };
653
+
654
+ I18n.prototype.replacePlaceholders = function(translationString, options) {
655
+ var formatter;
656
+ if (this.hasIntlNumberFormatSupport()) {
657
+ formatter = new Intl.NumberFormat(this.locale);
658
+ }
659
+ return translationString.replace(/%{(.\S+)}/g, (function(placeholderWithBraces, placeholderKey) {
660
+ if (Object.prototype.hasOwnProperty.call(options, placeholderKey)) {
661
+ var placeholderValue = options[placeholderKey];
662
+ if (placeholderValue === false) {
663
+ return "";
664
+ }
665
+ if (typeof placeholderValue === "number" && formatter) {
666
+ return formatter.format(placeholderValue);
667
+ }
668
+ return placeholderValue;
669
+ } else {
670
+ throw new Error("i18n: no data found to replace " + placeholderWithBraces + " placeholder in string");
671
+ }
672
+ }));
673
+ };
674
+
675
+ I18n.prototype.hasIntlPluralRulesSupport = function() {
676
+ return Boolean(window.Intl && ("PluralRules" in window.Intl && Intl.PluralRules.supportedLocalesOf(this.locale).length));
677
+ };
678
+
679
+ I18n.prototype.hasIntlNumberFormatSupport = function() {
680
+ return Boolean(window.Intl && ("NumberFormat" in window.Intl && Intl.NumberFormat.supportedLocalesOf(this.locale).length));
681
+ };
682
+
683
+ I18n.prototype.getPluralSuffix = function(lookupKey, count) {
684
+ count = Number(count);
685
+ if (!isFinite(count)) {
686
+ return "other";
687
+ }
688
+ var preferredForm;
689
+ if (this.hasIntlPluralRulesSupport()) {
690
+ preferredForm = new Intl.PluralRules(this.locale).select(count);
691
+ } else {
692
+ preferredForm = this.selectPluralFormUsingFallbackRules(count);
693
+ }
694
+ if (lookupKey + "." + preferredForm in this.translations) {
695
+ return preferredForm;
696
+ } else if (lookupKey + ".other" in this.translations) {
697
+ if (console && "warn" in console) {
698
+ console.warn('i18n: Missing plural form ".' + preferredForm + '" for "' + this.locale + '" locale. Falling back to ".other".');
699
+ }
700
+ return "other";
701
+ } else {
702
+ throw new Error('i18n: Plural form ".other" is required for "' + this.locale + '" locale');
703
+ }
704
+ };
705
+
706
+ I18n.prototype.selectPluralFormUsingFallbackRules = function(count) {
707
+ count = Math.abs(Math.floor(count));
708
+ var ruleset = this.getPluralRulesForLocale();
709
+ if (ruleset) {
710
+ return I18n.pluralRules[ruleset](count);
711
+ }
712
+ return "other";
448
713
  };
449
714
 
715
+ I18n.prototype.getPluralRulesForLocale = function() {
716
+ var locale = this.locale;
717
+ var localeShort = locale.split("-")[0];
718
+ for (var pluralRule in I18n.pluralRulesMap) {
719
+ if (Object.prototype.hasOwnProperty.call(I18n.pluralRulesMap, pluralRule)) {
720
+ var languages = I18n.pluralRulesMap[pluralRule];
721
+ for (var i = 0; i < languages.length; i++) {
722
+ if (languages[i] === locale || languages[i] === localeShort) {
723
+ return pluralRule;
724
+ }
725
+ }
726
+ }
727
+ }
728
+ };
729
+
730
+ I18n.pluralRulesMap = {
731
+ arabic: [ "ar" ],
732
+ chinese: [ "my", "zh", "id", "ja", "jv", "ko", "ms", "th", "vi" ],
733
+ french: [ "hy", "bn", "fr", "gu", "hi", "fa", "pa", "zu" ],
734
+ german: [ "af", "sq", "az", "eu", "bg", "ca", "da", "nl", "en", "et", "fi", "ka", "de", "el", "hu", "lb", "no", "so", "sw", "sv", "ta", "te", "tr", "ur" ],
735
+ irish: [ "ga" ],
736
+ russian: [ "ru", "uk" ],
737
+ scottish: [ "gd" ],
738
+ spanish: [ "pt-PT", "it", "es" ],
739
+ welsh: [ "cy" ]
740
+ };
741
+
742
+ I18n.pluralRules = {
743
+ arabic: function(n) {
744
+ if (n === 0) {
745
+ return "zero";
746
+ }
747
+ if (n === 1) {
748
+ return "one";
749
+ }
750
+ if (n === 2) {
751
+ return "two";
752
+ }
753
+ if (n % 100 >= 3 && n % 100 <= 10) {
754
+ return "few";
755
+ }
756
+ if (n % 100 >= 11 && n % 100 <= 99) {
757
+ return "many";
758
+ }
759
+ return "other";
760
+ },
761
+ chinese: function() {
762
+ return "other";
763
+ },
764
+ french: function(n) {
765
+ return n === 0 || n === 1 ? "one" : "other";
766
+ },
767
+ german: function(n) {
768
+ return n === 1 ? "one" : "other";
769
+ },
770
+ irish: function(n) {
771
+ if (n === 1) {
772
+ return "one";
773
+ }
774
+ if (n === 2) {
775
+ return "two";
776
+ }
777
+ if (n >= 3 && n <= 6) {
778
+ return "few";
779
+ }
780
+ if (n >= 7 && n <= 10) {
781
+ return "many";
782
+ }
783
+ return "other";
784
+ },
785
+ russian: function(n) {
786
+ var lastTwo = n % 100;
787
+ var last = lastTwo % 10;
788
+ if (last === 1 && lastTwo !== 11) {
789
+ return "one";
790
+ }
791
+ if (last >= 2 && last <= 4 && !(lastTwo >= 12 && lastTwo <= 14)) {
792
+ return "few";
793
+ }
794
+ if (last === 0 || last >= 5 && last <= 9 || lastTwo >= 11 && lastTwo <= 14) {
795
+ return "many";
796
+ }
797
+ return "other";
798
+ },
799
+ scottish: function(n) {
800
+ if (n === 1 || n === 11) {
801
+ return "one";
802
+ }
803
+ if (n === 2 || n === 12) {
804
+ return "two";
805
+ }
806
+ if (n >= 3 && n <= 10 || n >= 13 && n <= 19) {
807
+ return "few";
808
+ }
809
+ return "other";
810
+ },
811
+ spanish: function(n) {
812
+ if (n === 1) {
813
+ return "one";
814
+ }
815
+ if (n % 1e6 === 0 && n !== 0) {
816
+ return "many";
817
+ }
818
+ return "other";
819
+ },
820
+ welsh: function(n) {
821
+ if (n === 0) {
822
+ return "zero";
823
+ }
824
+ if (n === 1) {
825
+ return "one";
826
+ }
827
+ if (n === 2) {
828
+ return "two";
829
+ }
830
+ if (n === 3) {
831
+ return "few";
832
+ }
833
+ if (n === 6) {
834
+ return "many";
835
+ }
836
+ return "other";
837
+ }
838
+ };
839
+
840
+ (function(undefined$1) {
841
+ var detect = "Date" in self && "now" in self.Date && "getTime" in self.Date.prototype;
842
+ if (detect) return;
843
+ Date.now = function() {
844
+ return (new Date).getTime();
845
+ };
846
+ }).call("object" === typeof window && window || "object" === typeof self && self || "object" === typeof global && global || {});
847
+
450
848
  (function(undefined$1) {
451
849
  var detect = "DOMTokenList" in this && function(x) {
452
850
  return "classList" in x ? !x.classList.toggle("x", false) && !x.className : true;
@@ -685,7 +1083,57 @@ Button.prototype.init = function() {
685
1083
  })(this);
686
1084
  }).call("object" === typeof window && window || "object" === typeof self && self || "object" === typeof global && global || {});
687
1085
 
688
- function CharacterCount($module) {
1086
+ var CHARACTER_COUNT_TRANSLATIONS = {
1087
+ charactersUnderLimit: {
1088
+ one: "You have %{count} character remaining",
1089
+ other: "You have %{count} characters remaining"
1090
+ },
1091
+ charactersAtLimit: "You have 0 characters remaining",
1092
+ charactersOverLimit: {
1093
+ one: "You have %{count} character too many",
1094
+ other: "You have %{count} characters too many"
1095
+ },
1096
+ wordsUnderLimit: {
1097
+ one: "You have %{count} word remaining",
1098
+ other: "You have %{count} words remaining"
1099
+ },
1100
+ wordsAtLimit: "You have 0 words remaining",
1101
+ wordsOverLimit: {
1102
+ one: "You have %{count} word too many",
1103
+ other: "You have %{count} words too many"
1104
+ },
1105
+ textareaDescription: {
1106
+ other: ""
1107
+ }
1108
+ };
1109
+
1110
+ function CharacterCount($module, config) {
1111
+ if (!$module) {
1112
+ return this;
1113
+ }
1114
+ var defaultConfig = {
1115
+ threshold: 0,
1116
+ i18n: CHARACTER_COUNT_TRANSLATIONS
1117
+ };
1118
+ var datasetConfig = normaliseDataset($module.dataset);
1119
+ var configOverrides = {};
1120
+ if ("maxwords" in datasetConfig || "maxlength" in datasetConfig) {
1121
+ configOverrides = {
1122
+ maxlength: false,
1123
+ maxwords: false
1124
+ };
1125
+ }
1126
+ this.config = mergeConfigs(defaultConfig, config || {}, configOverrides, datasetConfig);
1127
+ this.i18n = new I18n(extractConfigByNamespace(this.config, "i18n"), {
1128
+ locale: closestAttributeValue($module, "lang")
1129
+ });
1130
+ if (this.config.maxwords) {
1131
+ this.maxLength = this.config.maxwords;
1132
+ } else if (this.config.maxlength) {
1133
+ this.maxLength = this.config.maxlength;
1134
+ } else {
1135
+ return;
1136
+ }
689
1137
  this.$module = $module;
690
1138
  this.$textarea = $module.querySelector(".govuk-js-character-count");
691
1139
  this.$visibleCountMessage = null;
@@ -693,40 +1141,30 @@ function CharacterCount($module) {
693
1141
  this.lastInputTimestamp = null;
694
1142
  }
695
1143
 
696
- CharacterCount.prototype.defaults = {
697
- characterCountAttribute: "data-maxlength",
698
- wordCountAttribute: "data-maxwords"
699
- };
700
-
701
1144
  CharacterCount.prototype.init = function() {
702
1145
  if (!this.$textarea) {
703
1146
  return;
704
1147
  }
705
- var $module = this.$module;
706
1148
  var $textarea = this.$textarea;
707
- var $fallbackLimitMessage = document.getElementById($textarea.id + "-info");
708
- $textarea.insertAdjacentElement("afterend", $fallbackLimitMessage);
1149
+ var $textareaDescription = document.getElementById($textarea.id + "-info");
1150
+ if ($textareaDescription.innerText.match(/^\s*$/)) {
1151
+ $textareaDescription.innerText = this.i18n.t("textareaDescription", {
1152
+ count: this.maxLength
1153
+ });
1154
+ }
1155
+ $textarea.insertAdjacentElement("afterend", $textareaDescription);
709
1156
  var $screenReaderCountMessage = document.createElement("div");
710
1157
  $screenReaderCountMessage.className = "govuk-character-count__sr-status govuk-visually-hidden";
711
1158
  $screenReaderCountMessage.setAttribute("aria-live", "polite");
712
1159
  this.$screenReaderCountMessage = $screenReaderCountMessage;
713
- $fallbackLimitMessage.insertAdjacentElement("afterend", $screenReaderCountMessage);
1160
+ $textareaDescription.insertAdjacentElement("afterend", $screenReaderCountMessage);
714
1161
  var $visibleCountMessage = document.createElement("div");
715
- $visibleCountMessage.className = $fallbackLimitMessage.className;
1162
+ $visibleCountMessage.className = $textareaDescription.className;
716
1163
  $visibleCountMessage.classList.add("govuk-character-count__status");
717
1164
  $visibleCountMessage.setAttribute("aria-hidden", "true");
718
1165
  this.$visibleCountMessage = $visibleCountMessage;
719
- $fallbackLimitMessage.insertAdjacentElement("afterend", $visibleCountMessage);
720
- $fallbackLimitMessage.classList.add("govuk-visually-hidden");
721
- this.options = this.getDataset($module);
722
- var countAttribute = this.defaults.characterCountAttribute;
723
- if (this.options.maxwords) {
724
- countAttribute = this.defaults.wordCountAttribute;
725
- }
726
- this.maxLength = $module.getAttribute(countAttribute);
727
- if (!this.maxLength) {
728
- return;
729
- }
1166
+ $textareaDescription.insertAdjacentElement("afterend", $visibleCountMessage);
1167
+ $textareaDescription.classList.add("govuk-visually-hidden");
730
1168
  $textarea.removeAttribute("maxlength");
731
1169
  this.bindChangeEvents();
732
1170
  if ("onpageshow" in window) {
@@ -737,32 +1175,6 @@ CharacterCount.prototype.init = function() {
737
1175
  this.updateCountMessage();
738
1176
  };
739
1177
 
740
- CharacterCount.prototype.getDataset = function(element) {
741
- var dataset = {};
742
- var attributes = element.attributes;
743
- if (attributes) {
744
- for (var i = 0; i < attributes.length; i++) {
745
- var attribute = attributes[i];
746
- var match = attribute.name.match(/^data-(.+)/);
747
- if (match) {
748
- dataset[match[1]] = attribute.value;
749
- }
750
- }
751
- }
752
- return dataset;
753
- };
754
-
755
- CharacterCount.prototype.count = function(text) {
756
- var length;
757
- if (this.options.maxwords) {
758
- var tokens = text.match(/\S+/g) || [];
759
- length = tokens.length;
760
- } else {
761
- length = text.length;
762
- }
763
- return length;
764
- };
765
-
766
1178
  CharacterCount.prototype.bindChangeEvents = function() {
767
1179
  var $textarea = this.$textarea;
768
1180
  $textarea.addEventListener("keyup", this.handleKeyUp.bind(this));
@@ -770,7 +1182,24 @@ CharacterCount.prototype.bindChangeEvents = function() {
770
1182
  $textarea.addEventListener("blur", this.handleBlur.bind(this));
771
1183
  };
772
1184
 
773
- CharacterCount.prototype.checkIfValueChanged = function() {
1185
+ CharacterCount.prototype.handleKeyUp = function() {
1186
+ this.updateVisibleCountMessage();
1187
+ this.lastInputTimestamp = Date.now();
1188
+ };
1189
+
1190
+ CharacterCount.prototype.handleFocus = function() {
1191
+ this.valueChecker = setInterval(function() {
1192
+ if (!this.lastInputTimestamp || Date.now() - 500 >= this.lastInputTimestamp) {
1193
+ this.updateIfValueChanged();
1194
+ }
1195
+ }.bind(this), 1e3);
1196
+ };
1197
+
1198
+ CharacterCount.prototype.handleBlur = function() {
1199
+ clearInterval(this.valueChecker);
1200
+ };
1201
+
1202
+ CharacterCount.prototype.updateIfValueChanged = function() {
774
1203
  if (!this.$textarea.oldValue) this.$textarea.oldValue = "";
775
1204
  if (this.$textarea.value !== this.$textarea.oldValue) {
776
1205
  this.$textarea.oldValue = this.$textarea.value;
@@ -801,7 +1230,7 @@ CharacterCount.prototype.updateVisibleCountMessage = function() {
801
1230
  $visibleCountMessage.classList.remove("govuk-error-message");
802
1231
  $visibleCountMessage.classList.add("govuk-hint");
803
1232
  }
804
- $visibleCountMessage.innerHTML = this.formattedUpdateMessage();
1233
+ $visibleCountMessage.innerText = this.getCountMessage();
805
1234
  };
806
1235
 
807
1236
  CharacterCount.prototype.updateScreenReaderCountMessage = function() {
@@ -811,52 +1240,45 @@ CharacterCount.prototype.updateScreenReaderCountMessage = function() {
811
1240
  } else {
812
1241
  $screenReaderCountMessage.setAttribute("aria-hidden", true);
813
1242
  }
814
- $screenReaderCountMessage.innerHTML = this.formattedUpdateMessage();
1243
+ $screenReaderCountMessage.innerText = this.getCountMessage();
815
1244
  };
816
1245
 
817
- CharacterCount.prototype.formattedUpdateMessage = function() {
818
- var $textarea = this.$textarea;
819
- var options = this.options;
820
- var remainingNumber = this.maxLength - this.count($textarea.value);
821
- var charVerb = "remaining";
822
- var charNoun = "character";
823
- var displayNumber = remainingNumber;
824
- if (options.maxwords) {
825
- charNoun = "word";
1246
+ CharacterCount.prototype.count = function(text) {
1247
+ if (this.config.maxwords) {
1248
+ var tokens = text.match(/\S+/g) || [];
1249
+ return tokens.length;
1250
+ } else {
1251
+ return text.length;
1252
+ }
1253
+ };
1254
+
1255
+ CharacterCount.prototype.getCountMessage = function() {
1256
+ var remainingNumber = this.maxLength - this.count(this.$textarea.value);
1257
+ var countType = this.config.maxwords ? "words" : "characters";
1258
+ return this.formatCountMessage(remainingNumber, countType);
1259
+ };
1260
+
1261
+ CharacterCount.prototype.formatCountMessage = function(remainingNumber, countType) {
1262
+ if (remainingNumber === 0) {
1263
+ return this.i18n.t(countType + "AtLimit");
826
1264
  }
827
- charNoun = charNoun + (remainingNumber === -1 || remainingNumber === 1 ? "" : "s");
828
- charVerb = remainingNumber < 0 ? "too many" : "remaining";
829
- displayNumber = Math.abs(remainingNumber);
830
- return "You have " + displayNumber + " " + charNoun + " " + charVerb;
1265
+ var translationKeySuffix = remainingNumber < 0 ? "OverLimit" : "UnderLimit";
1266
+ return this.i18n.t(countType + translationKeySuffix, {
1267
+ count: Math.abs(remainingNumber)
1268
+ });
831
1269
  };
832
1270
 
833
1271
  CharacterCount.prototype.isOverThreshold = function() {
1272
+ if (!this.config.threshold) {
1273
+ return true;
1274
+ }
834
1275
  var $textarea = this.$textarea;
835
- var options = this.options;
836
1276
  var currentLength = this.count($textarea.value);
837
1277
  var maxLength = this.maxLength;
838
- var thresholdPercent = options.threshold ? options.threshold : 0;
839
- var thresholdValue = maxLength * thresholdPercent / 100;
1278
+ var thresholdValue = maxLength * this.config.threshold / 100;
840
1279
  return thresholdValue <= currentLength;
841
1280
  };
842
1281
 
843
- CharacterCount.prototype.handleKeyUp = function() {
844
- this.updateVisibleCountMessage();
845
- this.lastInputTimestamp = Date.now();
846
- };
847
-
848
- CharacterCount.prototype.handleFocus = function() {
849
- this.valueChecker = setInterval(function() {
850
- if (!this.lastInputTimestamp || Date.now() - 500 >= this.lastInputTimestamp) {
851
- this.checkIfValueChanged();
852
- }
853
- }.bind(this), 1e3);
854
- };
855
-
856
- CharacterCount.prototype.handleBlur = function() {
857
- clearInterval(this.valueChecker);
858
- };
859
-
860
1282
  function Checkboxes($module) {
861
1283
  this.$module = $module;
862
1284
  this.$inputs = $module.querySelectorAll('input[type="checkbox"]');
@@ -866,11 +1288,11 @@ Checkboxes.prototype.init = function() {
866
1288
  var $module = this.$module;
867
1289
  var $inputs = this.$inputs;
868
1290
  nodeListForEach($inputs, (function($input) {
869
- var target = $input.getAttribute("data-aria-controls");
870
- if (!target || !document.getElementById(target)) {
1291
+ var targetId = $input.getAttribute("data-aria-controls");
1292
+ if (!targetId || !document.getElementById(targetId)) {
871
1293
  return;
872
1294
  }
873
- $input.setAttribute("aria-controls", target);
1295
+ $input.setAttribute("aria-controls", targetId);
874
1296
  $input.removeAttribute("data-aria-controls");
875
1297
  }));
876
1298
  if ("onpageshow" in window) {
@@ -918,53 +1340,34 @@ Checkboxes.prototype.unCheckExclusiveInputs = function($input) {
918
1340
  };
919
1341
 
920
1342
  Checkboxes.prototype.handleClick = function(event) {
921
- var $target = event.target;
922
- if ($target.type !== "checkbox") {
1343
+ var $clickedInput = event.target;
1344
+ if ($clickedInput.type !== "checkbox") {
923
1345
  return;
924
1346
  }
925
- var hasAriaControls = $target.getAttribute("aria-controls");
1347
+ var hasAriaControls = $clickedInput.getAttribute("aria-controls");
926
1348
  if (hasAriaControls) {
927
- this.syncConditionalRevealWithInputState($target);
1349
+ this.syncConditionalRevealWithInputState($clickedInput);
928
1350
  }
929
- if (!$target.checked) {
1351
+ if (!$clickedInput.checked) {
930
1352
  return;
931
1353
  }
932
- var hasBehaviourExclusive = $target.getAttribute("data-behaviour") === "exclusive";
1354
+ var hasBehaviourExclusive = $clickedInput.getAttribute("data-behaviour") === "exclusive";
933
1355
  if (hasBehaviourExclusive) {
934
- this.unCheckAllInputsExcept($target);
1356
+ this.unCheckAllInputsExcept($clickedInput);
935
1357
  } else {
936
- this.unCheckExclusiveInputs($target);
1358
+ this.unCheckExclusiveInputs($clickedInput);
937
1359
  }
938
1360
  };
939
1361
 
940
- (function(undefined$1) {
941
- var detect = "document" in this && "matches" in document.documentElement;
942
- if (detect) return;
943
- Element.prototype.matches = Element.prototype.webkitMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.mozMatchesSelector || function matches(selector) {
944
- var element = this;
945
- var elements = (element.document || element.ownerDocument).querySelectorAll(selector);
946
- var index = 0;
947
- while (elements[index] && elements[index] !== element) {
948
- ++index;
949
- }
950
- return !!elements[index];
951
- };
952
- }).call("object" === typeof window && window || "object" === typeof self && self || "object" === typeof global && global || {});
953
-
954
- (function(undefined$1) {
955
- var detect = "document" in this && "closest" in document.documentElement;
956
- if (detect) return;
957
- Element.prototype.closest = function closest(selector) {
958
- var node = this;
959
- while (node) {
960
- if (node.matches(selector)) return node; else node = "SVGElement" in window && node instanceof SVGElement ? node.parentNode : node.parentElement;
961
- }
962
- return null;
963
- };
964
- }).call("object" === typeof window && window || "object" === typeof self && self || "object" === typeof global && global || {});
965
-
966
- function ErrorSummary($module) {
1362
+ function ErrorSummary($module, config) {
1363
+ if (!$module) {
1364
+ return this;
1365
+ }
967
1366
  this.$module = $module;
1367
+ var defaultConfig = {
1368
+ disableAutoFocus: false
1369
+ };
1370
+ this.config = mergeConfigs(defaultConfig, config || {}, normaliseDataset($module.dataset));
968
1371
  }
969
1372
 
970
1373
  ErrorSummary.prototype.init = function() {
@@ -978,7 +1381,7 @@ ErrorSummary.prototype.init = function() {
978
1381
 
979
1382
  ErrorSummary.prototype.setFocus = function() {
980
1383
  var $module = this.$module;
981
- if ($module.getAttribute("data-disable-auto-focus") === "true") {
1384
+ if (this.config.disableAutoFocus) {
982
1385
  return;
983
1386
  }
984
1387
  $module.setAttribute("tabindex", "-1");
@@ -989,8 +1392,8 @@ ErrorSummary.prototype.setFocus = function() {
989
1392
  };
990
1393
 
991
1394
  ErrorSummary.prototype.handleClick = function(event) {
992
- var target = event.target;
993
- if (this.focusTarget(target)) {
1395
+ var $target = event.target;
1396
+ if (this.focusTarget($target)) {
994
1397
  event.preventDefault();
995
1398
  }
996
1399
  };
@@ -1025,9 +1428,9 @@ ErrorSummary.prototype.getFragmentFromUrl = function(url) {
1025
1428
  ErrorSummary.prototype.getAssociatedLegendOrLabel = function($input) {
1026
1429
  var $fieldset = $input.closest("fieldset");
1027
1430
  if ($fieldset) {
1028
- var legends = $fieldset.getElementsByTagName("legend");
1029
- if (legends.length) {
1030
- var $candidateLegend = legends[0];
1431
+ var $legends = $fieldset.getElementsByTagName("legend");
1432
+ if ($legends.length) {
1433
+ var $candidateLegend = $legends[0];
1031
1434
  if ($input.type === "checkbox" || $input.type === "radio") {
1032
1435
  return $candidateLegend;
1033
1436
  }
@@ -1053,11 +1456,11 @@ Radios.prototype.init = function() {
1053
1456
  var $module = this.$module;
1054
1457
  var $inputs = this.$inputs;
1055
1458
  nodeListForEach($inputs, (function($input) {
1056
- var target = $input.getAttribute("data-aria-controls");
1057
- if (!target || !document.getElementById(target)) {
1459
+ var targetId = $input.getAttribute("data-aria-controls");
1460
+ if (!targetId || !document.getElementById(targetId)) {
1058
1461
  return;
1059
1462
  }
1060
- $input.setAttribute("aria-controls", target);
1463
+ $input.setAttribute("aria-controls", targetId);
1061
1464
  $input.removeAttribute("data-aria-controls");
1062
1465
  }));
1063
1466
  if ("onpageshow" in window) {