(function (exports) { 'use strict'; var DEFAULT_TIMEOUT = 10000; function request(url, options, onSuccess, onError) { try { var req = new XMLHttpRequest(); var isTimeout = false; options = options || {}; req.onreadystatechange = function () { if (req.readyState === req.DONE) { if (req.status >= 200 && req.status < 400) { var jsonResponse = void 0; try { jsonResponse = JSON.parse(req.responseText); onSuccess(jsonResponse); } catch (error) { return onError(error); } } else if (req.status === 0 && req.timeout > 0) { // Possible timeout, waiting for ontimeout event // Timeout will throw a status = 0 request // onreadystatechange preempts ontimeout // And we can't know for sure at this stage if it's a timeout setTimeout(function () { if (isTimeout) { return; } return onError(new Error('Request to ' + url + ' failed with status: ' + req.status)); }, 500); } else { return onError(new Error('Request to ' + url + ' failed with status: ' + req.status)); } } }; req.open(options.method || 'GET', url, true); if (options.timeout) { req.timeout = options.timeout; } else { req.timeout = DEFAULT_TIMEOUT; } req.ontimeout = function () { isTimeout = true; return onError(new Error('Request to ' + url + ' timed out')); }; var headers = options.headers || {}; for (var name in headers) { req.setRequestHeader(name, headers[name]); } req.send(options.body || null); } catch (error) { return onError(error); } } function addUrlParameter(url, name, value) { url = parseUrl(url); var newParam = name.concat('=', value); var modified = false; findByKey(name, url.params, function (index) { url.params[index] = newParam; modified = true; }); if (!modified) { url.params.push(newParam); } return buildUrl(url); } function removeUrlParameter(url, name) { url = parseUrl(url); findByKey(name, url.params, function (index) { url.params.splice(index--, 1); }); return buildUrl(url); } function parseUrl(url) { var parts = url.split('?'); return { address: parts[0], params: (parts[1] || '').split('&').filter(Boolean), }; } function buildUrl(parts) { return [parts.address, parts.params.join('&')].join('?').replace(/\??$/, ''); } function findByKey(key, keyvals, callback) { key += '='; for (var index = 0; keyvals.length > index; index++) { if (keyvals[index].trim().slice(0, key.length) === key) { var value = keyvals[index].trim().slice(key.length); if (callback) { callback(index, value); } else { return value; } } } } function isCrossOrigin(link) { return (link.protocol !== window.location.protocol || link.hostname !== window.location.hostname || (link.port || '80') !== (window.location.port || '80')); } function getOriginFromLink(link) { var origin = link.protocol.concat('//', link.hostname); if (link.port && link.port !== '80' && link.port !== '443') { origin = origin.concat(':', link.port); } return origin; } function isBrowser() { return typeof module === 'undefined'; } function setCookie(_a) { var name = _a.name, value = _a.value, lifetime = _a.lifetime; // const encodedValue = encodeURIComponent(value) var encodedValue = value; document.cookie = name .concat('=', encodedValue) .concat('; path=/', '; max-age='.concat(lifetime.toString()), document.location.protocol === 'https:' ? '; Secure' : ''); } function getCookie(name, defaultValue) { name += '='; var cookies = document.cookie.split(';'); var cookie = null; for (var i = 0; i < cookies.length; i++) { var currentCookie = cookies[i].trim(); if (currentCookie.indexOf(name) === 0) { cookie = currentCookie; break; } } if (cookie) { return decodeURIComponent(cookie.trim().slice(name.length)); } return defaultValue || null; } function validateConsentObject(response) { try { if (typeof response !== 'object' || response === null) { return false; } var expectedKeys = ['essential', 'settings', 'usage', 'campaigns']; var allKeysPresent = true; var responseKeysCount = 0; for (var i = 0; i < expectedKeys.length; i++) { if (!(expectedKeys[i] in response)) { allKeysPresent = false; break; } } var allValuesBoolean = true; for (var key in response) { if (response.hasOwnProperty(key)) { responseKeysCount++; if (typeof response[key] !== 'boolean') { allValuesBoolean = false; break; } } } var correctNumberOfKeys = responseKeysCount === expectedKeys.length; } catch (err) { return false; } return allKeysPresent && allValuesBoolean && correctNumberOfKeys; } var COOKIE_DAYS = 365; var GovConsentConfig = /** @class */ (function () { function GovConsentConfig(baseUrl) { this.uidFromCookie = findByKey(GovConsentConfig.UID_KEY, document.cookie.split(';')); this.uidFromUrl = findByKey(GovConsentConfig.UID_KEY, parseUrl(location.href).params); this.baseUrl = baseUrl; } GovConsentConfig.UID_KEY = 'gov_singleconsent_uid'; GovConsentConfig.CONSENTS_COOKIE_NAME = 'cookies_policy'; GovConsentConfig.PREFERENCES_SET_COOKIE_NAME = 'cookies_preferences_set'; GovConsentConfig.COOKIE_LIFETIME = COOKIE_DAYS * 24 * 60 * 60; return GovConsentConfig; }()); var ApiV1 = /** @class */ (function () { function ApiV1(baseUrl) { this.version = 'v1'; this.baseUrl = baseUrl; } ApiV1.prototype.buildUrl = function (endpoint, pathParam) { var url = "".concat(this.baseUrl, "/api/").concat(this.version).concat(endpoint); if (pathParam) { url += "/".concat(pathParam); } return url; }; ApiV1.prototype.origins = function () { return this.buildUrl(ApiV1.Routes.origins); }; ApiV1.prototype.consents = function (id) { return this.buildUrl(ApiV1.Routes.consents, id || ''); }; ApiV1.Routes = { origins: '/origins', consents: '/consent', }; return ApiV1; }()); var GovSingleConsent = /** @class */ (function () { function GovSingleConsent(consentsUpdateCallback, baseUrlOrEnv) { /** Initialises _GovConsent object by performing the following: 1. Removes 'uid' from URL. 2. Sets 'uid' attribute from cookie or URL. 3. Fetches consent status from API if 'uid' exists. 4. Notifies event listeners with API response. @arg baseUrl: string - the domain of where the single consent API is. Required. @arg consentsUpdateCallback: function(consents, consentsPreferencesSet, error) - callback when the consents are updated "consents": this is the consents object. It has the following properties: "essential", "usage", "campaigns", "settings". Each property is a boolean. "consentsPreferencesSet": true if the consents have been set for this user and this domain. Typically, only display the cookie banner if this is true. "error": if an error occurred, this is the error object. Otherwise, this is null. */ this.cachedConsentsCookie = null; this.validateConstructorArguments(baseUrlOrEnv, consentsUpdateCallback); var baseUrl = this.resolveBaseUrl(baseUrlOrEnv); this._consentsUpdateCallback = consentsUpdateCallback; this.config = new GovConsentConfig(baseUrl); this.urls = new ApiV1(this.config.baseUrl); window.cachedConsentsCookie = null; this.hideUIDParameter(); this.initialiseUIDandConsents(); } GovSingleConsent.prototype.validateConstructorArguments = function (baseUrlOrEnv, consentsUpdateCallback) { if (!baseUrlOrEnv) { throw new Error('Argument baseUrl is required'); } if (typeof baseUrlOrEnv !== 'string') { throw new Error('Argument baseUrl must be a string'); } if (!consentsUpdateCallback) { throw new Error('Argument consentsUpdateCallback is required'); } if (typeof consentsUpdateCallback !== 'function') { throw new Error('Argument consentsUpdateCallback must be a function'); } }; GovSingleConsent.prototype.resolveBaseUrl = function (baseUrlOrEnv) { if (baseUrlOrEnv === 'staging') { return 'https://gds-single-consent-staging.app'; } else if (baseUrlOrEnv === 'production') { return 'https://gds-single-consent.app'; } // If not "staging" or "production", assume it's a custom URL return baseUrlOrEnv; }; GovSingleConsent.prototype.initialiseUIDandConsents = function () { var currentUID = this.getCurrentUID(); if (this.isNewUID(currentUID)) { this.handleNewUID(currentUID); } if (this.uid) { this.fetchAndUpdateConsents(); } else { this._consentsUpdateCallback(null, false, null); } }; GovSingleConsent.prototype.handleNewUID = function (newUID) { this.uid = newUID; this.updateLinksEventHandlers(newUID); this.setUIDCookie(newUID); }; GovSingleConsent.prototype.isNewUID = function (currentUID) { return currentUID && currentUID !== this.uid; }; GovSingleConsent.prototype.fetchAndUpdateConsents = function () { var _this = this; var consentsUrl = this.urls.consents(this.uid); request(consentsUrl, { timeout: 1000 }, function (jsonResponse) { if (!validateConsentObject(jsonResponse.status)) { var error = new Error('Invalid consents object returned from the API: ' + JSON.stringify(jsonResponse)); return _this.defaultToRejectAllConsents(error); } var consents = jsonResponse.status; _this.updateBrowserConsents(consents); _this._consentsUpdateCallback(consents, GovSingleConsent.isConsentPreferencesSet(), null); }, function (error) { return _this.defaultToRejectAllConsents(error); }); }; GovSingleConsent.prototype.getCurrentUID = function () { // Get the current uid from URL or from the cookie if it exists return this.config.uidFromUrl || this.config.uidFromCookie; }; GovSingleConsent.prototype.setConsents = function (consents) { var _this = this; if (!consents) { throw new Error('consents is required in GovSingleConsent.setConsents()'); } var successCallback = function (response) { if (!response.uid) { throw new Error('No UID returned from the API'); } if (_this.isNewUID(response.uid)) { _this.handleNewUID(response.uid); } _this.updateBrowserConsents(consents); _this._consentsUpdateCallback(consents, GovSingleConsent.isConsentPreferencesSet(), null); }; var url = this.urls.consents(this.uid); request(url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'status='.concat(JSON.stringify(consents)), }, successCallback, function (error) { return _this.defaultToRejectAllConsents(error); }); }; GovSingleConsent.prototype.defaultToRejectAllConsents = function (error) { this.updateBrowserConsents(GovSingleConsent.REJECT_ALL); this._consentsUpdateCallback(GovSingleConsent.REJECT_ALL, GovSingleConsent.isConsentPreferencesSet(), error); }; GovSingleConsent.getConsents = function () { if (window.cachedConsentsCookie) { return window.cachedConsentsCookie; } var cookieValue = getCookie(GovConsentConfig.CONSENTS_COOKIE_NAME, null); if (cookieValue) { return JSON.parse(cookieValue); } return null; }; GovSingleConsent.hasConsentedToEssential = function () { var consents = GovSingleConsent.getConsents(); return consents === null || consents === void 0 ? void 0 : consents.essential; }; GovSingleConsent.hasConsentedToUsage = function () { var consents = GovSingleConsent.getConsents(); return consents === null || consents === void 0 ? void 0 : consents.usage; }; GovSingleConsent.hasConsentedToCampaigns = function () { var consents = GovSingleConsent.getConsents(); return consents === null || consents === void 0 ? void 0 : consents.campaigns; }; GovSingleConsent.hasConsentedToSettings = function () { var consents = GovSingleConsent.getConsents(); return consents === null || consents === void 0 ? void 0 : consents.settings; }; GovSingleConsent.isConsentPreferencesSet = function () { var value = getCookie(GovConsentConfig.PREFERENCES_SET_COOKIE_NAME, null); return value === 'true'; }; GovSingleConsent.prototype.updateLinksEventHandlers = function (currentUID) { var _this = this; request(this.urls.origins(), {}, // Update links with UID function (origins) { return _this.addUIDtoCrossOriginLinks(origins, currentUID); }, function (error) { throw error; }); }; GovSingleConsent.prototype.addUIDtoCrossOriginLinks = function (origins, uid) { /** * Adds uid URL parameter to consent sharing links. * Only links with known origins are updated. */ var links = document.querySelectorAll('a[href]'); Array.prototype.forEach.call(links, function (link) { if (isCrossOrigin(link) && origins.indexOf(getOriginFromLink(link)) >= 0) { link.addEventListener('click', function (event) { event.target.href = addUrlParameter(event.target.href, GovConsentConfig.UID_KEY, uid); }); } }); }; GovSingleConsent.prototype.setUIDCookie = function (uid) { setCookie({ name: GovConsentConfig.UID_KEY, value: uid, lifetime: GovConsentConfig.COOKIE_LIFETIME, }); }; GovSingleConsent.prototype.updateBrowserConsents = function (consents) { this.setConsentsCookie(consents); this.setPreferencesSetCookie(true); window.cachedConsentsCookie = consents; }; GovSingleConsent.prototype.setConsentsCookie = function (consents) { setCookie({ name: GovConsentConfig.CONSENTS_COOKIE_NAME, value: JSON.stringify(consents), lifetime: GovConsentConfig.COOKIE_LIFETIME, }); }; GovSingleConsent.prototype.setPreferencesSetCookie = function (value) { setCookie({ name: GovConsentConfig.PREFERENCES_SET_COOKIE_NAME, value: value.toString(), lifetime: GovConsentConfig.COOKIE_LIFETIME, }); }; GovSingleConsent.prototype.hideUIDParameter = function () { history.replaceState(null, null, removeUrlParameter(location.href, GovConsentConfig.UID_KEY)); }; GovSingleConsent.ACCEPT_ALL = { essential: true, usage: true, campaigns: true, settings: true, }; GovSingleConsent.REJECT_ALL = { essential: true, usage: false, campaigns: false, settings: false, }; return GovSingleConsent; }()); if (isBrowser()) { window.GovSingleConsent = GovSingleConsent; } exports.GovSingleConsent = GovSingleConsent; return exports; })({});