diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000000..af82887879 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v8.16.0 \ No newline at end of file diff --git a/_js/standard-bundle.js.tmp b/_js/standard-bundle.js.tmp deleted file mode 100644 index e1b4e804aa..0000000000 --- a/_js/standard-bundle.js.tmp +++ /dev/null @@ -1,4 +0,0 @@ -// import './modules/details-toggle'; -import './modules/domready'; -import './modules/focus-styles'; -import './modules/inpagelink'; diff --git a/_prototypes/your-household-v13/assets/household.js b/_prototypes/your-household-v13/assets/household.js new file mode 100644 index 0000000000..a041218fc3 --- /dev/null +++ b/_prototypes/your-household-v13/assets/household.js @@ -0,0 +1,137 @@ +import {autoIncrementId} from './utils'; + +export const HOUSEHOLD_MEMBERS_STORAGE_KEY = 'household-members'; +export const USER_HOUSEHOLD_MEMBER_ID = 'person_me'; +export const HOUSEHOLD_MEMBER_TYPE = 'household-member'; +export const VISITOR_TYPE = 'visitor'; + +/** + * Types + */ +export function person(opts) { + if (opts.firstName === '' || opts.lastName === '') { + console.log('Unable to create person with data: ', + opts.firstName, + !opts.middleName, + !opts.lastName); + } + + let middleName = opts.middleName || ''; + + return { + fullName: opts.firstName + ' ' + middleName + ' ' + opts.lastName, + firstName: opts.firstName, + middleName, + lastName: opts.lastName + }; +} + +/** + * Storage + */ +export function getUserAsHouseholdMember() { + return getAllHouseholdMembers().find((member) => { + return member['@person'].id === USER_HOUSEHOLD_MEMBER_ID; + }); +} + +export function deleteUserAsHouseholdMember() { + deleteHouseholdMember(USER_HOUSEHOLD_MEMBER_ID); +} + +export function deleteHouseholdMember(personId) { + let members = getAllHouseholdMembers().filter((member) => { + return member['@person'].id !== personId; + }); + + sessionStorage.setItem(HOUSEHOLD_MEMBERS_STORAGE_KEY, + JSON.stringify(members)); +} + +export function updateUserAsHouseholdMember(person, memberData) { + let userAsHouseholdMember = getUserAsHouseholdMember(); + + userAsHouseholdMember + ? updateHouseholdMember({ + ...userAsHouseholdMember['@person'], + ...person + }, memberData) + : addHouseholdMember(person, memberData, USER_HOUSEHOLD_MEMBER_ID); +} + +export function updateHouseholdMember(person, memberData) { + let membersUpdated = getAllHouseholdMembers().map((member) => { + return member['@person'].id === person.id + ? {...member, ...memberData, '@person': {...member['@person'], ...person}} + : member; + }); + + sessionStorage.setItem(HOUSEHOLD_MEMBERS_STORAGE_KEY, + JSON.stringify(membersUpdated)); +} + +export function addHouseholdMember(person, memberData, id) { + let people = getAllHouseholdMembers() || []; + memberData = memberData || {}; + + /** + * User is always first in the household list + */ + people[id === USER_HOUSEHOLD_MEMBER_ID ? 'unshift' : 'push']({ + ...memberData, + type: memberData.type || HOUSEHOLD_MEMBER_TYPE, + '@person': { + ...person, + id: id || 'person' + autoIncrementId('household-members') + } + }); + + sessionStorage.setItem(HOUSEHOLD_MEMBERS_STORAGE_KEY, JSON.stringify(people)); +} + +export function getAllHouseholdMembers() { + return JSON.parse(sessionStorage.getItem(HOUSEHOLD_MEMBERS_STORAGE_KEY)) || []; +} + +export function getHouseholdMemberByPersonId(id) { + return getAllHouseholdMembers().find(function(member) { + return member['@person'].id === id; + }); +} + +export function getMemberPersonId(member) { + return member['@person'].id; +} + +/** + * Comparators + */ +export function isVisitor(member) { + return member.type === window.ONS.storage.KEYS.VISITOR_TYPE; +} + +export function isHouseholdMember(member) { + return member.type === window.ONS.storage.KEYS.HOUSEHOLD_MEMBER_TYPE; +} + +export function isOtherHouseholdMember(member) { + return member.type === window.ONS.storage.KEYS.HOUSEHOLD_MEMBER_TYPE && + member['@person'].id !== window.ONS.storage.IDS.USER_HOUSEHOLD_MEMBER_ID; +} + +export const tempAwayQuestionSentenceMap = { + 'three-more': 'People who usually live outside the UK who are staying in the UK for 3 months or more', + 'perm-away': 'People who work away from home within the UK if this is their permanent or family home', + 'armed-forces': 'Members of the armed forces if this is their permanent or family home', + 'less-twelve': 'People who are temporarily outside the UK for less than 12 months', + 'usually-temp': 'People staying temporarily who usually live in the UK but' + + ' do not have another UK address for example, relatives, friends', + 'other': 'Other people who usually live here but are temporarily away' +}; + +export const visitorQuestionSentenceMap = { + 'usually-in-uk': 'People who usually live somewhere else in the UK, for example boy/girlfriends, friends or relatives', + 'second-address': 'People staying here because it is their second address, for example, for work. Their permanent or family home is elsewhere', + 'less-three': 'People who usually live outside the UK who are staying in the UK for less than three months', + 'on-holiday': 'People here on holiday', +}; diff --git a/_prototypes/your-household-v13/assets/lib/CustomEvent-polyfill.js b/_prototypes/your-household-v13/assets/lib/CustomEvent-polyfill.js new file mode 100644 index 0000000000..0dd012c6a6 --- /dev/null +++ b/_prototypes/your-household-v13/assets/lib/CustomEvent-polyfill.js @@ -0,0 +1,16 @@ +// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent +(function () { + + if ( typeof window.CustomEvent === "function" ) return false; + + function CustomEvent ( event, params ) { + params = params || { bubbles: false, cancelable: false, detail: null }; + var evt = document.createEvent( 'CustomEvent' ); + evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail ); + return evt; + } + + CustomEvent.prototype = window.Event.prototype; + + window.CustomEvent = CustomEvent; +})(); diff --git a/_prototypes/your-household-v13/assets/lib/FileSaver.js b/_prototypes/your-household-v13/assets/lib/FileSaver.js new file mode 100644 index 0000000000..3f3077b273 --- /dev/null +++ b/_prototypes/your-household-v13/assets/lib/FileSaver.js @@ -0,0 +1,174 @@ +/* +* FileSaver.js +* A saveAs() FileSaver implementation. +* +* By Eli Grey, http://eligrey.com +* +* License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT) +* source : http://purl.eligrey.com/github/FileSaver.js +*/ + + +// The one and only way of getting global scope in all environments +// https://stackoverflow.com/q/3277182/1008999 +var _global = typeof window === 'object' && window.window === window + ? window : typeof self === 'object' && self.self === self + ? self : typeof global === 'object' && global.global === global + ? global + : this; + +function bom(blob, opts) { + if (typeof opts === 'undefined') opts = {autoBom: false} + else if (typeof opts !== 'object') { + console.warn('Deprecated: Expected third argument to be a object') + opts = {autoBom: !opts} + } + + // prepend BOM for UTF-8 XML and text/* types (including HTML) + // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF + if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { + return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type}) + } + return blob +} + +function download(url, name, opts) { + var xhr = new XMLHttpRequest() + xhr.open('GET', url) + xhr.responseType = 'blob' + xhr.onload = function() { + saveAs(xhr.response, name, opts) + } + xhr.onerror = function() { + console.error('could not download file') + } + xhr.send() +} + +function corsEnabled(url) { + var xhr = new XMLHttpRequest() + // use sync to avoid popup blocker + xhr.open('HEAD', url, false) + xhr.send() + return xhr.status >= 200 && xhr.status <= 299 +} + +// `a.click()` doesn't work for all browsers (#465) +function click(node) { + try { + node.dispatchEvent(new MouseEvent('click')) + } catch (e) { + var evt = document.createEvent('MouseEvents') + evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, + 20, false, false, false, false, 0, null) + node.dispatchEvent(evt) + } +} + +var saveAs = _global.saveAs || ( + // probably in some web worker + (typeof window !== 'object' || window !== _global) + ? function saveAs() { /* noop */ + } + + // Use download attribute first if possible (#193 Lumia mobile) + : 'download' in HTMLAnchorElement.prototype + ? function saveAs(blob, name, opts) { + var URL = _global.URL || _global.webkitURL + var a = document.createElement('a') + name = name || blob.name || 'download' + + a.download = name + a.rel = 'noopener' // tabnabbing + + // TODO: detect chrome extensions & packaged apps + // a.target = '_blank' + + if (typeof blob === 'string') { + // Support regular links + a.href = blob + if (a.origin !== location.origin) { + corsEnabled(a.href) + ? download(blob, name, opts) + : click(a, a.target = '_blank') + } else { + click(a) + } + } else { + // Support blobs + a.href = URL.createObjectURL(blob) + setTimeout(function() { + URL.revokeObjectURL(a.href) + }, 4E4) // 40s + setTimeout(function() { + click(a) + }, 0) + } + } + + // Use msSaveOrOpenBlob as a second approach + : 'msSaveOrOpenBlob' in navigator + ? function saveAs(blob, name, opts) { + name = name || blob.name || 'download' + + if (typeof blob === 'string') { + if (corsEnabled(blob)) { + download(blob, name, opts) + } else { + var a = document.createElement('a') + a.href = blob + a.target = '_blank' + setTimeout(function() { + click(a) + }) + } + } else { + navigator.msSaveOrOpenBlob(bom(blob, opts), name) + } + } + + // Fallback to using FileReader and a popup + : function saveAs(blob, name, opts, popup) { + // Open a popup immediately do go around popup blocker + // Mostly only available on user interaction and the fileReader is async so... + popup = popup || open('', '_blank') + if (popup) { + popup.document.title = + popup.document.body.innerText = 'downloading...' + } + + if (typeof blob === 'string') return download(blob, name, opts) + + var force = blob.type === 'application/octet-stream' + var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari + var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent) + + if ((isChromeIOS || (force && isSafari)) && typeof FileReader === 'object') { + // Safari doesn't allow downloading of blob URLs + var reader = new FileReader() + reader.onloadend = function() { + var url = reader.result + url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;') + if (popup) popup.location.href = url + else location = url + popup = null // reverse-tabnabbing #460 + } + reader.readAsDataURL(blob) + } else { + var URL = _global.URL || _global.webkitURL + var url = URL.createObjectURL(blob) + if (popup) popup.location = url + else location.href = url + popup = null // reverse-tabnabbing #460 + setTimeout(function() { + URL.revokeObjectURL(url) + }, 4E4) // 40s + } + } +) + +_global.saveAs = saveAs.saveAs = saveAs + +if (typeof module !== 'undefined') { + module.exports = saveAs; +} diff --git a/_prototypes/your-household-v13/assets/lib/abortcontroller-polyfill.js b/_prototypes/your-household-v13/assets/lib/abortcontroller-polyfill.js new file mode 100644 index 0000000000..e78488d7bc --- /dev/null +++ b/_prototypes/your-household-v13/assets/lib/abortcontroller-polyfill.js @@ -0,0 +1,477 @@ +(function (factory) { + typeof define === 'function' && define.amd ? define(factory) : + factory(); +}(function () { 'use strict'; + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + function _inherits(subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function"); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + writable: true, + configurable: true + } + }); + if (superClass) _setPrototypeOf(subClass, superClass); + } + + function _getPrototypeOf(o) { + _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { + return o.__proto__ || Object.getPrototypeOf(o); + }; + return _getPrototypeOf(o); + } + + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { + o.__proto__ = p; + return o; + }; + + return _setPrototypeOf(o, p); + } + + function _assertThisInitialized(self) { + if (self === void 0) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return self; + } + + function _possibleConstructorReturn(self, call) { + if (call && (typeof call === "object" || typeof call === "function")) { + return call; + } + + return _assertThisInitialized(self); + } + + function _superPropBase(object, property) { + while (!Object.prototype.hasOwnProperty.call(object, property)) { + object = _getPrototypeOf(object); + if (object === null) break; + } + + return object; + } + + function _get(target, property, receiver) { + if (typeof Reflect !== "undefined" && Reflect.get) { + _get = Reflect.get; + } else { + _get = function _get(target, property, receiver) { + var base = _superPropBase(target, property); + + if (!base) return; + var desc = Object.getOwnPropertyDescriptor(base, property); + + if (desc.get) { + return desc.get.call(receiver); + } + + return desc.value; + }; + } + + return _get(target, property, receiver || target); + } + + var Emitter = + /*#__PURE__*/ + function () { + function Emitter() { + _classCallCheck(this, Emitter); + + Object.defineProperty(this, 'listeners', { + value: {}, + writable: true, + configurable: true + }); + } + + _createClass(Emitter, [{ + key: "addEventListener", + value: function addEventListener(type, callback) { + if (!(type in this.listeners)) { + this.listeners[type] = []; + } + + this.listeners[type].push(callback); + } + }, { + key: "removeEventListener", + value: function removeEventListener(type, callback) { + if (!(type in this.listeners)) { + return; + } + + var stack = this.listeners[type]; + + for (var i = 0, l = stack.length; i < l; i++) { + if (stack[i] === callback) { + stack.splice(i, 1); + return; + } + } + } + }, { + key: "dispatchEvent", + value: function dispatchEvent(event) { + var _this = this; + + if (!(event.type in this.listeners)) { + return; + } + + var debounce = function debounce(callback) { + setTimeout(function () { + return callback.call(_this, event); + }); + }; + + var stack = this.listeners[event.type]; + + for (var i = 0, l = stack.length; i < l; i++) { + debounce(stack[i]); + } + + return !event.defaultPrevented; + } + }]); + + return Emitter; + }(); + + var AbortSignal = + /*#__PURE__*/ + function (_Emitter) { + _inherits(AbortSignal, _Emitter); + + function AbortSignal() { + var _this2; + + _classCallCheck(this, AbortSignal); + + _this2 = _possibleConstructorReturn(this, _getPrototypeOf(AbortSignal).call(this)); // Some versions of babel does not transpile super() correctly for IE <= 10, if the parent + // constructor has failed to run, then "this.listeners" will still be undefined and then we call + // the parent constructor directly instead as a workaround. For general details, see babel bug: + // https://github.com/babel/babel/issues/3041 + // This hack was added as a fix for the issue described here: + // https://github.com/Financial-Times/polyfill-library/pull/59#issuecomment-477558042 + + if (!_this2.listeners) { + Emitter.call(_assertThisInitialized(_this2)); + } // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and + // we want Object.keys(new AbortController().signal) to be [] for compat with the native impl + + + Object.defineProperty(_assertThisInitialized(_this2), 'aborted', { + value: false, + writable: true, + configurable: true + }); + Object.defineProperty(_assertThisInitialized(_this2), 'onabort', { + value: null, + writable: true, + configurable: true + }); + return _this2; + } + + _createClass(AbortSignal, [{ + key: "toString", + value: function toString() { + return '[object AbortSignal]'; + } + }, { + key: "dispatchEvent", + value: function dispatchEvent(event) { + if (event.type === 'abort') { + this.aborted = true; + + if (typeof this.onabort === 'function') { + this.onabort.call(this, event); + } + } + + _get(_getPrototypeOf(AbortSignal.prototype), "dispatchEvent", this).call(this, event); + } + }]); + + return AbortSignal; + }(Emitter); + var AbortController = + /*#__PURE__*/ + function () { + function AbortController() { + _classCallCheck(this, AbortController); + + // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and + // we want Object.keys(new AbortController()) to be [] for compat with the native impl + Object.defineProperty(this, 'signal', { + value: new AbortSignal(), + writable: true, + configurable: true + }); + } + + _createClass(AbortController, [{ + key: "abort", + value: function abort() { + var event; + + try { + event = new Event('abort'); + } catch (e) { + if (typeof document !== 'undefined') { + if (!document.createEvent) { + // For Internet Explorer 8: + event = document.createEventObject(); + event.type = 'abort'; + } else { + // For Internet Explorer 11: + event = document.createEvent('Event'); + event.initEvent('abort', false, false); + } + } else { + // Fallback where document isn't available: + event = { + type: 'abort', + bubbles: false, + cancelable: false + }; + } + } + + this.signal.dispatchEvent(event); + } + }, { + key: "toString", + value: function toString() { + return '[object AbortController]'; + } + }]); + + return AbortController; + }(); + + if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { + // These are necessary to make sure that we get correct output for: + // Object.prototype.toString.call(new AbortController()) + AbortController.prototype[Symbol.toStringTag] = 'AbortController'; + AbortSignal.prototype[Symbol.toStringTag] = 'AbortSignal'; + } + + function polyfillNeeded(self) { + if (self.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) { + console.log('__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL=true is set, will force install polyfill'); + return true; + } // Note that the "unfetch" minimal fetch polyfill defines fetch() without + // defining window.Request, and this polyfill need to work on top of unfetch + // so the below feature detection needs the !self.AbortController part. + // The Request.prototype check is also needed because Safari versions 11.1.2 + // up to and including 12.1.x has a window.AbortController present but still + // does NOT correctly implement abortable fetch: + // https://bugs.webkit.org/show_bug.cgi?id=174980#c2 + + + return typeof self.Request === 'function' && !self.Request.prototype.hasOwnProperty('signal') || !self.AbortController; + } + + /** + * Note: the "fetch.Request" default value is available for fetch imported from + * the "node-fetch" package and not in browsers. This is OK since browsers + * will be importing umd-polyfill.js from that path "self" is passed the + * decorator so the default value will not be used (because browsers that define + * fetch also has Request). One quirky setup where self.fetch exists but + * self.Request does not is when the "unfetch" minimal fetch polyfill is used + * on top of IE11; for this case the browser will try to use the fetch.Request + * default value which in turn will be undefined but then then "if (Request)" + * will ensure that you get a patched fetch but still no Request (as expected). + * @param {fetch, Request = fetch.Request} + * @returns {fetch: abortableFetch, Request: AbortableRequest} + */ + + function abortableFetchDecorator(patchTargets) { + if ('function' === typeof patchTargets) { + patchTargets = { + fetch: patchTargets + }; + } + + var _patchTargets = patchTargets, + fetch = _patchTargets.fetch, + _patchTargets$Request = _patchTargets.Request, + NativeRequest = _patchTargets$Request === void 0 ? fetch.Request : _patchTargets$Request, + NativeAbortController = _patchTargets.AbortController, + _patchTargets$__FORCE = _patchTargets.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL, + __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL = _patchTargets$__FORCE === void 0 ? false : _patchTargets$__FORCE; + + if (!polyfillNeeded({ + fetch: fetch, + Request: NativeRequest, + AbortController: NativeAbortController, + __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL: __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL + })) { + return { + fetch: fetch, + Request: Request + }; + } + + var Request = NativeRequest; // Note that the "unfetch" minimal fetch polyfill defines fetch() without + // defining window.Request, and this polyfill need to work on top of unfetch + // hence we only patch it if it's available. Also we don't patch it if signal + // is already available on the Request prototype because in this case support + // is present and the patching below can cause a crash since it assigns to + // request.signal which is technically a read-only property. This latter error + // happens when you run the main5.js node-fetch example in the repo + // "abortcontroller-polyfill-examples". The exact error is: + // request.signal = init.signal; + // ^ + // TypeError: Cannot set property signal of # which has only a getter + + if (Request && !Request.prototype.hasOwnProperty('signal') || __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) { + Request = function Request(input, init) { + var signal; + + if (init && init.signal) { + signal = init.signal; // Never pass init.signal to the native Request implementation when the polyfill has + // been installed because if we're running on top of a browser with a + // working native AbortController (i.e. the polyfill was installed due to + // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our + // fake AbortSignal to the native fetch will trigger: + // TypeError: Failed to construct 'Request': member signal is not of type AbortSignal. + + delete init.signal; + } + + var request = new NativeRequest(input, init); + + if (signal) { + Object.defineProperty(request, 'signal', { + writable: false, + enumerable: false, + configurable: true, + value: signal + }); + } + + return request; + }; + + Request.prototype = NativeRequest.prototype; + } + + var realFetch = fetch; + + var abortableFetch = function abortableFetch(input, init) { + var signal = Request && Request.prototype.isPrototypeOf(input) ? input.signal : init ? init.signal : undefined; + + if (signal) { + var abortError; + + try { + abortError = new DOMException('Aborted', 'AbortError'); + } catch (err) { + // IE 11 does not support calling the DOMException constructor, use a + // regular error object on it instead. + abortError = new Error('Aborted'); + abortError.name = 'AbortError'; + } // Return early if already aborted, thus avoiding making an HTTP request + + + if (signal.aborted) { + return Promise.reject(abortError); + } // Turn an event into a promise, reject it once `abort` is dispatched + + + var cancellation = new Promise(function (_, reject) { + signal.addEventListener('abort', function () { + return reject(abortError); + }, { + once: true + }); + }); + + if (init && init.signal) { + // Never pass .signal to the native implementation when the polyfill has + // been installed because if we're running on top of a browser with a + // working native AbortController (i.e. the polyfill was installed due to + // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our + // fake AbortSignal to the native fetch will trigger: + // TypeError: Failed to execute 'fetch' on 'Window': member signal is not of type AbortSignal. + delete init.signal; + } // Return the fastest promise (don't need to wait for request to finish) + + + return Promise.race([cancellation, realFetch(input, init)]); + } + + return realFetch(input, init); + }; + + return { + fetch: abortableFetch, + Request: Request + }; + } + + (function (self) { + + if (!polyfillNeeded(self)) { + return; + } + + if (!self.fetch) { + console.warn('fetch() is not available, cannot install abortcontroller-polyfill'); + return; + } + + var _abortableFetch = abortableFetchDecorator(self), + fetch = _abortableFetch.fetch, + Request = _abortableFetch.Request; + + self.fetch = fetch; + self.Request = Request; + Object.defineProperty(self, 'AbortController', { + writable: true, + enumerable: false, + configurable: true, + value: AbortController + }); + Object.defineProperty(self, 'AbortSignal', { + writable: true, + enumerable: false, + configurable: true, + value: AbortSignal + }); + })(typeof self !== 'undefined' ? self : global); + +})); diff --git a/_prototypes/your-household-v13/assets/lib/array-find-polyfill.js b/_prototypes/your-household-v13/assets/lib/array-find-polyfill.js new file mode 100644 index 0000000000..c289597c09 --- /dev/null +++ b/_prototypes/your-household-v13/assets/lib/array-find-polyfill.js @@ -0,0 +1,46 @@ +// https://tc39.github.io/ecma262/#sec-array.prototype.find +if (!Array.prototype.find) { + Object.defineProperty(Array.prototype, 'find', { + value: function(predicate) { + // 1. Let O be ? ToObject(this value). + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + var o = Object(this); + + // 2. Let len be ? ToLength(? Get(O, "length")). + var len = o.length >>> 0; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. + var thisArg = arguments[1]; + + // 5. Let k be 0. + var k = 0; + + // 6. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(O, Pk). + // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). + // d. If testResult is true, return kValue. + var kValue = o[k]; + if (predicate.call(thisArg, kValue, k, o)) { + return kValue; + } + // e. Increase k by 1. + k++; + } + + // 7. Return undefined. + return undefined; + }, + configurable: true, + writable: true + }); +} diff --git a/_prototypes/your-household-v13/assets/lib/array-from-polyfill.js b/_prototypes/your-household-v13/assets/lib/array-from-polyfill.js new file mode 100644 index 0000000000..9736fb71fe --- /dev/null +++ b/_prototypes/your-household-v13/assets/lib/array-from-polyfill.js @@ -0,0 +1,77 @@ +if (!Array.from) { + Array.from = (function () { + var toStr = Object.prototype.toString; + var isCallable = function (fn) { + return typeof fn === 'function' || toStr.call(fn) === '[object Function]'; + }; + var toInteger = function (value) { + var number = Number(value); + if (isNaN(number)) { return 0; } + if (number === 0 || !isFinite(number)) { return number; } + return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)); + }; + var maxSafeInteger = Math.pow(2, 53) - 1; + var toLength = function (value) { + var len = toInteger(value); + return Math.min(Math.max(len, 0), maxSafeInteger); + }; + + // The length property of the from method is 1. + return function from(arrayLike/*, mapFn, thisArg */) { + // 1. Let C be the this value. + var C = this; + + // 2. Let items be ToObject(arrayLike). + var items = Object(arrayLike); + + // 3. ReturnIfAbrupt(items). + if (arrayLike == null) { + throw new TypeError('Array.from requires an array-like object - not null or undefined'); + } + + // 4. If mapfn is undefined, then let mapping be false. + var mapFn = arguments.length > 1 ? arguments[1] : void undefined; + var T; + if (typeof mapFn !== 'undefined') { + // 5. else + // 5. a If IsCallable(mapfn) is false, throw a TypeError exception. + if (!isCallable(mapFn)) { + throw new TypeError('Array.from: when provided, the second argument must be a function'); + } + + // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined. + if (arguments.length > 2) { + T = arguments[2]; + } + } + + // 10. Let lenValue be Get(items, "length"). + // 11. Let len be ToLength(lenValue). + var len = toLength(items.length); + + // 13. If IsConstructor(C) is true, then + // 13. a. Let A be the result of calling the [[Construct]] internal method + // of C with an argument list containing the single item len. + // 14. a. Else, Let A be ArrayCreate(len). + var A = isCallable(C) ? Object(new C(len)) : new Array(len); + + // 16. Let k be 0. + var k = 0; + // 17. Repeat, while k < len… (also steps a - h) + var kValue; + while (k < len) { + kValue = items[k]; + if (mapFn) { + A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k); + } else { + A[k] = kValue; + } + k += 1; + } + // 18. Let putStatus be Put(A, "length", len, true). + A.length = len; + // 20. Return A. + return A; + }; + }()); +} diff --git a/_prototypes/your-household-v13/assets/lib/fetch-polyfill.js b/_prototypes/your-household-v13/assets/lib/fetch-polyfill.js new file mode 100644 index 0000000000..657dd2f42f --- /dev/null +++ b/_prototypes/your-household-v13/assets/lib/fetch-polyfill.js @@ -0,0 +1,462 @@ +(function (self) { + 'use strict'; + + if (self.fetch) { + return; + } + + var support = { + searchParams: 'URLSearchParams' in self, + iterable: 'Symbol' in self && 'iterator' in Symbol, + blob: 'FileReader' in self && 'Blob' in self && function () { + try { + new Blob(); + return true; + } catch (e) { + return false; + } + }(), + formData: 'FormData' in self, + arrayBuffer: 'ArrayBuffer' in self + }; + + if (support.arrayBuffer) { + var viewClasses = ['[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]']; + + var isDataView = function isDataView(obj) { + return obj && DataView.prototype.isPrototypeOf(obj); + }; + + var isArrayBufferView = ArrayBuffer.isView || function (obj) { + return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1; + }; + } + + function normalizeName(name) { + if (typeof name !== 'string') { + name = String(name); + } + if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { + throw new TypeError('Invalid character in header field name'); + } + return name.toLowerCase(); + } + + function normalizeValue(value) { + if (typeof value !== 'string') { + value = String(value); + } + return value; + } + + // Build a destructive iterator for the value list + function iteratorFor(items) { + var iterator = { + next: function next() { + var value = items.shift(); + return { done: value === undefined, value: value }; + } + }; + + if (support.iterable) { + iterator[Symbol.iterator] = function () { + return iterator; + }; + } + + return iterator; + } + + function Headers(headers) { + this.map = {}; + + if (headers instanceof Headers) { + headers.forEach(function (value, name) { + this.append(name, value); + }, this); + } else if (Array.isArray(headers)) { + headers.forEach(function (header) { + this.append(header[0], header[1]); + }, this); + } else if (headers) { + Object.getOwnPropertyNames(headers).forEach(function (name) { + this.append(name, headers[name]); + }, this); + } + } + + Headers.prototype.append = function (name, value) { + name = normalizeName(name); + value = normalizeValue(value); + var oldValue = this.map[name]; + this.map[name] = oldValue ? oldValue + ',' + value : value; + }; + + Headers.prototype['delete'] = function (name) { + delete this.map[normalizeName(name)]; + }; + + Headers.prototype.get = function (name) { + name = normalizeName(name); + return this.has(name) ? this.map[name] : null; + }; + + Headers.prototype.has = function (name) { + return this.map.hasOwnProperty(normalizeName(name)); + }; + + Headers.prototype.set = function (name, value) { + this.map[normalizeName(name)] = normalizeValue(value); + }; + + Headers.prototype.forEach = function (callback, thisArg) { + for (var name in this.map) { + if (this.map.hasOwnProperty(name)) { + callback.call(thisArg, this.map[name], name, this); + } + } + }; + + Headers.prototype.keys = function () { + var items = []; + this.forEach(function (value, name) { + items.push(name); + }); + return iteratorFor(items); + }; + + Headers.prototype.values = function () { + var items = []; + this.forEach(function (value) { + items.push(value); + }); + return iteratorFor(items); + }; + + Headers.prototype.entries = function () { + var items = []; + this.forEach(function (value, name) { + items.push([name, value]); + }); + return iteratorFor(items); + }; + + if (support.iterable) { + Headers.prototype[Symbol.iterator] = Headers.prototype.entries; + } + + function consumed(body) { + if (body.bodyUsed) { + return Promise.reject(new TypeError('Already read')); + } + body.bodyUsed = true; + } + + function fileReaderReady(reader) { + return new Promise(function (resolve, reject) { + reader.onload = function () { + resolve(reader.result); + }; + reader.onerror = function () { + reject(reader.error); + }; + }); + } + + function readBlobAsArrayBuffer(blob) { + var reader = new FileReader(); + var promise = fileReaderReady(reader); + reader.readAsArrayBuffer(blob); + return promise; + } + + function readBlobAsText(blob) { + var reader = new FileReader(); + var promise = fileReaderReady(reader); + reader.readAsText(blob); + return promise; + } + + function readArrayBufferAsText(buf) { + var view = new Uint8Array(buf); + var chars = new Array(view.length); + + for (var i = 0; i < view.length; i++) { + chars[i] = String.fromCharCode(view[i]); + } + return chars.join(''); + } + + function bufferClone(buf) { + if (buf.slice) { + return buf.slice(0); + } else { + var view = new Uint8Array(buf.byteLength); + view.set(new Uint8Array(buf)); + return view.buffer; + } + } + + function Body() { + this.bodyUsed = false; + + this._initBody = function (body) { + this._bodyInit = body; + if (!body) { + this._bodyText = ''; + } else if (typeof body === 'string') { + this._bodyText = body; + } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { + this._bodyBlob = body; + } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { + this._bodyFormData = body; + } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { + this._bodyText = body.toString(); + } else if (support.arrayBuffer && support.blob && isDataView(body)) { + this._bodyArrayBuffer = bufferClone(body.buffer); + // IE 10-11 can't handle a DataView body. + this._bodyInit = new Blob([this._bodyArrayBuffer]); + } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { + this._bodyArrayBuffer = bufferClone(body); + } else { + throw new Error('unsupported BodyInit type'); + } + + if (!this.headers.get('content-type')) { + if (typeof body === 'string') { + this.headers.set('content-type', 'text/plain;charset=UTF-8'); + } else if (this._bodyBlob && this._bodyBlob.type) { + this.headers.set('content-type', this._bodyBlob.type); + } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { + this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8'); + } + } + }; + + if (support.blob) { + this.blob = function () { + var rejected = consumed(this); + if (rejected) { + return rejected; + } + + if (this._bodyBlob) { + return Promise.resolve(this._bodyBlob); + } else if (this._bodyArrayBuffer) { + return Promise.resolve(new Blob([this._bodyArrayBuffer])); + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as blob'); + } else { + return Promise.resolve(new Blob([this._bodyText])); + } + }; + + this.arrayBuffer = function () { + if (this._bodyArrayBuffer) { + return consumed(this) || Promise.resolve(this._bodyArrayBuffer); + } else { + return this.blob().then(readBlobAsArrayBuffer); + } + }; + } + + this.text = function () { + var rejected = consumed(this); + if (rejected) { + return rejected; + } + + if (this._bodyBlob) { + return readBlobAsText(this._bodyBlob); + } else if (this._bodyArrayBuffer) { + return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)); + } else if (this._bodyFormData) { + throw new Error('could not read FormData body as text'); + } else { + return Promise.resolve(this._bodyText); + } + }; + + if (support.formData) { + this.formData = function () { + return this.text().then(decode); + }; + } + + this.json = function () { + return this.text().then(JSON.parse); + }; + + return this; + } + + // HTTP methods whose capitalization should be normalized + var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']; + + function normalizeMethod(method) { + var upcased = method.toUpperCase(); + return methods.indexOf(upcased) > -1 ? upcased : method; + } + + function Request(input, options) { + options = options || {}; + var body = options.body; + + if (input instanceof Request) { + if (input.bodyUsed) { + throw new TypeError('Already read'); + } + this.url = input.url; + this.credentials = input.credentials; + if (!options.headers) { + this.headers = new Headers(input.headers); + } + this.method = input.method; + this.mode = input.mode; + if (!body && input._bodyInit != null) { + body = input._bodyInit; + input.bodyUsed = true; + } + } else { + this.url = String(input); + } + + this.credentials = options.credentials || this.credentials || 'omit'; + if (options.headers || !this.headers) { + this.headers = new Headers(options.headers); + } + this.method = normalizeMethod(options.method || this.method || 'GET'); + this.mode = options.mode || this.mode || null; + this.referrer = null; + + if ((this.method === 'GET' || this.method === 'HEAD') && body) { + throw new TypeError('Body not allowed for GET or HEAD requests'); + } + this._initBody(body); + } + + Request.prototype.clone = function () { + return new Request(this, { body: this._bodyInit }); + }; + + function decode(body) { + var form = new FormData(); + body.trim().split('&').forEach(function (bytes) { + if (bytes) { + var split = bytes.split('='); + var name = split.shift().replace(/\+/g, ' '); + var value = split.join('=').replace(/\+/g, ' '); + form.append(decodeURIComponent(name), decodeURIComponent(value)); + } + }); + return form; + } + + function parseHeaders(rawHeaders) { + var headers = new Headers(); + // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space + // https://tools.ietf.org/html/rfc7230#section-3.2 + var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' '); + preProcessedHeaders.split(/\r?\n/).forEach(function (line) { + var parts = line.split(':'); + var key = parts.shift().trim(); + if (key) { + var value = parts.join(':').trim(); + headers.append(key, value); + } + }); + return headers; + } + + Body.call(Request.prototype); + + function Response(bodyInit, options) { + if (!options) { + options = {}; + } + + this.type = 'default'; + this.status = options.status === undefined ? 200 : options.status; + this.ok = this.status >= 200 && this.status < 300; + this.statusText = 'statusText' in options ? options.statusText : 'OK'; + this.headers = new Headers(options.headers); + this.url = options.url || ''; + this._initBody(bodyInit); + } + + Body.call(Response.prototype); + + Response.prototype.clone = function () { + return new Response(this._bodyInit, { + status: this.status, + statusText: this.statusText, + headers: new Headers(this.headers), + url: this.url + }); + }; + + Response.error = function () { + var response = new Response(null, { status: 0, statusText: '' }); + response.type = 'error'; + return response; + }; + + var redirectStatuses = [301, 302, 303, 307, 308]; + + Response.redirect = function (url, status) { + if (redirectStatuses.indexOf(status) === -1) { + throw new RangeError('Invalid status code'); + } + + return new Response(null, { status: status, headers: { location: url } }); + }; + + self.Headers = Headers; + self.Request = Request; + self.Response = Response; + + self.fetch = function (input, init) { + return new Promise(function (resolve, reject) { + var request = new Request(input, init); + var xhr = new XMLHttpRequest(); + + xhr.onload = function () { + var options = { + status: xhr.status, + statusText: xhr.statusText, + headers: parseHeaders(xhr.getAllResponseHeaders() || '') + }; + options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL'); + var body = 'response' in xhr ? xhr.response : xhr.responseText; + resolve(new Response(body, options)); + }; + + xhr.onerror = function () { + reject(new TypeError('Network request failed')); + }; + + xhr.ontimeout = function () { + reject(new TypeError('Network request failed')); + }; + + xhr.open(request.method, request.url, true); + + if (request.credentials === 'include') { + xhr.withCredentials = true; + } else if (request.credentials === 'omit') { + xhr.withCredentials = false; + } + + if ('responseType' in xhr && support.blob) { + xhr.responseType = 'blob'; + } + + request.headers.forEach(function (value, name) { + xhr.setRequestHeader(name, value); + }); + + xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit); + }); + }; + self.fetch.polyfill = true; +})(typeof self !== 'undefined' ? self : undefined); diff --git a/_prototypes/your-household-v13/assets/lib/url-search-params-polyfill.js b/_prototypes/your-household-v13/assets/lib/url-search-params-polyfill.js new file mode 100644 index 0000000000..dac0f50205 --- /dev/null +++ b/_prototypes/your-household-v13/assets/lib/url-search-params-polyfill.js @@ -0,0 +1,315 @@ +/** + * + * + * @author Jerry Bendy + * @licence MIT + * + */ + +(function(self) { + 'use strict'; + + var nativeURLSearchParams = (self.URLSearchParams && self.URLSearchParams.prototype.get) ? self.URLSearchParams : null, + isSupportObjectConstructor = nativeURLSearchParams && (new nativeURLSearchParams({a: 1})).toString() === 'a=1', + // There is a bug in safari 10.1 (and earlier) that incorrectly decodes `%2B` as an empty space and not a plus. + decodesPlusesCorrectly = nativeURLSearchParams && (new nativeURLSearchParams('s=%2B').get('s') === '+'), + __URLSearchParams__ = "__URLSearchParams__", + // Fix bug in Edge which cannot encode ' &' correctly + encodesAmpersandsCorrectly = nativeURLSearchParams ? (function() { + var ampersandTest = new nativeURLSearchParams(); + ampersandTest.append('s', ' &'); + return ampersandTest.toString() === 's=+%26'; + })() : true, + prototype = URLSearchParamsPolyfill.prototype, + iterable = !!(self.Symbol && self.Symbol.iterator); + + if (nativeURLSearchParams && isSupportObjectConstructor && decodesPlusesCorrectly && encodesAmpersandsCorrectly) { + return; + } + + + /** + * Make a URLSearchParams instance + * + * @param {object|string|URLSearchParams} search + * @constructor + */ + function URLSearchParamsPolyfill(search) { + search = search || ""; + + // support construct object with another URLSearchParams instance + if (search instanceof URLSearchParams || search instanceof URLSearchParamsPolyfill) { + search = search.toString(); + } + this [__URLSearchParams__] = parseToDict(search); + } + + + /** + * Appends a specified key/value pair as a new search parameter. + * + * @param {string} name + * @param {string} value + */ + prototype.append = function(name, value) { + appendTo(this [__URLSearchParams__], name, value); + }; + + /** + * Deletes the given search parameter, and its associated value, + * from the list of all search parameters. + * + * @param {string} name + */ + prototype['delete'] = function(name) { + delete this [__URLSearchParams__] [name]; + }; + + /** + * Returns the first value associated to the given search parameter. + * + * @param {string} name + * @returns {string|null} + */ + prototype.get = function(name) { + var dict = this [__URLSearchParams__]; + return name in dict ? dict[name][0] : null; + }; + + /** + * Returns all the values association with a given search parameter. + * + * @param {string} name + * @returns {Array} + */ + prototype.getAll = function(name) { + var dict = this [__URLSearchParams__]; + return name in dict ? dict [name].slice(0) : []; + }; + + /** + * Returns a Boolean indicating if such a search parameter exists. + * + * @param {string} name + * @returns {boolean} + */ + prototype.has = function(name) { + return name in this [__URLSearchParams__]; + }; + + /** + * Sets the value associated to a given search parameter to + * the given value. If there were several values, delete the + * others. + * + * @param {string} name + * @param {string} value + */ + prototype.set = function set(name, value) { + this [__URLSearchParams__][name] = ['' + value]; + }; + + /** + * Returns a string containg a query string suitable for use in a URL. + * + * @returns {string} + */ + prototype.toString = function() { + var dict = this[__URLSearchParams__], query = [], i, key, name, value; + for (key in dict) { + name = encode(key); + for (i = 0, value = dict[key]; i < value.length; i++) { + query.push(name + '=' + encode(value[i])); + } + } + return query.join('&'); + }; + + // There is a bug in Safari 10.1 and `Proxy`ing it is not enough. + var forSureUsePolyfill = !decodesPlusesCorrectly; + var useProxy = (!forSureUsePolyfill && nativeURLSearchParams && !isSupportObjectConstructor && self.Proxy) + /* + * Apply polifill to global object and append other prototype into it + */ + Object.defineProperty(self, 'URLSearchParams', { + value: (useProxy ? + // Safari 10.0 doesn't support Proxy, so it won't extend URLSearchParams on safari 10.0 + new Proxy(nativeURLSearchParams, { + construct: function(target, args) { + return new target((new URLSearchParamsPolyfill(args[0]).toString())); + } + }) : + URLSearchParamsPolyfill) + }); + + var USPProto = self.URLSearchParams.prototype; + + USPProto.polyfill = true; + + /** + * + * @param {function} callback + * @param {object} thisArg + */ + USPProto.forEach = USPProto.forEach || function(callback, thisArg) { + var dict = parseToDict(this.toString()); + Object.getOwnPropertyNames(dict).forEach(function(name) { + dict[name].forEach(function(value) { + callback.call(thisArg, value, name, this); + }, this); + }, this); + }; + + /** + * Sort all name-value pairs + */ + USPProto.sort = USPProto.sort || function() { + var dict = parseToDict(this.toString()), keys = [], k, i, j; + for (k in dict) { + keys.push(k); + } + keys.sort(); + + for (i = 0; i < keys.length; i++) { + this['delete'](keys[i]); + } + for (i = 0; i < keys.length; i++) { + var key = keys[i], values = dict[key]; + for (j = 0; j < values.length; j++) { + this.append(key, values[j]); + } + } + }; + + /** + * Returns an iterator allowing to go through all keys of + * the key/value pairs contained in this object. + * + * @returns {function} + */ + USPProto.keys = USPProto.keys || function() { + var items = []; + this.forEach(function(item, name) { + items.push(name); + }); + return makeIterator(items); + }; + + /** + * Returns an iterator allowing to go through all values of + * the key/value pairs contained in this object. + * + * @returns {function} + */ + USPProto.values = USPProto.values || function() { + var items = []; + this.forEach(function(item) { + items.push(item); + }); + return makeIterator(items); + }; + + /** + * Returns an iterator allowing to go through all key/value + * pairs contained in this object. + * + * @returns {function} + */ + USPProto.entries = USPProto.entries || function() { + var items = []; + this.forEach(function(item, name) { + items.push([name, item]); + }); + return makeIterator(items); + }; + + + if (iterable) { + USPProto[self.Symbol.iterator] = USPProto[self.Symbol.iterator] || USPProto.entries; + } + + + function encode(str) { + var replace = { + '!': '%21', + "'": '%27', + '(': '%28', + ')': '%29', + '~': '%7E', + '%20': '+', + '%00': '\x00' + }; + return encodeURIComponent(str).replace(/[!'\(\)~]|%20|%00/g, function(match) { + return replace[match]; + }); + } + + function decode(str) { + return decodeURIComponent(str.replace(/\+/g, ' ')); + } + + function makeIterator(arr) { + var iterator = { + next: function() { + var value = arr.shift(); + return {done: value === undefined, value: value}; + } + }; + + if (iterable) { + iterator[self.Symbol.iterator] = function() { + return iterator; + }; + } + + return iterator; + } + + function parseToDict(search) { + var dict = {}; + + if (typeof search === "object") { + for (var key in search) { + if (search.hasOwnProperty(key)) { + appendTo(dict, key, search[key]) + } + } + + } else { + // remove first '?' + if (search.indexOf("?") === 0) { + search = search.slice(1); + } + + var pairs = search.split("&"); + for (var j = 0; j < pairs.length; j++) { + var value = pairs [j], + index = value.indexOf('='); + + if (-1 < index) { + appendTo(dict, decode(value.slice(0, index)), decode(value.slice(index + 1))); + + } else { + if (value) { + appendTo(dict, decode(value), ''); + } + } + } + } + + return dict; + } + + function appendTo(dict, name, value) { + var val = typeof value === 'string' ? value : ( + value !== null && value !== undefined && typeof value.toString === 'function' ? value.toString() : JSON.stringify(value) + ) + + if (name in dict) { + dict[name].push(val); + } else { + dict[name] = [val]; + } + } + +})(typeof global !== 'undefined' ? global : (typeof window !== 'undefined' ? window : this)); diff --git a/_prototypes/your-household-v13/assets/modules/abortable-fetch.js b/_prototypes/your-household-v13/assets/modules/abortable-fetch.js new file mode 100644 index 0000000000..a0ced36c67 --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/abortable-fetch.js @@ -0,0 +1,49 @@ +export default class AbortableFetch { + constructor(url, options) { + this.url = url; + this.options = options; + this.controller = new window.AbortController(); + this.status = 'UNSENT'; + } + + send() { + this.status = 'LOADING'; + + return new Promise((resolve, reject) => { + abortableFetch(this.url, { signal: this.controller.signal, ...this.options }) + .then(response => { + if (response.status >= 200 && response.status < 300) { + this.status = 'DONE'; + resolve(response); + } else { + this.status = 'DONE'; + reject(response); + } + }) + .catch(error => { + this.status = 'DONE'; + reject(error); + }); + }); + } + + abort() { + this.controller.abort(); + } +} + +function abortableFetch(url, options) { + return window.fetch(url, options) + .then(response => { + if (response.ok) { + return response; + } else { + const error = new Error(response.statusText); + error.response = response; + throw error; + } + }) + .catch(error => { + throw error; + }); +} diff --git a/_prototypes/your-household-v13/assets/modules/form-body-from-object.js b/_prototypes/your-household-v13/assets/modules/form-body-from-object.js new file mode 100644 index 0000000000..ac4b357b7d --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/form-body-from-object.js @@ -0,0 +1,3 @@ +export default function formBodyFromObject(object) { + return Object.keys(object).map(key => `${encodeURIComponent(key)}=${encodeURIComponent(object[key])}`).join('&'); +} diff --git a/_prototypes/your-household-v13/assets/modules/typeahead-refactored/abortable-fetch.js b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/abortable-fetch.js new file mode 100644 index 0000000000..a0ced36c67 --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/abortable-fetch.js @@ -0,0 +1,49 @@ +export default class AbortableFetch { + constructor(url, options) { + this.url = url; + this.options = options; + this.controller = new window.AbortController(); + this.status = 'UNSENT'; + } + + send() { + this.status = 'LOADING'; + + return new Promise((resolve, reject) => { + abortableFetch(this.url, { signal: this.controller.signal, ...this.options }) + .then(response => { + if (response.status >= 200 && response.status < 300) { + this.status = 'DONE'; + resolve(response); + } else { + this.status = 'DONE'; + reject(response); + } + }) + .catch(error => { + this.status = 'DONE'; + reject(error); + }); + }); + } + + abort() { + this.controller.abort(); + } +} + +function abortableFetch(url, options) { + return window.fetch(url, options) + .then(response => { + if (response.ok) { + return response; + } else { + const error = new Error(response.statusText); + error.response = response; + throw error; + } + }) + .catch(error => { + throw error; + }); +} diff --git a/_prototypes/your-household-v13/assets/modules/typeahead-refactored/form-body-from-object.js b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/form-body-from-object.js new file mode 100644 index 0000000000..ac4b357b7d --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/form-body-from-object.js @@ -0,0 +1,3 @@ +export default function formBodyFromObject(object) { + return Object.keys(object).map(key => `${encodeURIComponent(key)}=${encodeURIComponent(object[key])}`).join('&'); +} diff --git a/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead-helpers.js b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead-helpers.js new file mode 100644 index 0000000000..800c4e5155 --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead-helpers.js @@ -0,0 +1,11 @@ +export function sanitiseTypeaheadText(string, sanitisedQueryReplaceChars = [], trimEnd = true) { + let sanitisedString = string.toLowerCase().replace(/\s\s+/g, ' '); + + sanitisedString = trimEnd ? sanitisedString.trim() : sanitisedString.trimStart(); + + sanitisedQueryReplaceChars.forEach(char => { + sanitisedString = sanitisedString.replace(new RegExp(char, 'g'), ''); + }); + + return sanitisedString; +} diff --git a/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.component.js b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.component.js new file mode 100644 index 0000000000..571f762326 --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.component.js @@ -0,0 +1,406 @@ +import EventEmitter from 'events'; +import {sanitiseTypeaheadText} from './typeahead-helpers'; + +const classTypeaheadCombobox = 'js-typeahead-combobox'; +const classTypeaheadLabel = 'js-typeahead-label'; +const classTypeaheadInput = 'js-typeahead-input'; +const classTypeaheadInstructions = 'js-typeahead-instructions'; +const classTypeaheadListbox = 'js-typeahead-listbox'; +const classTypeaheadAriaStatus = 'js-typeahead-aria-status'; + +const classTypeaheadOption = 'typeahead__option'; +const classTypeaheadOptionFocused = `${classTypeaheadOption}--focused`; +const classTypeaheadOptionNoResults = `${classTypeaheadOption}--no-results`; +const classTypeaheadOptionMoreResults = `${classTypeaheadOption}--more-results`; +const classTypeaheadComboboxFocused = 'typeahead__combobox--focused'; +const classTypeaheadHasResults = 'typeahead--has-results'; + +const KEYCODE = { + BACK_SPACE: 8, + RETURN: 13, + ENTER: 14, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + DELETE: 46, + V: 86 +}; + +export const NEW_FIELD_VALUE_EVENT = 'NEW_FIELD_VALUE'; +export const NEW_ITEM_SELECTED_EVENT = 'NEW_ITEM_SELECTED'; +export const UNSET_FIELD_VALUE_EVENT = 'UNSET_FIELD_VALUE'; + +export default class TypeaheadComponent { + emitter = new EventEmitter(); + + constructor({ + context, + apiUrl, + minChars, + resultLimit, + sanitisedQueryReplaceChars = [], + lang = null}) { + + // DOM Elements + this.context = context; + this.combobox = context.querySelector(`.${classTypeaheadCombobox}`); + this.label = context.querySelector(`.${classTypeaheadLabel}`); + this.input = context.querySelector(`.${classTypeaheadInput}`); + this.listbox = context.querySelector(`.${classTypeaheadListbox}`); + this.instructions = context.querySelector(`.${classTypeaheadInstructions}`); + this.ariaStatus = context.querySelector(`.${classTypeaheadAriaStatus}`); + + // Suggestion URL + this.apiUrl = apiUrl || context.getAttribute('data-api-url'); + + // Settings + this.content = JSON.parse(context.getAttribute('data-content')); + this.listboxId = this.listbox.getAttribute('id'); + this.minChars = minChars || 2; + this.resultLimit = resultLimit || null; + + // State + this.ctrlKey = false; + this.deleting = false; + this.query = ''; + this.sanitisedQuery = ''; + this.previousQuery = ''; + this.results = []; + this.resultOptions = []; + this.foundResults = 0; + this.numberOfResults = 0; + this.highlightedResultIndex = 0; + this.settingResult = false; + this.resultSelected = false; + this.blurring = false; + this.blurTimeout = null; + this.sanitisedQueryReplaceChars = sanitisedQueryReplaceChars; + this.lang = lang || document.documentElement.getAttribute('lang').toLowerCase(); + + // Modify DOM + this.label.setAttribute('for', this.input.getAttribute('id')); + this.input.setAttribute('aria-autocomplete', 'list'); + this.input.setAttribute('aria-controls', this.listbox.getAttribute('id')); + this.input.setAttribute('aria-describedby', this.instructions.getAttribute('id')); + this.input.setAttribute('autocomplete', this.input.getAttribute('data-autocomplete')); + this.context.classList.add('typeahead--initialised'); + + // Bind event listeners + this.bindEventListeners(); + } + + bindEventListeners() { + this.input.addEventListener('keydown', this.handleKeydown.bind(this)); + this.input.addEventListener('keyup', this.handleKeyup.bind(this)); + this.input.addEventListener('input', this.handleChange.bind(this)); + this.input.addEventListener('focus', this.handleFocus.bind(this)); + this.input.addEventListener('blur', this.handleBlur.bind(this)); + + this.listbox.addEventListener('mouseover', this.handleMouseover.bind(this)); + this.listbox.addEventListener('mouseout', this.handleMouseout.bind(this)); + } + + handleKeydown(event) { + this.ctrlKey = ((event.ctrlKey || event.metaKey) && event.keyCode !== KEYCODE.V); + + switch (event.keyCode) { + case KEYCODE.UP: { + event.preventDefault(); + this.navigateResults(-1); + break; + } + case KEYCODE.DOWN: { + event.preventDefault(); + this.navigateResults(1); + break; + } + case KEYCODE.ENTER: + case KEYCODE.RETURN: { + event.preventDefault(); + break; + } + } + } + + handleKeyup(event) { + switch (event.keyCode) { + case KEYCODE.UP: + case KEYCODE.DOWN: { + event.preventDefault(); + break; + } + case KEYCODE.ENTER: + case KEYCODE.RETURN: { + this.selectResult(); + break; + } + case KEYCODE.LEFT: + case KEYCODE.RIGHT: { + break; + } + } + + this.ctrlKey = false; + } + + handleChange() { + if (!this.blurring) { + this.valueChanged(); + } + } + + handleFocus() { + clearTimeout(this.blurTimeout); + this.combobox.classList.add(classTypeaheadComboboxFocused); + } + + handleBlur() { + clearTimeout(this.blurTimeout); + this.blurring = true; + + this.blurTimeout = setTimeout(() => { + this.combobox.classList.remove(classTypeaheadComboboxFocused); + this.blurring = false; + }, 0); + } + + handleMouseover() { + const focusedItem = this.resultOptions[this.highlightedResultIndex]; + + if (focusedItem) { + focusedItem.classList.remove(classTypeaheadOptionFocused); + } + } + + handleMouseout() { + const focusedItem = this.resultOptions[this.highlightedResultIndex]; + + if (focusedItem) { + focusedItem.classList.add(classTypeaheadOptionFocused); + } + } + + navigateResults(direction) { + let index = 0; + + if (this.highlightedResultIndex !== null) { + index = this.highlightedResultIndex + direction; + } + + if (index < this.numberOfResults) { + if (index < 0) { + index = null; + } + + this.setHighlightedResult(index); + } + } + + valueChanged(force) { + if (!this.settingResult) { + const query = this.input.value; + const sanitisedQuery = sanitiseTypeaheadText(query, this.sanitisedQueryReplaceChars); + + if (sanitisedQuery !== this.sanitisedQuery || (force && !this.resultSelected)) { + this.unsetResults(); + this.setAriaStatus(); + + this.query = query; + this.sanitisedQuery = sanitisedQuery; + + if (this.sanitisedQuery.length >= this.minChars) { + this.emitter.emit(NEW_FIELD_VALUE_EVENT, sanitisedQuery); + } else { + this.clearListbox(); + } + } + } + } + + unsetResults() { + this.results = []; + this.resultOptions = []; + this.resultSelected = false; + + this.emitter.emit(UNSET_FIELD_VALUE_EVENT); + } + + clearListbox(preventAriaStatusUpdate) { + this.listbox.innerHTML = ''; + this.context.classList.remove(classTypeaheadHasResults); + this.input.removeAttribute('aria-activedescendant'); + this.combobox.removeAttribute('aria-expanded'); + + if (!preventAriaStatusUpdate) { + this.setAriaStatus(); + } + } + + updateData(dataMap) { + this.results = dataMap.results; + this.foundResults = dataMap.totalResults; + this.numberOfResults = Math.max(this.results.length, 0); + + this.render(); + } + + render() { + if (!this.deleting || (this.numberOfResults && this.deleting)) { + if (this.numberOfResults.length === 1 && this.results[0].sanitisedText === this.sanitisedQuery) { + this.clearListbox(true); + this.selectResult(0); + } else { + this.listbox.innerHTML = ''; + this.resultOptions = this.results.map((result, index) => { + let ariaLabel = result[this.lang]; + let innerHTML = this.emboldenMatch(ariaLabel, this.query); + + if (Array.isArray(result.sanitisedAlternatives)) { + const alternativeMatch = result.sanitisedAlternatives.find(alternative => alternative !== result.sanitisedText && alternative.includes(this.sanitisedQuery)); + + if (alternativeMatch) { + const alternativeText = result.alternatives[result.sanitisedAlternatives.indexOf(alternativeMatch)]; + innerHTML += ` (${this.emboldenMatch(alternativeText, this.query)})`; + ariaLabel += `, (${alternativeText})`; + } + } + + const listElement = document.createElement('li'); + listElement.className = classTypeaheadOption; + listElement.setAttribute('id', `${this.listboxId}__option--${index}`); + listElement.setAttribute('role', 'option'); + listElement.setAttribute('tabindex', '-1'); + listElement.setAttribute('aria-label', ariaLabel); + listElement.innerHTML = innerHTML; + + listElement.addEventListener('click', () => { + this.selectResult(index); + }); + + return listElement; + }); + + this.resultOptions.forEach(listElement => this.listbox.appendChild(listElement)); + + if (this.numberOfResults < this.foundResults) { + const listElement = document.createElement('li'); + listElement.className = `${classTypeaheadOption} ${classTypeaheadOptionMoreResults} u-fs-b`; + listElement.setAttribute('tabindex', '-1'); + listElement.setAttribute('aria-hidden', 'true'); + listElement.innerHTML = this.content.more_results; + this.listbox.appendChild(listElement); + } + + this.setHighlightedResult(null); + this.combobox.setAttribute('aria-expanded', true); + this.context.classList.add(classTypeaheadHasResults); + } + } + + if (this.numberOfResults === 0 && this.content.no_results) { + this.listbox.innerHTML = `
  • ${this.content.no_results}
  • `; + this.combobox.setAttribute('aria-expanded', true); + this.context.classList.add(classTypeaheadHasResults); + } + } + + setHighlightedResult(index) { + this.highlightedResultIndex = index; + + if (this.setHighlightedResult === null) { + this.input.removeAttribute('aria-activedescendant'); + } else if (this.numberOfResults) { + this.resultOptions.forEach((option, optionIndex) => { + if (optionIndex === index) { + option.classList.add(classTypeaheadOptionFocused); + option.setAttribute('aria-selected', true); + this.input.setAttribute('aria-activedescendant', option.getAttribute('id')); + } else { + option.classList.remove(classTypeaheadOptionFocused); + option.removeAttribute('aria-selected'); + } + }); + + this.setAriaStatus(); + } + } + + setAriaStatus(content) { + if (!content) { + const queryTooShort = this.sanitisedQuery.length < this.minChars; + const noResults = this.numberOfResults === 0; + + if (queryTooShort) { + content = this.content.aria_min_chars; + } else if (noResults) { + content = `${this.content.aria_no_results}: "${this.query}"`; + } else if (this.numberOfResults === 1) { + content = this.content.aria_one_result; + } else { + content = this.content.aria_n_results.replace('{n}', this.numberOfResults); + + if (this.resultLimit && this.foundResults > this.resultLimit) { + content += ` ${this.content.aria_limited_results}`; + } + } + } + + this.ariaStatus.innerHTML = content; + } + + selectResult(index) { + if (this.results.length) { + //this.settingResult = true; + + const result = this.results[index || this.highlightedResultIndex || 0]; + + // TODO: This condition should be removed if we go with the internal address lookup API, or made configurable if we use a third party API + if (result.type !== 'Postcode') { + this.input.value = result[this.lang]; + this.query = result[this.lang]; + } + + this.resultSelected = true; + + this.emitter.emit(NEW_ITEM_SELECTED_EVENT, result); + + /*this.onSelect(result).then(() => { + this.settingResult = false; + // this.input.setAttribute('autocomplete', 'false'); + });*/ + + let ariaAlternativeMessage = ''; + + if (!result.sanitisedText.includes(this.sanitisedQuery) && result.sanitisedAlternatives) { + const alternativeMatch = result.sanitisedAlternatives.find(alternative => alternative.includes(this.sanitisedQuery)); + + if (alternativeMatch) { + ariaAlternativeMessage = `, ${this.content.aria_found_by_alternative_name}: ${alternativeMatch}`; + } + } + + const ariaMessage = `${this.content.aria_you_have_selected}: ${result[this.lang]}${ariaAlternativeMessage}.`; + + this.clearListbox(); + this.setAriaStatus(ariaMessage); + } + } + + emboldenMatch(string, query) { + query = query.toLowerCase().trim(); + + if (string.toLowerCase().includes(query)) { + const queryLength = query.length; + const matchIndex = string.toLowerCase().indexOf(query); + const matchEnd = matchIndex + queryLength; + const before = string.substr(0, matchIndex); + const match = string.substr(matchIndex, queryLength); + const after = string.substr(matchEnd, string.length - matchEnd); + + return `${before}${match}${after}`; + } else { + return string; + } + } +} diff --git a/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.container.js b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.container.js new file mode 100644 index 0000000000..f4b4371243 --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.container.js @@ -0,0 +1,44 @@ +import TypeaheadComponent, { + NEW_FIELD_VALUE_EVENT, + NEW_ITEM_SELECTED_EVENT, + UNSET_FIELD_VALUE_EVENT +} from './typeahead.component'; +import TypeaheadService from './typeahead.service'; +import typeaheadDataMap from './typeahead.service-data-map'; + +export default class TypeaheadContainer { + constructor(context) { + this.typeahead = new TypeaheadComponent({ context }); + + this.service = new TypeaheadService({ + apiUrl: this.typeahead.apiUrl, + lang: document.documentElement.getAttribute('lang').toLowerCase(), + sanitisedQueryReplaceChars: this.typeahead.sanitisedQueryReplaceChars + }); + + this.typeahead.emitter.on(NEW_FIELD_VALUE_EVENT, value => { + + /** + * Call service, partially apply config for promise callbacks + */ + this.service.get(value) + .then(typeaheadDataMap.bind(null, { + query: value, + lang: this.typeahead.lang, + sanitisedQueryReplaceChars: this.typeahead.sanitisedQueryReplaceChars + })) + .then(this.typeahead.updateData.bind(this.typeahead)) + .catch(error => { + if (error.name !== 'AbortError') { + console.log('TypeaheadService error: ', error, 'query: ', value); + } + }); + }); + + this.code = context.querySelector('.js-typeahead-code'); + + this.typeahead.emitter.on(NEW_ITEM_SELECTED_EVENT, value => (this.code.value = value.code)); + + this.typeahead.emitter.on(UNSET_FIELD_VALUE_EVENT, () => (this.code.value = '')); + } +} diff --git a/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.css b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.css new file mode 100644 index 0000000000..64925be718 --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.css @@ -0,0 +1,114 @@ +.typeahead { + position: relative +} + +.typeahead__combobox { + display: inline-block; + border-radius: 3px +} + +.typeahead__combobox--focused { + -webkit-box-shadow: 0 0 0 3px #fe781f; + box-shadow: 0 0 0 3px #fe781f +} + +.typeahead__preview { + position: absolute; + width: 100%; + border-color: transparent; + color: #595959; + pointer-events: none +} + +.typeahead__listbox { + margin: 0; + padding: 0; + width: 100%; + list-style: none; + border: 1px solid #999; + border-top: 0; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px +} + +@media only screen and (min-width: 500px) { + .typeahead__listbox:not(.typeahead__listbox--full-width) { + width: 19.5rem + } +} + +.typeahead__listbox:empty { + display: none +} + +.typeahead__option { + margin: 0; + padding: .5rem; + outline: none; + cursor: pointer +} + +.typeahead__option:not(:last-child) { + border-bottom: 1px solid #999 +} + +.typeahead__option--focused:not(.typeahead__option--no-results), .typeahead__option:not(.typeahead__option--no-results):not(.typeahead__option--more-results):hover { + border-color: #4263c2; + background: #4263c2; + color: #fff +} + +.typeahead__option--more-results, .typeahead__option--no-results { + background: #ccc; + cursor: not-allowed +} + +.typeahead__combobox--focused .typeahead__listbox { + border-color: #999 +} + +.typeahead:not(.typeahead--initialised) .typeahead__instructions, .typeahead:not(.typeahead--initialised) .typeahead__listbox, .typeahead:not(.typeahead--initialised) .typeahead__preview, .typeahead:not(.typeahead--initialised) .typeahead__status { + display: none +} + +.typeahead--initialised .typeahead__input { + background: transparent +} + +.typeahead--initialised .typeahead__input:focus { + -webkit-box-shadow: none; + box-shadow: none +} + +.typeahead--has-results .typeahead__input { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0 +} + +.address-input { + max-width: 580px +} + +.address-input--search .address-input__manual, .address-input__search { + display: none +} + +.address-input--search .address-input__search { + display: block +} + +.address-input__typeahead .typeahead__combobox .typeahead__listbox { + width: 100% +} + +.address-input__typeahead .typeahead__combobox { + display: block +} + +.previous-link { + line-height: 1 +} + +.field__description { + margin-top: 1rem +} diff --git a/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.module.js b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.module.js new file mode 100644 index 0000000000..05075901a5 --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.module.js @@ -0,0 +1,12 @@ +import TypeaheadContainer from './typeahead.container'; + +function TypeaheadModule() { + const typeaheads = [...document.querySelectorAll('.js-typeahead')]; + + typeaheads.forEach(typeahead => new TypeaheadContainer(typeahead)); +} + +/** + * Temporary - just for prototype, should belong in main/boot file + */ +document.addEventListener('TYPEAHEAD-READY', () => new TypeaheadModule()); diff --git a/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.service-data-map.js b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.service-data-map.js new file mode 100644 index 0000000000..8473e98b22 --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.service-data-map.js @@ -0,0 +1,35 @@ +import {sanitiseTypeaheadText} from './typeahead-helpers'; + +export default function typeaheadDataMap(opts, response) { + + /** + * Required parameter validation needed + */ + + return response.json() + .then(data => { + const results = data.results; + + results.forEach(result => { + result.sanitisedText = sanitiseTypeaheadText(result[opts.lang], opts.sanitisedQueryReplaceChars); + + if (opts.lang !== 'en-gb') { + const english = result['en-gb']; + const sanitisedAlternative = sanitiseTypeaheadText(english, this.sanitisedQueryReplaceChars); + + if (sanitisedAlternative.match(opts.query)) { + result.alternatives = [english]; + result.sanitisedAlternatives = [sanitisedAlternative]; + } + } else { + result.alternatives = []; + result.sanitisedAlternatives = []; + } + }); + + return { + results, + totalResults: data.totalResults + }; + }); +} diff --git a/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.service.js b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.service.js new file mode 100644 index 0000000000..9ed7f8937e --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/typeahead-refactored/typeahead.service.js @@ -0,0 +1,42 @@ +import formBodyFromObject from './form-body-from-object'; +import AbortableFetch from './abortable-fetch'; + +export default class TypeaheadService { + requestConfig = { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }; + + constructor({ apiUrl, lang }) { + if (!apiUrl || !lang) { + throw Error( + '[TypeaheadService] \'apiUrl\', \'lang\' parameters are required' + ); + } + + this.apiUrl = apiUrl; + this.lang = lang; + } + + get(sanitisedQuery) { + return new Promise((resolve, reject) => { + const query = { + query: sanitisedQuery, + lang: this.lang + }; + + if (this.fetch && this.fetch.status !== 'DONE') { + this.fetch.abort(); + } + + this.requestConfig.body = formBodyFromObject(query); + this.fetch = new AbortableFetch(this.apiUrl, this.requestConfig); + + this.fetch.send() + .then(resolve) + .catch(reject); + }); + } +} diff --git a/_prototypes/your-household-v13/assets/modules/typeahead/typeahead-core.js b/_prototypes/your-household-v13/assets/modules/typeahead/typeahead-core.js new file mode 100644 index 0000000000..8e9dd15a8a --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/typeahead/typeahead-core.js @@ -0,0 +1,459 @@ +import AbortableFetch from '../abortable-fetch'; +import formBodyFromObject from '../form-body-from-object'; +import {sanitiseTypeaheadText} from './typeahead-helpers'; + +const classTypeaheadCombobox = 'js-typeahead-combobox'; +const classTypeaheadLabel = 'js-typeahead-label'; +const classTypeaheadInput = 'js-typeahead-input'; +const classTypeaheadInstructions = 'js-typeahead-instructions'; +const classTypeaheadListbox = 'js-typeahead-listbox'; +const classTypeaheadAriaStatus = 'js-typeahead-aria-status'; + +const classTypeaheadOption = 'typeahead__option'; +const classTypeaheadOptionFocused = `${classTypeaheadOption}--focused`; +const classTypeaheadOptionNoResults = `${classTypeaheadOption}--no-results`; +const classTypeaheadOptionMoreResults = `${classTypeaheadOption}--more-results`; +const classTypeaheadComboboxFocused = 'typeahead__combobox--focused'; +const classTypeaheadHasResults = 'typeahead--has-results'; + +const KEYCODE = { + BACK_SPACE: 8, + RETURN: 13, + ENTER: 14, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + DELETE: 46, + V: 86 +}; + +export default class TypeaheadCore { + constructor({context, apiUrl, onSelect, onUnsetResult, onError, minChars, resultLimit, sanitisedQueryReplaceChars = [], suggestionFunction}) { + // DOM Elements + this.context = context; + this.combobox = context.querySelector(`.${classTypeaheadCombobox}`); + this.label = context.querySelector(`.${classTypeaheadLabel}`); + this.input = context.querySelector(`.${classTypeaheadInput}`); + this.listbox = context.querySelector(`.${classTypeaheadListbox}`); + this.instructions = context.querySelector(`.${classTypeaheadInstructions}`); + this.ariaStatus = context.querySelector(`.${classTypeaheadAriaStatus}`); + + // Suggestion URL + this.apiUrl = apiUrl || context.getAttribute('data-api-url'); + + // Callbacks + this.onSelect = onSelect; + this.onUnsetResult = onUnsetResult; + this.onError = onError; + + // Settings + this.content = JSON.parse(context.getAttribute('data-content')); + this.listboxId = this.listbox.getAttribute('id'); + this.minChars = minChars || 2; + this.resultLimit = resultLimit || null; + + if (suggestionFunction) { + this.fetchSuggestions = suggestionFunction; + } + + // State + this.ctrlKey = false; + this.deleting = false; + this.query = ''; + this.sanitisedQuery = ''; + this.previousQuery = ''; + this.results = []; + this.resultOptions = []; + this.foundResults = 0; + this.numberOfResults = 0; + this.highlightedResultIndex = 0; + this.settingResult = false; + this.resultSelected = false; + this.blurring = false; + this.blurTimeout = null; + this.sanitisedQueryReplaceChars = sanitisedQueryReplaceChars; + this.lang = document.documentElement.getAttribute('lang').toLowerCase(); + + // Modify DOM + this.label.setAttribute('for', this.input.getAttribute('id')); + this.input.setAttribute('aria-autocomplete', 'list'); + this.input.setAttribute('aria-controls', this.listbox.getAttribute('id')); + this.input.setAttribute('aria-describedby', this.instructions.getAttribute('id')); + this.input.setAttribute('autocomplete', this.input.getAttribute('data-autocomplete')); + this.context.classList.add('typeahead--initialised'); + + // Bind event listeners + this.bindEventListeners(); + } + + bindEventListeners() { + this.input.addEventListener('keydown', this.handleKeydown.bind(this)); + this.input.addEventListener('keyup', this.handleKeyup.bind(this)); + this.input.addEventListener('input', this.handleChange.bind(this)); + this.input.addEventListener('focus', this.handleFocus.bind(this)); + this.input.addEventListener('blur', this.handleBlur.bind(this)); + + this.listbox.addEventListener('mouseover', this.handleMouseover.bind(this)); + this.listbox.addEventListener('mouseout', this.handleMouseout.bind(this)); + } + + handleKeydown(event) { + this.ctrlKey = ((event.ctrlKey || event.metaKey) && event.keyCode !== KEYCODE.V); + + switch (event.keyCode) { + case KEYCODE.UP: { + event.preventDefault(); + this.navigateResults(-1); + break; + } + case KEYCODE.DOWN: { + event.preventDefault(); + this.navigateResults(1); + break; + } + case KEYCODE.ENTER: + case KEYCODE.RETURN: { + event.preventDefault(); + break; + } + } + } + + handleKeyup(event) { + switch (event.keyCode) { + case KEYCODE.UP: + case KEYCODE.DOWN: { + event.preventDefault(); + break; + } + case KEYCODE.ENTER: + case KEYCODE.RETURN: { + this.selectResult(); + break; + } + case KEYCODE.LEFT: + case KEYCODE.RIGHT: { + break; + } + } + + this.ctrlKey = false; + } + + handleChange() { + if (!this.blurring) { + this.getSuggestions(); + } + } + + handleFocus() { + clearTimeout(this.blurTimeout); + this.combobox.classList.add(classTypeaheadComboboxFocused); + this.getSuggestions(true); + } + + handleBlur() { + clearTimeout(this.blurTimeout); + this.blurring = true; + + this.blurTimeout = setTimeout(() => { + this.combobox.classList.remove(classTypeaheadComboboxFocused); + this.blurring = false; + }, 300); + } + + handleMouseover() { + const focusedItem = this.resultOptions[this.highlightedResultIndex]; + + if (focusedItem) { + focusedItem.classList.remove(classTypeaheadOptionFocused); + } + } + + handleMouseout() { + const focusedItem = this.resultOptions[this.highlightedResultIndex]; + + if (focusedItem) { + focusedItem.classList.add(classTypeaheadOptionFocused); + } + } + + navigateResults(direction) { + let index = 0; + + if (this.highlightedResultIndex !== null) { + index = this.highlightedResultIndex + direction; + } + + if (index < this.numberOfResults) { + if (index < 0) { + index = null; + } + + this.setHighlightedResult(index); + } + } + + getSuggestions(force) { + if (!this.settingResult) { + const query = this.input.value; + const sanitisedQuery = sanitiseTypeaheadText(query, this.sanitisedQueryReplaceChars); + + if (sanitisedQuery !== this.sanitisedQuery || (force && !this.resultSelected)) { + this.unsetResults(); + this.setAriaStatus(); + + this.query = query; + this.sanitisedQuery = sanitisedQuery; + + if (this.sanitisedQuery.length >= this.minChars) { + this.fetchSuggestions(this.sanitisedQuery) + .then(this.handleResults.bind(this)) + .catch(error => { + if (error.name !== 'AbortError' && this.onError) { + this.onError(error); + } + }); + } else { + this.clearListbox(); + } + } + } + } + + fetchSuggestions(sanitisedQuery) { + return new Promise((resolve, reject) => { + const query = { + query: sanitisedQuery, + lang: this.lang + }; + + if (this.fetch && this.fetch.status !== 'DONE') { + this.fetch.abort(); + } + + const body = formBodyFromObject(query); + + this.fetch = new AbortableFetch(this.apiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body + }); + + this.fetch.send() + .then(async response => { + const data = await response.json(); + + const results = data.results; + + results.forEach(result => { + result.sanitisedText = sanitiseTypeaheadText(result[this.lang], this.sanitisedQueryReplaceChars); + + if (this.lang !== 'en-gb') { + const english = result['en-gb']; + const sanitisedAlternative = sanitiseTypeaheadText(english, this.sanitisedQueryReplaceChars); + + if (sanitisedAlternative.match(sanitisedQuery)) { + result.alternatives = [english]; + result.sanitisedAlternatives = [sanitisedAlternative]; + } + } else { + result.alternatives = []; + result.sanitisedAlternatives = []; + } + }); + + resolve({ + results, + totalResults: data.totalResults + }); + }) + .catch(reject); + }); + } + + unsetResults() { + this.results = []; + this.resultOptions = []; + this.resultSelected = false; + + if (this.onUnsetResult) { + this.onUnsetResult(); + } + } + + clearListbox(preventAriaStatusUpdate) { + this.listbox.innerHTML = ''; + this.context.classList.remove(classTypeaheadHasResults); + this.input.removeAttribute('aria-activedescendant'); + this.combobox.removeAttribute('aria-expanded'); + + if (!preventAriaStatusUpdate) { + this.setAriaStatus(); + } + } + + handleResults(result) { + this.foundResults = result.totalResults; + this.results = result.results; + this.numberOfResults = Math.max(this.results.length, 0); + + if (!this.deleting || (this.numberOfResults && this.deleting)) { + if (this.numberOfResults.length === 1 && this.results[0].sanitisedText === this.sanitisedQuery) { + this.clearListbox(true); + this.selectResult(0); + } else { + this.listbox.innerHTML = ''; + this.resultOptions = this.results.map((result, index) => { + let ariaLabel = result[this.lang]; + let innerHTML = this.emboldenMatch(ariaLabel, this.query); + + if (Array.isArray(result.sanitisedAlternatives)) { + const alternativeMatch = result.sanitisedAlternatives.find(alternative => alternative !== result.sanitisedText && alternative.includes(this.sanitisedQuery)); + + if (alternativeMatch) { + const alternativeText = result.alternatives[result.sanitisedAlternatives.indexOf(alternativeMatch)]; + innerHTML += ` (${this.emboldenMatch(alternativeText, this.query)})`; + ariaLabel += `, (${alternativeText})`; + } + } + + const listElement = document.createElement('li'); + listElement.className = classTypeaheadOption; + listElement.setAttribute('id', `${this.listboxId}__option--${index}`); + listElement.setAttribute('role', 'option'); + listElement.setAttribute('tabindex', '-1'); + listElement.setAttribute('aria-label', ariaLabel); + listElement.innerHTML = innerHTML; + + listElement.addEventListener('click', () => { + this.selectResult(index); + }); + + this.listbox.appendChild(listElement); + + return listElement; + }); + + if (this.numberOfResults < this.foundResults) { + const listElement = document.createElement('li'); + listElement.className = `${classTypeaheadOption} ${classTypeaheadOptionMoreResults} u-fs-b`; + listElement.setAttribute('tabindex', '-1'); + listElement.setAttribute('aria-hidden', 'true'); + listElement.innerHTML = this.content.more_results; + this.listbox.appendChild(listElement); + } + + this.setHighlightedResult(null); + this.combobox.setAttribute('aria-expanded', true); + this.context.classList.add(classTypeaheadHasResults); + } + } + + if (this.numberOfResults === 0 && this.content.no_results) { + this.listbox.innerHTML = `
  • ${this.content.no_results}
  • `; + this.combobox.setAttribute('aria-expanded', true); + this.context.classList.add(classTypeaheadHasResults); + } + } + + setHighlightedResult(index) { + this.highlightedResultIndex = index; + + if (this.setHighlightedResult === null) { + this.input.removeAttribute('aria-activedescendant'); + } else if (this.numberOfResults) { + this.resultOptions.forEach((option, optionIndex) => { + if (optionIndex === index) { + option.classList.add(classTypeaheadOptionFocused); + option.setAttribute('aria-selected', true); + this.input.setAttribute('aria-activedescendant', option.getAttribute('id')); + } else { + option.classList.remove(classTypeaheadOptionFocused); + option.removeAttribute('aria-selected'); + } + }); + + this.setAriaStatus(); + } + } + + setAriaStatus(content) { + if (!content) { + const queryTooShort = this.sanitisedQuery.length < this.minChars; + const noResults = this.numberOfResults === 0; + + if (queryTooShort) { + content = this.content.aria_min_chars; + } else if (noResults) { + content = `${this.content.aria_no_results}: "${this.query}"`; + } else if (this.numberOfResults === 1) { + content = this.content.aria_one_result; + } else { + content = this.content.aria_n_results.replace('{n}', this.numberOfResults); + + if (this.resultLimit && this.foundResults > this.resultLimit) { + content += ` ${this.content.aria_limited_results}`; + } + } + } + + this.ariaStatus.innerHTML = content; + } + + selectResult(index) { + if (this.results.length) { + this.settingResult = true; + + const result = this.results[index || this.highlightedResultIndex || 0]; + + // TODO: This condition should be removed if we go with the internal address lookup API, or made configurable if we use a third party API + if (result.type !== 'Postcode') { + this.input.value = result[this.lang]; + this.query = result[this.lang]; + } + + this.resultSelected = true; + + this.onSelect(result).then(() => { + this.settingResult = false; + // this.input.setAttribute('autocomplete', 'false'); + }); + + let ariaAlternativeMessage = ''; + + if (!result.sanitisedText.includes(this.sanitisedQuery) && result.sanitisedAlternatives) { + const alternativeMatch = result.sanitisedAlternatives.find(alternative => alternative.includes(this.sanitisedQuery)); + + if (alternativeMatch) { + ariaAlternativeMessage = `, ${this.content.aria_found_by_alternative_name}: ${alternativeMatch}`; + } + } + + const ariaMessage = `${this.content.aria_you_have_selected}: ${result[this.lang]}${ariaAlternativeMessage}.`; + + this.clearListbox(); + this.setAriaStatus(ariaMessage); + } + } + + emboldenMatch(string, query) { + query = query.toLowerCase().trim(); + + if (string.toLowerCase().includes(query)) { + const queryLength = query.length; + const matchIndex = string.toLowerCase().indexOf(query); + const matchEnd = matchIndex + queryLength; + const before = string.substr(0, matchIndex); + const match = string.substr(matchIndex, queryLength); + const after = string.substr(matchEnd, string.length - matchEnd); + + return `${before}${match}${after}`; + } else { + return string; + } + } +} diff --git a/_prototypes/your-household-v13/assets/modules/typeahead/typeahead-helpers.js b/_prototypes/your-household-v13/assets/modules/typeahead/typeahead-helpers.js new file mode 100644 index 0000000000..800c4e5155 --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/typeahead/typeahead-helpers.js @@ -0,0 +1,11 @@ +export function sanitiseTypeaheadText(string, sanitisedQueryReplaceChars = [], trimEnd = true) { + let sanitisedString = string.toLowerCase().replace(/\s\s+/g, ' '); + + sanitisedString = trimEnd ? sanitisedString.trim() : sanitisedString.trimStart(); + + sanitisedQueryReplaceChars.forEach(char => { + sanitisedString = sanitisedString.replace(new RegExp(char, 'g'), ''); + }); + + return sanitisedString; +} diff --git a/_prototypes/your-household-v13/assets/modules/typeahead/typeahead.css b/_prototypes/your-household-v13/assets/modules/typeahead/typeahead.css new file mode 100644 index 0000000000..64925be718 --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/typeahead/typeahead.css @@ -0,0 +1,114 @@ +.typeahead { + position: relative +} + +.typeahead__combobox { + display: inline-block; + border-radius: 3px +} + +.typeahead__combobox--focused { + -webkit-box-shadow: 0 0 0 3px #fe781f; + box-shadow: 0 0 0 3px #fe781f +} + +.typeahead__preview { + position: absolute; + width: 100%; + border-color: transparent; + color: #595959; + pointer-events: none +} + +.typeahead__listbox { + margin: 0; + padding: 0; + width: 100%; + list-style: none; + border: 1px solid #999; + border-top: 0; + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px +} + +@media only screen and (min-width: 500px) { + .typeahead__listbox:not(.typeahead__listbox--full-width) { + width: 19.5rem + } +} + +.typeahead__listbox:empty { + display: none +} + +.typeahead__option { + margin: 0; + padding: .5rem; + outline: none; + cursor: pointer +} + +.typeahead__option:not(:last-child) { + border-bottom: 1px solid #999 +} + +.typeahead__option--focused:not(.typeahead__option--no-results), .typeahead__option:not(.typeahead__option--no-results):not(.typeahead__option--more-results):hover { + border-color: #4263c2; + background: #4263c2; + color: #fff +} + +.typeahead__option--more-results, .typeahead__option--no-results { + background: #ccc; + cursor: not-allowed +} + +.typeahead__combobox--focused .typeahead__listbox { + border-color: #999 +} + +.typeahead:not(.typeahead--initialised) .typeahead__instructions, .typeahead:not(.typeahead--initialised) .typeahead__listbox, .typeahead:not(.typeahead--initialised) .typeahead__preview, .typeahead:not(.typeahead--initialised) .typeahead__status { + display: none +} + +.typeahead--initialised .typeahead__input { + background: transparent +} + +.typeahead--initialised .typeahead__input:focus { + -webkit-box-shadow: none; + box-shadow: none +} + +.typeahead--has-results .typeahead__input { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0 +} + +.address-input { + max-width: 580px +} + +.address-input--search .address-input__manual, .address-input__search { + display: none +} + +.address-input--search .address-input__search { + display: block +} + +.address-input__typeahead .typeahead__combobox .typeahead__listbox { + width: 100% +} + +.address-input__typeahead .typeahead__combobox { + display: block +} + +.previous-link { + line-height: 1 +} + +.field__description { + margin-top: 1rem +} diff --git a/_prototypes/your-household-v13/assets/modules/typeahead/typeahead.js b/_prototypes/your-household-v13/assets/modules/typeahead/typeahead.js new file mode 100644 index 0000000000..96a9c1f85d --- /dev/null +++ b/_prototypes/your-household-v13/assets/modules/typeahead/typeahead.js @@ -0,0 +1,34 @@ +import TypeaheadCore from './typeahead-core'; + +class Typeahead { + constructor(context) { + this.context = context; + this.typeahead = new TypeaheadCore({ + context, + onSelect: this.onSelect.bind(this), + onUnsetResult: this.onUnsetResult.bind(this), + }); + + this.code = context.querySelector('.js-typeahead-code'); + } + + onSelect(result) { + return new Promise(resolve => { + this.code.value = result.code; + + resolve(); + }); + } + + onUnsetResult() { + this.code.value = ''; + } +} + +function typeaheads() { + const typeaheads = [...document.querySelectorAll('.js-typeahead')]; + + typeaheads.forEach(typeahead => new Typeahead(typeahead)); +} + +document.addEventListener('TYPEAHEAD-READY', typeaheads); diff --git a/_prototypes/your-household-v13/assets/numbers-to-words.js b/_prototypes/your-household-v13/assets/numbers-to-words.js new file mode 100644 index 0000000000..5016db1b60 --- /dev/null +++ b/_prototypes/your-household-v13/assets/numbers-to-words.js @@ -0,0 +1,154 @@ +/** + * Copied from: + * https://codereview.stackexchange.com/questions/90349/changing-number-to-words-in-javascript + * =============== + */ +var ONE_TO_NINETEEN = [ + 'one', 'two', 'three', 'four', 'five', + 'six', 'seven', 'eight', 'nine', 'ten', + 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', + 'sixteen', 'seventeen', 'eighteen', 'nineteen' +]; + +var TENS = [ + 'ten', 'twenty', 'thirty', 'forty', 'fifty', + 'sixty', 'seventy', 'eighty', 'ninety' +]; + +var SCALES = ['thousand', 'million', 'billion', 'trillion']; + +// helper function for use with Array.filter +function isTruthy(item) { + return !!item; +} + +// convert a number into 'chunks' of 0-999 +function chunk(number) { + var thousands = []; + + while(number > 0) { + thousands.push(number % 1000); + number = Math.floor(number / 1000); + } + + return thousands; +} + +// translate a number from 1-999 into English +function inEnglish(number) { + var thousands, hundreds, tens, ones, words = []; + + if(number < 20) { + return ONE_TO_NINETEEN[number - 1]; // may be undefined + } + + if(number < 100) { + ones = number % 10; + tens = number / 10 | 0; // equivalent to Math.floor(number / 10) + + words.push(TENS[tens - 1]); + words.push(inEnglish(ones)); + + return words.filter(isTruthy).join('-'); + } + + hundreds = number / 100 | 0; + words.push(inEnglish(hundreds)); + words.push('hundred'); + words.push(inEnglish(number % 100)); + + return words.filter(isTruthy).join(' '); +} + +// append the word for a scale. Made for use with Array.map +function appendScale(chunk, exp) { + var scale; + if (!chunk) { + return null; + } + scale = SCALES[exp - 1]; + return [chunk, scale].filter(isTruthy).join(' '); +} + +/** + * =============== + * End copy + */ + +/** + * Modification - decorator + */ +var NUMBER_TO_POSITION_TEXT_MAP = { + 'one': 'first', + 'two': 'second', + 'three': 'third', + 'four': 'fourth', + 'five': 'fifth', + 'six': 'sixth', + 'seven': 'seventh', + 'eight': 'eighth', + 'nine': 'ninth', + 'ten': 'tenth', + 'eleven': 'eleventh', + 'twelve': 'twelfth', + 'thirteen': 'thirteenth', + 'fourteen': 'fourteenth', + 'fifteen': 'fifteenth', + 'sixteen': 'sixteenth', + 'seventeen': 'seventeenth', + 'eighteen': 'eighteenth', + 'nineteen': 'nineteenth', + + 'twenty': 'twentieth', + 'thirty': 'thirtieth', + 'forty': 'fortieth', + 'fifty': 'fiftieth', + 'sixty': 'sixtieth', + 'seventy': 'seventieth', + 'eighty': 'eightieth', + 'ninety': 'ninetieth', + 'hundred': 'hundredth', + + 'thousand': 'thousandth', + 'million': 'millionth', + 'billion': 'billionth', + 'trillion': 'trillionth' +}; + +export function numberToPositionWord(num) { + const str = chunk(num) + .map(inEnglish) + .map(appendScale) + .filter(isTruthy) + .reverse() + .join(' '); + + const sub = str.split(' '), + lastWordDashSplitArr = sub[sub.length - 1].split('-'), + lastWord = lastWordDashSplitArr[lastWordDashSplitArr.length - 1], + + newLastWord = (lastWordDashSplitArr.length > 1? lastWordDashSplitArr[0] + '-' : '') + + NUMBER_TO_POSITION_TEXT_MAP[lastWord]; + + /*console.log('str:', str); + console.log('sub:', sub); + console.log('lastWordDashSplitArr:', lastWordDashSplitArr); + console.log('lastWord:', lastWord); + console.log('newLastWord:', newLastWord);*/ + + const subCopy = [].concat(sub); + subCopy.pop(); + const prefix = subCopy.join(' '); + const result = (prefix ? prefix + ' ' : '') + newLastWord; + + // console.log('result', (prefix ? prefix + ' ' : '') + newLastWord); + return result; +} + +export function numberToWordsStyleguide(number) { + if (number > 9) { + return number; + } + + return ONE_TO_NINETEEN[number - 1]; +} \ No newline at end of file diff --git a/_prototypes/your-household-v13/assets/personal-details.js b/_prototypes/your-household-v13/assets/personal-details.js new file mode 100644 index 0000000000..e3d7de135d --- /dev/null +++ b/_prototypes/your-household-v13/assets/personal-details.js @@ -0,0 +1,673 @@ +export const PERSONAL_DETAILS_KEY = 'individual-details'; +export const PERSONAL_PINS_KEY = 'individual-pins'; + +export const personalDetailsMaritalStatusMap = { + 'never': { + description: 'Never married and never registered a same-sex civil' + + ' partnership' + }, + 'married': { + description: 'Married' + }, + 'registered': { + description: 'In a registered same-sex civil partnership' + }, + 'separated-married': { + description: 'Separated, but still legally married' + }, + 'divorced': { + description: 'Divorced' + }, + 'former-partnership': { + description: 'Formerly in a same-sex civil partnership which is now' + + ' legally dissolved' + }, + 'widowed': { + description: 'Widowed' + }, + 'surviving-partner': { + description: 'Surviving partner from a same-sex civil partnership' + }, + 'separated-partnership': { + description: 'Separated, but still legally in a same-sex civil partnership' + } +}; + +export const personalDetailsCountryMap = { + 'england': { + description: 'England' + }, + 'wales': { + description: 'Wales' + }, + 'scotland': { + description: 'Scotland' + }, + 'northern-ireland': { + description: 'Northern Ireland' + }, + 'republic-ireland': { + description: 'Republic of Ireland' + } +}; + +export const personalDetailsOrientationMap = { + 'straight': { + description: 'Straight or Heterosexual' + }, + 'gay': { + description: 'Gay or Lesbian' + }, + 'bisexual': { + description: 'Bisexual' + }, + 'other': { + description: 'Other' + }, + 'no-say': { + description: 'Prefer not to say' + } +}; + +export const personalDetailsGenderMap = { + 'male': { + description: 'Male' + }, + 'female': { + description: 'Female' + } +}; + +export const personalDetailsNationalIdentityMap = { + 'english': { + description: 'English' + }, + 'welsh': { + description: 'Welsh' + }, + 'scottish': { + description: 'Scottish' + }, + 'northern-irish': { + description: 'Northern Irish' + }, + 'british': { + description: 'British' + } +}; + +export const personalDetailsPassportCountriesMap = { + 'united-kingdom': { + description: 'United Kingdom' + }, + 'ireland': { + description: 'Ireland' + }, + 'none': { + description: 'None' + } +}; + +export const personalDetailsEthnicGroupMap = { + 'white': { + 'question': 'White', + 'options': [ + { + val: 'british', + label: 'English, Welsh, Scottish, Northern Irish or British' + }, + { + val: 'irish', + label: 'Irish' + }, + { + val: 'gypsy', + label: 'Gypsy or Irish Traveler' + }, + { + val: 'roma', + label: 'Roma' + }, + { + val: 'other', + label: 'Other White ethnic group or background' + } + ] + }, + 'mixed': { + 'question': 'Mixed or Multiple', + 'options': [ + { + val: 'white-black-caribbean', + label: 'White and Black Caribbean' + }, + { + val: 'white-black-african', + label: 'White and Black African' + }, + { + val: 'white-asian', + label: 'White and Asian' + }, + { + val: 'other', + label: 'Any other Mixed or Multiple background' + } + ] + }, + 'asian': { + 'question': 'Asian or Asian British', + 'options': [ + { + val: 'indian', + label: 'Indian' + }, + { + val: 'pakistani', + label: 'Pakistani' + }, + { + val: 'bangladeshi', + label: 'Bangladeshi' + }, + { + val: 'chinese', + label: 'Chinese' + }, + { + val: 'other', + label: 'Other Asian ethnic group or background' + } + ] + }, + 'black': { + 'question': 'Black, African, Caribbean or Black British', + 'options': [ + { + val: 'african', + label: 'African' + }, + { + val: 'caribbean', + label: 'Caribbean' + }, + { + val: 'other', + label: 'Any other Black, African or Caribbean background' + } + ] + }, + 'other': { + 'question': 'Other', + 'options': [ + { + val: 'arab', + label: 'Arab' + }, + { + val: 'other', + label: 'Any other ethnic group' + } + ] + } +}; + +export const personalDetailsApprenticeshipMap = { + 'yes': { + description: 'Yes' + }, + 'no': { + description: 'No' + } +}; + +export const personalDetailsDegreeAboveMap = { + 'yes': { + description: 'Yes' + }, + 'no': { + description: 'No' + } +}; + +export const personalDetailsNVQMap = { + 'nvq-level-1': { + description: 'NVQ level 1 or equivalent' + }, + 'nvq-level-2': { + description: 'NVQ level 2 or equivalent' + }, + 'nvq-level-3': { + description: 'NVQ level 3 or equivalent' + }, + 'none': { + description: 'None' + } +}; + +export const personalDetailsALevelMap = { + 'a-level-2': { + description: '2 or more A levels' + }, + 'a-level-1-btec': { + description: '1 A level' + }, + 'a-level-1': { + description: '1 AS level' + }, + 'baccalaureate': { + description: 'Advanced Welsh Baccalaureate' + }, + 'none': { + description: 'None' + } +}; + +export const personalDetailsGCSEMap = { + 'gcse-5': { + description: '5 or more GCSEs grades A* to C or 9 to 4' + }, + 'other-gcses': { + description: 'Any other GCSEs' + }, + 'basic-skills': { + description: 'Basic skills course' + }, + 'national-baccalaureate': { + description: 'National Welsh Baccalaureate' + }, + 'foundation-baccalaureate': { + description: 'Foundation Welsh Baccalaureate' + }, + 'none': { + description: 'None of these apply' + } +}; + +export const personalDetailsOtherWhere = { + 'in-england-wales': { + description: 'Yes, in England or Wales' + }, + 'outside-england-wales': { + description: 'Yes, anywhere outside of England and Wales' + }, + 'none': { + description: 'No qualifications' + } +}; + +export const personalDetailsEmploymentStatus = { + 'employee': { + description: 'Employee' + }, + 'freelance-without-employees': { + description: 'Self-employed or freelance without employees' + }, + 'freelance-with-employees': { + description: 'Self-employed with employees' + }, + 'not-employed': { + description: 'Not employed' + } +}; + +function changeDetailsFor(personId, mutation) { + let details = getPersonalDetailsFor(personId); + + updatePersonalDetails(personId, { + ...details, + ...mutation(details || {}) + }); + + return details; +} + +export function addUpdatePersonalDetailsDOB(personId, day, month, year) { + return changeDetailsFor(personId, () => + ({ + 'dob': { + day, + month, + year + } + })); +} + +export function addUpdateMaritalStatus(personId, val) { + return changeDetailsFor(personId, () => + ({ + 'maritalStatus': val + })); +} + +export function addUpdateCountry(personId, val) { + return changeDetailsFor(personId, () => + ({ + 'country': { + val + } + })); +} + +export function addUpdateCountryOther(personId, otherText) { + return changeDetailsFor(personId, details => + ({ + 'country': { + ...(details['country'] || {}), + otherText + } + })); +} + +export function addUpdateNationalIdentity(personId, collection, otherText) { + return changeDetailsFor(personId, () => + ({ + 'national-identity': { + collection, + ...(collection.find(val => val === 'other') ? { otherText } : {}) + } + })); +} + +export function addUpdateNationalIdentityOther(personId, otherText) { + return changeDetailsFor(personId, details => + ({ + 'national-identity': { + ...(details['national-identity'] || {}), + ...{ otherText } + } + })); +} + +export function addUpdateEthnicGroup(personId, val) { + return changeDetailsFor(personId, () => + ({ + 'ethnic-group': { + val + } + })); +} + +export function addUpdateEthnicGroupDescription(personId, description) { + return changeDetailsFor(personId, details => + ({ + 'ethnic-group': { + ...(details['ethnic-group'] || {}), + ...{ description } + } + })); +} + +export function addUpdateEthnicGroupOther(personId, otherText) { + return changeDetailsFor(personId, details => + ({ + 'ethnic-group': { + ...(details['ethnic-group'] || {}), + ...{ otherText } + } + })); +} + +export function addUpdatePassportCountry(personId, countries) { + return changeDetailsFor(personId, details => + ({ + 'passport': { + ...(details['passport'] || {}), + countries + } + })); +} + +export function addUpdatePassportCountryOther(personId, otherText) { + return changeDetailsFor(personId, details => + ({ + 'passport': { + ...(details['passport'] || {}), + otherText + } + })); +} + +export function addUpdateOrientation(personId, val) { + return changeDetailsFor(personId, () => + ({ + 'orientation': val + })); +} + +export function addUpdateSalary(personId, val) { + return changeDetailsFor(personId, () => + ({ + 'salary': val + })); +} + +export function addUpdateSex(personId, val) { + return changeDetailsFor(personId, () => + ({ + 'sex': val + })); +} + +export function addUpdateAddressWhere(personId, val) { + return changeDetailsFor(personId, () => + ({ + 'address-where': val + })); +} + +export function addUpdateAge(personId, val, { isApproximate = false }) { + return changeDetailsFor(personId, () => + ({ + 'age': { + val, + isApproximate + } + })); +} + +export function addUpdateAgeConfirm(personId, val) { + return changeDetailsFor(personId, () => + ({ + 'age-confirm': { + val + } + })); +} + +export function addUpdateAddressOutsideUK(personId, val) { + return changeDetailsFor(personId, () => + ({ + 'address-outside-uk': val + })); +} + +export function addUpdateAddressIndividual(personId, val) { + return changeDetailsFor(personId, () => + ({ + 'address': val + })); +} + +export function addUpdateApprenticeship(personId, hasApprenticeship) { + return changeDetailsFor(personId, () => + ({ + 'apprenticeship': { + hasApprenticeship + } + })); +} + +export function addUpdateHasQualificationAbove(personId, aboveDegree) { + return changeDetailsFor(personId, details => + ({ + 'qualifications': { + ...(details['qualifications'] || {}), + aboveDegree + } + })); +} + +export function addUpdateQualificationsNvqEquivalent(personId, nvqEquivalent) { + return changeDetailsFor(personId, details => + ({ + 'qualifications': { + ...(details['qualifications'] || {}), + nvqEquivalent + } + })); +} + +export function addUpdateQualificationsALevel(personId, aLevels) { + return changeDetailsFor(personId, details => + ({ + 'qualifications': { + ...(details['qualifications'] || {}), + aLevels + } + })); +} + +export function addUpdateQualificationsGCSEs(personId, gcses) { + return changeDetailsFor(personId, details => + ({ + 'qualifications': { + ...(details['qualifications'] || {}), + gcses + } + })); +} + +export function addUpdateQualificationsOtherWhere(personId, othersWhere) { + return changeDetailsFor(personId, details => + ({ + 'qualifications': { + ...(details['qualifications'] || {}), + othersWhere + } + })); +} + +export function addUpdateEmploymentStatus(personId, val) { + return changeDetailsFor(personId, () => + ({ + 'employment-status': { + val + } + })); +} + +export function addUpdateJobTitle(personId, val) { + return changeDetailsFor(personId, () => + ({ + 'job-title': { + val + } + })); +} + +export function addUpdateJobDescribe(personId, val) { + return changeDetailsFor(personId, () => + ({ + 'job-describe': { + val + } + })); +} + +export function getPins() { + return JSON.parse(sessionStorage.getItem(PERSONAL_PINS_KEY)) || {}; +} + +export function createPinFor(personId, opts = {}) { + let pins = getPins(); + + pins[personId] = { + pin: _.random(10000, 99999), + exported: !!opts.exported + }; + + sessionStorage.setItem(PERSONAL_PINS_KEY, JSON.stringify(pins)); + + return pins[personId]; +} + +export function getPinFor(personId) { + return getPins()[personId]; +} + +export function unsetPinFor(personId) { + let pins = getPins(); + + delete pins[personId]; + + sessionStorage.setItem(PERSONAL_PINS_KEY, JSON.stringify(pins)); +} + +export function updatePersonalDetails(personId, details) { + sessionStorage.setItem(PERSONAL_DETAILS_KEY, JSON.stringify({ + ...getAllPersonalDetails(), + [personId]: details + })); + + return details; +} + +export function getAllPersonalDetails() { + return JSON.parse(sessionStorage.getItem(PERSONAL_DETAILS_KEY)) || {}; +} + +export function getPersonalDetailsFor(personId) { + const storageObj = getAllPersonalDetails(), + personObj = storageObj[personId]; + + if (!personObj) { + console.log('Personal details for ' + personId + ' not found'); + } + + return personObj; +} + +export function removePersonalDetailsFor(personId) { + const storageObj = getAllPersonalDetails(); + + delete storageObj[personId]; + + sessionStorage.setItem(PERSONAL_DETAILS_KEY, JSON.stringify(storageObj)); +} + +export function personalBookmark(personId, page) { + return changeDetailsFor(personId, () => + ({ + '_bookmark': { + page + } + })); +} + +export function getBookmarkFor(personId) { + return getPersonalDetailsFor(personId)['_bookmark'].page; +} + +export function personalQuestionSubmitDecorator(personId, callback, e) { + const urlParams = new URLSearchParams(window.location.search), + isEditing = urlParams.get('edit'); + + !isEditing + ? personalBookmark(personId, window.location.href) + : clearPersonalBookmark(personId); + + callback(e); +} + +export function clearPersonalBookmark(personId) { + let details = getPersonalDetailsFor(personId); + + delete details._bookmark; + + updatePersonalDetails(personId, { + ...details + }); + + return details; +} diff --git a/_prototypes/your-household-v13/assets/prototype-tools.js b/_prototypes/your-household-v13/assets/prototype-tools.js new file mode 100644 index 0000000000..e27e48071b --- /dev/null +++ b/_prototypes/your-household-v13/assets/prototype-tools.js @@ -0,0 +1,300 @@ +export function tools () { + + const $listLinks = $('.test-data-links'), + + $clearData = + $('
  • ' + + 'Clear all data
  • '), + + $createFamilyHousehold = + $('
  • ' + + 'Create family household
  • '), + + $createFamilyRelationships = + $('
  • ' + + 'Create family with relationships
  • '), + + $createFamilyWithRelationshipsAndVisitors = + $('
  • ' + + 'Create family with relationships and visitors
  • '), + + $createFamilyWithRelationshipsPersonalDetailsAndVisitors = + $('
  • ' + + 'Create family with relationships, just family individual responses and' + + ' visitors
  • '), + + $createFamilyWithRelationshipsPersonalDetailsAndVisitorsPersonalDetails = + $('
  • ' + + 'Create family with relationships, family individual responses and' + + ' visitors individual responses
  • '), + + familyHouseholdMembersData = [{ + 'type': 'household-member', + '@person': { + 'fullName': 'Dave Jones', + 'firstName': 'Dave', + 'middleName': '', + 'lastName': 'Jones', + 'id': 'person_me' + } + }, { + 'type': + 'household-member', + '@person': { + 'fullName': 'Sally Jones', + 'firstName': 'Sally', + 'middleName': '', + 'lastName': 'Jones', + 'id': 'person1' + } + }, { + 'type': 'household-member', + '@person': { + 'fullName': 'Rebecca Jones', + 'firstName': 'Rebecca', + 'middleName': '', + 'lastName': 'Jones', + 'id': 'person2' + } + }, { + 'type': 'household-member', + '@person': { + 'fullName': 'Amy Jones', + 'firstName': 'Amy', + 'middleName': '', + 'lastName': 'Jones', + 'id': 'person3' + } + }], + + visitorsMemberData = [{ + 'type': 'visitor', + '@person': { + 'fullName': 'Gareth Johnson', + 'firstName': 'Gareth', + 'middleName': '', + 'lastName': 'Johnson', + 'id': 'person4' + } + }, { + 'type': 'visitor', + '@person': { + 'fullName': 'John Hamilton', + 'firstName': 'John', + 'middleName': '', + 'lastName': 'Hamilton', + 'id': 'person5' + } + }], + + familyHouseholdRelationshipsData = [{ + 'personIsDescription': 'husband-wife', + 'personIsId': 'person1', + 'personToId': 'person_me', + 'inferred': false, + 'id': 1 + }, { + 'personIsDescription': 'son-daughter', + 'personIsId': 'person2', + 'personToId': 'person_me', + 'inferred': false, + 'id': 2 + }, { + 'personIsDescription': 'mother-father', + 'personIsId': 'person_me', + 'personToId': 'person3', + 'inferred': false, + 'id': 3 + }, { + 'personIsDescription': 'son-daughter', + 'personIsId': 'person2', + 'personToId': 'person1', + 'inferred': false, + 'id': 4 + }, { + 'personIsDescription': 'mother-father', + 'personIsId': 'person1', + 'personToId': 'person3', + 'inferred': false, + 'id': 5 + }, { + 'personIsDescription': 'brother-sister', + 'personIsId': 'person3', + 'personToId': 'person2', + 'inferred': true, + 'inferredBy': [3, 5, 2, 4], + 'id': 6 + }], + + familyPersonalDetails = { + 'person_me': { + 'dob': { + 'day': '17', + 'month': '4', + 'year': '1967' + }, + 'maritalStatus': 'married', + 'country': 'wales', + 'orientation': 'straight', + 'salary': '40000' + }, + 'person1': { + 'dob': {'day': '02', 'month': '10', 'year': '1965'}, + 'maritalStatus': 'married', + 'country': 'wales', + 'orientation': 'straight', + 'salary': '40000' + }, + 'person2': { + 'dob': {'day': '20', 'month': '5', 'year': '1981'}, + 'maritalStatus': 'never', + 'country': 'wales', + 'orientation': 'straight', + 'salary': '20000' + }, + 'person3': { + 'dob': {'day': '11', 'month': '7', 'year': '1984'}, + 'maritalStatus': 'never', + 'country': 'wales', + 'orientation': 'straight', + 'salary': '20000' + } + }, + + visitorsPersonalDetails = { + 'person4': { + 'sex': 'male', + 'dob': {'day': '20', 'month': '7', 'year': '1990'}, + 'address-where': 'in-uk', + 'address': { + 'address-line-1': '15', + 'address-line-2': 'Somewhere near', + 'town-city': 'Llandridnod', + 'county': 'Powys', + 'postcode': 'LL34 AN5' + } + }, + 'person5': { + 'sex': 'male', + 'dob': {'day': '02', 'month': '5', 'year': '1991'}, + 'address-where': 'out-uk', + 'address': { + 'address-line-1': '94', + 'address-line-2': 'Somewhere Far', + 'town-city': 'Springfield', + 'county': 'New York', + 'postcode': 'NY10A' + } + } + }, + + userData = { + 'fullName': 'Dave Jones', + 'firstName': 'Dave', + 'middleName': '', + 'lastName': 'Jones' + }; + + $createFamilyHousehold.on('click', function(e) { + e.preventDefault(); + clearStorage(); + prerequisites(); + createFamilyHousehold(); + window.location.href = '../summary'; + }); + + $createFamilyRelationships.on('click', function(e) { + e.preventDefault(); + clearStorage(); + prerequisites(); + createFamilyHousehold(); + createFamilyRelationships(); + window.location.href = '../hub'; + }); + + $createFamilyWithRelationshipsAndVisitors.on('click', function(e) { + e.preventDefault(); + clearStorage(); + prerequisites(); + createFamilyHouseholdWithVisitors(); + createFamilyRelationships(); + window.location.href = '../hub'; + }); + + $createFamilyWithRelationshipsPersonalDetailsAndVisitors.on('click', function(e) { + e.preventDefault(); + clearStorage(); + prerequisites(); + createFamilyHouseholdWithVisitors(); + createFamilyRelationships(); + createFamilyPersonalDetails(); + window.location.href = '../hub'; + }); + + $createFamilyWithRelationshipsPersonalDetailsAndVisitorsPersonalDetails.on('click', function(e) { + e.preventDefault(); + clearStorage(); + prerequisites(); + createFamilyHouseholdWithVisitors(); + createFamilyRelationships(); + createFamilyVisitorsPersonalDetails(); + window.location.href = '../hub'; + }); + + $clearData.on('click', function(e) { + e.preventDefault(); + clearStorage(); + window.location.href = '../test-address'; + }); + + function prerequisites() { + sessionStorage.setItem('address', '12 Somewhere Close, Newport, CF12 3AB'); + sessionStorage.setItem('address-line-1', '12'); + sessionStorage.setItem('address-line-2', 'Somewhere close'); + sessionStorage.setItem('county', 'Newport'); + sessionStorage.setItem('lives-here', 'yes'); + sessionStorage.setItem('postcode', 'CF12 3AB'); + sessionStorage.setItem('town-city', 'Newport'); + } + + function createFamilyHousehold() { + sessionStorage.setItem('user-details', JSON.stringify(userData)); + sessionStorage.setItem(window.ONS.storage.KEYS.HOUSEHOLD_MEMBERS_STORAGE_KEY, JSON.stringify(familyHouseholdMembersData)); + sessionStorage.setItem('household-members-increment', JSON.stringify(4)); + } + + function createFamilyHouseholdWithVisitors() { + sessionStorage.setItem(window.ONS.storage.KEYS.HOUSEHOLD_MEMBERS_STORAGE_KEY, JSON.stringify([ + ...familyHouseholdMembersData, + ...visitorsMemberData + ])); + } + + function createFamilyRelationships() { + sessionStorage.setItem(window.ONS.storage.KEYS.RELATIONSHIPS_STORAGE_KEY, JSON.stringify(familyHouseholdRelationshipsData)); + sessionStorage.setItem('relationships-increment', JSON.stringify(6)); + } + + function createFamilyPersonalDetails() { + sessionStorage.setItem(window.ONS.storage.KEYS.PERSONAL_DETAILS_KEY, JSON.stringify(familyPersonalDetails)); + } + + function createFamilyVisitorsPersonalDetails() { + sessionStorage.setItem(window.ONS.storage.KEYS.PERSONAL_DETAILS_KEY, JSON.stringify({ + ...familyPersonalDetails, + ...visitorsPersonalDetails + })); + } + + function clearStorage() { + sessionStorage.clear(); + } + + $listLinks.append($clearData); +} diff --git a/_prototypes/your-household-v13/assets/relationships.js b/_prototypes/your-household-v13/assets/relationships.js new file mode 100644 index 0000000000..37472d7412 --- /dev/null +++ b/_prototypes/your-household-v13/assets/relationships.js @@ -0,0 +1,619 @@ +import {autoIncrementId, removeFromList, trailingNameS} from './utils'; +import { + isHouseholdMember, + getAllHouseholdMembers, + getHouseholdMemberByPersonId, + USER_HOUSEHOLD_MEMBER_ID +} from './household'; + +/** + * Augment Underscore library + */ +const _ = window._ || {}; + +export const RELATIONSHIPS_STORAGE_KEY = 'relationships'; + +export const relationshipTypes = { + 'spouse': {id: 'spouse'}, + 'child-parent': {id: 'child-parent'}, + 'step-child-parent': {id: 'step-child-parent'}, + 'grandchild-grandparent': {id: 'grandchild-grandparent'}, + 'half-sibling': {id: 'half-sibling'}, + 'sibling': {id: 'sibling'}, + 'step-brother-sister': {id: 'step-brother-sister'}, + 'partner': {id: 'partner'}, + 'unrelated': {id: 'unrelated'}, + 'other-relation': {id: 'other-relation'} +}; + +export const relationshipDescriptionMap = { + // covered + 'husband-wife': { + sentanceLabel: 'husband or wife', + summaryAdjective: 'husband or wife', + type: relationshipTypes['spouse'] + }, + // covered + 'mother-father': { + sentanceLabel: 'mother or father', + summaryAdjective: 'mother or father', + type: relationshipTypes['child-parent'] + }, + // covered + 'step-mother-father': { + sentanceLabel: 'stepmother or stepfather', + summaryAdjective: 'stepmother or stepfather', + type: relationshipTypes['step-child-parent'] + }, + // covered + 'son-daughter': { + sentanceLabel: 'son or daughter', + summaryAdjective: 'son or daughter', + type: relationshipTypes['child-parent'] + }, + // covered + 'half-brother-sister': { + sentanceLabel: 'half-brother or half-sister', + summaryAdjective: 'half-brother or half-sister', + type: relationshipTypes['half-sibling'] + }, + // covered + 'step-child': { + sentanceLabel: 'stepchild', + summaryAdjective: 'stepchild', + type: relationshipTypes['step-child-parent'] + }, + // covered + 'grandparent': { + sentanceLabel: 'grandparent', + summaryAdjective: 'grandparent', + type: relationshipTypes['grandchild-grandparent'] + }, + // covered + 'grandchild': { + sentanceLabel: 'grandchild', + summaryAdjective: 'grandchild', + type: relationshipTypes['grandchild-grandparent'] + }, + // covered + 'brother-sister': { + sentanceLabel: 'brother or sister', + summaryAdjective: 'brother or sister', + type: relationshipTypes['sibling'] + }, + // covered + 'step-brother-sister': { + sentanceLabel: 'stepbrother or stepsister', + summaryAdjective: 'stepbrother or stepsister', + type: relationshipTypes['step-brother-sister'] + }, + // covered + 'other-relation': { + sentanceLabel: 'other relation', + summaryAdjective: 'related', + type: relationshipTypes['other-relation'] + }, + // covered + 'partner': { + sentanceLabel: 'partner', + summaryAdjective: 'partner', + type: relationshipTypes['partner'] + }, + 'same-sex-partner': { + sentanceLabel: 'legally registered civil partner', + summaryAdjective: 'legally registered civil partner', + type: relationshipTypes['partner'] + }, + // covered + 'unrelated': { + sentanceLabel: 'unrelated', + summaryAdjective: 'unrelated', + type: relationshipTypes['unrelated'] + } +}; + +function nameElement(name) { + return '' + name + ''; +} + +function personListStr(peopleArr, opts = {}) { + if (peopleArr.length < 1) { + console.log(peopleArr, 'not enough people to create a list string'); + return; + } + + if (peopleArr.length === 1) { + return nameElement(peopleArr[0].fullName + formatPersonIfYou(peopleArr[0])); + } + + let peopleCopy = [...peopleArr], + lastPerson = peopleCopy.pop(); + + return peopleCopy + .map((person) => `${nameElement(person.fullName + + (opts.isFamily ? trailingNameS(person.fullName) : '') + + formatPersonIfYou(person))}`) + .join(', ') + ' and ' + nameElement(lastPerson.fullName + + (opts.isFamily ? trailingNameS(lastPerson.fullName) : '') + + formatPersonIfYou(lastPerson)) +} + +function formatPersonIfYou(person) { + return person.id === USER_HOUSEHOLD_MEMBER_ID ? ' (You)' : ''; +} + +export const relationshipSummaryTemplates = { + 'partnership': (person1, person2, description) => { + return `${nameElement(person1.fullName + formatPersonIfYou(person1))} is ${nameElement(person2.fullName + trailingNameS(person2.fullName) + formatPersonIfYou(person2))} ${description}`; + }, + 'twoFamilyMembersToMany': (parent1, parent2, childrenArr, description) => { + return `${nameElement(parent1.fullName + formatPersonIfYou(parent1))} and ${nameElement(parent2.fullName + formatPersonIfYou(parent2))} are ${personListStr(childrenArr, {isFamily: true})} ${description}`; + }, + 'oneFamilyMemberToMany': (parent, childrenArr, description) => { + console.log(parent, childrenArr, description); + return `${nameElement(parent.fullName + formatPersonIfYou(parent))} is ${personListStr(childrenArr, {isFamily: true})} ${description}`; + }, + 'manyToMany': (peopleArr1, peopleArr2, description) => { + return `${personListStr(peopleArr1)} ${peopleArr1.length > 1 ? 'are' : 'is'} ${description} to ${personListStr(peopleArr2)}`; + }, + 'allMutual': (peopleArr, description) => { + return `${personListStr(peopleArr)} are ${description}`; + } +}; + +/** + * Types + */ +export function relationship(description, personIsId, personToId, opts = {}) { + return { + personIsDescription: description, + personIsId: personIsId, + personToId: personToId, + inferred: !!opts.inferred, + inferredBy: opts.inferredBy + }; +} + +/** + * Storage + */ +export function addRelationship(relationshipObj) { + let householdRelationships = getAllRelationships() || [], + item = { + ...relationshipObj, + id: autoIncrementId(RELATIONSHIPS_STORAGE_KEY) + }; + + householdRelationships.push(item); + + sessionStorage.setItem(RELATIONSHIPS_STORAGE_KEY, + JSON.stringify(householdRelationships)); + + return item; +} + +export function deleteRelationship(relationshipObj) { + let householdRelationships = (getAllRelationships() || []) + .filter(relationship => relationship.id !== relationshipObj.id); + + sessionStorage.setItem(RELATIONSHIPS_STORAGE_KEY, + JSON.stringify(householdRelationships)); +} + +export function editRelationship(relationshipId, valueObject) { + let householdRelationships = (getAllRelationships() || []).map(function(relationship) { + return (relationship.id + '') === (relationshipId + '') ? { + ...valueObject, + id: relationshipId + } : relationship; + }); + + sessionStorage.setItem(RELATIONSHIPS_STORAGE_KEY, + JSON.stringify(householdRelationships)); +} + +export function getAllRelationships() { + return JSON.parse(sessionStorage.getItem(RELATIONSHIPS_STORAGE_KEY)) || []; +} + +export function getAllManualRelationships() { + return getAllRelationships().filter((relationship) => { + return !relationship.inferred; + }); +} + +export function deleteAllRelationshipsForMember(personId) { + const householdRelationships = getAllRelationships() + .filter((relationship) => { + return !(personId === relationship.personIsId || personId === relationship.personToId); + }); + + sessionStorage.setItem(RELATIONSHIPS_STORAGE_KEY, + JSON.stringify(householdRelationships)); +} + +/** + * Comparators + */ +export function isInRelationship(personId, relationship) { + return relationship.personToId === personId || relationship.personIsId === personId; +} + +export function isAChildInRelationship(personId, relationship) { + /** + * Guard + */ + if (!isInRelationship(personId, relationship)) { + return false; + } + + return ( + relationship.personIsDescription === 'mother-father' && + relationship.personToId === personId + ) || ( + relationship.personIsDescription === 'son-daughter' && + relationship.personIsId === personId + ); +} + +export function isASiblingInRelationship(personId, relationship) { + return isInRelationship(personId, relationship) && + relationshipDescriptionMap[relationship.personIsDescription].type.id === 'sibling'; +} + +export function isAParentInRelationship(personId, relationship) { + /** + * Guard + */ + if (!isInRelationship(personId, relationship)) { + return false; + } + + return ( + relationship.personIsDescription === 'mother-father' && + relationship.personIsId === personId + ) || ( + relationship.personIsDescription === 'son-daughter' && + relationship.personToId === personId + ); +} + +export function areAnyChildrenInRelationshipNotParent(childrenIds, notParentId, relationship) { + /** + * Guard + * If relationship type is not child-parent + */ + if (relationshipDescriptionMap[relationship.personIsDescription] + .type.id !== 'child-parent') { + + return false; + } + + let childIndexAsPersonIs = childrenIds.indexOf(relationship.personIsId), + childIndexAsPersonTo = childrenIds.indexOf(relationship.personToId); + + /** + * Find parents with the same children + * + * If a personIs-child is not in relationship + * or 2 children are found in relationship + */ + if ( + (childIndexAsPersonIs === -1 && childIndexAsPersonTo === -1) || + (childIndexAsPersonIs !== -1 && childIndexAsPersonTo !== -1) + ) { + return false; + } + + /** + * Child must be in relationship, get child index + */ + let childIndex = childIndexAsPersonIs !== -1 + ? childIndexAsPersonIs + : childIndexAsPersonTo; + + /** + * If personIs is not in relationship + * and child from previous relationship is a child in this relationship + */ + return !isInRelationship(notParentId, relationship) && + isAChildInRelationship(childrenIds[childIndex], relationship); +} + +export function isRelationshipType(relationshipType, relationship) { + const typeOfRelationship = relationshipDescriptionMap[relationship.personIsDescription] + .type.id; + + /** + * relationshipType can be an array of types + */ + return _.isArray(relationshipType) + ? !!_.find(relationshipType, function(rType) { + return rType === typeOfRelationship; + }) + : typeOfRelationship === relationshipType; +} + +export function isRelationshipInferred(relationship) { + return relationship.inferred; +} + +/** + * Retrieve people by role in relationships + */ +export function getParentIdFromRelationship(relationship) { + let parentId; + + if (relationship.personIsDescription === 'mother-father') { + parentId = relationship.personIsId; + } + + if (relationship.personIsDescription === 'son-daughter') { + parentId = relationship.personToId; + } + + if (!parentId) { + console.log('Parent not found in relationship: ', relationship); + return false; + } + + return parentId; +} + +export function getChildIdFromRelationship(relationship) { + let childId; + + if (relationship.personIsDescription === 'mother-father') { + childId = relationship.personToId; + } + + if (relationship.personIsDescription === 'son-daughter') { + childId = relationship.personIsId; + } + + if (!childId) { + console.log('Child not found in relationship: ', relationship); + return false; + } + + return childId; +} + +export function getSiblingIdFromRelationship(personId, relationship) { + if (!isInRelationship(personId, relationship)) { + console.log('Person ' + personId + ' not found in relationship: ', relationship); + return false; + } + + return relationship[relationship.personIsId === personId ? 'personToId' : 'personIsId']; +} + +export function getOtherPersonIdFromRelationship(personId, relationship) { + return relationship.personIsId === personId + ? relationship.personToId : relationship.personIsId; +} + +export function getAllParentsOf(personId) { + return getAllRelationships() + .filter(isAChildInRelationship.bind(null, personId)) + .map(relationship => getPersonFromMember(getHouseholdMemberByPersonId(getParentIdFromRelationship(relationship)))); +} + +export function getAllChildrenOf(personId) { + return getAllRelationships() + .filter(isAParentInRelationship.bind(null, personId)) + .map(relationship => getHouseholdMemberByPersonId(getChildIdFromRelationship(relationship))['@person']); +} + +export function getPersonIdFromPerson(person) { + return person.id; +} + +export function getPersonFromMember(member) { + return member['@person']; +} + +/** + * Missing relationship inference + */ +export const missingRelationshipInference = { + siblingsOf(subjectMember) { + + const missingRelationships = [], + allRelationships = getAllRelationships(), + person = getPersonFromMember(subjectMember), + personId = person.id, + + parents = getAllParentsOf(personId), + + siblingIds = allRelationships + .filter(isASiblingInRelationship.bind(null, personId)) + .map(getSiblingIdFromRelationship.bind(null, personId)); + + /** + * If 2 parent relationships of 'person' are found we can attempt to infer + * sibling relationships + */ + if (parents.length === 2) { + + getAllHouseholdMembers() + .filter(isHouseholdMember) + .forEach((member) => { + + const memberPersonId = member['@person'].id; + + /** + * Guard + * If member is the subject member + * or member is a parent + * or member already has a sibling relationship with 'person' + * skip member + */ + if (memberPersonId === personId || + memberPersonId === parents[0].id || memberPersonId === parents[1].id || + siblingIds.indexOf(memberPersonId) > -1) { + return; + } + + const memberParents = getAllParentsOf(memberPersonId); + + /** + * If 2 parents of 'member' are found + * and they are the same parents of 'person' + * we have identified a missing inferred relationship + */ + if (memberParents.length === 2 && + _.difference( + parents.map(getPersonIdFromPerson), + memberParents.map(getPersonIdFromPerson) + ).length === 0) { + + /** + * Add to missingRelationships + */ + missingRelationships.push(relationship( + 'brother-sister', + personId, + memberPersonId, + { + inferred: true, + inferredBy: [ + /** + * Must be 4 relationships + * Could have used member's parents but we can assume they + * must be the same at this point or the inferrence + * couldn't happen. + */ + getRelationshipOf(personId, parents[0].id).id, + getRelationshipOf(personId, parents[1].id).id, + getRelationshipOf(memberPersonId, parents[0].id).id, + getRelationshipOf(memberPersonId, parents[1].id).id + ] + } + )); + } + }); + } + + return missingRelationships; + } +}; + +export function inferRelationships(relationship, personIs, personTo) { + var missingRelationships = []; + + if (relationship.personIsDescription === 'mother-father') { + missingRelationships = missingRelationships.concat( + missingRelationshipInference.siblingsOf(personTo) + ); + } + + if (relationship.personIsDescription === 'son-daughter') { + missingRelationships = missingRelationships.concat( + missingRelationshipInference.siblingsOf(personIs) + ); + } + + $.each(missingRelationships, function(i, relationship) { + addRelationship(relationship); + }); +} + +export function findNextMissingRelationship() { + let householdMembers = getAllHouseholdMembers().filter(isHouseholdMember), + relationships = getAllRelationships(), + missingRelationshipMembers = [], + personIs = null; + + /** + * Find the next missing relationship + */ + $.each(householdMembers, function(i, member) { + const personId = member['@person'].id; + + /** + * Get all relationships for this member + */ + const memberRelationships = relationships.filter(function(relationship) { + return relationship.personIsId === personId || relationship.personToId === personId; + }), + + memberRelationshipToIds = memberRelationships.map(function(relationship) { + return relationship.personIsId === personId ? relationship.personToId : relationship.personIsId; + }) || []; + + /** + * If total relationships related to this member isn't equal to + * total household members -1, indicates missing relationship + */ + if (memberRelationships.length < householdMembers.length - 1) { + + /** + * All missing relationship members + */ + missingRelationshipMembers = householdMembers.filter(function(m) { + return memberRelationshipToIds.indexOf(m['@person'].id) === -1 && + m['@person'].id !== personId; + }); + + personIs = member; + + return false; + } + }); + + return personIs ? { + personIs: personIs, + personTo: missingRelationshipMembers[0] + } : null; +} + +export function getPeopleIdsMissingRelationshipsWithPerson(personId) { + const remainingPersonIds = getAllHouseholdMembers() + .filter(isHouseholdMember) + .map(function(member) { + return member['@person'].id; + }); + + /** + * Remove this person from the list + */ + removeFromList(remainingPersonIds, personId); + + $.each(getAllRelationships(), function(i, relationship) { + if (!isInRelationship(personId, relationship)) { + return; + } + + /** + * Remove the other person from the remainingPersonIds list + */ + removeFromList( + remainingPersonIds, + getOtherPersonIdFromRelationship(personId, relationship) + ); + }); + + return remainingPersonIds; +} + +export function getRelationshipType(relationship) { + return relationshipDescriptionMap[relationship.personIsDescription].type; +} + +/** + * Retrieve from relationship group + */ +export function getRelationshipsWithPersonIds(relationships, idArr) { + return relationships.filter(function(childRelationship) { + return idArr.indexOf(childRelationship.personIsId) !== -1 || + idArr.indexOf(childRelationship.personToId) !== -1; + }); +} + +export function getRelationshipOf(person1, person2) { + return getAllRelationships().find(function(relationship) { + return isInRelationship(person1, relationship) && + isInRelationship(person2, relationship); + }); +} diff --git a/_prototypes/your-household-v13/assets/utils.js b/_prototypes/your-household-v13/assets/utils.js new file mode 100644 index 0000000000..c0898a3d80 --- /dev/null +++ b/_prototypes/your-household-v13/assets/utils.js @@ -0,0 +1,39 @@ +export function autoIncrementId(collection) { + let k = collection + '-increment', + id = parseInt(sessionStorage.getItem(k)) || 0; + + id++; + + sessionStorage.setItem(k, JSON.stringify(id)); + + return id; +} + +export function removeFromList(list, val) { + + function doRemove(item) { + var foundId = list.indexOf(item); + + /** + * Guard + */ + if (foundId === -1) { + console.log('Attempt to remove from list failed: ', list, val); + return; + } + + list.splice(foundId, 1); + } + + if (_.isArray(val)) { + $.each(val, function(i, item) { + doRemove(item); + }); + } else { + doRemove(val); + } +} + +export function trailingNameS(name) { + return name[name.length - 1] === 's' ? '\’' : '\’s'; +} diff --git a/_prototypes/your-household-v13/bundle.js b/_prototypes/your-household-v13/bundle.js new file mode 100644 index 0000000000..6f9bc823ba --- /dev/null +++ b/_prototypes/your-household-v13/bundle.js @@ -0,0 +1,667 @@ +/** + * Libraries + */ +import './assets/lib/FileSaver'; +import './assets/lib/array-from-polyfill'; +import './assets/lib/url-search-params-polyfill'; +import './assets/lib/array-find-polyfill'; +import './assets/lib/CustomEvent-polyfill'; +import './assets/lib/fetch-polyfill'; +import './assets/lib/abortcontroller-polyfill'; + +/** + * DOM modules + */ +import './assets/modules/typeahead-refactored/typeahead.module'; + +import { + RELATIONSHIPS_STORAGE_KEY, + relationshipDescriptionMap, + addRelationship, + deleteRelationship, + editRelationship, + getAllRelationships, + getAllManualRelationships, + deleteAllRelationshipsForMember, + relationshipSummaryTemplates, + missingRelationshipInference, + inferRelationships, + getAllParentsOf, + getAllChildrenOf, + getParentIdFromRelationship, + getChildIdFromRelationship, + getOtherPersonIdFromRelationship, + isAChildInRelationship, + isAParentInRelationship, + isInRelationship, + areAnyChildrenInRelationshipNotParent, + isRelationshipType, + isRelationshipInferred, + getRelationshipOf, + getRelationshipsWithPersonIds, + getPeopleIdsMissingRelationshipsWithPerson, + getRelationshipType, + findNextMissingRelationship, + relationship +} from './assets/relationships'; +import { + HOUSEHOLD_MEMBER_TYPE, + VISITOR_TYPE, + USER_HOUSEHOLD_MEMBER_ID, + HOUSEHOLD_MEMBERS_STORAGE_KEY, + addHouseholdMember, + updateHouseholdMember, + deleteHouseholdMember, + getAllHouseholdMembers, + getUserAsHouseholdMember, + getHouseholdMemberByPersonId, + getMemberPersonId, + updateUserAsHouseholdMember, + deleteUserAsHouseholdMember, + isVisitor, + isOtherHouseholdMember, + isHouseholdMember, + person, + tempAwayQuestionSentenceMap, + visitorQuestionSentenceMap +} from './assets/household'; +import { + addUpdatePersonalDetailsDOB, + getPersonalDetailsFor, + removePersonalDetailsFor, + addUpdateMaritalStatus, + addUpdateCountry, + addUpdateCountryOther, + addUpdateNationalIdentity, + addUpdateNationalIdentityOther, + addUpdateEthnicGroup, + addUpdateEthnicGroupDescription, + addUpdateEthnicGroupOther, + addUpdatePassportCountry, + addUpdatePassportCountryOther, + addUpdateOrientation, + addUpdateSalary, + addUpdateSex, + addUpdateAddressWhere, + addUpdateAddressIndividual, + addUpdateAge, + addUpdateAgeConfirm, + addUpdateAddressOutsideUK, + addUpdateApprenticeship, + addUpdateHasQualificationAbove, + addUpdateQualificationsNvqEquivalent, + addUpdateQualificationsALevel, + addUpdateQualificationsGCSEs, + addUpdateQualificationsOtherWhere, + addUpdateEmploymentStatus, + addUpdateJobTitle, + addUpdateJobDescribe, + + personalDetailsMaritalStatusMap, + personalDetailsCountryMap, + personalDetailsOrientationMap, + personalDetailsGenderMap, + personalDetailsNationalIdentityMap, + personalDetailsEthnicGroupMap, + personalDetailsPassportCountriesMap, + personalDetailsApprenticeshipMap, + personalDetailsDegreeAboveMap, + personalDetailsNVQMap, + personalDetailsALevelMap, + personalDetailsGCSEMap, + personalDetailsOtherWhere, + personalDetailsEmploymentStatus, + + PERSONAL_DETAILS_KEY, + + createPinFor, + getPinFor, + unsetPinFor, + personalBookmark, + getBookmarkFor, + clearPersonalBookmark, + personalQuestionSubmitDecorator +} from './assets/personal-details'; +import { removeFromList, trailingNameS } from './assets/utils'; + +import { numberToPositionWord, numberToWordsStyleguide } from './assets/numbers-to-words'; + +import { tools } from './assets/prototype-tools'; + +export const USER_STORAGE_KEY = 'user-details'; +export const INDIVIDUAL_PROXY_STORAGE_KEY = 'proxy-person'; + +export function getAddress() { + let addressLines = sessionStorage.getItem('address').split(','); + + return { + addressLine1: addressLines[0], + addressLine2: addressLines[1], + addressLine3: addressLines[2], + addressCounty: addressLines[4], + addressTownCity: addressLines[3], + addressPostcode: addressLines[5] + } +} + +/** + * User + */ +export function addUserPerson(person) { + sessionStorage.setItem(USER_STORAGE_KEY, JSON.stringify(person)); +} + +export function getUserPerson() { + return JSON.parse(sessionStorage.getItem(USER_STORAGE_KEY)); +} + +/** + * Helpers + */ +function createNavItem(member) { + let $nodeEl = $(''), + $linkEl = $nodeEl.find('.js-template-nav-item-label'); + + $linkEl.html(member['@person'].fullName); + + if (member['@person'].id === USER_HOUSEHOLD_MEMBER_ID) { + $linkEl.attr('href', '../what-is-your-name'); + } else { + $linkEl.attr('href', '../who-else-to-add?edit=' + member['@person'].id); + } + + return $nodeEl; +} + +function updateHouseholdVisitorsNavigationItems() { + let allHouseholdMembers = window.ONS.storage.getAllHouseholdMembers(), + householdMembers = allHouseholdMembers.filter(window.ONS.storage.isHouseholdMember), + visitors = allHouseholdMembers.filter(window.ONS.storage.isVisitor); + + const $navigationHouseholdMembersEl = $('#navigation-household-members'), + $navigationVisitorsEl = $('#navigation-visitors'); + + if (householdMembers.length) { + $.each(householdMembers, function(i, member) { + $navigationHouseholdMembersEl.append(createNavItem(member)); + }); + } else { + $navigationHouseholdMembersEl.parent().hide(); + } + + if (visitors.length) { + $.each(visitors, function(i, member) { + $navigationVisitorsEl.append(createNavItem(member)); + }); + } else { + $navigationVisitorsEl.parent().hide(); + } +} + +function createListItemPerson(member) { + return $('
  • ').addClass('mars').html( + '' + + member['@person'].fullName + + (member['@person'].id === USER_HOUSEHOLD_MEMBER_ID ? ' (You)' : '') + + '' + ); +} + +function populateList($el, memberType) { + if (!$el.length) { + return; + } + + let members = getAllHouseholdMembers() || []; + + $el.empty().append(members.filter((member) => { + return member.type === memberType; + }).map(createListItemPerson)); + + $el.addClass('list list--people-plain'); +} + +function populateHouseholdList() { + populateList($('#household-members'), HOUSEHOLD_MEMBER_TYPE); +} + +function populateVisitorList() { + populateList($('#visitors-list'), VISITOR_TYPE); +} + +function cleanHTMLPlaceholderStringReplacment(el, val) { + const $el = $(el), + $parent = $el.parent(); + + $el.before(val); + $el.remove(); + + $parent.html($parent.html().replace(/[\s]+/g, ' ').trim()); +} + +function updateAddresses() { + let addressLines = (sessionStorage.getItem('address') || '').split(','), + addressLine1 = addressLines[0], + addressLine2 = addressLines[1]; + + $('.address-text').each((i, el) => cleanHTMLPlaceholderStringReplacment(el, + addressLine1 && addressLine2 + ? ( + addressLine1 + (addressLine2 ? ', ' + addressLine2 : '') + ) + : 'Address not found' + )); + + $('.address-text-line1').each((i, el) => cleanHTMLPlaceholderStringReplacment(el, addressLine1)); + + const personId = new URLSearchParams(window.location.search).get('person'); + + if (personId) { + const person = getHouseholdMemberByPersonId(personId)['@person'], + $sectionIndividualEl = $('#section-individual'), + $nameEl = $('.js-person-fullname-from-url-id'); + + $sectionIndividualEl.length && cleanHTMLPlaceholderStringReplacment($sectionIndividualEl, person.fullName); + $nameEl.length && cleanHTMLPlaceholderStringReplacment($nameEl, person.fullName); + } +} + +const secureLinkTextMap = { + 'question-you': { + description: 'Want to keep your answers secure from other people at this' + + ' address?', + linkText: 'Get a separate access code to submit an individual response', + link: '../individual-decision-secure' + }, + 'pin-you': { + description: 'You\'ve chosen to keep your answers secure', + linkText: 'Cancel this and make answers available to the rest of the' + + ' household', + link: '../individual-decision-secure' + }, + 'question-proxy': { + description: 'Not happy to continue answering for $[NAME]?', + linkText: 'Request an individual access code to be sent to them', + link: '../individual-decision-other-secure' + } +}; + +function updateAllPreviousLinks() { + $('.js-previous-link').attr('href', document.referrer); +} + +function updatePersonLink() { + const personId = new URLSearchParams(window.location.search).get('person'); + + if (personId) { + let urlParam = new URLSearchParams(window.location.search), + person = getHouseholdMemberByPersonId(personId)['@person'], + pinObj = getPinFor(personId), + secureLinkTextConfig = secureLinkTextMap[ + (getAnsweringIndividualByProxy() ? 'question-proxy' : (pinObj && pinObj.pin ? 'pin-you' : 'question-you')) + ], + linkHref = secureLinkTextConfig.link + '?person=' + personId + + '&returnurl=' + window.location.pathname, + surveyType = urlParam.get('survey'); + + linkHref += (surveyType ? '&survey=' + surveyType : ''); + + let $secureLink = $('.js-link-secure'); + $secureLink.attr('href', linkHref); + + $secureLink.html(secureLinkTextConfig.linkText); + $('.js-link-secure-label').html(secureLinkTextConfig.description.replace('$[NAME]', person.fullName)); + + let personLink = $('.js-link-person'); + personLink.attr('href', personLink.attr('href') + '?person=' + personId + + (surveyType ? '&survey=' + surveyType : '')); + } +} + +function updateBySurveyType() { + const urlParams = new URLSearchParams(window.location.search), + surveyType = urlParams.get('survey'); + + if (surveyType) { + $('.js-header-title').html(surveyTypeConfig[surveyType].title); + $('#people-living-here').html(surveyTypeConfig[surveyType].householdSectionTitle); + $('#people-living-here').attr('href', surveyTypeConfig[surveyType].householdSectionLink); + $('#relationships-section').attr('href', surveyTypeConfig[surveyType].relationshipsSection); + $('title').html(surveyTypeConfig[surveyType].title); + } +} + +function setAnsweringIndividualByProxy(bool) { + sessionStorage.setItem(INDIVIDUAL_PROXY_STORAGE_KEY, JSON.stringify(bool)); +} + +function getAnsweringIndividualByProxy() { + return JSON.parse(sessionStorage.getItem(INDIVIDUAL_PROXY_STORAGE_KEY)); +} + +function unsetAnsweringIndividualByProxy() { + (getAnsweringIndividualByProxy() !== null) && sessionStorage.removeItem(INDIVIDUAL_PROXY_STORAGE_KEY); +} + +const surveyTypeConfig = { + lms: { + title: 'Online Household Study', + householdSectionTitle: 'About your household', + householdSectionLink: '../summary/?survey=lms', + relationshipsSection: '../relationships/?survey=lms' + } +}; + +function doILiveHere() { + return sessionStorage.getItem('lives-here') === 'yes'; +} + +function getSignificant() { + return '3 February 2019'; +} + +function updateSignificantDate() { + $('.js-significant-date').each((i, el) => cleanHTMLPlaceholderStringReplacment(el, getSignificant())); +} + +function personRecordTemplate() { + return $(`
  • + +
    + + Change + | + Remove + +
    +
  • `); +} + +function createMemberItem(member, { redirect } = { redirect: null }) { + var $nodeEl = personRecordTemplate(), + $editLink = $nodeEl.find('.js-record-edit'), + $removeLink = $nodeEl.find('.js-record-remove'), + $spacer = $nodeEl.find('.js-spacer'), + urlParams = new URLSearchParams(window.location.search), + personNameText = member['@person'].fullName, + memberIsUser = isMemberUser(member), + surveyType = urlParams.get('survey'), + altPage = surveyType && surveyType === 'lms' ? surveyType + '/' : '', + redirectTo = (redirect ? '&redirect=' + encodeURIComponent(window.location.href) : ''); + + if (memberIsUser) { + personNameText += ' (You)'; + $editLink.html('Change'); + $removeLink.hide(); + $spacer.hide(); + } + + $nodeEl.attr('id', ''); + $nodeEl.find('.js-person-name').html(personNameText); + + $editLink.attr('href', ( + (memberIsUser + ? '../' + altPage + 'what-is-your-name/?edit=true' + : '../' + altPage + 'who-else-to-add/?edit=' + member['@person'].id + + (isVisitor(member) ? '&journey=visitors' : '')) + + redirectTo + )); + + $removeLink.attr('href', ( + '../remove-household-member/?person=' + member['@person'].id + + redirectTo + )); + + return $nodeEl; +} + +function updateHouseholdSummary() { + const members = getAllHouseholdMembers(); + + $('.js-household-members-summary').each(function(i, el) { + const $el = $(el); + + $.each([ + ...members.filter(isMemberUser), + ...members.filter(isOtherHouseholdMember) + ], function(i, member) { + $el.append(createMemberItem(member, { redirect: $el.attr('data-redirect') })); + }); + }); +} + +function updateVisitorsSummary() { + const members = getAllHouseholdMembers(); + + $('.js-visitors-summary').each(function(i, el) { + const $el = $(el); + + $.each(members.filter(isVisitor), (i, member) => { + $el.append(createMemberItem(member, { redirect: $el.attr('data-redirect') })); + }); + }); +} + +function updateContinueNotice() { + const urlParams = new URLSearchParams(window.location.search), + isContinuing = urlParams.get('continuing'), + personId = urlParams.get('person'); + + if (!isContinuing) { + return false; + } + + const template = `
    +
    + This was the last question + you answered in the section +

    You can review your answers + at the start + of this section +

    +
    +
    `; + + $('.js-heading').closest('.question').prepend(template); +} + +function updateSaveAndCompleteLater() { + $('.complete-later').on('click', function(e) { + e.preventDefault(); + + window.location.href = '../index/?redirect=../hub'; + }); +} + +function updateFoortListCol() { + $('.js-footer-list-col').append('
  • Test' + + ' data
  • '); +} + +function isMemberUser(member) { + return member['@person'].id === window.ONS.storage.IDS.USER_HOUSEHOLD_MEMBER_ID; +} + +function sessionBookmark() { + var pieces = window.location.href + .replace(window.location.pathname, '[delimeter]') + .split('[delimeter]'); + + pieces.shift(); + + if (window.location.pathname.match(/test-data/g)) { + console.log('match'); + return; + } + + sessionStorage.setItem('_session_bookmark', [].concat(window.location.pathname, pieces).join('')); +} + +function fieldItemDisplayHack() { + $('.field__item').after('
    '); +} + +window.ONS = window.ONS || {}; +window.ONS.storage = { + getAddress, + addHouseholdMember, + updateHouseholdMember, + deleteHouseholdMember, + getAllHouseholdMembers, + addUserPerson, + getUserPerson, + getUserAsHouseholdMember, + getHouseholdMemberByPersonId, + getMemberPersonId, + updateUserAsHouseholdMember, + deleteUserAsHouseholdMember, + tempAwayQuestionSentenceMap, + visitorQuestionSentenceMap, + + isVisitor, + isOtherHouseholdMember, + isHouseholdMember, + + addRelationship, + deleteRelationship, + editRelationship, + getAllRelationships, + getAllManualRelationships, + deleteAllRelationshipsForMember, + + getAllParentsOf, + getAllChildrenOf, + getParentIdFromRelationship, + getChildIdFromRelationship, + getOtherPersonIdFromRelationship, + isAParentInRelationship, + isAChildInRelationship, + isInRelationship, + areAnyChildrenInRelationshipNotParent, + isRelationshipType, + isRelationshipInferred, + getRelationshipOf, + + relationshipDescriptionMap, + relationshipSummaryTemplates, + missingRelationshipInference, + inferRelationships, + getRelationshipsWithPersonIds, + getPeopleIdsMissingRelationshipsWithPerson, + getRelationshipType, + findNextMissingRelationship, + + addUpdatePersonalDetailsDOB, + getPersonalDetailsFor, + removePersonalDetailsFor, + addUpdateMaritalStatus, + addUpdateCountry, + addUpdateCountryOther, + addUpdateNationalIdentity, + addUpdateNationalIdentityOther, + addUpdateEthnicGroup, + addUpdateEthnicGroupDescription, + addUpdateEthnicGroupOther, + addUpdatePassportCountry, + addUpdatePassportCountryOther, + addUpdateOrientation, + addUpdateSalary, + addUpdateSex, + addUpdateAddressWhere, + addUpdateAddressIndividual, + addUpdateAge, + addUpdateAgeConfirm, + addUpdateAddressOutsideUK, + addUpdateApprenticeship, + addUpdateHasQualificationAbove, + addUpdateQualificationsNvqEquivalent, + addUpdateQualificationsALevel, + addUpdateQualificationsGCSEs, + addUpdateQualificationsOtherWhere, + addUpdateEmploymentStatus, + addUpdateJobTitle, + addUpdateJobDescribe, + + personalDetailsMaritalStatusMap, + personalDetailsCountryMap, + personalDetailsOrientationMap, + personalDetailsGenderMap, + personalDetailsNationalIdentityMap, + personalDetailsEthnicGroupMap, + personalDetailsPassportCountriesMap, + personalDetailsApprenticeshipMap, + personalDetailsDegreeAboveMap, + personalDetailsNVQMap, + personalDetailsALevelMap, + personalDetailsGCSEMap, + personalDetailsOtherWhere, + personalDetailsEmploymentStatus, + + createPinFor, + getPinFor, + unsetPinFor, + personalBookmark, + getBookmarkFor, + clearPersonalBookmark, + personalQuestionSubmitDecorator, + + setAnsweringIndividualByProxy, + getAnsweringIndividualByProxy, + unsetAnsweringIndividualByProxy, + + doILiveHere, + isMemberUser, + + KEYS: { + HOUSEHOLD_MEMBERS_STORAGE_KEY, + USER_STORAGE_KEY, + INDIVIDUAL_PROXY_STORAGE_KEY, + HOUSEHOLD_MEMBER_TYPE, + VISITOR_TYPE, + RELATIONSHIPS_STORAGE_KEY, + PERSONAL_DETAILS_KEY + }, + + IDS: { + USER_HOUSEHOLD_MEMBER_ID + }, + + TYPES: { + person, + relationship + } +}; + +window.ONS.helpers = { + populateHouseholdList, + populateVisitorList +}; + +window.ONS.utils = { + removeFromList, + trailingNameS, + numberToPositionWord, + numberToWordsStyleguide, + getSignificant, + cleanHTMLPlaceholderStringReplacment +}; + +$(populateHouseholdList); +$(populateVisitorList); +$(updateHouseholdVisitorsNavigationItems); +$(updateAddresses); +$(updatePersonLink); +$(tools); +$(updateAllPreviousLinks); +$(updateBySurveyType); +$(updateSignificantDate); +$(updateHouseholdSummary); +$(updateVisitorsSummary); +$(updateContinueNotice); +$(updateSaveAndCompleteLater); +$(updateFoortListCol); +$(sessionBookmark); +$(fieldItemDisplayHack); diff --git a/_prototypes/your-household-v13/complete.html b/_prototypes/your-household-v13/complete.html new file mode 100644 index 0000000000..1d8a1aed85 --- /dev/null +++ b/_prototypes/your-household-v13/complete.html @@ -0,0 +1,39 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +hideSaveLater: true +--- + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    + You have successfully completed the Census +

    + +
    +
    + Your reference number +

    HDJ2123F

    +
    +
    + +

    It is now safe to close this window.

    +
    +
    +
    +
    + + diff --git a/_prototypes/your-household-v13/confirm-address.html b/_prototypes/your-household-v13/confirm-address.html new file mode 100644 index 0000000000..bec667902d --- /dev/null +++ b/_prototypes/your-household-v13/confirm-address.html @@ -0,0 +1,99 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    + Can you confirm this address is correct? +

    +
    + +

    Address not + set

    + +
    +
    +
    +
    + + +
    + +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    + + + + + + + + + diff --git a/_prototypes/your-household-v13/do-you-live-here.html b/_prototypes/your-household-v13/do-you-live-here.html new file mode 100644 index 0000000000..14467ddb21 --- /dev/null +++ b/_prototypes/your-household-v13/do-you-live-here.html @@ -0,0 +1,153 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    + +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    + Do you usually live at ? +

    + +
    +
    +

    What does 'usually live' mean?

    +
    +

    The address at which you + generally spend most + time. For most people, + this will be their + permanent or family home.

    +

    Full-time + students, include + yourself at your term + time address.

    +

    Armed + forces members, + include yourself at your + home address if you have + one.

    + +
    +
    +
    + +
    +
    +
    +
    + + +
    + +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + + + + + diff --git a/_prototypes/your-household-v13/does-anyone-else-live-here.html b/_prototypes/your-household-v13/does-anyone-else-live-here.html new file mode 100644 index 0000000000..b642b83456 --- /dev/null +++ b/_prototypes/your-household-v13/does-anyone-else-live-here.html @@ -0,0 +1,189 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + + + +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    +

    + Does anyone else live at ? +

    +
    + + + + + + +
    +
    +
    +
    + + +
    + +
    + + +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/hub.html b/_prototypes/your-household-v13/hub.html new file mode 100644 index 0000000000..c2aae083d7 --- /dev/null +++ b/_prototypes/your-household-v13/hub.html @@ -0,0 +1,438 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    + Remaining sections to complete +

    +
    + +
    + You must complete all sections in order to submit this survey +
    +
    + +
    +
    +
    +
    +
    +
    + + People who live + here + + — Completed +
    +
    +
    +
    Completed
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + diff --git a/_prototypes/your-household-v13/i-dont-live-here.html b/_prototypes/your-household-v13/i-dont-live-here.html new file mode 100644 index 0000000000..28b194f27b --- /dev/null +++ b/_prototypes/your-household-v13/i-dont-live-here.html @@ -0,0 +1,117 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    + +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    + Does anyone usually live at ? +

    + +
    +
    +
    +
    + + +
    + +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + + + + + + diff --git a/_prototypes/your-household-v13/index.html b/_prototypes/your-household-v13/index.html new file mode 100644 index 0000000000..19b70ba6af --- /dev/null +++ b/_prototypes/your-household-v13/index.html @@ -0,0 +1,117 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + + + +
    +
    + +
    +
    +
    +
    + +
    +
    + + +
    +
    +
    +

    + Welcome to the household prototype +

    + +
    +
    +
    + + +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + +

    The unique access code + is printed on your invitation + letter or form

    +
    +
    + +

    Your personal information is protected by law and will be kept confidential

    + +
    +
    +
    + + +
    +
    +
    +
    + + + + + diff --git a/_prototypes/your-household-v13/individual-decision-answer-behalf.html b/_prototypes/your-household-v13/individual-decision-answer-behalf.html new file mode 100644 index 0000000000..25d3c59885 --- /dev/null +++ b/_prototypes/your-household-v13/individual-decision-answer-behalf.html @@ -0,0 +1,198 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + You have now completed your individual questions. Is there anyone in your household you cannot answer the questions for? +

    +
    + +
    +

    Select all that apply

    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + + +
    +
    +
    +
    +
    + +
    +
    + + +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-decision-behalf-continue.html b/_prototypes/your-household-v13/individual-decision-behalf-continue.html new file mode 100644 index 0000000000..c1a7add696 --- /dev/null +++ b/_prototypes/your-household-v13/individual-decision-behalf-continue.html @@ -0,0 +1,154 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    + Completing the individual questions +

    +

    If you cannot answer individual questions + for others, you can ask them for their + informtion and complete their questions + later. If you want them to complete + their own questions, they will need the + unique access code from the Census + letter we sent you.

    + +

    Alternatively, you can request a separate + survey for them. We will send them a new + access code by text.

    +
    + +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    + + + + + +
    +
    +
    +
    +
    + +
    +
    + + +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-decision-enter-mobile-correct.html b/_prototypes/your-household-v13/individual-decision-enter-mobile-correct.html new file mode 100644 index 0000000000..1df7a367a0 --- /dev/null +++ b/_prototypes/your-household-v13/individual-decision-enter-mobile-correct.html @@ -0,0 +1,122 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    + Is this mobile number correct? +

    +

    +
    + +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    + + + + + +
    +
    +
    +
    +
    + +
    +
    + + +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-decision-enter-mobile-sent.html b/_prototypes/your-household-v13/individual-decision-enter-mobile-sent.html new file mode 100644 index 0000000000..5676f62a4e --- /dev/null +++ b/_prototypes/your-household-v13/individual-decision-enter-mobile-sent.html @@ -0,0 +1,88 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    + We have sent the access code to +

    +

    + +

    A text with the code should + arrive shortly with a message on how + they can complete their own Census

    +
    +
    +
    + + + + + +
    +
    +
    +
    +
    + +
    +
    + + +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-decision-enter-mobile.html b/_prototypes/your-household-v13/individual-decision-enter-mobile.html new file mode 100644 index 0000000000..af57e4d212 --- /dev/null +++ b/_prototypes/your-household-v13/individual-decision-enter-mobile.html @@ -0,0 +1,123 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    + What is + mobile number? +

    +

    We will send an access code by text to + this number

    +
    + +
    + + +
    +
    +
    + + + + + +
    +
    +
    +
    +
    + +
    +
    + + +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-decision-other-pin.html b/_prototypes/your-household-v13/individual-decision-other-pin.html new file mode 100644 index 0000000000..61271c8d79 --- /dev/null +++ b/_prototypes/your-household-v13/individual-decision-other-pin.html @@ -0,0 +1,126 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +cdn: v1.5.0 +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + Send individual access code +

    +

    + You can request the access code to be + sent by text message to their mobile + phone, or we can post it to them. +

    +
    + +
    +
    +
    +
    +
    + + +
    + + +
    +
    + +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-decision-other-secure.html b/_prototypes/your-household-v13/individual-decision-other-secure.html new file mode 100644 index 0000000000..68020b5052 --- /dev/null +++ b/_prototypes/your-household-v13/individual-decision-other-secure.html @@ -0,0 +1,140 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +cdn: v1.5.0 +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + Are you happy to continue answering + for +

    +

    + This section asks you to provide + information about their age, marital + status, sexual orientation, health, + education and employment. +

    +

    + If you are not happy to answer these + questions and would like them to + complete their own section you can + request an individual access pin on + their behalf. +

    +
    + +
    +
    +
    +
    +
    + + +
    + +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-decision-pin.html b/_prototypes/your-household-v13/individual-decision-pin.html new file mode 100644 index 0000000000..340900dcdb --- /dev/null +++ b/_prototypes/your-household-v13/individual-decision-pin.html @@ -0,0 +1,112 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +cdn: v1.5.0 +--- + + + + +
    +
    + Previous +
    +
    + +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +

    + Your individual access code +

    + +
    + 34567 +
    + +

    + Make a note of your access code as + you will need it to view or edit your + answers once you have signed out. +

    +

    + If you lose your code you won’t be + able to retrieve it and you’ll need + to enter your answers again. +

    +
    + +
    +
    + + +
    + + +
    +
    +
    +
    +
    +
    + + + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-decision-secure.html b/_prototypes/your-household-v13/individual-decision-secure.html new file mode 100644 index 0000000000..6c76fc9cac --- /dev/null +++ b/_prototypes/your-household-v13/individual-decision-secure.html @@ -0,0 +1,148 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +cdn: v1.5.0 +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + + +
    +
    +
    +
    +

    + Do you want to keep your answers + secure from other people at this + address? +

    +

    + We’ll provide you with a separate + access code if you don’t want other + people living at this address to view + your answers. +

    +

    + An example of this might be if you + live in a shared or student house. + You might not want other people to + have access to information about you + such as your age, marital status, + health, education and employment. +

    +
    + +
    +
    +
    +
    +
    + + +
    + +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-decision.html b/_prototypes/your-household-v13/individual-decision.html new file mode 100644 index 0000000000..218e5213c3 --- /dev/null +++ b/_prototypes/your-household-v13/individual-decision.html @@ -0,0 +1,123 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + Are you ? +

    +
    + +
    +
    +
    +
    +
    + + +
    + +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and return to this later

    +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-address-in-uk.html b/_prototypes/your-household-v13/individual-details-address-in-uk.html new file mode 100644 index 0000000000..a123f9db9a --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-address-in-uk.html @@ -0,0 +1,196 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +cdn: v2.0.0 +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + What is your usual address? +

    +
    + +
    +
    +
    +
    + +
    + + +
    +
    + + +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-address.html b/_prototypes/your-household-v13/individual-details-address.html new file mode 100644 index 0000000000..091659bf63 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-address.html @@ -0,0 +1,256 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +cdn: v1.5.0 +--- + + +
    +
    + Previous +
    +
    + + + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + What is + usual address? +

    +
    + +
    +
    +
    +
    +
    + + + + +
    +
    +
    +
    + +
    +
    +
    +
    + + + +
    +
    +
    +
    + +
    +
    +
    +
    + + + +
    +
    +
    +
    + +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-age-confirm.html b/_prototypes/your-household-v13/individual-details-age-confirm.html new file mode 100644 index 0000000000..67f952144c --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-age-confirm.html @@ -0,0 +1,179 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + +

    +
    + +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + + diff --git a/_prototypes/your-household-v13/individual-details-apprenticeship.html b/_prototypes/your-household-v13/individual-details-apprenticeship.html new file mode 100644 index 0000000000..fed36666ed --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-apprenticeship.html @@ -0,0 +1,156 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + Have you completed an apprenticeship? +

    +
    + +
    +
    +

    Include equivalent apprenticeships completed anywhere outside England and Wales

    +
    +
    + +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-country-other.html b/_prototypes/your-household-v13/individual-details-country-other.html new file mode 100644 index 0000000000..6285ed9b78 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-country-other.html @@ -0,0 +1,137 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + You selected ‘Elsewhere’ for your country of birth, can you specify this further? +

    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-country.html b/_prototypes/your-household-v13/individual-details-country.html new file mode 100644 index 0000000000..50ac3f6a7c --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-country.html @@ -0,0 +1,198 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + What is your country of birth? +

    +
    + +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-dob.html b/_prototypes/your-household-v13/individual-details-dob.html new file mode 100644 index 0000000000..0dca3e6f53 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-dob.html @@ -0,0 +1,177 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + What is your date of birth? +

    +

    For + example 20 03 1980

    +
    + +
    +
    +
    + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another + section and + return + to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-employment-status.html b/_prototypes/your-household-v13/individual-details-employment-status.html new file mode 100644 index 0000000000..485c7cd71d --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-employment-status.html @@ -0,0 +1,192 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + In your main job, what is your + employment status? +

    +
    + +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    + + +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-ethnic-group-description.html b/_prototypes/your-household-v13/individual-details-ethnic-group-description.html new file mode 100644 index 0000000000..061437ee77 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-ethnic-group-description.html @@ -0,0 +1,181 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + Which of these best describes your ethnic group or background? +

    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + + + diff --git a/_prototypes/your-household-v13/individual-details-ethnic-group-other.html b/_prototypes/your-household-v13/individual-details-ethnic-group-other.html new file mode 100644 index 0000000000..8185c1c708 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-ethnic-group-other.html @@ -0,0 +1,177 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + You selected ‘Other White background’ for your White ethnic group, can you specify this further? +

    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-ethnic-group.html b/_prototypes/your-household-v13/individual-details-ethnic-group.html new file mode 100644 index 0000000000..819198102a --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-ethnic-group.html @@ -0,0 +1,220 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + What is your ethnic group? +

    +
    + +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-job-describe.html b/_prototypes/your-household-v13/individual-details-job-describe.html new file mode 100644 index 0000000000..424b92c289 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-job-describe.html @@ -0,0 +1,141 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + Can you briefly describe what you do in your main job? +

    +
    + +
    +
    + + + +
    + You have 200 characters remaining
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-job-title.html b/_prototypes/your-household-v13/individual-details-job-title.html new file mode 100644 index 0000000000..e0534a8423 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-job-title.html @@ -0,0 +1,143 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + In your main job, what is your job title? +

    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-national-identity-other.html b/_prototypes/your-household-v13/individual-details-national-identity-other.html new file mode 100644 index 0000000000..a32315810a --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-national-identity-other.html @@ -0,0 +1,140 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + You selected ‘Other’ for your national identity, can you specify this further? +

    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-national-identity.html b/_prototypes/your-household-v13/individual-details-national-identity.html new file mode 100644 index 0000000000..945b6701c1 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-national-identity.html @@ -0,0 +1,205 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + How would you describe your national identity? +

    +
    + +
    +
    +

    What is national identity?

    +
    +

    To do

    + +
    +
    +
    + +
    +
    +
    +
    + Select all that apply: +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-orientation.html b/_prototypes/your-household-v13/individual-details-orientation.html new file mode 100644 index 0000000000..67967b1fe4 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-orientation.html @@ -0,0 +1,187 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + Which of the following best describes your sexual orientation? +

    +
    + +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    + + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + +


    + Get a separate access code to submit an + individual response

    +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-passport-country-other.html b/_prototypes/your-household-v13/individual-details-passport-country-other.html new file mode 100644 index 0000000000..238e37d6b5 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-passport-country-other.html @@ -0,0 +1,134 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + You selected ‘Other’ for the passport you hold, can you specify this further? +

    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-passport-country.html b/_prototypes/your-household-v13/individual-details-passport-country.html new file mode 100644 index 0000000000..745371979f --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-passport-country.html @@ -0,0 +1,191 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + What passport do you hold? +

    +
    + +
    +
    +
    +
    + Select all that apply: +
    + + +
    +
    + + +
    +
    + + +
    + +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-qualifications-a-level.html b/_prototypes/your-household-v13/individual-details-qualifications-a-level.html new file mode 100644 index 0000000000..334efffc89 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-qualifications-a-level.html @@ -0,0 +1,215 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + Have you achieved an AS, A level or equivalent qualification? +

    +
    + +
    +
    +

    Include equivalent qualifications achieved anywhere outside England and Wales

    +
    +
    + +
    +
    +
    +
    + Select all that apply: +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    + + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-qualifications-degree.html b/_prototypes/your-household-v13/individual-details-qualifications-degree.html new file mode 100644 index 0000000000..d304a298c5 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-qualifications-degree.html @@ -0,0 +1,157 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + Have you achieved a qualification at degree level or above? +

    +
    + +
    +
    +

    Include equivalent qualifications achieved anywhere outside England and Wales

    +
    +
    + +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-qualifications-gcse.html b/_prototypes/your-household-v13/individual-details-qualifications-gcse.html new file mode 100644 index 0000000000..be3d094fa8 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-qualifications-gcse.html @@ -0,0 +1,231 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + Have you achieved a GCSE or equivalent qualification? +

    +
    + +
    +
    +

    Include equivalent qualifications achieved anywhere outside England and Wales

    +
    +
    + +
    +
    +
    +
    + Select all that apply: +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    + + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-qualifications-nvq-equivalent.html b/_prototypes/your-household-v13/individual-details-qualifications-nvq-equivalent.html new file mode 100644 index 0000000000..9020b38f09 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-qualifications-nvq-equivalent.html @@ -0,0 +1,205 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + Have you achieved an NVQ or equivalent qualification? +

    +
    + +
    +
    +

    Include equivalent qualifications achieved anywhere outside England and Wales

    +
    +
    + +
    +
    +
    +
    + Select all that apply: +
    + + +
    +
    + + +
    +
    + + +
    + +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    + + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-qualifications-other-where.html b/_prototypes/your-household-v13/individual-details-qualifications-other-where.html new file mode 100644 index 0000000000..075ab380e6 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-qualifications-other-where.html @@ -0,0 +1,179 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + Have you achieved any other qualifications? +

    +
    + +
    +
    +
    +
    + Select all that apply: +
    + + +
    +
    + + +
    + +
    + + + +
    +
    +
    +
    +
    +
    +
    +
    + + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-relationship.html b/_prototypes/your-household-v13/individual-details-relationship.html new file mode 100644 index 0000000000..3d93af784e --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-relationship.html @@ -0,0 +1,244 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + On , + what is your legal marital or + registered civil partnership status? +

    +
    + +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return + to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-salary.html b/_prototypes/your-household-v13/individual-details-salary.html new file mode 100644 index 0000000000..a5f87306c8 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-salary.html @@ -0,0 +1,136 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + What was your gross pay, that is your + pay before any deductions, the last + time you were paid? +

    +
    + +
    +
    +

    + Exclude + expenses (if possible) +

    +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + +


    + Get a separate access code to submit an + individual response

    +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-sex.html b/_prototypes/your-household-v13/individual-details-sex.html new file mode 100644 index 0000000000..7554798658 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-sex.html @@ -0,0 +1,154 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + What is your sex? +

    +
    + +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + + +

    Can't complete this question?
    + Choose another section and + return to this later

    + + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/individual-details-summary.html b/_prototypes/your-household-v13/individual-details-summary.html new file mode 100644 index 0000000000..e3727683d9 --- /dev/null +++ b/_prototypes/your-household-v13/individual-details-summary.html @@ -0,0 +1,790 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + + + +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    +

    + +

    +
    + +
    + This section + is now complete +

    You can check your + answers below

    +
    +
    + +
    + + +
    +
    +
    +
    +
    Full name
    +
    +
    +
    +
    +
    + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    + + + + + diff --git a/_prototypes/your-household-v13/individual-intro.html b/_prototypes/your-household-v13/individual-intro.html new file mode 100644 index 0000000000..ef8b0a9123 --- /dev/null +++ b/_prototypes/your-household-v13/individual-intro.html @@ -0,0 +1,87 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    + + +

    In this section, we’re going to ask you + questions about . +

    + +

    Information you need:

    +
      +
    • Personal details such as date of birth, + country of birth, religion etc.
    • +
    • Education and qualifications
    • +
    • Employment and travel to work
    • +
    • Second or holiday homes
    • +
    • Unpaid care and health
    • +
    • Languages
    • +
    + + +
    +
    +
    + + +
    +
    +
    +
    + + + + + + + diff --git a/_prototypes/your-household-v13/individual-pin-security.html b/_prototypes/your-household-v13/individual-pin-security.html new file mode 100644 index 0000000000..8e45419f9a --- /dev/null +++ b/_prototypes/your-household-v13/individual-pin-security.html @@ -0,0 +1,121 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +cdn: v1.5.0 +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +

    + Enter the individual access code +

    + +

    + You’ll need the code you were provided when you chose to submit an individual response. +

    + +
    + + +

    Help with individual access code

    + + +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/relationships-intro.html b/_prototypes/your-household-v13/relationships-intro.html new file mode 100644 index 0000000000..0fcd3de401 --- /dev/null +++ b/_prototypes/your-household-v13/relationships-intro.html @@ -0,0 +1,68 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    + +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    Household relationships

    + +

    In this section, we’ll ask you how the people who usually live in this household are related to each other.

    + +

    For the displayed household members, select the appropriate relationship from the options shown. The selected relationship will display at the bottom of the page for you to check.

    +
    +
    +
    + + +
    +
    +
    +
    + + + + + + + diff --git a/_prototypes/your-household-v13/relationships-person-select.html b/_prototypes/your-household-v13/relationships-person-select.html new file mode 100644 index 0000000000..0fd511e4e8 --- /dev/null +++ b/_prototypes/your-household-v13/relationships-person-select.html @@ -0,0 +1,286 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +householdMembers: +visitors: +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    + Are any of the following household + members also + + ? +

    + + + + + +

    Remaining household + members

    +

    Select all that apply

    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    + + +
    +
    + + + + diff --git a/_prototypes/your-household-v13/relationships-summary-proxy.html b/_prototypes/your-household-v13/relationships-summary-proxy.html new file mode 100644 index 0000000000..4fcdb9a017 --- /dev/null +++ b/_prototypes/your-household-v13/relationships-summary-proxy.html @@ -0,0 +1,20 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +householdMembers: +visitors: +cdn: v1.5.0 +--- + + + + diff --git a/_prototypes/your-household-v13/relationships-summary.html b/_prototypes/your-household-v13/relationships-summary.html new file mode 100644 index 0000000000..98689e4fdf --- /dev/null +++ b/_prototypes/your-household-v13/relationships-summary.html @@ -0,0 +1,1054 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +householdMembers: +visitors: +layout: eq-default-extras +cdn: v1.5.0 +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +

    + Relationships

    + +
    + This sub section is + now complete +
      +
    • You can check your answers below +
    • +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + +
    +
    +

    + + +

    +
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + + + + diff --git a/_prototypes/your-household-v13/relationships.html b/_prototypes/your-household-v13/relationships.html new file mode 100644 index 0000000000..ebff17f54b --- /dev/null +++ b/_prototypes/your-household-v13/relationships.html @@ -0,0 +1,598 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +householdMembers: +visitors: +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    + ... +

    +

    + Complete the sentence by selecting + the appropriate relationship. +

    + +
    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + + + + + diff --git a/_prototypes/your-household-v13/remove-household-member.html b/_prototypes/your-household-v13/remove-household-member.html new file mode 100644 index 0000000000..0e0c7d7ef9 --- /dev/null +++ b/_prototypes/your-household-v13/remove-household-member.html @@ -0,0 +1,114 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    + +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    + +
    +

    + Are you sure you want to remove + ? +

    +
    + +
    +
    + All of the data entered + about this person will be removed + +
    +
    + +
    +
    +
    +
    +
    + + +
    + +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + + diff --git a/_prototypes/your-household-v13/request-survey-confirm-number.html b/_prototypes/your-household-v13/request-survey-confirm-number.html new file mode 100644 index 0000000000..1b38def979 --- /dev/null +++ b/_prototypes/your-household-v13/request-survey-confirm-number.html @@ -0,0 +1,96 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Is {x}{s} number correct?

    +

    {n}

    + +
    + Is {x}{s} number correct? +
    + + +
    + +
    + + +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/_prototypes/your-household-v13/request-survey-confirmation.html b/_prototypes/your-household-v13/request-survey-confirmation.html new file mode 100644 index 0000000000..f245921dfb --- /dev/null +++ b/_prototypes/your-household-v13/request-survey-confirmation.html @@ -0,0 +1,80 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    We have sent a text message to {n}

    +

    {x} should recieve a text message with their own access code to complete their own survey soon.

    + + +
    +
    +
    +
    +
    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/_prototypes/your-household-v13/request-survey.html b/_prototypes/your-household-v13/request-survey.html new file mode 100644 index 0000000000..dbc99f1146 --- /dev/null +++ b/_prototypes/your-household-v13/request-survey.html @@ -0,0 +1,88 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +

    Requesting a seperate survey for {x}

    +

    If you have {x}{s} mobile number, we can send a text message to let them know you have requested a seperate survey for them to complete.

    +

    Alternatively, you can contact us for help

    + +
    + + +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/_prototypes/your-household-v13/section-intro.html b/_prototypes/your-household-v13/section-intro.html new file mode 100644 index 0000000000..c720787340 --- /dev/null +++ b/_prototypes/your-household-v13/section-intro.html @@ -0,0 +1,68 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    + +
    + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    People who live here

    + +

    In this section, we’re going to ask you about the people living or staying at + .

    + +

    Information you will need:

    +
      +
    • Names of the people living at this address including anyone temporarily away or who has been or intends to be in the UK 3 months or more.
    • +
    • Names of visitors staying overnight at this address on
    • +
    +
    +
    +
    + + +
    +
    +
    +
    + + + + + + + diff --git a/_prototypes/your-household-v13/style.css b/_prototypes/your-household-v13/style.css new file mode 100644 index 0000000000..389d133c45 --- /dev/null +++ b/_prototypes/your-household-v13/style.css @@ -0,0 +1,138 @@ +.list.list--people-plain { + margin-bottom : 1rem; + list-style-type : none; +} + +.list.list--people-plain .list__item { + background: url(/img/icons/person.svg) 0 .2rem no-repeat; +} + +.list.list--people-plain .list__item-name { + padding-left: 1.5rem; +} + +.list.list--people-plain .list__item-actions { + margin-top: -4px; +} + +.list.list--people-plain .list__item-action + .list__item-action { + margin-left : .5rem; +} + + +/** + * Sumamry records + */ +.list.list--records .list__item { + margin-bottom: 0; + padding: 1rem 0; +} + +.list.list--records .list__item + .list__item { + border-top: 1px solid #ccc; +} + +.list.list--people { + list-style-type: none; +} + +.list.list--people .list__item { + background: url(/img/icons/person.svg) 0 1.3rem no-repeat; +} + +.list.list--people .list__item-name { + padding-left: 2rem; +} + +.list.list--people .list__item-action + .list__item-action { + margin-left: .5rem; +} + + +.modal { + position: fixed; + z-index: 1000; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; +} + +.modal__window { + position: relative; + background-color: white; + max-width: 520px; + padding: 2rem; + text-align: center; +} + +/** + * Pattern library fix + */ +.header { + margin-bottom : 0; +} + +.lock { + padding-left : 1.5rem; + background : url('../img/icons/lockicon.svg') no-repeat left top; +} + +.footer { + margin-top : 7rem; +} + +.util-language { + float : right; +} + +.btn--danger { + background-color : #D0021B; + border-color : #D0021B; +} + +.btn--danger:hover { + background-color : #b00015; + border-color : #b00015; +} + +.page__content { + padding-bottom : .5rem; +} + +.ghost-link { + position : absolute; + bottom : 1rem; + right : 1rem; + margin-right : 0 !important; + color : #aaa; +} + +.footer { + position : relative; +} + +/** + * Save and complete later - copied from design system + */ +.btn--ghost-blue { + background-color: transparent; + color: #4263c2; + border-color: #334f9e; + border-width: 2px; + padding: .5rem 1rem +} + +.btn--ghost-blue:focus, .btn--ghost-blue:hover { + color: #4263c2; + background-color: transparent; + border-color: #4263c2 +} + +.btn--ghost-blue:focus { + outline: 3px solid #fe781f +} diff --git a/_prototypes/your-household-v13/summary.html b/_prototypes/your-household-v13/summary.html new file mode 100644 index 0000000000..82ee43131e --- /dev/null +++ b/_prototypes/your-household-v13/summary.html @@ -0,0 +1,181 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
    +
    + Previous +
    +
    + + + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    + People who live here and overnight + visitors +

    +
    + + +
    + +

    You’ve added {n} household member

    +
      + +

      + + Add someone to this household +

      + +
      +

      You’ve added {n} visitor staying + overnight on

      +
        + +

        Add a visitor +

        +
        +
        +
        +
        + +
        +
        +
        +
        +
        + + + + diff --git a/_prototypes/your-household-v13/temp-away-from-home.html b/_prototypes/your-household-v13/temp-away-from-home.html new file mode 100644 index 0000000000..6f6f3e163a --- /dev/null +++ b/_prototypes/your-household-v13/temp-away-from-home.html @@ -0,0 +1,101 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +cdn: v1.5.0 +--- + + +
        +
        + Previous +
        +
        + +
        +
        +
        + +
        +
        +
        +
        + + +
        +
        +
        + +
        +

        + Is there anyone else living at + + who is currently away? +

        +
        + +
          + +
          +
          +
          +
          + + +
          + +
          + + +
          +
          +
          +
          +
          +
          +
          + +
          +
          +
          +
          +
          + + + + diff --git a/_prototypes/your-household-v13/temp-living-here-feedback.html b/_prototypes/your-household-v13/temp-living-here-feedback.html new file mode 100644 index 0000000000..382a75d5a2 --- /dev/null +++ b/_prototypes/your-household-v13/temp-living-here-feedback.html @@ -0,0 +1,97 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +cdn: v1.5.0 +--- + + +
          +
          + Previous +
          +
          + +
          +
          +
          + +
          +
          +
          +
          + + +
          +
          +
          +
          +

          + Is there anyone else staying at + + who does not have another UK address? +

          +
          + +
            + +
            +
            +
            +
            + + +
            + +
            + + +
            +
            +
            +
            +
            +
            +
            + +
            +
            +
            +
            +
            + + + + diff --git a/_prototypes/your-household-v13/temp-living-here.html b/_prototypes/your-household-v13/temp-living-here.html new file mode 100644 index 0000000000..410a3d6c62 --- /dev/null +++ b/_prototypes/your-household-v13/temp-living-here.html @@ -0,0 +1,111 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +cdn: v1.5.0 +--- + + +
            +
            + Previous +
            +
            + +
            +
            +
            + +
            +
            +
            +
            + + +
            +
            +
            +
            +

            + Is there anyone else staying at + + who does not have another UK address? +

            +
            + +
              + +
              +
              + Select all that apply: +
              Select all that apply:
              +
              + + +
              +
              + + +
              + +
              + + + +
              +
              +
              +
              +
              +
              + +
              +
              +
              +
              +
              + + + + diff --git a/_prototypes/your-household-v13/temp-types-feedback.html b/_prototypes/your-household-v13/temp-types-feedback.html new file mode 100644 index 0000000000..dc1d2ff268 --- /dev/null +++ b/_prototypes/your-household-v13/temp-types-feedback.html @@ -0,0 +1,135 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +cdn: v1.5.0 +--- + + +
              +
              + Previous +
              +
              + +
              +
              +
              + +
              +
              +
              +
              +
              +
              +
              +
              +

              + Is there anyone else apart from the people already included, who you need to add? +

              +
              + +
              +
              + Include + +
                +
                +
                + +
                + +
                +
                +
                +
                + + +
                + +
                + + +
                +
                +
                +
                +
                +
                +
                + +
                +
                +
                +
                +
                + + + + + + diff --git a/_prototypes/your-household-v13/temp-types-living-away.html b/_prototypes/your-household-v13/temp-types-living-away.html new file mode 100644 index 0000000000..af8270a6a5 --- /dev/null +++ b/_prototypes/your-household-v13/temp-types-living-away.html @@ -0,0 +1,167 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
                +
                + Previous +
                +
                + +
                +
                +
                + +
                +
                +
                +
                + +
                +
                +
                +
                +

                + +

                +
                + +
                  + +
                  +
                  + Include + people who are temporarily away + +
                    + Include + people who are temporarily staying + +
                      +
                      +
                      +
                      +
                      +
                      + +
                      +
                      +
                      +
                      + + +
                      + +
                      + + +
                      +
                      +
                      +
                      + +
                      +
                      +

                      Why do we ask this question?

                      +
                      +

                      We ask this question to help ensure that everyone is correctly counted on the census. This may include people who are staying temporarily or are away.

                      + +
                      +
                      +
                      + + +
                      +
                      +
                      +
                      +
                      + + + + + + diff --git a/_prototypes/your-household-v13/temp-types-question.html b/_prototypes/your-household-v13/temp-types-question.html new file mode 100644 index 0000000000..6e9559b7d5 --- /dev/null +++ b/_prototypes/your-household-v13/temp-types-question.html @@ -0,0 +1,162 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +cdn: v1.5.0 +--- + + +
                      +
                      + Previous +
                      +
                      + +
                      +
                      +
                      + +
                      +
                      +
                      +
                      + +
                      +
                      +
                      +
                      +

                      + Who do you need to add? +

                      +
                      + +
                      +
                      + Include + +
                        +
                        +
                        + +
                          + +
                          + What is your name? + +
                          +
                          + + +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          +
                          + +
                          +
                          +
                          +
                          +
                          + + + + + + diff --git a/_prototypes/your-household-v13/test-address.html b/_prototypes/your-household-v13/test-address.html new file mode 100644 index 0000000000..d22c7c41c3 --- /dev/null +++ b/_prototypes/your-household-v13/test-address.html @@ -0,0 +1,224 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + + + +
                          +
                          + +
                          +
                          +
                          +
                          + +
                          +
                          + + +
                          +
                          +
                          +

                          + What is your address? +

                          + +
                          + + What is your address? + + +
                          +
                          +
                          +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          +
                          +
                          +
                          + + + +
                          +
                          +
                          +
                          + +
                          +
                          +
                          +
                          + + + +
                          +
                          +
                          +
                          + +
                          +
                          +
                          +
                          + + + +
                          +
                          +
                          +
                          +
                          +
                          +
                          +
                          +
                          + + +
                          +
                          +
                          +
                          + + + + + diff --git a/_prototypes/your-household-v13/test-data.html b/_prototypes/your-household-v13/test-data.html new file mode 100644 index 0000000000..5e8035c1ec --- /dev/null +++ b/_prototypes/your-household-v13/test-data.html @@ -0,0 +1,105 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
                          +
                          + Previous +
                          +
                          + +
                          +
                          +
                          +
                          +
                          +
                          +

                          + Test data +

                          + +
                          +
                          +
                          + +
                          +
                          +
                          + +
                          + + +
                          + +
                          + + + + +
                          +
                          +
                          +
                          +
                          + + + + diff --git a/_prototypes/your-household-v13/test-data/basic4personhousehold.json b/_prototypes/your-household-v13/test-data/basic4personhousehold.json new file mode 100644 index 0000000000..40fa35583d --- /dev/null +++ b/_prototypes/your-household-v13/test-data/basic4personhousehold.json @@ -0,0 +1 @@ +{"address":"12 Somewhere Close, Newport, CF12 3AB","address-line-1":"12","address-line-2":"Somewhere close","county":"Newport","household-members":"[{\"type\":\"household-member\",\"@person\":{\"fullName\":\"Dave Jones\",\"firstName\":\"Dave\",\"middleName\":\"\",\"lastName\":\"Jones\",\"id\":\"person_me\"}},{\"type\":\"household-member\",\"@person\":{\"fullName\":\"Sally Jones\",\"firstName\":\"Sally\",\"middleName\":\"\",\"lastName\":\"Jones\",\"id\":\"person1\"}},{\"type\":\"household-member\",\"@person\":{\"fullName\":\"Rebecca Jones\",\"firstName\":\"Rebecca\",\"middleName\":\"\",\"lastName\":\"Jones\",\"id\":\"person2\"}},{\"type\":\"household-member\",\"@person\":{\"fullName\":\"Amy Jones\",\"firstName\":\"Amy\",\"middleName\":\"\",\"lastName\":\"Jones\",\"id\":\"person3\"}}]","household-members-increment":"4","lives-here":"yes","postcode":"CF12 3AB","proxy-person":"false","relationships":"[{\"personIsDescription\":\"husband-wife\",\"personIsId\":\"person1\",\"personToId\":\"person_me\",\"inferred\":false,\"id\":1},{\"personIsDescription\":\"son-daughter\",\"personIsId\":\"person2\",\"personToId\":\"person_me\",\"inferred\":false,\"id\":2},{\"personIsDescription\":\"mother-father\",\"personIsId\":\"person_me\",\"personToId\":\"person3\",\"inferred\":false,\"id\":3},{\"personIsDescription\":\"son-daughter\",\"personIsId\":\"person2\",\"personToId\":\"person1\",\"inferred\":false,\"id\":4},{\"personIsDescription\":\"mother-father\",\"personIsId\":\"person1\",\"personToId\":\"person3\",\"inferred\":false,\"id\":5},{\"personIsDescription\":\"brother-sister\",\"personIsId\":\"person3\",\"personToId\":\"person2\",\"inferred\":true,\"inferredBy\":[3,5,2,4],\"id\":6}]","relationships-increment":"6","town-city":"Newport","user-details":"{\"fullName\":\"Dave Jones\",\"firstName\":\"Dave\",\"middleName\":\"\",\"lastName\":\"Jones\"}"} \ No newline at end of file diff --git a/_prototypes/your-household-v13/test-data/basic4personindividualdetails.json b/_prototypes/your-household-v13/test-data/basic4personindividualdetails.json new file mode 100644 index 0000000000..86130c0841 --- /dev/null +++ b/_prototypes/your-household-v13/test-data/basic4personindividualdetails.json @@ -0,0 +1 @@ +{"address":"12 Somewhere Close, Newport, CF12 3AB","address-line-1":"12","address-line-2":"Somewhere close","county":"Newport","household-members":"[{\"type\":\"household-member\",\"@person\":{\"fullName\":\"Dave Jones\",\"firstName\":\"Dave\",\"middleName\":\"\",\"lastName\":\"Jones\",\"id\":\"person_me\"}},{\"type\":\"household-member\",\"@person\":{\"fullName\":\"Sally Jones\",\"firstName\":\"Sally\",\"middleName\":\"\",\"lastName\":\"Jones\",\"id\":\"person1\"}},{\"type\":\"household-member\",\"@person\":{\"fullName\":\"Rebecca Jones\",\"firstName\":\"Rebecca\",\"middleName\":\"\",\"lastName\":\"Jones\",\"id\":\"person2\"}},{\"type\":\"household-member\",\"@person\":{\"fullName\":\"Amy Jones\",\"firstName\":\"Amy\",\"middleName\":\"\",\"lastName\":\"Jones\",\"id\":\"person3\"}}]","household-members-increment":"4","individual-details":"{\"person_me\":{\"sex\":\"male\",\"dob\":{\"day\":\"12\",\"month\":\"3\",\"year\":\"1982\"},\"age-confirm\":{\"val\":\"yes\"},\"maritalStatus\":\"married\",\"country\":{\"val\":\"elsewhere\",\"otherText\":\"French guiana\"},\"national-identity\":{\"collection\":[\"welsh\",\"british\"]},\"ethnic-group\":{\"val\":\"mixed\",\"description\":\"white-asian\"},\"passport\":{\"countries\":[\"ireland\",\"other\"],\"otherText\":\"British overseas territories (bot)\"},\"apprenticeship\":{\"hasApprenticeship\":\"yes\"},\"qualifications\":{\"aboveDegree\":\"yes\",\"nvqEquivalent\":[\"nvq-level-1\",\"nvq-level-3\"],\"aLevels\":[\"a-level-1-btec\"],\"gcses\":[\"gcse-5\"],\"othersWhere\":[\"none\"]},\"employment-status\":{\"val\":\"freelance-with-employees\"},\"job-title\":{\"val\":\"Binder's assistant\"},\"job-describe\":{\"val\":\"Assistance in binding\"}},\"person1\":{\"sex\":\"female\",\"dob\":{\"day\":\"23\",\"month\":\"6\",\"year\":\"1980\"},\"age-confirm\":{\"val\":\"yes\"},\"maritalStatus\":\"married\",\"country\":{\"val\":\"wales\"},\"national-identity\":{\"collection\":[\"english\",\"other\"],\"otherText\":\"French\"},\"ethnic-group\":{\"val\":\"white\",\"description\":\"other\",\"otherText\":\"English and french\"},\"passport\":{\"countries\":[\"united-kingdom\",\"ireland\"]},\"apprenticeship\":{\"hasApprenticeship\":\"no\"},\"qualifications\":{\"aboveDegree\":\"no\",\"nvqEquivalent\":[\"nvq-level-1\",\"nvq-level-3\"],\"aLevels\":[\"a-level-2\"],\"gcses\":[\"other-gcses\"],\"othersWhere\":[\"in-england-wales\",\"outside-england-wales\"]},\"employment-status\":{\"val\":\"freelance-without-employees\"},\"job-title\":{\"val\":\"Window cleaning contractor\"},\"job-describe\":{\"val\":\"Cleaning windows\"}},\"person2\":{\"sex\":\"female\",\"dob\":{\"day\":\"11\",\"month\":\"5\",\"year\":\"1999\"},\"age-confirm\":{\"val\":\"yes\"},\"maritalStatus\":\"never\",\"country\":{\"val\":\"england\"},\"national-identity\":{\"collection\":[\"english\",\"other\"],\"otherText\":\"Beninese\"},\"ethnic-group\":{\"val\":\"white\",\"description\":\"british\"},\"passport\":{\"countries\":[\"united-kingdom\"]},\"apprenticeship\":{\"hasApprenticeship\":\"no\"},\"qualifications\":{\"aboveDegree\":\"no\",\"nvqEquivalent\":[\"nvq-level-2\"],\"aLevels\":[\"a-level-2\"],\"gcses\":[\"gcse-5\",\"other-gcses\",\"basic-skills\",\"national-baccalaureate\",\"foundation-baccalaureate\"],\"othersWhere\":[\"none\"]},\"employment-status\":{\"val\":\"employee\"},\"job-title\":{\"val\":\"Shop assistant\"},\"job-describe\":{\"val\":\"Shelf stacking\"}},\"person3\":{\"sex\":\"female\",\"dob\":{\"day\":\"15\",\"month\":\"10\",\"year\":\"2001\"},\"age-confirm\":{\"val\":\"yes\"},\"maritalStatus\":\"never\",\"country\":{\"val\":\"england\"},\"national-identity\":{\"collection\":[\"english\"]},\"ethnic-group\":{\"val\":\"white\",\"description\":\"british\"},\"passport\":{\"countries\":[\"united-kingdom\"]},\"apprenticeship\":{\"hasApprenticeship\":\"yes\"},\"qualifications\":{\"aboveDegree\":\"no\",\"nvqEquivalent\":[\"nvq-level-1\"],\"aLevels\":[\"none\"],\"gcses\":[\"gcse-5\",\"other-gcses\",\"basic-skills\"],\"othersWhere\":[\"in-england-wales\"]},\"employment-status\":{\"val\":\"employee\"},\"job-title\":{\"val\":\"Bartender\"},\"job-describe\":{\"val\":\"Serving drinks\"}}}","lives-here":"yes","postcode":"CF12 3AB","proxy-person":"false","relationships":"[{\"personIsDescription\":\"husband-wife\",\"personIsId\":\"person1\",\"personToId\":\"person_me\",\"inferred\":false,\"id\":1},{\"personIsDescription\":\"son-daughter\",\"personIsId\":\"person2\",\"personToId\":\"person_me\",\"inferred\":false,\"id\":2},{\"personIsDescription\":\"mother-father\",\"personIsId\":\"person_me\",\"personToId\":\"person3\",\"inferred\":false,\"id\":3},{\"personIsDescription\":\"son-daughter\",\"personIsId\":\"person2\",\"personToId\":\"person1\",\"inferred\":false,\"id\":4},{\"personIsDescription\":\"mother-father\",\"personIsId\":\"person1\",\"personToId\":\"person3\",\"inferred\":false,\"id\":5},{\"personIsDescription\":\"brother-sister\",\"personIsId\":\"person3\",\"personToId\":\"person2\",\"inferred\":true,\"inferredBy\":[3,5,2,4],\"id\":6}]","relationships-increment":"6","town-city":"Newport","user-details":"{\"fullName\":\"Dave Jones\",\"firstName\":\"Dave\",\"middleName\":\"\",\"lastName\":\"Jones\"}"} \ No newline at end of file diff --git a/_prototypes/your-household-v13/uac-question.html b/_prototypes/your-household-v13/uac-question.html new file mode 100644 index 0000000000..49719a41aa --- /dev/null +++ b/_prototypes/your-household-v13/uac-question.html @@ -0,0 +1,139 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
                          +
                          + Previous +
                          +
                          + +
                          +
                          +
                          +
                          +
                          +
                          +
                          +
                          +
                          +

                          {x}{s} questions

                          +

                          If you want {x} to complete their own questions, they will need the unique access code from the census letter we sent you.

                          +

                          Alternatively, you can request a seperate survey for them to complete.

                          + +
                          + {x}{s} questions +
                          + + +
                          + +
                          + + +
                          + +
                          + +
                          + + +
                          +
                          + + +
                          +
                          +
                          +
                          +
                          +
                          +
                          +
                          +
                          + + \ No newline at end of file diff --git a/_prototypes/your-household-v13/visitors.html b/_prototypes/your-household-v13/visitors.html new file mode 100644 index 0000000000..15e7480a9c --- /dev/null +++ b/_prototypes/your-household-v13/visitors.html @@ -0,0 +1,163 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
                          + +
                          + +
                          +
                          +
                          + +
                          +
                          +
                          +
                          + + +
                          +
                          +
                          +
                          +

                          + Are there any visitors staying + overnight on at + ? +

                          +
                          + +
                          +
                          +
                          +
                          + Select all that apply: +
                          + + +
                          +
                          + + +
                          +
                          + + +
                          +
                          + + +
                          + +
                          + + + +
                          +
                          +
                          +
                          +
                          +
                          +
                          +
                          + +
                          +
                          +

                          Why do I have to add visitors?

                          +
                          +

                          We ask for visitor information to ensure that everyone is counted. This helps to produce accurate population estimates. Add any visitors, even if you think they may have been included on a census form at another address.

                          + +
                          +
                          +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + diff --git a/_prototypes/your-household-v13/what-is-your-name.html b/_prototypes/your-household-v13/what-is-your-name.html new file mode 100644 index 0000000000..ffaae60bac --- /dev/null +++ b/_prototypes/your-household-v13/what-is-your-name.html @@ -0,0 +1,224 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
                          +
                          + Previous +
                          +
                          + +
                          +
                          +
                          + +
                          + +
                          +
                          + + +
                          +
                          + +
                          +

                          + What is your name? +

                          + +
                          + What is your name? + +
                          +
                          + + +
                          + +
                          + + +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          +
                          + + + + +
                          +
                          +
                          +
                          + + + + + + + diff --git a/_prototypes/your-household-v13/who-else-to-add.html b/_prototypes/your-household-v13/who-else-to-add.html new file mode 100644 index 0000000000..6be5e92e98 --- /dev/null +++ b/_prototypes/your-household-v13/who-else-to-add.html @@ -0,0 +1,307 @@ +--- +title: Household prototype v13 +project: your-household +globalcss: false +layout: eq-default-extras +assetPrefixUrl: https://sdc-global-design-patterns.netlify.com +--- + + +
                          +
                          + Previous +
                          +
                          + +
                          +
                          +
                          + +
                          +
                          +
                          + + +
                          +
                          + +
                          +

                          + Who do you need to add to ? +

                          +
                          + + + +
                          + + + +
                          + Who do you need to add to ? + +
                          +
                          + + +
                          + +
                          + + +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + +

                          + + +
                          +
                          +
                          +
                          + + + + + + + diff --git a/package-lock.json b/package-lock.json index d11fa53685..5a8daa55da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,12 +24,6 @@ "through": ">=2.2.7 <3" } }, - "a-sync-waterfall": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.0.tgz", - "integrity": "sha1-OOgxnXk3niRiiEW1O5ZyKyng5Hw=", - "dev": true - }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1105,6 +1099,23 @@ "babel-types": "^6.24.1" } }, + "babel-polyfill": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", + "requires": { + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "regenerator-runtime": "^0.10.5" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=" + } + } + }, "babel-preset-es2015": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", @@ -1196,6 +1207,11 @@ "babel-plugin-transform-object-rest-spread": "^6.22.0" } }, + "babel-regenerator-runtime": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/babel-regenerator-runtime/-/babel-regenerator-runtime-6.5.0.tgz", + "integrity": "sha1-DkHNHJ+ARCRm8BXHSf/4upj44RA=" + }, "babel-register": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", @@ -1232,7 +1248,6 @@ "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, "requires": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" @@ -2116,8 +2131,7 @@ "core-js": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", - "dev": true + "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=" }, "core-util-is": { "version": "1.0.2", @@ -4639,232 +4653,6 @@ "through2": "^2.0.0" } }, - "gulp-nunjucks-render": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/gulp-nunjucks-render/-/gulp-nunjucks-render-2.2.1.tgz", - "integrity": "sha1-Nxxqyv6Uup1pzlpfxDwVI4Y7EPA=", - "dev": true, - "requires": { - "gulp-util": "~2.2.0", - "lodash": "^3.10.0", - "nunjucks": "^3.0.0", - "through2": "~0.4.0" - }, - "dependencies": { - "ansi-regex": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", - "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", - "dev": true - }, - "ansi-styles": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", - "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", - "dev": true - }, - "chalk": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", - "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", - "dev": true, - "requires": { - "ansi-styles": "^1.1.0", - "escape-string-regexp": "^1.0.0", - "has-ansi": "^0.1.0", - "strip-ansi": "^0.3.0", - "supports-color": "^0.2.0" - } - }, - "dateformat": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", - "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1", - "meow": "^3.3.0" - } - }, - "gulp-util": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz", - "integrity": "sha1-1xRuVyiRC9jwR6awseVJvCLb1kw=", - "dev": true, - "requires": { - "chalk": "^0.5.0", - "dateformat": "^1.0.7-1.2.3", - "lodash._reinterpolate": "^2.4.1", - "lodash.template": "^2.4.1", - "minimist": "^0.2.0", - "multipipe": "^0.1.0", - "through2": "^0.5.0", - "vinyl": "^0.2.1" - }, - "dependencies": { - "through2": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", - "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", - "dev": true, - "requires": { - "readable-stream": "~1.0.17", - "xtend": "~3.0.0" - } - } - } - }, - "has-ansi": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", - "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", - "dev": true, - "requires": { - "ansi-regex": "^0.2.0" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - }, - "lodash._reinterpolate": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz", - "integrity": "sha1-TxInqlqHEfxjL1sHofRgequLMiI=", - "dev": true - }, - "lodash.escape": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz", - "integrity": "sha1-LOEsXghNsKV92l5dHu659dF1o7Q=", - "dev": true, - "requires": { - "lodash._escapehtmlchar": "~2.4.1", - "lodash._reunescapedhtml": "~2.4.1", - "lodash.keys": "~2.4.1" - } - }, - "lodash.keys": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", - "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", - "dev": true, - "requires": { - "lodash._isnative": "~2.4.1", - "lodash._shimkeys": "~2.4.1", - "lodash.isobject": "~2.4.1" - } - }, - "lodash.template": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz", - "integrity": "sha1-nmEQB+32KRKal0qzxIuBez4c8g0=", - "dev": true, - "requires": { - "lodash._escapestringchar": "~2.4.1", - "lodash._reinterpolate": "~2.4.1", - "lodash.defaults": "~2.4.1", - "lodash.escape": "~2.4.1", - "lodash.keys": "~2.4.1", - "lodash.templatesettings": "~2.4.1", - "lodash.values": "~2.4.1" - } - }, - "lodash.templatesettings": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz", - "integrity": "sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk=", - "dev": true, - "requires": { - "lodash._reinterpolate": "~2.4.1", - "lodash.escape": "~2.4.1" - } - }, - "minimist": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz", - "integrity": "sha1-Tf/lJdriuGTGbC4jxicdev3s784=", - "dev": true - }, - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "strip-ansi": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", - "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", - "dev": true, - "requires": { - "ansi-regex": "^0.2.1" - } - }, - "supports-color": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", - "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", - "dev": true - }, - "through2": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", - "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", - "dev": true, - "requires": { - "readable-stream": "~1.0.17", - "xtend": "~2.1.1" - }, - "dependencies": { - "xtend": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", - "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", - "dev": true, - "requires": { - "object-keys": "~0.4.0" - } - } - } - }, - "vinyl": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.2.3.tgz", - "integrity": "sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI=", - "dev": true, - "requires": { - "clone-stats": "~0.0.1" - } - }, - "xtend": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", - "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", - "dev": true - } - } - }, "gulp-plumber": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/gulp-plumber/-/gulp-plumber-1.1.0.tgz", @@ -5993,51 +5781,18 @@ "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=", "dev": true }, - "lodash._escapehtmlchar": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz", - "integrity": "sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0=", - "dev": true, - "requires": { - "lodash._htmlescapes": "~2.4.1" - } - }, - "lodash._escapestringchar": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz", - "integrity": "sha1-7P4iYYoq3lC/7qQ5N+Ud9m8O23I=", - "dev": true - }, "lodash._getnative": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", "dev": true }, - "lodash._htmlescapes": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz", - "integrity": "sha1-MtFL8IRLbeb4tioFG09nwii2JMs=", - "dev": true - }, "lodash._isiterateecall": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", "dev": true }, - "lodash._isnative": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz", - "integrity": "sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw=", - "dev": true - }, - "lodash._objecttypes": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz", - "integrity": "sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE=", - "dev": true - }, "lodash._reescape": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", @@ -6056,44 +5811,12 @@ "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", "dev": true }, - "lodash._reunescapedhtml": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz", - "integrity": "sha1-dHxPxAED6zu4oJduVx96JlnpO6c=", - "dev": true, - "requires": { - "lodash._htmlescapes": "~2.4.1", - "lodash.keys": "~2.4.1" - }, - "dependencies": { - "lodash.keys": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", - "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", - "dev": true, - "requires": { - "lodash._isnative": "~2.4.1", - "lodash._shimkeys": "~2.4.1", - "lodash.isobject": "~2.4.1" - } - } - } - }, "lodash._root": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", "dev": true }, - "lodash._shimkeys": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", - "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", - "dev": true, - "requires": { - "lodash._objecttypes": "~2.4.1" - } - }, "lodash._topath": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/lodash._topath/-/lodash._topath-3.8.1.tgz", @@ -6115,29 +5838,6 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, - "lodash.defaults": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", - "integrity": "sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ=", - "dev": true, - "requires": { - "lodash._objecttypes": "~2.4.1", - "lodash.keys": "~2.4.1" - }, - "dependencies": { - "lodash.keys": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", - "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", - "dev": true, - "requires": { - "lodash._isnative": "~2.4.1", - "lodash._shimkeys": "~2.4.1", - "lodash.isobject": "~2.4.1" - } - } - } - }, "lodash.escape": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", @@ -6165,15 +5865,6 @@ "integrity": "sha1-+4m2WpqAKBgz8LdHizpRBPiY67M=", "dev": true }, - "lodash.isobject": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", - "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", - "dev": true, - "requires": { - "lodash._objecttypes": "~2.4.1" - } - }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -6282,28 +5973,6 @@ "lodash.escape": "^3.0.0" } }, - "lodash.values": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz", - "integrity": "sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ=", - "dev": true, - "requires": { - "lodash.keys": "~2.4.1" - }, - "dependencies": { - "lodash.keys": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", - "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", - "dev": true, - "requires": { - "lodash._isnative": "~2.4.1", - "lodash._shimkeys": "~2.4.1", - "lodash.isobject": "~2.4.1" - } - } - } - }, "log-symbols": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", @@ -6768,41 +6437,6 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, - "nunjucks": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.0.1.tgz", - "integrity": "sha1-TedKPlULr2+jNwMj89HHwqhr3E0=", - "dev": true, - "requires": { - "a-sync-waterfall": "^1.0.0", - "asap": "^2.0.3", - "chokidar": "^1.6.0", - "yargs": "^3.32.0" - }, - "dependencies": { - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - }, - "yargs": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", - "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", - "dev": true, - "requires": { - "camelcase": "^2.0.1", - "cliui": "^3.0.3", - "decamelize": "^1.1.1", - "os-locale": "^1.4.0", - "string-width": "^1.0.1", - "window-size": "^0.1.4", - "y18n": "^3.2.0" - } - } - } - }, "oauth-sign": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", @@ -6821,12 +6455,6 @@ "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", "dev": true }, - "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", - "dev": true - }, "object-path": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.9.2.tgz", @@ -7728,8 +7356,7 @@ "regenerator-runtime": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", - "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==", - "dev": true + "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==" }, "regenerator-transform": { "version": "0.10.1",