digitalmarketplace-govuk-frontend
Version:
Digital Marketplace GOV.UK Frontend contains the code you need to start building a user interface for Digital Marketplace.
1,085 lines (873 loc) • 39.8 kB
JavaScript
"use strict";
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
(function (global, factory) {
(typeof exports === "undefined" ? "undefined" : _typeof(exports)) === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define('DMGOVUKFrontend', ['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.DMGOVUKFrontend = {}));
})(void 0, function (exports) {
'use strict';
(function (undefined$1) {
// Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-library/master/polyfills/NodeList/prototype/forEach/detect.js
var detect = ('forEach' in NodeList.prototype);
if (detect) return; // Polyfill from https://raw.githubusercontent.com/Financial-Times/polyfill-service/1f3c09b402f65bf6e393f933a15ba63f1b86ef1f/packages/polyfill-library/polyfills/Element/prototype/matches/polyfill.js
NodeList.prototype.forEach = Array.prototype.forEach;
}).call('object' === (typeof window === "undefined" ? "undefined" : _typeof(window)) && window || 'object' === (typeof self === "undefined" ? "undefined" : _typeof(self)) && self || 'object' === (typeof global === "undefined" ? "undefined" : _typeof(global)) && global || {}); // used by the cookie banner component
var DEFAULT_COOKIE_CONSENT = {
analytics: false
};
var COOKIE_CATEGORIES = {
_ga: 'analytics',
_gid: 'analytics',
_gat_govuk_shared: 'analytics'
};
function getCookies() {
return document.cookie.split('; ').map(function (fullCookie) {
return fullCookie.split('=')[0];
});
}
function getCookie(name) {
var nameEQ = name + '=';
var cookies = document.cookie.split(';');
for (var i = 0, len = cookies.length; i < len; i++) {
var cookie = cookies[i];
while (cookie.charAt(0) === ' ') {
cookie = cookie.substring(1, cookie.length);
}
if (cookie.indexOf(nameEQ) === 0) {
return decodeURIComponent(cookie.substring(nameEQ.length));
}
}
return null;
}
/*
Cookie methods
==============
Usage:
Setting a cookie:
setCookie('hobnob', 'tasty', { days: 30 })
Reading a cookie:
getCookie('hobnob')
Deleting a cookie:
Cookie('hobnob', null)
*/
function Cookie(name, value, options) {
if (typeof value !== 'undefined') {
if (value === false || value === null) {
return setCookie(name, '', {
days: -1
});
} else {
// Default expiry date of 30 days
if (typeof options === 'undefined') {
options = {
days: 30
};
}
return setCookie(name, value, options);
}
} else {
return getCookie(name);
}
}
function getConsentCookie() {
var consentCookie = getCookie('dm_cookies_policy');
var consentCookieObj;
if (consentCookie) {
try {
consentCookieObj = JSON.parse(consentCookie);
} catch (err) {
return null;
}
if (_typeof(consentCookieObj) !== 'object' && consentCookieObj !== null) {
consentCookieObj = JSON.parse(consentCookieObj);
}
} else {
return null;
}
return consentCookieObj;
}
function setConsentCookie(options) {
var cookieConsent = getConsentCookie();
if (!cookieConsent) {
cookieConsent = JSON.parse(JSON.stringify(DEFAULT_COOKIE_CONSENT));
}
for (var cookieType in options) {
cookieConsent[cookieType] = options[cookieType]; // Delete cookies of that type if consent being set to false
if (!options[cookieType]) {
for (var cookie in COOKIE_CATEGORIES) {
if (COOKIE_CATEGORIES[cookie] === cookieType) {
getCookies().filter(function (cookieName) {
return cookieName.startsWith(cookie);
}).forEach(function (cookieName) {
Cookie(cookieName, null);
if (Cookie(cookieName)) {
document.cookie = cookieName + '=;expires=' + new Date() + ';domain=' + window.location.hostname.replace(/^www\./, '.') + ';path=/';
}
});
}
}
}
}
setCookie('dm_cookies_policy', JSON.stringify(cookieConsent), {
days: 365
});
}
function checkConsentCookieCategory(cookieName, cookieCategory) {
var currentConsentCookie = getConsentCookie(); // If the consent cookie doesn't exist, but the cookie is in our known list, return true
if (!currentConsentCookie && COOKIE_CATEGORIES[cookieName]) {
return true;
}
currentConsentCookie = getConsentCookie(); // Sometimes currentConsentCookie is malformed in some of the tests, so we need to handle these
try {
return currentConsentCookie[cookieCategory];
} catch (e) {
console.error(e);
return false;
}
}
function checkConsentCookie(cookieName, cookieValue) {
// If we're setting the consent cookie OR deleting a cookie, allow by default
if (cookieName === 'dm_cookies_policy' || cookieValue === null || cookieValue === false) {
return true;
}
if (COOKIE_CATEGORIES[cookieName]) {
var cookieCategory = COOKIE_CATEGORIES[cookieName];
return checkConsentCookieCategory(cookieName, cookieCategory);
} else {
// Deny the cookie if it is not known to us
return false;
}
} // Usage :
// Setting a cookie:
// Cookie('hobnob', 'tasty', { days: 30 })
function setCookie(name, value, options) {
if (checkConsentCookie(name, value)) {
if (typeof options === 'undefined') {
options = {};
}
var cookieString = name + '=' + value + '; path=/';
if (options.days) {
var date = new Date();
date.setTime(date.getTime() + options.days * 24 * 60 * 60 * 1000);
cookieString = cookieString + '; expires=' + date.toGMTString();
}
if (document.location.protocol === 'https:') {
cookieString = cookieString + '; Secure';
}
document.cookie = cookieString;
}
} // Based on https://github.com/alphagov/static/pull/1863
var EMAIL_PATTERN = /[^\s=/?&]+(?:@|%40|%2540)[^\s=/?&]+/g; // Not quite sure what this is doing,
// see https://github.com/alphagov/digitalmarketplace-frontend-toolkit/blob/master/toolkit/javascripts/analytics/_govukAnalytics.js#L19
var PIISafe = function PIISafe(value) {
this.value = value;
};
function stripPII(value) {
if (typeof value === 'string') {
return stripPIIFromString(value);
} else if (Object.prototype.toString.call(value) === '[object Array]' || Object.prototype.toString.call(value) === '[object Arguments]') {
return stripPIIFromArray(value);
} else if (_typeof(value) === 'object') {
return stripPIIFromObject(value);
} else {
return value;
}
}
function stripPIIFromString(string) {
return string.replace(EMAIL_PATTERN, '[email]');
}
function stripPIIFromObject(object) {
if (object) {
if (object instanceof PIISafe) {
return object.value;
} else {
for (var property in object) {
var value = object[property];
object[property] = stripPII(value);
}
return object;
}
}
}
function stripPIIFromArray(array) {
for (var i = 0, l = array.length; i < l; i++) {
var elem = array[i];
array[i] = stripPII(elem);
}
return array;
} // Stripped-down wrapper for Google Analytics, based on:
// https://github.com/alphagov/static/blob/master/doc/analytics.md
function SetupAnalytics(config) {
window.ga('create', config.trackingId, config.cookieDomain, {
cookieExpires: config.expires * 24 * 60 * 60
});
window.ga('set', 'anonymizeIp', config.anonymizeIp);
window.ga('set', 'displayFeaturesTask', config.displayFeaturesTask);
window.ga('set', 'transport', config.transport);
}
function LoadGoogleAnalytics() {
/* eslint-disable */
// Copied from Google Analytics installation instructions
/* jshint ignore:start */
(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
i[r] = i[r] || function () {
(i[r].q = i[r].q || []).push(arguments);
}, i[r].l = 1 * new Date();
a = s.createElement(o), m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m);
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
/* jshint ignore:end */
}
function TrackPageview(path, title, options) {
var page = window.location.pathname + window.location.search;
window.ga('send', 'pageview', stripPII(page));
} // https://developers.google.com/analytics/devguides/collection/analyticsjs/events
function TrackEvent(category, action, options) {
options = options || {};
var evt = {
eventCategory: category,
eventAction: action
};
if (options.label) {
evt.eventLabel = options.label;
delete options.label;
}
if (_typeof(options) === 'object') {
Object.assign(evt, options);
}
window.ga('send', 'event', stripPII(evt));
}
function AddLinkedTrackerDomain(trackingId, name, domains) {
window.ga('create', trackingId, 'auto', {
name: name
}); // Load the plugin.
window.ga('require', 'linker');
window.ga(name + '.require', 'linker'); // Define which domains to autoLink.
window.ga('linker:autoLink', domains);
window.ga(name + '.linker:autoLink', domains);
window.ga(name + '.set', 'anonymizeIp', true);
window.ga(name + '.set', 'location', stripPII(window.location.href));
window.ga(name + '.send', 'pageview');
}
var analytics = /*#__PURE__*/Object.freeze({
__proto__: null,
SetupAnalytics: SetupAnalytics,
LoadGoogleAnalytics: LoadGoogleAnalytics,
TrackPageview: TrackPageview,
TrackEvent: TrackEvent,
AddLinkedTrackerDomain: AddLinkedTrackerDomain
});
function LoadGoogleTagManager(googleTagManagerId) {
/* eslint-disable */
// Copied from Google Tag Manager installation instructions
/* jshint ignore:start */
(function (w, d, s, l, i) {
w[l] = w[l] || [];
w[l].push({
'gtm.start': new Date().getTime(),
event: 'gtm.js'
});
var f = d.getElementsByTagName(s)[0],
j = d.createElement(s),
dl = l != 'dataLayer' ? '&l=' + l : '';
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'dataLayer', googleTagManagerId);
/* jshint ignore:end */
}
window.DMGOVUKFrontend = window.DMGOVUKFrontend || {}; // TODO: Remove hard coded tracking IDs to make this more generic and useful to others
var trackingId = 'UA-49258698-1';
var linkedTrackingId = 'UA-145652997-1';
var googleTagManagerId = 'GTM-NMNTD5C';
window["ga-disable-".concat(trackingId)] = true;
function InitialiseAnalytics() {
// guard against being called more than once
if (!('analytics' in window.DMGOVUKFrontend)) {
window["ga-disable-".concat(trackingId)] = false; // TODO: Check if we still need this hack for the domain
var cookieDomain = document.domain === 'www.digitalmarketplace.service.gov.uk' ? '.digitalmarketplace.service.gov.uk' : document.domain; // Load Analytics libraries
LoadGoogleAnalytics(); // Configure profiles and make interface public
// for custom dimensions, virtual pageviews and events
window.DMGOVUKFrontend.analytics = new SetupAnalytics({
trackingId: trackingId,
cookieDomain: cookieDomain,
anonymizeIp: true,
displayFeaturesTask: null,
transport: 'beacon',
expires: 365
}); // Add cross-domain tracking for www.gov.uk domain
AddLinkedTrackerDomain(linkedTrackingId, 'govuk_shared', ['www.gov.uk']); // Track initial pageview
TrackPageview(); // Load google Tag Manager
LoadGoogleTagManager(googleTagManagerId);
}
}
var CookieBanner = function CookieBanner($module) {
this.$module = $module;
};
CookieBanner.prototype.clearOldCookies = function () {
// clear any cookies set by the previous version
var oldCookies = ['seen_cookie_message', '_ga', '_gid'];
for (var i = 0; i < oldCookies.length; i++) {
if (getCookie(oldCookies[i])) {
var cookieString = oldCookies[i] + '=;expires=' + new Date() + ';domain=' + window.location.hostname.replace(/^www\./, '.') + ';path=/';
document.cookie = cookieString;
}
}
};
CookieBanner.prototype.init = function ($module) {
this.$module.hideCookieMessage = this.hideCookieMessage.bind(this);
this.$module.showConfirmationMessage = this.showConfirmationMessage.bind(this);
this.$module.setCookieConsent = this.setCookieConsent.bind(this);
this.$module.cookieBanner = document.querySelector('.dm-cookie-banner');
this.$module.cookieBannerConfirmationMessage = this.$module.querySelector('.dm-cookie-banner__confirmation');
this.setupCookieMessage();
};
CookieBanner.prototype.setupCookieMessage = function () {
var _this = this;
this.$hideLink = this.$module.querySelector('button[data-hide-cookie-banner]');
if (this.$hideLink) {
this.$hideLink.addEventListener('click', this.$module.hideCookieMessage);
}
this.$acceptCookiesLink = this.$module.querySelector('button[data-accept-cookies=true]');
if (this.$acceptCookiesLink) {
this.$acceptCookiesLink.addEventListener('click', function () {
return _this.$module.setCookieConsent(true);
});
}
this.$rejectCookiesLink = this.$module.querySelector('button[data-accept-cookies=false]');
if (this.$rejectCookiesLink) {
this.$rejectCookiesLink.addEventListener('click', function () {
return _this.$module.setCookieConsent(false);
});
}
this.showCookieMessage();
};
CookieBanner.prototype.showCookieMessage = function () {
// Show the cookie banner if policy cookie not set
var hasCookiesPolicy = getCookie('dm_cookies_policy');
if (this.$module && !hasCookiesPolicy) {
this.$module.style.display = 'block';
}
};
CookieBanner.prototype.hideCookieMessage = function (event) {
if (this.$module) {
this.$module.style.display = 'none';
}
if (event.target) {
event.preventDefault();
}
};
CookieBanner.prototype.setCookieConsent = function (analyticsConsent) {
setConsentCookie({
analytics: analyticsConsent
});
this.$module.showConfirmationMessage(analyticsConsent);
this.$module.cookieBannerConfirmationMessage.focus();
if (analyticsConsent) {
InitialiseAnalytics();
}
};
CookieBanner.prototype.showConfirmationMessage = function (analyticsConsent) {
var messagePrefix = analyticsConsent ? 'You’ve accepted analytics cookies.' : 'You told us not to use analytics cookies.';
this.$cookieBannerMainContent = document.querySelector('.dm-cookie-banner__wrapper');
this.$cookieBannerConfirmationMessage = document.querySelector('.dm-cookie-banner__confirmation-message');
this.$cookieBannerConfirmationMessage.insertAdjacentText('afterbegin', messagePrefix);
this.$cookieBannerMainContent.style.display = 'none';
this.$module.cookieBannerConfirmationMessage.style.display = 'block';
}; // Javascript code to support the Cookie Settings page
function CookieSettings($module) {
this.$module = $module;
}
CookieSettings.prototype.init = function () {
this.$module.submitSettingsForm = this.submitSettingsForm.bind(this);
this.$module.addEventListener('submit', this.$module.submitSettingsForm); // Ensure there aren't two forms for setting cookie preferences on the same page
this.hideCookieBanner();
this.setInitialFormValues();
};
CookieSettings.prototype.setInitialFormValues = function () {
var currentConsentCookie = getConsentCookie();
if (!currentConsentCookie) {
// Don't populate the form
return;
}
this.hideWarningMessage(); // Populate the form with the existing choice
var radioButton;
if (currentConsentCookie.analytics) {
radioButton = this.$module.querySelector('input[name=cookies-analytics][value=On]');
} else {
radioButton = this.$module.querySelector('input[name=cookies-analytics][value=Off]');
}
radioButton.checked = true;
};
CookieSettings.prototype.submitSettingsForm = function (event) {
event.preventDefault();
var formInputs = event.target.querySelectorAll('input[name=cookies-analytics]');
var options = {}; // Retrieve the selected value from the form inputs
for (var i = 0; i < formInputs.length; i++) {
var input = formInputs[i];
if (input.checked) {
var value = input.value === 'On';
options.analytics = value;
break;
}
} // the cookie choice must be set when form is submitted
if (options.analytics === undefined) {
this.showError();
return false;
} // Set the analytics cookie preferences
// If 'Off' option not checked, this function will also delete any existing Google Analytics cookies
setConsentCookie(options); // If 'On' option checked and analytics not yet present,
// initialise Analytics (this includes firing the initial pageview)
if (options.analytics && !window.DMGOVUKFrontend.analytics) {
InitialiseAnalytics();
}
this.hideWarningMessage();
this.hideError();
this.showConfirmationMessage();
return false;
};
CookieSettings.prototype.showConfirmationMessage = function () {
var confirmationMessage = document.querySelector('#dm-cookie-settings-confirmation');
var previousPageLink = document.querySelector('.dm-cookie-settings__prev-page');
var referrer = CookieSettings.prototype.getReferrerLink();
document.body.scrollTop = document.documentElement.scrollTop = 0;
if (referrer && referrer !== document.location.pathname) {
previousPageLink.href = referrer;
previousPageLink.style.display = 'block';
} else {
previousPageLink.style.display = 'none';
}
confirmationMessage.style.display = 'block';
};
CookieSettings.prototype.showError = function () {
var errorSummary = document.querySelector('#dm-cookie-settings-error');
if (errorSummary !== null) {
errorSummary.style.display = 'block';
document.body.scrollTop = document.documentElement.scrollTop = 0;
}
this.showErrorMessage();
};
CookieSettings.prototype.hideError = function () {
var errorSummary = document.querySelector('#dm-cookie-settings-error');
if (errorSummary !== null) {
errorSummary.style.display = 'none';
}
this.hideErrorMessage();
};
CookieSettings.prototype.hideWarningMessage = function () {
var warningMessage = document.querySelector('#dm-cookie-settings-warning');
if (warningMessage !== null) {
warningMessage.style.display = 'none';
}
};
CookieSettings.prototype.hideErrorMessage = function () {
var errorMessage = document.querySelector('#dm-cookie-settings-error-message');
if (errorMessage !== null) {
errorMessage.style.display = 'none';
}
var errorMessageHighlight = document.querySelector('.govuk-form-group--error');
errorMessageHighlight && errorMessageHighlight.classList.remove('govuk-form-group--error');
};
CookieSettings.prototype.showErrorMessage = function () {
var formGroup = document.querySelector('#dm-cookie-settings .govuk-form-group');
formGroup && formGroup.classList.add('govuk-form-group--error');
var errorMessageSpan = document.createElement('span');
errorMessageSpan.setAttribute('id', 'dm-cookie-settings-error-message');
errorMessageSpan.className = 'govuk-error-message';
errorMessageSpan.innerHTML = '<span class="govuk-visually-hidden">Error:</span> Select yes to accept analytics cookies';
var siblingElement = document.querySelector('#cookie-settings-1, .govuk-radios--inline');
var parentElement = siblingElement.parentElement;
parentElement.insertBefore(errorMessageSpan, siblingElement);
};
CookieSettings.prototype.hideCookieBanner = function () {
var cookieBanner = document.querySelector('.dm-cookie-banner');
if (cookieBanner !== null) {
cookieBanner.style.display = 'none';
}
};
CookieSettings.prototype.getReferrerLink = function () {
return document.referrer ? new URL(document.referrer).pathname : false;
};
var getSibling = function getSibling(direction, elem, selector) {
// Get the next sibling element
var sibling = direction === 'next' ? elem.nextElementSibling : elem.previousElementSibling; // If there's no selector, return the first sibling
if (!selector) return sibling; // If the sibling matches our selector, use it
// If not, jump to the next sibling and continue the loop
// For IE10,11 (Polyfill for matches)
if (!Element.prototype.matches) {
Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
}
while (sibling) {
if (sibling.matches(selector)) return sibling;
sibling = direction === 'next' ? sibling.nextElementSibling : sibling = sibling.previousElementSibling;
}
};
function ListInput($module) {
this.$module = $module;
this.$allVisibleItems = $module.querySelectorAll('.dm-list-input__item--visible');
this.$addAnotherButton = $module.querySelector('.dm-list-input__item-add');
this.$allItems = $module.querySelectorAll('.dm-list-input__item');
this.visibleItems = 0;
this.itemContainerClass = 'dm-list-input__item-container';
this.hiddenItemClass = 'dm-list-input__item--hidden';
this.visibleItemClass = 'dm-list-input__item--visible';
this.itemInputClass = 'dm-list-input__item-input';
this.itemErrorClass = 'dm-list-input__item--error';
this.itemErrorMsgClass = 'dm-list-input-error-message';
this.itemCounterClass = 'dm-list-input__counter';
this.inputErrorClass = 'govuk-input--error';
this.removeButtonClass = 'dm-list-input__item-remove';
this.hiddenRemoveButtonClass = 'dm-list-input__item-remove--hidden';
this.formGroupErrorClass = 'govuk-form-group--error';
this.hiddenAddButtonClass = 'dm-list-input__item-add--hidden';
this.addButtonRemainingClass = 'dm-list-input__js-remaining-counter';
}
ListInput.prototype.init = function () {
// Check for module
if (!this.$module) {
return;
}
this.hideEmptyItems();
this.updateAllCounters();
this.bindRemoveClickEvent();
this.toggleAddAnotherButton();
this.bindAddClickEvent();
}; // Hide all items that do not have a value (except for the first one, or the first two if no items have a value)
ListInput.prototype.hideEmptyItems = function () {
var numberOfVisibleEmptyItems = 0;
var numberOfFilledInItems = 0;
this.$allItems.forEach(function ($item) {
var $input = $item.querySelector('.' + this.itemInputClass);
var $removeButton = $item.querySelector('.' + this.removeButtonClass);
if ($input.value === '') {
if (numberOfVisibleEmptyItems === 0) {
$removeButton.classList.remove(this.hiddenRemoveButtonClass);
} else if (numberOfVisibleEmptyItems === 1 && numberOfFilledInItems === 0) {
$removeButton.classList.remove(this.hiddenRemoveButtonClass);
} else {
$item.classList.add(this.hiddenItemClass);
$item.classList.remove(this.visibleItemClass);
$removeButton.classList.add(this.hiddenRemoveButtonClass);
$input.setAttribute('disabled', 'true');
}
numberOfVisibleEmptyItems += 1;
} else {
$item.classList.remove(this.hiddenItemClass);
$item.classList.add(this.visibleItemClass);
$removeButton.classList.remove(this.hiddenRemoveButtonClass);
numberOfFilledInItems += 1;
}
}, this);
this.$allVisibleItems = this.$module.querySelectorAll('.' + this.visibleItemClass);
}; // Binds an event listener to module to listen for any
// click events fired by the items "Remove" button
ListInput.prototype.bindRemoveClickEvent = function () {
this.$module.addEventListener('click', function (event) {
var $clickedEl = event.target;
if ($clickedEl.tagName === 'BUTTON' && $clickedEl.classList.contains(this.removeButtonClass)) {
var $item = $clickedEl.parentNode.parentNode; // Remove the input's content and remove its value attribute
var $input = $item.querySelector('.' + this.itemInputClass);
$input.value = '';
$input.removeAttribute('value');
$input.setAttribute('disabled', 'true');
$clickedEl.classList.add(this.hiddenRemoveButtonClass);
$item.classList.add(this.hiddenItemClass);
$item.classList.remove(this.visibleItemClass);
this.$allVisibleItems = this.$module.querySelectorAll('.' + this.visibleItemClass); // Remove Error messages and styling
if ($item.classList.contains(this.itemErrorClass)) {
$item.classList.remove(this.itemErrorClass);
var $errorFormGroup = $item.querySelector('.' + this.formGroupErrorClass);
$errorFormGroup.classList.remove(this.formGroupErrorClass);
$errorFormGroup.querySelector('.' + this.itemErrorMsgClass).remove();
var $errorInput = $errorFormGroup.querySelector('.' + this.inputErrorClass);
var inputId = $errorInput.getAttribute('id');
var inputDescribedBy = $errorInput.getAttribute('aria-describedby');
$errorInput.setAttribute('aria-describedby', inputDescribedBy.replace(inputId + '-error', ''));
$errorInput.classList.remove(this.inputErrorClass);
} // Hide Remove buttons if there is only one item left
if (this.$allVisibleItems.length === 1) {
var $removeButtons = this.$module.querySelectorAll('.' + this.removeButtonClass);
$removeButtons.forEach(function ($button) {
$button.classList.add(this.hiddenRemoveButtonClass);
}, this);
this.$allVisibleItems[0].querySelector('input').focus();
} else {
// Set focus to the next input
var $nextVisibleItem = getSibling('next', $item, '.' + this.visibleItemClass);
if ($nextVisibleItem) {
$nextVisibleItem.querySelector('input').focus();
} else {
var $previousVisibleItem = getSibling('previous', $item, '.' + this.visibleItemClass);
$previousVisibleItem.querySelector('input').focus();
}
}
this.updateAllCounters();
this.toggleAddAnotherButton();
}
}.bind(this));
}; // Used to update each item's label and remove button hidden text
ListInput.prototype.updateCounters = function () {
var $visibleItems = this.$allVisibleItems;
var counter = 1;
$visibleItems.forEach(function (item) {
var $counters = item.querySelectorAll('.' + this.itemCounterClass);
$counters.forEach(function ($counter) {
$counter.innerHTML = counter;
});
counter += 1;
}, this);
}; // Used to update "Add another" buttons "Remaining" counter
// and sets focus to the new additional input
ListInput.prototype.updateRemainingCounter = function () {
var $visibleItems = this.$allVisibleItems;
var totalItems = this.$allItems.length;
this.$module.querySelector('.' + this.addButtonRemainingClass).innerHTML = totalItems - $visibleItems.length;
};
ListInput.prototype.bindAddClickEvent = function () {
this.$addAnotherButton.addEventListener('click', function () {
// Find the first hidden item
var $firstHiddenItem = this.$module.querySelector('.' + this.hiddenItemClass);
var $firstHiddenInput = $firstHiddenItem.querySelector('.' + this.itemInputClass);
if ($firstHiddenItem) {
$firstHiddenInput.removeAttribute('disabled');
this.$module.querySelector('.' + this.itemContainerClass).appendChild($firstHiddenItem);
$firstHiddenItem.classList.remove(this.hiddenItemClass);
$firstHiddenItem.classList.add(this.visibleItemClass);
this.$allVisibleItems = this.$module.querySelectorAll('.' + this.visibleItemClass);
this.updateAllCounters();
$firstHiddenItem.querySelector('.' + this.removeButtonClass).classList.remove(this.hiddenRemoveButtonClass);
$firstHiddenInput.focus();
this.toggleAddAnotherButton(); // Show Remove buttons if there is more one item
if (this.$allVisibleItems.length > 1) {
var $removeButtons = this.$module.querySelectorAll('.' + this.removeButtonClass);
$removeButtons.forEach(function ($button) {
$button.classList.remove(this.hiddenRemoveButtonClass);
}, this);
}
}
}.bind(this));
};
ListInput.prototype.toggleAddAnotherButton = function () {
var $firstHiddenItem = this.$module.querySelector('.' + this.hiddenItemClass);
if ($firstHiddenItem) {
this.$addAnotherButton.classList.remove(this.hiddenAddButtonClass);
} else {
this.$addAnotherButton.classList.add(this.hiddenAddButtonClass);
}
};
ListInput.prototype.updateAllCounters = function () {
this.updateCounters();
this.updateRemainingCounter();
};
function OptionSelect($module) {
this.$optionSelect = $module;
this.$options = this.$optionSelect.querySelectorAll("input[type='checkbox']");
this.$optionsContainer = this.$optionSelect.querySelector('.js-options-container');
this.$optionList = this.$optionsContainer.querySelector('.js-auto-height-inner');
this.$allCheckboxes = this.$optionsContainer.querySelectorAll('.govuk-checkboxes__item');
this.checkedCheckboxes = [];
}
OptionSelect.prototype.init = function () {
// Attach listener to update checked count
this.$optionSelect.addEventListener('change', function (event) {
var $changedEl = event.target;
if ($changedEl.tagName === 'INPUT' && $changedEl.type === 'checkbox') {
this.updateCheckedCount();
}
}.bind(this)); // Replace div.container-head with a button
this.replaceHeadingSpanWithButton(); // Add js-collapsible class to parent for CSS
this.$optionSelect.classList.add('js-collapsible'); // Add open/close listeners
this.$optionSelect.querySelector('.js-container-button').addEventListener('click', this.toggleOptionSelect.bind(this));
if (this.$optionSelect.dataset.closedOnLoad === 'true') {
this.close();
} else {
this.setupHeight();
}
var checkedString = this.checkedString();
if (checkedString) {
this.attachCheckedCounter(checkedString);
}
};
OptionSelect.prototype.getAllCheckedCheckboxes = function getAllCheckedCheckboxes() {
this.checkedCheckboxes = [];
var that = this;
this.$allCheckboxes.forEach(function (i) {
if (i.querySelector('input[type=checkbox]').checked) {
that.checkedCheckboxes.push(i);
}
}, this);
};
OptionSelect.prototype.replaceHeadingSpanWithButton = function replaceHeadingSpanWithButton() {
/* Replace the span within the heading with a button element. This is based on feedback from Léonie Watson.
* The button has all of the accessibility hooks that are used by screen readers and etc.
* We do this in the JavaScript because if the JavaScript is not active then the button shouldn't
* be there as there is no JS to handle the click event.
*/
var $containerHead = this.$optionSelect.querySelector('.js-container-button');
var jsContainerHeadHTML = $containerHead.innerHTML; // Create button and replace the preexisting html with the button.
var $button = document.createElement('button');
$button.classList.add('js-container-button', 'dm-option-select__title', 'dm-option-select__button'); // Add type button to override default type submit when this component is used within a form
$button.setAttribute('type', 'button');
$button.setAttribute('aria-expanded', true);
$button.setAttribute('id', $containerHead.id);
$button.setAttribute('aria-controls', this.$optionsContainer.id);
$button.innerHTML = jsContainerHeadHTML;
$containerHead.insertAdjacentHTML('afterend', $button.outerHTML);
$containerHead.parentNode.removeChild($containerHead);
};
OptionSelect.prototype.attachCheckedCounter = function attachCheckedCounter(checkedString) {
this.$optionSelect.querySelector('.js-container-button').insertAdjacentHTML('afterend', '<div class="dm-option-select__selected-counter js-selected-counter">' + checkedString + '</div>');
};
OptionSelect.prototype.updateCheckedCount = function updateCheckedCount() {
var checkedString = this.checkedString();
var checkedStringElement = this.$optionSelect.querySelector('.js-selected-counter');
if (checkedString) {
if (checkedStringElement) {
checkedStringElement.textContent = checkedString;
} else {
this.attachCheckedCounter(checkedString);
}
} else {
checkedStringElement.parentNode.removeChild(checkedStringElement);
}
};
OptionSelect.prototype.checkedString = function checkedString() {
this.getAllCheckedCheckboxes();
var count = this.checkedCheckboxes.length;
var checkedString = false;
if (count > 0) {
checkedString = count + ' selected';
}
return checkedString;
};
OptionSelect.prototype.toggleOptionSelect = function toggleOptionSelect(e) {
if (this.isClosed()) {
this.open();
} else {
this.close();
}
e.preventDefault();
};
OptionSelect.prototype.open = function open() {
if (this.isClosed()) {
this.$optionSelect.querySelector('.js-container-button').setAttribute('aria-expanded', true);
this.$optionSelect.classList.remove('js-closed');
this.$optionSelect.classList.add('js-opened');
if (!this.$optionsContainer.style.height) {
this.setupHeight();
}
}
};
OptionSelect.prototype.close = function close() {
this.$optionSelect.classList.remove('js-opened');
this.$optionSelect.classList.add('js-closed');
this.$optionSelect.querySelector('.js-container-button').setAttribute('aria-expanded', false);
};
OptionSelect.prototype.isClosed = function isClosed() {
return this.$optionSelect.classList.contains('js-closed');
};
OptionSelect.prototype.setContainerHeight = function setContainerHeight(height) {
this.$optionsContainer.style.height = height + 'px';
};
OptionSelect.prototype.isCheckboxVisible = function isCheckboxVisible($checkbox) {
var initialOptionContainerHeight = this.$optionsContainer.clientHeight;
var optionListOffsetTop = this.$optionList.getBoundingClientRect().top + document.body.scrollTop;
var distanceFromTopOfContainer = $checkbox.getBoundingClientRect().top + document.body.scrollTop - optionListOffsetTop;
return distanceFromTopOfContainer < initialOptionContainerHeight;
};
OptionSelect.prototype.getVisibleCheckboxes = function getVisibleCheckboxes() {
var visibleCheckboxes = [].slice.call(this.$options).filter(this.isCheckboxVisible.bind(this)); // add an extra checkbox, if the label of the first is too long it collapses onto itself
if (visibleCheckboxes.length < this.$options) {
visibleCheckboxes.push(this.$options[visibleCheckboxes.length]);
}
return visibleCheckboxes;
};
OptionSelect.prototype.setupHeight = function setupHeight() {
var initialOptionContainerHeight = this.$optionsContainer.clientHeight;
var height = this.$optionList.offsetHeight; // check whether this is hidden by progressive disclosure,
// because height calculations won't work
if (this.$optionsContainer.offsetParent === null) {
initialOptionContainerHeight = 200;
height = 200;
} // Resize if the list is only slightly bigger than its container
if (height < initialOptionContainerHeight + 50) {
this.setContainerHeight(height + 1);
return;
} // Resize to cut last item cleanly in half
var visibleCheckboxes = this.getVisibleCheckboxes();
var lastVisibleCheckbox = visibleCheckboxes[visibleCheckboxes.length - 1];
var position = lastVisibleCheckbox.parentNode.offsetTop; // parent element is relative
this.setContainerHeight(position + parseFloat(window.getComputedStyle(lastVisibleCheckbox, null).height.replace('px', '')) / 1.5);
};
function SearchBox($module) {
this.$module = $module;
this.toggleTarget = this.$module.querySelector('.js-class-toggle');
}
SearchBox.prototype.init = function () {
// Check for module
if (!this.$module) {
return;
}
this.$module.addFocusClass = this.addFocusClass.bind(this);
this.$module.removeFocusClassFromEmptyInput = this.removeFocusClassFromEmptyInput.bind(this);
if (!this.inputIsEmpty()) {
this.addFocusClass();
}
this.toggleTarget.addEventListener('focus', this.$module.addFocusClass);
this.toggleTarget.addEventListener('blur', this.$module.removeFocusClassFromEmptyInput);
};
SearchBox.prototype.inputIsEmpty = function () {
return this.toggleTarget.value === '';
};
SearchBox.prototype.addFocusClass = function () {
this.toggleTarget.classList.add('focus');
};
SearchBox.prototype.removeFocusClassFromEmptyInput = function () {
if (this.inputIsEmpty()) {
this.toggleTarget.classList.remove('focus');
}
};
function initAll(options) {
// Set the options to an empty object by default if no options are passed.
options = typeof options !== 'undefined' ? options : {}; // Allow the user to initialise Digital Marketplace GOV.UK Frontend in only
// certain sections of the page
// Defaults to the entire document if nothing is set.
var scope = typeof options.scope !== 'undefined' ? options.scope : document;
var $cookieBanner = document.querySelector('[data-module="dm-cookie-banner"]');
if ($cookieBanner) {
new CookieBanner($cookieBanner).init();
}
var $cookieSettingsPage = document.querySelector('[data-module="dm-cookie-settings"]');
if ($cookieSettingsPage) {
new CookieSettings($cookieSettingsPage).init();
}
var currentConsentCookie = getConsentCookie();
if (currentConsentCookie && currentConsentCookie.analytics) {
InitialiseAnalytics();
}
var $ListInput = scope.querySelectorAll('[data-module="dm-list-input"]');
$ListInput.forEach(function ($ListInput) {
new ListInput($ListInput).init();
});
var $OptionSelect = scope.querySelectorAll('[data-module="dm-option-select"]');
$OptionSelect.forEach(function ($OptionSelect) {
new OptionSelect($OptionSelect).init();
});
var $SearchBox = scope.querySelectorAll('[data-module="dm-search-box"]');
$SearchBox.forEach(function ($SearchBox) {
new SearchBox($SearchBox).init();
});
}
exports.Analytics = analytics;
exports.CookieBanner = CookieBanner;
exports.CookieSettings = CookieSettings;
exports.ListInput = ListInput;
exports.OptionSelect = OptionSelect;
exports.SearchBox = SearchBox;
exports.initAll = initAll;
exports.initAnalytics = InitialiseAnalytics;
Object.defineProperty(exports, '__esModule', {
value: true
});
});