UNPKG

digitalmarketplace-frontend

Version:

Digital Marketplace Frontend contains assets and components used in Digital Marketplace projects

1,259 lines (1,241 loc) 223 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.DigitalMarketplaceFrontend = {})); })(this, (function (exports) { 'use strict'; /* * This variable is automatically overwritten during builds and releases. * It doesn't need to be updated manually. */ /** * Digital Marketplace Frontend release version * * {@link https://github.com/Crown-Commercial-Service/ccs-digitalmarketplace-govuk-frontend/releases} */ const version = '3.8.0'; const GOOGLE_TAG_MANAGER_ID = 'GTM-WCGFX3GW'; const googleTagManagerInit = (w, d, s, l, i) => { w[l] = w[l] || []; w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' }); const f = d.getElementsByTagName(s)[0]; const j = d.createElement(s); const dl = ''; j.async = true; j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f); }; const loadGoogleTagManager = () => googleTagManagerInit(window, document, 'script', 'dataLayer', GOOGLE_TAG_MANAGER_ID); function assign(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { target[key] = source[key]; } } return target; } var defaultConverter = { read: function read(value) { if (value[0] === '"') { value = value.slice(1, -1); } return value.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent); }, write: function write(value) { return encodeURIComponent(value).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g, decodeURIComponent); } }; function init(converter, defaultAttributes) { function set(name, value, attributes) { if (typeof document === 'undefined') { return; } attributes = assign({}, defaultAttributes, attributes); if (typeof attributes.expires === 'number') { attributes.expires = new Date(Date.now() + attributes.expires * 864e5); } if (attributes.expires) { attributes.expires = attributes.expires.toUTCString(); } name = encodeURIComponent(name).replace(/%(2[346B]|5E|60|7C)/g, decodeURIComponent).replace(/[()]/g, escape); var stringifiedAttributes = ''; for (var attributeName in attributes) { if (!attributes[attributeName]) { continue; } stringifiedAttributes += '; ' + attributeName; if (attributes[attributeName] === true) { continue; } stringifiedAttributes += '=' + attributes[attributeName].split(';')[0]; } return document.cookie = name + '=' + converter.write(value, name) + stringifiedAttributes; } function get(name) { if (typeof document === 'undefined' || arguments.length && !name) { return; } var cookies = document.cookie ? document.cookie.split('; ') : []; var jar = {}; for (var i = 0; i < cookies.length; i++) { var parts = cookies[i].split('='); var value = parts.slice(1).join('='); try { var found = decodeURIComponent(parts[0]); jar[found] = converter.read(value, found); if (name === found) { break; } } catch (e) {} } return name ? jar[name] : jar; } return Object.create({ set: set, get: get, remove: function remove(name, attributes) { set(name, '', assign({}, attributes, { expires: -1 })); }, withAttributes: function withAttributes(attributes) { return init(this.converter, assign({}, this.attributes, attributes)); }, withConverter: function withConverter(converter) { return init(assign({}, this.converter, converter), this.attributes); } }, { attributes: { value: Object.freeze(defaultAttributes) }, converter: { value: Object.freeze(converter) } }); } var api = init(defaultConverter, { path: '/' }); var GrantType; (function (GrantType) { GrantType["GRANTED"] = "granted"; GrantType["NOT_GRANTED"] = "not granted"; })(GrantType || (GrantType = {})); const getGrantedText = (state) => state ? GrantType.GRANTED : GrantType.NOT_GRANTED; const updateDataLayer = (cookiePreferences) => { window.dataLayer.push({ event: 'gtm_consent_update', usage_consent: getGrantedText(cookiePreferences.usage), glassbox_consent: getGrantedText(cookiePreferences.glassbox), marketing_consent: GrantType.NOT_GRANTED }); }; const cookieUpdateOptions = [ { cookieName: 'usage', cookiePrefixes: ['_ga', '_gi'] }, { cookieName: 'glassbox', cookiePrefixes: ['_cls'] } ]; const DOMAINS = [ '.marketplace.team', '.digitalmarketplace.service.gov.uk', 'www.applytosupply.digitalmarketplace.service.gov.uk' ]; // When not in production we want to delete cookies in localhost too const getDomains = () => { if (window.location.hostname === 'localhost') { return DOMAINS.concat(['localhost']); } return DOMAINS; }; const getCookiePreferences = () => { const defaultCookieSettings = '{"usage":false,"glassbox":false}'; return JSON.parse(api.get('cookie_preferences_dmp') ?? defaultCookieSettings); }; const setCookiePreferences = (usage) => { const cookiePreferences = getCookiePreferences(); cookiePreferences.usage = usage; cookiePreferences.settings_viewed = true; api.set('cookie_preferences_dmp', JSON.stringify(cookiePreferences), { expires: 365 }); updateDataLayer(cookiePreferences); removeUnwantedCookies(); }; const removeUnwantedCookies = () => { const cookieList = Object.keys(api.get()); const cookiesToRemove = ['digitalmarketplace_cookie_settings_viewed', 'digitalmarketplace_google_analytics_enabled', 'digitalmarketplace_cookie_options_v1', 'dm_cookies_policy']; const cookiePreferences = getCookiePreferences(); const cookiePrefixes = []; cookieUpdateOptions.forEach((cookieUpdateOption) => { if (!cookiePreferences[cookieUpdateOption.cookieName]) cookiePrefixes.push(...cookieUpdateOption.cookiePrefixes); }); for (let i = 0; i < cookieList.length; i++) { const cookieName = cookieList[i]; if (cookiePrefixes.some((cookiePrefix) => cookieName.startsWith(cookiePrefix))) cookiesToRemove.push(cookieName); } getDomains().forEach((domain) => { cookiesToRemove.forEach((cookieName) => { api.remove(cookieName, { path: '/', domain: domain }); }); }); }; const initGoogleAnalytics = () => { loadGoogleTagManager(); removeUnwantedCookies(); }; function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _construct(t, e, r) { if (_isNativeReflectConstruct()) return Reflect.construct.apply(null, arguments); var o = [null]; o.push.apply(o, e); var p = new (t.bind.apply(t, o))(); return r && _setPrototypeOf(p, r.prototype), p; } function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || false, o.configurable = true, "value" in o && (o.writable = true), Object.defineProperty(e, _toPropertyKey(o.key), o); } } function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), Object.defineProperty(e, "prototype", { writable: false }), e; } function _createForOfIteratorHelperLoose(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (t) return (t = t.call(r)).next.bind(t); if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e) { t && (r = t); var o = 0; return function () { return o >= r.length ? { done: true } : { done: false, value: r[o++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); } function _inheritsLoose(t, o) { t.prototype = Object.create(o.prototype), t.prototype.constructor = t, _setPrototypeOf(t, o); } function _isNativeFunction(t) { try { return -1 !== Function.toString.call(t).indexOf("[native code]"); } catch (n) { return "function" == typeof t; } } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function () { return !!t; })(); } function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return (String )(t); } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _wrapNativeSuper(t) { var r = "function" == typeof Map ? new Map() : void 0; return _wrapNativeSuper = function (t) { if (null === t || !_isNativeFunction(t)) return t; if ("function" != typeof t) throw new TypeError("Super expression must either be null or a function"); if (void 0 !== r) { if (r.has(t)) return r.get(t); r.set(t, Wrapper); } function Wrapper() { return _construct(t, arguments, _getPrototypeOf(this).constructor); } return Wrapper.prototype = Object.create(t.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }), _setPrototypeOf(Wrapper, t); }, _wrapNativeSuper(t); } function isInitialised($root, moduleName) { return $root instanceof HTMLElement && $root.hasAttribute("data-" + moduleName + "-init"); } /** * Checks if GOV.UK Frontend is supported on this page * * Some browsers will load and run our JavaScript but GOV.UK Frontend * won't be supported. * * @param {HTMLElement | null} [$scope] - (internal) `<body>` HTML element checked for browser support * @returns {boolean} Whether GOV.UK Frontend is supported on this page */ function isSupported($scope) { if ($scope === void 0) { $scope = document.body; } if (!$scope) { return false; } return $scope.classList.contains('govuk-frontend-supported'); } function isArray(option) { return Array.isArray(option); } function isObject(option) { return !!option && typeof option === 'object' && !isArray(option); } function formatErrorMessage(Component, message) { return Component.moduleName + ": " + message; } var GOVUKFrontendError = function (_Error) { function GOVUKFrontendError() { var _this; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _Error.call.apply(_Error, [this].concat(args)) || this; _this.name = 'GOVUKFrontendError'; return _this; } _inheritsLoose(GOVUKFrontendError, _Error); return GOVUKFrontendError; }(_wrapNativeSuper(Error)); var SupportError = function (_GOVUKFrontendError) { /** * Checks if GOV.UK Frontend is supported on this page * * @param {HTMLElement | null} [$scope] - HTML element `<body>` checked for browser support */ function SupportError($scope) { var _this2; if ($scope === void 0) { $scope = document.body; } var supportMessage = 'noModule' in HTMLScriptElement.prototype ? 'GOV.UK Frontend initialised without `<body class="govuk-frontend-supported">` from template `<script>` snippet' : 'GOV.UK Frontend is not supported in this browser'; _this2 = _GOVUKFrontendError.call(this, $scope ? supportMessage : 'GOV.UK Frontend initialised without `<script type="module">`') || this; _this2.name = 'SupportError'; return _this2; } _inheritsLoose(SupportError, _GOVUKFrontendError); return SupportError; }(GOVUKFrontendError); var ConfigError = function (_GOVUKFrontendError2) { function ConfigError() { var _this3; for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } _this3 = _GOVUKFrontendError2.call.apply(_GOVUKFrontendError2, [this].concat(args)) || this; _this3.name = 'ConfigError'; return _this3; } _inheritsLoose(ConfigError, _GOVUKFrontendError2); return ConfigError; }(GOVUKFrontendError); var ElementError = function (_GOVUKFrontendError3) { function ElementError(messageOrOptions) { var _this4; var message = typeof messageOrOptions === 'string' ? messageOrOptions : ''; if (typeof messageOrOptions === 'object') { var component = messageOrOptions.component, identifier = messageOrOptions.identifier, element = messageOrOptions.element, expectedType = messageOrOptions.expectedType; message = identifier; message += element ? " is not of type " + (expectedType != null ? expectedType : 'HTMLElement') : ' not found'; message = formatErrorMessage(component, message); } _this4 = _GOVUKFrontendError3.call(this, message) || this; _this4.name = 'ElementError'; return _this4; } _inheritsLoose(ElementError, _GOVUKFrontendError3); return ElementError; }(GOVUKFrontendError); var InitError = function (_GOVUKFrontendError4) { function InitError(componentOrMessage) { var _this5; var message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, "Root element (`$root`) already initialised"); _this5 = _GOVUKFrontendError4.call(this, message) || this; _this5.name = 'InitError'; return _this5; } _inheritsLoose(InitError, _GOVUKFrontendError4); return InitError; }(GOVUKFrontendError); var Component = function () { function Component($root) { this._$root = void 0; var childConstructor = this.constructor; if (typeof childConstructor.moduleName !== 'string') { throw new InitError("`moduleName` not defined in component"); } if (!($root instanceof childConstructor.elementType)) { throw new ElementError({ element: $root, component: childConstructor, identifier: 'Root element (`$root`)', expectedType: childConstructor.elementType.name }); } else { this._$root = $root; } childConstructor.checkSupport(); this.checkInitialised(); var moduleName = childConstructor.moduleName; this.$root.setAttribute("data-" + moduleName + "-init", ''); } var _proto = Component.prototype; _proto.checkInitialised = function checkInitialised() { var constructor = this.constructor; var moduleName = constructor.moduleName; if (moduleName && isInitialised(this.$root, moduleName)) { throw new InitError(constructor); } }; Component.checkSupport = function checkSupport() { if (!isSupported()) { throw new SupportError(); } }; return _createClass(Component, [{ key: "$root", get: /** * Returns the root element of the component * * @protected * @returns {RootElementType} - the root element of component */ function get() { return this._$root; } }]); }(); /** * @typedef ChildClass * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component */ /** * @typedef {typeof Component & ChildClass} ChildClassConstructor */ Component.elementType = HTMLElement; var configOverride = Symbol["for"]('configOverride'); var ConfigurableComponent = function (_Component) { function ConfigurableComponent($root, config) { var _this; _this = _Component.call(this, $root) || this; _this._config = void 0; var childConstructor = _this.constructor; if (!isObject(childConstructor.defaults)) { throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined')); } var datasetConfig = normaliseDataset(childConstructor, _this._$root.dataset); _this._config = mergeConfigs(childConstructor.defaults, config != null ? config : {}, _this[configOverride](datasetConfig), datasetConfig); return _this; } _inheritsLoose(ConfigurableComponent, _Component); var _proto = ConfigurableComponent.prototype; _proto[configOverride] = function (param) { return {}; } /** * Returns the root element of the component * * @protected * @returns {ConfigurationType} - the root element of component */; return _createClass(ConfigurableComponent, [{ key: "config", get: function get() { return this._config; } }]); }(Component); function normaliseString(value, property) { var trimmedValue = value ? value.trim() : ''; var output; var outputType = property == null ? void 0 : property.type; if (!outputType) { if (['true', 'false'].includes(trimmedValue)) { outputType = 'boolean'; } if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) { outputType = 'number'; } } switch (outputType) { case 'boolean': output = trimmedValue === 'true'; break; case 'number': output = Number(trimmedValue); break; default: output = value; } return output; } function normaliseDataset(Component, dataset) { if (!isObject(Component.schema)) { throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined')); } var out = {}; var entries = Object.entries(Component.schema.properties); for (var _i = 0, _entries = entries; _i < _entries.length; _i++) { var entry = _entries[_i]; var namespace = entry[0], property = entry[1]; var field = namespace.toString(); if (field in dataset) { out[field] = normaliseString(dataset[field], property); } if ((property == null ? void 0 : property.type) === 'object') { out[field] = extractConfigByNamespace(Component.schema, dataset, namespace); } } return out; } function mergeConfigs() { var formattedConfigObject = {}; for (var _len = arguments.length, configObjects = new Array(_len), _key = 0; _key < _len; _key++) { configObjects[_key] = arguments[_key]; } for (var _i2 = 0, _configObjects = configObjects; _i2 < _configObjects.length; _i2++) { var configObject = _configObjects[_i2]; for (var _i3 = 0, _Object$keys = Object.keys(configObject); _i3 < _Object$keys.length; _i3++) { var key = _Object$keys[_i3]; var option = formattedConfigObject[key]; var override = configObject[key]; if (isObject(option) && isObject(override)) { formattedConfigObject[key] = mergeConfigs(option, override); } else { formattedConfigObject[key] = override; } } } return formattedConfigObject; } function extractConfigByNamespace(schema, dataset, namespace) { var _newObject; var property = schema.properties[namespace]; if ((property == null ? void 0 : property.type) !== 'object') { return; } var newObject = (_newObject = {}, _newObject[namespace] = {}, _newObject); for (var _i5 = 0, _Object$entries2 = Object.entries(dataset); _i5 < _Object$entries2.length; _i5++) { var _Object$entries2$_i = _Object$entries2[_i5], key = _Object$entries2$_i[0], value = _Object$entries2$_i[1]; var current = newObject; var keyParts = key.split('.'); for (var _iterator2 = _createForOfIteratorHelperLoose(keyParts.entries()), _step2; !(_step2 = _iterator2()).done;) { var _step2$value = _step2.value, index = _step2$value[0], name = _step2$value[1]; if (isObject(current)) { if (index < keyParts.length - 1) { if (!isObject(current[name])) { current[name] = {}; } current = current[name]; } else if (key !== namespace) { current[name] = normaliseString(value); } } } } return newObject[namespace]; } var I18n = function () { function I18n(translations, config) { if (translations === void 0) { translations = {}; } if (config === void 0) { config = {}; } var _config$locale; this.translations = void 0; this.locale = void 0; this.translations = translations; this.locale = (_config$locale = config.locale) != null ? _config$locale : document.documentElement.lang || 'en'; } var _proto = I18n.prototype; _proto.t = function t(lookupKey, options) { if (!lookupKey) { throw new Error('i18n: lookup key missing'); } var translation = this.translations[lookupKey]; if (typeof (options == null ? void 0 : options.count) === 'number' && typeof translation === 'object') { var translationPluralForm = translation[this.getPluralSuffix(lookupKey, options.count)]; if (translationPluralForm) { translation = translationPluralForm; } } if (typeof translation === 'string') { if (translation.match(/%{(.\S+)}/)) { if (!options) { throw new Error('i18n: cannot replace placeholders in string if no option data provided'); } return this.replacePlaceholders(translation, options); } return translation; } return lookupKey; }; _proto.replacePlaceholders = function replacePlaceholders(translationString, options) { var formatter = Intl.NumberFormat.supportedLocalesOf(this.locale).length ? new Intl.NumberFormat(this.locale) : undefined; return translationString.replace(/%{(.\S+)}/g, function (placeholderWithBraces, placeholderKey) { if (Object.prototype.hasOwnProperty.call(options, placeholderKey)) { var placeholderValue = options[placeholderKey]; if (placeholderValue === false || typeof placeholderValue !== 'number' && typeof placeholderValue !== 'string') { return ''; } if (typeof placeholderValue === 'number') { return formatter ? formatter.format(placeholderValue) : "" + placeholderValue; } return placeholderValue; } throw new Error("i18n: no data found to replace " + placeholderWithBraces + " placeholder in string"); }); }; _proto.hasIntlPluralRulesSupport = function hasIntlPluralRulesSupport() { return Boolean('PluralRules' in window.Intl && Intl.PluralRules.supportedLocalesOf(this.locale).length); }; _proto.getPluralSuffix = function getPluralSuffix(lookupKey, count) { count = Number(count); if (!isFinite(count)) { return 'other'; } var translation = this.translations[lookupKey]; var preferredForm = this.hasIntlPluralRulesSupport() ? new Intl.PluralRules(this.locale).select(count) : this.selectPluralFormUsingFallbackRules(count); if (typeof translation === 'object') { if (preferredForm in translation) { return preferredForm; } else if ('other' in translation) { console.warn("i18n: Missing plural form \"." + preferredForm + "\" for \"" + this.locale + "\" locale. Falling back to \".other\"."); return 'other'; } } throw new Error("i18n: Plural form \".other\" is required for \"" + this.locale + "\" locale"); }; _proto.selectPluralFormUsingFallbackRules = function selectPluralFormUsingFallbackRules(count) { count = Math.abs(Math.floor(count)); var ruleset = this.getPluralRulesForLocale(); if (ruleset) { return I18n.pluralRules[ruleset](count); } return 'other'; }; _proto.getPluralRulesForLocale = function getPluralRulesForLocale() { var localeShort = this.locale.split('-')[0]; for (var pluralRule in I18n.pluralRulesMap) { var languages = I18n.pluralRulesMap[pluralRule]; if (languages.includes(this.locale) || languages.includes(localeShort)) { return pluralRule; } } }; return I18n; }(); I18n.pluralRulesMap = { arabic: ['ar'], chinese: ['my', 'zh', 'id', 'ja', 'jv', 'ko', 'ms', 'th', 'vi'], french: ['hy', 'bn', 'fr', 'gu', 'hi', 'fa', 'pa', 'zu'], 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'], irish: ['ga'], russian: ['ru', 'uk'], scottish: ['gd'], spanish: ['pt-PT', 'it', 'es'], welsh: ['cy'] }; I18n.pluralRules = { arabic: function arabic(n) { if (n === 0) { return 'zero'; } if (n === 1) { return 'one'; } if (n === 2) { return 'two'; } if (n % 100 >= 3 && n % 100 <= 10) { return 'few'; } if (n % 100 >= 11 && n % 100 <= 99) { return 'many'; } return 'other'; }, chinese: function chinese() { return 'other'; }, french: function french(n) { return n === 0 || n === 1 ? 'one' : 'other'; }, german: function german(n) { return n === 1 ? 'one' : 'other'; }, irish: function irish(n) { if (n === 1) { return 'one'; } if (n === 2) { return 'two'; } if (n >= 3 && n <= 6) { return 'few'; } if (n >= 7 && n <= 10) { return 'many'; } return 'other'; }, russian: function russian(n) { var lastTwo = n % 100; var last = lastTwo % 10; if (last === 1 && lastTwo !== 11) { return 'one'; } if (last >= 2 && last <= 4 && !(lastTwo >= 12 && lastTwo <= 14)) { return 'few'; } if (last === 0 || last >= 5 && last <= 9 || lastTwo >= 11 && lastTwo <= 14) { return 'many'; } return 'other'; }, scottish: function scottish(n) { if (n === 1 || n === 11) { return 'one'; } if (n === 2 || n === 12) { return 'two'; } if (n >= 3 && n <= 10 || n >= 13 && n <= 19) { return 'few'; } return 'other'; }, spanish: function spanish(n) { if (n === 1) { return 'one'; } if (n % 1000000 === 0 && n !== 0) { return 'many'; } return 'other'; }, welsh: function welsh(n) { if (n === 0) { return 'zero'; } if (n === 1) { return 'one'; } if (n === 2) { return 'two'; } if (n === 3) { return 'few'; } if (n === 6) { return 'many'; } return 'other'; } }; function closestAttributeValue($element, attributeName) { var $closestElementWithAttribute = $element.closest("[" + attributeName + "]"); return $closestElementWithAttribute ? $closestElementWithAttribute.getAttribute(attributeName) : null; } /** * File upload component * * @preserve * @augments ConfigurableComponent<FileUploadConfig> */ var FileUpload = function (_ConfigurableComponen) { /** * @param {Element | null} $root - File input element * @param {FileUploadConfig} [config] - File Upload config */ function FileUpload($root, config) { var _this; if (config === void 0) { config = {}; } _this = _ConfigurableComponen.call(this, $root, config) || this; _this.$input = void 0; _this.$button = void 0; _this.$status = void 0; _this.i18n = void 0; _this.id = void 0; _this.$announcements = void 0; _this.enteredAnotherElement = void 0; var $input = _this.$root.querySelector('input'); if ($input === null) { throw new ElementError({ component: FileUpload, identifier: 'File inputs (`<input type="file">`)' }); } if ($input.type !== 'file') { throw new ElementError(formatErrorMessage(FileUpload, 'File input (`<input type="file">`) attribute (`type`) is not `file`')); } _this.$input = $input; _this.$input.setAttribute('hidden', 'true'); if (!_this.$input.id) { throw new ElementError({ component: FileUpload, identifier: 'File input (`<input type="file">`) attribute (`id`)' }); } _this.id = _this.$input.id; _this.i18n = new I18n(_this.config.i18n, { locale: closestAttributeValue(_this.$root, 'lang') }); var $label = _this.findLabel(); if (!$label.id) { $label.id = _this.id + "-label"; } _this.$input.id = _this.id + "-input"; var $button = document.createElement('button'); $button.classList.add('govuk-file-upload-button'); $button.type = 'button'; $button.id = _this.id; $button.classList.add('govuk-file-upload-button--empty'); var ariaDescribedBy = _this.$input.getAttribute('aria-describedby'); if (ariaDescribedBy) { $button.setAttribute('aria-describedby', ariaDescribedBy); } var $status = document.createElement('span'); $status.className = 'govuk-body govuk-file-upload-button__status'; $status.setAttribute('aria-live', 'polite'); $status.innerText = _this.i18n.t('noFileChosen'); $button.appendChild($status); var commaSpan = document.createElement('span'); commaSpan.className = 'govuk-visually-hidden'; commaSpan.innerText = ', '; commaSpan.id = _this.id + "-comma"; $button.appendChild(commaSpan); var containerSpan = document.createElement('span'); containerSpan.className = 'govuk-file-upload-button__pseudo-button-container'; var buttonSpan = document.createElement('span'); buttonSpan.className = 'govuk-button govuk-button--secondary govuk-file-upload-button__pseudo-button'; buttonSpan.innerText = _this.i18n.t('chooseFilesButton'); containerSpan.appendChild(buttonSpan); containerSpan.insertAdjacentText('beforeend', ' '); var instructionSpan = document.createElement('span'); instructionSpan.className = 'govuk-body govuk-file-upload-button__instruction'; instructionSpan.innerText = _this.i18n.t('dropInstruction'); containerSpan.appendChild(instructionSpan); $button.appendChild(containerSpan); $button.setAttribute('aria-labelledby', $label.id + " " + commaSpan.id + " " + $button.id); $button.addEventListener('click', _this.onClick.bind(_this)); $button.addEventListener('dragover', function (event) { event.preventDefault(); }); _this.$root.insertAdjacentElement('afterbegin', $button); _this.$input.setAttribute('tabindex', '-1'); _this.$input.setAttribute('aria-hidden', 'true'); _this.$button = $button; _this.$status = $status; _this.$input.addEventListener('change', _this.onChange.bind(_this)); _this.updateDisabledState(); _this.observeDisabledState(); _this.$announcements = document.createElement('span'); _this.$announcements.classList.add('govuk-file-upload-announcements'); _this.$announcements.classList.add('govuk-visually-hidden'); _this.$announcements.setAttribute('aria-live', 'assertive'); _this.$root.insertAdjacentElement('afterend', _this.$announcements); _this.$button.addEventListener('drop', _this.onDrop.bind(_this)); document.addEventListener('dragenter', _this.updateDropzoneVisibility.bind(_this)); document.addEventListener('dragenter', function () { _this.enteredAnotherElement = true; }); document.addEventListener('dragleave', function () { if (!_this.enteredAnotherElement && !_this.$button.disabled) { _this.hideDraggingState(); _this.$announcements.innerText = _this.i18n.t('leftDropZone'); } _this.enteredAnotherElement = false; }); return _this; } _inheritsLoose(FileUpload, _ConfigurableComponen); var _proto = FileUpload.prototype; _proto.updateDropzoneVisibility = function updateDropzoneVisibility(event) { if (this.$button.disabled) return; if (event.target instanceof Node) { if (this.$root.contains(event.target)) { if (event.dataTransfer && isContainingFiles(event.dataTransfer)) { if (!this.$button.classList.contains('govuk-file-upload-button--dragging')) { this.showDraggingState(); this.$announcements.innerText = this.i18n.t('enteredDropZone'); } } } else { if (this.$button.classList.contains('govuk-file-upload-button--dragging')) { this.hideDraggingState(); this.$announcements.innerText = this.i18n.t('leftDropZone'); } } } }; _proto.showDraggingState = function showDraggingState() { this.$button.classList.add('govuk-file-upload-button--dragging'); }; _proto.hideDraggingState = function hideDraggingState() { this.$button.classList.remove('govuk-file-upload-button--dragging'); }; _proto.onDrop = function onDrop(event) { event.preventDefault(); if (event.dataTransfer && isContainingFiles(event.dataTransfer)) { this.$input.files = event.dataTransfer.files; this.$input.dispatchEvent(new CustomEvent('change')); this.hideDraggingState(); } }; _proto.onChange = function onChange() { var fileCount = this.$input.files.length; if (fileCount === 0) { this.$status.innerText = this.i18n.t('noFileChosen'); this.$button.classList.add('govuk-file-upload-button--empty'); } else { if (fileCount === 1) { this.$status.innerText = this.$input.files[0].name; } else { this.$status.innerText = this.i18n.t('multipleFilesChosen', { count: fileCount }); } this.$button.classList.remove('govuk-file-upload-button--empty'); } }; _proto.findLabel = function findLabel() { var $label = document.querySelector("label[for=\"" + this.$input.id + "\"]"); if (!$label) { throw new ElementError({ component: FileUpload, identifier: "Field label (`<label for=" + this.$input.id + ">`)" }); } return $label; }; _proto.onClick = function onClick() { this.$input.click(); }; _proto.observeDisabledState = function observeDisabledState() { var _this2 = this; var observer = new MutationObserver(function (mutationList) { for (var _iterator = _createForOfIteratorHelperLoose(mutationList), _step; !(_step = _iterator()).done;) { var mutation = _step.value; if (mutation.type === 'attributes' && mutation.attributeName === 'disabled') { _this2.updateDisabledState(); } } }); observer.observe(this.$input, { attributes: true }); }; _proto.updateDisabledState = function updateDisabledState() { this.$button.disabled = this.$input.disabled; this.$root.classList.toggle('govuk-drop-zone--disabled', this.$button.disabled); }; return FileUpload; }(ConfigurableComponent); FileUpload.moduleName = 'govuk-file-upload'; FileUpload.defaults = Object.freeze({ i18n: { chooseFilesButton: 'Choose file', dropInstruction: 'or drop file', noFileChosen: 'No file chosen', multipleFilesChosen: { one: '%{count} file chosen', other: '%{count} files chosen' }, enteredDropZone: 'Entered drop zone', leftDropZone: 'Left drop zone' } }); FileUpload.schema = Object.freeze({ properties: { i18n: { type: 'object' } } }); function isContainingFiles(dataTransfer) { var hasNoTypesInfo = dataTransfer.types.length === 0; var isDraggingFiles = dataTransfer.types.some(function (type) { return type === 'Files'; }); return hasNoTypesInfo || isDraggingFiles; } class ComplianceCommunicationAttachmentListRowFileInput { $dropZone; $input; $label; $hint; $button; $comma; $announcements; constructor($row, column) { const $column = $row.find(`.govuk-grid-column-one-half:nth-of-type(${column})`); this.$dropZone = $column.find('.govuk-drop-zone'); this.$input = $column.find('input'); this.$label = $column.find('.govuk-label'); this.$hint = $column.find('div.govuk-hint'); this.$button = $column.find('button'); this.$comma = $column.find('button > span.govuk-visually-hidden'); this.$announcements = $column.find('span.govuk-file-upload-announcements'); } updateIndex(newIndex) { const newName = this.$input.attr('name').replace(/\d+/, newIndex.toString()); const newId = `input-${newName}`; if (this.$button.length) { this.$label.attr('for', newId); this.$label.attr('id', `${newId}-label`); this.$hint.attr('id', `${newId}-hint`); this.$input.attr('id', `${newId}-input`); this.$input.attr('name', newName); this.$input.attr('aria-describedby', `${newId}-hint`); this.$button.attr('id', newId); this.$button.attr('aria-describedby', `${newId}-hint`); this.$button.attr('aria-labelledby', `${newId}-label ${newId}-comma ${newId}`); this.$comma.attr('id', `${newId}-comma`); } else { this.$label.attr('for', newId); this.$hint.attr('id', `${newId}-hint`); this.$input.attr('id', newId); this.$input.attr('name', newName); this.$input.attr('aria-describedby', `${newId}-hint`); } } makeInteractable() { if (!this.$button.length) { return new FileUpload(this.$dropZone.get(0)); } } resetHtml() { this.$label.removeAttr('id'); this.$dropZone.removeAttr('data-govuk-file-upload-init'); this.$button.remove(); const id = this.$input.attr('id'); this.$input.removeAttr('hidden'); this.$input.removeAttr('aria-hidden'); this.$input.attr('id', id.substring(0, id.length - 6)); this.$announcements.remove(); } } class ComplianceCommunicationAttachmentListRequiredInput { $requiredSection; $input; $label; constructor($row) { this.$requiredSection = $row.find('.dm-compliance-communication__attachments-item-required'); this.$input = this.$requiredSection.find('.govuk-checkboxes__item > input'); this.$label = this.$requiredSection.find('.govuk-checkboxes__item > .govuk-label'); } isChecked() { return this.$input.is(':checked'); } updateIndex(newIndex) { const newName = this.$input.attr('name').replace(/\d+/, newIndex.toString()); const newId = `input-${newName}`; this.$label.attr('for', newId); this.$input.attr('id', newId); this.$input.attr('name', newName); } checkRow() { this.$requiredSection.toggleClass('govuk-visually-hidden', true); this.$input.prop('checked', true); this.$input.attr('tabindex', -1); this.$input.attr('tabindex', -1); } } class ComplianceCommunicationAttachmentListRowRemoveButton { attachmentListRow; $button; $removeButtonCounter; constructor(attachmentListRow, $row) { this.attachmentListRow = attachmentListRow; this.$button = $row.find('.dm-compliance-communication__attachments-item-remove'); this.$removeButtonCounter = this.$button.find('.dm-compliance-communication__attachments-item-remove-counter'); } init() { this.toggleVisibility(true); this.$button.on('click', () => { this.attachmentListRow.removeRow(); }); } toggleVisibility(isShown) { this.$button.toggleClass('govuk-visually-hidden', !isShown); if (isShown) { this.$button.removeAttr('tabindex'); } else { this.$button.attr('tabindex', -1); } } updateIndex(newIndex) { this.$removeButtonCounter.text(newIndex + 1); } } class ComplianceCommunicationAttachmentListRow { $row; attachmentList; index; $legendCounter; attachmentListRowFileInput; attachmentListRowRequiredInput; attachmentListRowRemoveButton; constructor(attachmentList, index, $row) { this.$row = $row; this.attachmentList = attachmentList; this.index = index; this.$legendCounter = this.$row.find('.dm-compliance-communication__attachments-item-legend-counter'); this.attachmentListRowFileInput = new ComplianceCommunicationAttachmentListRowFileInput($row, 1); this.attachmentListRowRequiredInput = new ComplianceCommunicationAttachmentListRequiredInput($row); this.attachmentListRowRemoveButton = new ComplianceCommunicationAttachmentListRowRemoveButton(this, $row); } init() { this.attachmentListRowRemoveButton.init(); this.attachmentListRowFileInput.makeInteractable(); this.attachmentListRowRequiredInput.checkRow(); } updateIndex(newIndex) { this.index = newIndex; this.attachmentListRowFileInput.updateIndex(newIndex); this.attachmentListRowRequiredInput.updateIndex(newIndex); this.attachmentListRowRemoveButton.updateIndex(newIndex); this.$legendCounter.text(newIndex + 1); } removeRow() { this.attachmentList.removeRow(this.index); this.deleteRow(); } deleteRow() { this.$row.remove(); } toggleRemoveButtonVisibility(isShown) { this.attachmentListRowRemoveButton.toggleVisibility(isShown); } isRowChecked() { return this.attachmentListRowRequiredInput.isChecked(); } } class ComplianceCommunicationAttachmentListAddButton { attachmentList; $button; $buttonCounter; constructor(attachmentList, $button) { this.attachmentList = attachmentList; this.$button = $button; this.$buttonCounter = $button.find('.dm-compliance-communication__attachments__js-remaining-counter'); } init(numberOfRows) { this.$button.on('click', () => { this.attachmentList.addRow(); }); this.updateButtonText(this.attachmentList.maxNumberOfAttachments - numberOfRows); } updateButtonText(numberRemaining) { this.$buttonCounter.text(numberRemaining); } toggleVisibility(isShown) { this.$button.toggleClass('govuk-visually-hidden', !isShown); if (isShown) { this.$button.removeAttr('tabindex'); } else { this.$button.attr('tabindex', -1); } } } class ComplianceCommunicationAttachments { static moduleName = 'dm-compliance-communication-attachments'; $attachmentList; $attachmentListItems; attachmentListRows; attachmentListTemplateRow; attachmentListAddButton; maxNumberOfAttachments; constructor($attachmentList) { this.$attachmentList = $attachmentList; this.$attachmentListItems = this.$attachmentList.find('.dm-compliance-communication__attachments-items'); this.attachmentListRows = this.$attachmentListItems.find('.dm-compliance-communication__attachments-item').get().map((row, index) => new ComplianceCommunicationAttachmentListRow(this, index, $(row))); this.attachmentListTemplateRow = new ComplianceCommunicationAttachmentListRow(this, 99, this.$attachmentList.find('.dm-compliance-communication__attachments-item-template > .dm-compliance-communication__attachments-item')); this.attachmentListAddButton = new ComplianceCommunicationAttachmentListAddButton(this, this.$attachmentList.find('.dm-compliance-communication__attachments-add')); this.maxNumberOfAttachments = this.$attachmentList.data('maxNumberOfAttachments'); } init() { const rowsChecked = this.attachmentListRows.map((attachmentListRow) => attachmentListRow.isRowChecked()); let indexToDeleteFrom; if (rowsChecked.every((rowChecked) => !rowChecked)) { indexToDeleteFrom = 0; } else { indexToDeleteFrom = rowsChecked.indexOf(false); } if (indexToDeleteFrom >= 0) { this.attachmentListRows.slice(indexToDeleteFrom).forEach((attachmentListRow) => attachmentListRow.deleteRow()); this.attachmentListRows.splice(indexToDeleteFrom); } this.attachmentListAddButton.init(this.attachmentListRows.length); this.attachmentListRows.forEach((attachmentListRow) => attachmentListRow.init()); this.attachmentListTemplateRow.attachmentListRowFileInput.resetHtml(); this.updateIfMinRows(); this.updateAttachmentButton(); } removeRow(indexToRemove) { this.attachmentListRows.splice(indexToRemove, 1); this.attachmentListRows.slice(indexT