{"version":3,"file":"tds.js","sources":["../source/js/shared/i18n.js","../source/js/components/date-input/js/localization.js","../source/js/shared/constants.js","../source/js/shared/eventDispatcher.js","../source/js/shared/when-dom-ready.js","../source/js/shared/onDOMChanges.js","../source/js/shared/instances.js","../source/js/components/date-input/js/date-utils.js","../source/js/components/date-input/js/calendar.js","../source/js/shared/containTabFocus.js","../source/js/shared/getTargetElement.js","../source/js/shared/animation.js","../source/js/shared/keyboard.js","../source/js/components/date-input/date-input.js","../source/js/components/dropdown/dropdown-menu.js","../source/js/shared/ModuleInstance.js","../source/js/shared/Dialog.js","../source/js/shared/debounce.js","../source/js/shared/scroll-shadow/ScrollShadow.js","../source/js/modules/modal/modal.js","../source/js/components/timepicker/timepicker.js","../source/js/modules/alert/alert.js","../source/js/modules/workflow-navigation/workflow-navigation.js","../source/js/templates/workflow/workflow.js","../source/js/components/compound-field/compound-field.js","../source/js/components/masked-field/masked-field.js","../source/js/modules/navigation/navigation.js","../source/js/modules/data-table/js/scrollpanel-shadow.js","../source/js/modules/data-table/js/bindTableSortControl.js","../source/js/modules/data-table/data-table.js"],"sourcesContent":["const languages = {\n default: 'en',\n en: {\n // date-input/calendar\n year: 'Year',\n month: 'Month',\n current: 'current',\n minimum: 'minimum',\n maximum: 'maximum',\n today: 'Today',\n close: 'Close',\n nextYear: 'Next year',\n nextMonth: 'Next month',\n prevYear: 'Previous year',\n prevMonth: 'Previous month',\n calendar: 'Calendar',\n pageUp: 'Page Up',\n pageDown: 'Page Down',\n escapeLabel: \"Press escape to close calendar\",\n altKey: 'Alt',\n optionKey: 'Option',\n day: 'Day',\n months: [\n {name: 'January', abbr: 'Jan'},\n {name: 'February', abbr: 'Feb'},\n {name: 'March', abbr: 'Mar'},\n {name: 'April', abbr: 'Apr'},\n {name: 'May', abbr: 'May'},\n {name: 'June', abbr: 'Jun'},\n {name: 'July', abbr: 'Jul'},\n {name: 'August', abbr: 'Aug'},\n {name: 'September', abbr: 'Sep'},\n {name: 'October', abbr: 'Oct'},\n {name: 'November', abbr: 'Nov'},\n {name: 'December', abbr: 'Dec'}\n ],\n days: [\n {name: 'Sunday', abbr: 'Sun', initial: 'S'},\n {name: 'Monday', abbr: 'Mon', initial: 'M'},\n {name: 'Tuesday', abbr: 'Tue', initial: 'T'},\n {name: 'Wednesday', abbr: 'Wed', initial: 'W'},\n {name: 'Thursday', abbr: 'Thu', initial: 'T'},\n {name: 'Friday', abbr: 'Fri', initial: 'F'},\n {name: 'Saturday', abbr: 'Sat', initial: 'S'}\n ],\n // sorted content\n ascending: 'ascending',\n descending: 'descending',\n htmlTableIs: 'Table {0} is ',\n tableSortStateDescription: 'sorted by {0}, {1}',\n notSorted: 'not sorted',\n sortColumnDescription: \"Sorts by this column\",\n // for unit test\n color: 'color',\n interpolate: '{0} {1} {2} - {2} {1} {0}'\n },\n // additional languages are for unit tests now\n \"en-gb\": {\n color: 'colour',\n },\n fr: {\n year: 'Année',\n sortColumnDescription: \"Trie cette colonne\"\n }\n}\n\nfunction translations(lang) {\n if ('string' !== typeof lang) {\n // hopefully, it's an Element\n lang = getLang(lang)\n }\n lang = lang.toLowerCase()\n const langParts = lang.split('-')\n const language = langParts.reduce((acc, value, index ) => {\n const langCode = langParts.slice(0, index+1).join('-')\n const l = languages[langCode]\n return l ? {...acc, ...l} : acc\n }, lang.indexOf(languages.default) === -1 ? languages[languages.default] : {})\n\n return {\n t: (key, ...replacements) => translate(language, key, ...replacements),\n language\n }\n}\n\nfunction translate(language, key, ...replacements) {\n let value = language[key] || ''\n if ('string' === typeof value) {\n // replace positional substitions.\n // e.g. {0} is replaced with replacements[0], {1} is replaced with replacements[1], etc.\n value = replacements.reduce((v, r, i) => {\n return v.replace(new RegExp(`\\\\{${i}\\\\}`, 'g'), r)\n }, value)\n }\n return value\n}\n\nfunction getLang(element) {\n const langEl = element && element.closest('[lang]')\n return ((langEl && langEl.lang) || window.navigator.language)\n}\n\nexport { getLang, translations }\n","/*\n Manages internationalization and localization for Dates\n\n Translations are managed by shared/i18n/index.js. Localization (e.g. Date part order) still managed here\n\n */\nimport { getLang, translations } from '../../../shared/i18n'\n\nconst l10n = {\n default: 'iso',\n us: { dateFormat: 'MM/DD/YYYY', firstDayOfWeek: 0},\n gb: {dateFormat: 'DD/MM/YYYY'},\n iso: {dateFormat: 'YYYY/MM/DD', firstDayOfWeek: 1}\n}\n\n/**\n * returns language map and format for the element\n * @param element {HTMLElement} The element to apply localization based on its or an ancestor's lang attribute\n * @returns {object} {lang: object, locale: string}\n */\nfunction getLocale (element) {\n const langAttr = getLang(element)\n const lang = translations(langAttr).language\n const country = langAttr.toLowerCase().split('-')[1] ||\n window.navigator.language.toLowerCase().split('-')[1] ||\n l10n.default\n const localization = l10n[country] || l10n[l10n.default]\n const format = localization.dateFormat || l10n[l10n.default].dateFormat\n const firstDayOfWeek = localization.hasOwnProperty('firstDayOfWeek') ?\n localization.firstDayOfWeek : l10n[l10n.default].firstDayOfWeek\n const dateFormatLabel = format\n .replace('MM', lang.month)\n .replace('DD', lang.day)\n .replace('YYYY', lang.year)\n .replace(/\\//g, ' ')\n .toLowerCase()\n return {lang, format, firstDayOfWeek, dateFormatLabel}\n}\n\nexport { getLocale }\n","export const NAMESPACE = 'tds'\nexport const CSS_NS = `${NAMESPACE}-`\n","/**\n * Cross-browser (meaning IE) function to create an Event to dispatch. Creates a Custom Event\n * @param eventType {string} - The name of the event\n * @param eventInit {object} - Dictionary with the event initialization values as defined in https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent.\n * For this function, though, bubbles defaults to true\n * @return {CustomEvent} A CustomEvent object\n */\nconst createEvent = (eventType, eventInit = {}) => {\n eventInit = {...eventInit}\n const additionalProperties = stripNonCustomEventInitProperties(eventInit)\n let event\n const init = {...{bubbles: true}, ...eventInit}\n if ('CustomEvent' in window && typeof window.CustomEvent === 'function') {\n event = new window.CustomEvent(eventType, init)\n } else {\n event = document.createEvent('CustomEvent')\n const bubbles = !!init.bubbles\n const cancelable = !!init.cancelable\n const details = init.detail\n event.initCustomEvent(eventType, bubbles, cancelable, details)\n }\n\n // add any additioab properties as read-only\n Object.keys(additionalProperties)\n .forEach(key => {\n Object.defineProperty(event, key, {\n get() {\n return additionalProperties[key]\n }\n })\n })\n\n return event\n}\n\n/**\n * Dispatches a (Custom) Event for the target\n * @param target {EventTarget} - The target to dispatch the event on\n * @param eventType {string} - The name of the event\n * @param eventInit {object} - Dictionary with the event initialization values as defined in https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent.\n * For this function, though, bubbles defaults to true\n * @return {boolean} False if event is cancelable and at least one of the event handlers called Event.preventDefault(); otherwise true.\n */\nconst dispatchEvent = (target, eventType, eventInit) => {\n return target.dispatchEvent(createEvent(eventType, eventInit))\n}\n\nfunction stripNonCustomEventInitProperties (eventInit) {\n const properties = 'detail bubbles cancelable composed'\n const strippedProperties = {}\n Object.keys(eventInit).forEach(key => {\n if (properties.indexOf(key) === -1) {\n strippedProperties[key] = eventInit[key]\n delete eventInit[key]\n }\n })\n return strippedProperties\n}\nexport {\n dispatchEvent,\n createEvent\n}\n","/**\n * Calls a function when the DOM is loaded\n * @param callback {function} The function to call when the DOM is loaded\n */\nconst whenDOMReady = callback => {\n if (document.readyState === \"loading\") {\n document.addEventListener('DOMContentLoaded', callback)\n }\n else {\n callback()\n }\n}\n\nexport default whenDOMReady\n","import whenReady from './when-dom-ready'\n\nlet observer\nlet nextId = 1\nconst listeners = []\nfunction onDomChanges (selector, onNodeAdded, onNodeRemoved) {\n const id = nextId++\n listeners.push({id, selector, onNodeAdded, onNodeRemoved})\n\n whenReady(() => {\n if ('function' === typeof onNodeAdded) {\n const existingNodes = document.body.querySelectorAll(selector)\n for(let i=0; i < existingNodes.length; i++) {\n onNodeAdded(existingNodes[i])\n }\n }\n\n if (!observer) {\n observer = new MutationObserver(observerCallback)\n observer.observe(document.body, {childList: true, subtree: true})\n }\n })\n\n const cancel = () => {\n for(let i =0; i< listeners.length; i++) {\n if (listeners[i].id === id) {\n listeners.splice(i,1)\n break;\n }\n }\n }\n return cancel\n}\n\nfunction observerCallback( mutationList) {\n mutationList.forEach((mutation) => {\n if (mutation.type === 'childList') {\n onChildListUpdates(mutation)\n }\n })\n}\n\nfunction onChildListUpdates({addedNodes, removedNodes}) {\n listeners.forEach(({selector, onNodeAdded, onNodeRemoved}) => {\n if (('function' === typeof onNodeAdded) && addedNodes && addedNodes.length) {\n dispatchUpdates(selector, addedNodes, onNodeAdded)\n }\n if (('function' === typeof onNodeRemoved) && removedNodes && removedNodes.length) {\n dispatchUpdates(selector, removedNodes, onNodeRemoved)\n }\n })\n}\n\nfunction dispatchUpdates(selector, nodeList, callback) {\n const matchedNodes = []\n for (let i = 0; i < nodeList.length; i++ ) {\n const node = nodeList[i]\n if (node instanceof HTMLElement) {\n if (node.matches(selector)) {\n matchedNodes.push(node)\n }\n const childNodes = node.querySelectorAll(selector)\n for (let j = 0; j< childNodes.length; j++) {\n matchedNodes.push(childNodes[j])\n }\n }\n }\n matchedNodes.forEach(callback)\n}\n\nexport default onDomChanges\n","const storeInstances = {}\nlet id = 1\n\nconst instances = {\n set(element, key, instance) {\n if (typeof element.tdsEnhancers === 'undefined') {\n element.tdsEnhancers = {}\n }\n\n if (typeof element.tdsEnhancers[key] === 'undefined') {\n element.tdsEnhancers[key] = {id}\n id++\n }\n\n storeInstances[element.tdsEnhancers[key].id] = instance\n },\n\n get(element, key) {\n if (!element || typeof element.tdsEnhancers === 'undefined' || typeof element.tdsEnhancers[key] === 'undefined') {\n return null\n }\n\n return storeInstances[element.tdsEnhancers[key].id] || null\n },\n\n remove(element, key) {\n if (!element || typeof element.tdsEnhancers === 'undefined' || typeof element.tdsEnhancers[key] === 'undefined') {\n return\n }\n\n delete storeInstances[element.tdsEnhancers[key].id]\n delete element.tdsEnhancers[key]\n if (Object.keys(element.tdsEnhancers).length === 0) {\n delete element.tdsEnhancers\n }\n }\n}\n\nexport { instances }\n","/**\n * Tests whether or not the parameter passed in is a valid date instance\n * Only validates against a Date object. Does not validate strings\n * @param date The date instance to evaluate\n * @return {boolean} True if the parameter is a Date instance with a valid date\n */\nfunction isValidDateInstance(date) {\n return !!(date && (date instanceof Date) && !isNaN(date.getDate()))\n}\n\n/**\n * Parses a string into a date.\n * @param str {string} The string to parse\n * @param localeFormat The locale format, e.g. MM-DD-YYYY\n * @return {*}\n */\nfunction parseDate(str, localeFormat) {\n const ISO_RX = /^(\\d{4})-(\\d{2})-(\\d{2})/\n const DELIMITED_RX = /^(\\d+)\\D(\\d+)\\D(\\d+)$/\n const NOT_DELIMITER_RX = /^\\d{8}$/\n let dateStr = str\n let date = null\n\n if (!ISO_RX.test(str)) {\n const formatParts = localeFormat.split('/')\n let parts\n\n // try to convert other formats into an ISO formatted string\n const delimitedMatch = DELIMITED_RX.exec(str)\n if (delimitedMatch) {\n // try locally formatted date such as MM/DD/YYYY or DD.MM.YYYY\n parts = formatParts.reduce((map, placeholder, i) => {\n map[placeholder] = delimitedMatch[i+1]\n return map\n }, {})\n }\n else if (NOT_DELIMITER_RX.test(str)) {\n // try local date formatted without\n let offset = 0\n parts = formatParts.reduce((map, placeholder) => {\n map[placeholder] = dateStr.substring(offset, offset + placeholder.length)\n offset += placeholder.length\n return map\n }, {})\n }\n if (parts) {\n dateStr = `${parts.YYYY}-${`0${parts.MM}`.slice(-2)}-${`0${parts.DD}`.slice(-2)}`\n }\n }\n\n const ISO_MATCH = ISO_RX.exec(dateStr);\n if (ISO_MATCH) {\n date = new Date(parseInt(ISO_MATCH[1]), parseInt(ISO_MATCH[2]) - 1, parseInt(ISO_MATCH[3]), 0, 0, 0, 0)\n const dateStr2 = date.toISOString()\n if (dateStr.substring(0,10) !== dateStr2.substring(0,10)) {\n // if date parts change, e.g 2020-04-31 becomes 2020-05-01,\n // then it is not a valid date\n date = null;\n }\n }\n\n if (!isValidDateInstance(date)) {\n // try as entered\n date = new Date(str)\n }\n\n if (!isValidDateInstance(date)) {\n date = null\n }\n\n return date\n}\n\n/**\n * Formats a Date in ISO format without time (YYYY-MM-DD)\n * @param date The date to format\n * @return {string} The formatted date\n */\nfunction toISODate(date) {\n const yy = date.getFullYear()\n const mm = `0${date.getMonth() + 1}`.slice(-2)\n const dd = `0${date.getDate()}`.slice(-2)\n return [yy, mm, dd].join('-')\n}\n\n\n/**\n * Formats a date into a readable string - day date month year. e.g. Monday 4 February 2019\n * @param date\n * @param lang\n * @return {*}\n */\nfunction toReadableDate(date, lang) {\n if (!date || isNaN(date.getDate())) {\n return ''\n }\n const day = lang.days[date.getDay()].name\n const month = lang.months[date.getMonth()].name\n return `${day} ${date.getDate()} ${month} ${date.getFullYear()}`\n}\n\nfunction configureDates(config, format) {\n return {...config, dueDate: stringifyConfigDate(config.dueDate, format), ...setMinMaxOptions(config.min, config.max, format), ...configureDisabledDates(config)}\n}\n\nfunction stringifyConfigDate(date, format) {\n if (date) {\n switch (typeof date) {\n case 'string':\n const dt = parseDate(date, format)\n return (dt && toISODate(dt)) || undefined\n default:\n return (isValidDateInstance(date) && toISODate(date)) || undefined\n }\n }\n return undefined\n}\n\nfunction setMinMaxOptions(minIn, maxIn, format) {\n const min_max = [minIn, maxIn]\n .map(d => stringifyConfigDate(d, format))\n let [min, max] = min_max\n if (min && max && min > max) {\n min = max = null\n }\n return {min, max}\n}\n\nfunction configureDisabledDates(config = {}) {\n const DATE_RANGE_RX = /(\\d{4}-\\d{2}-\\d{2})\\s*-\\s*(\\d{4}-\\d{2}-\\d{2})/\n let { disabledDates } = config\n if (typeof disabledDates === 'string') {\n disabledDates = disabledDates.split(',')\n for(let i=0; i< disabledDates.length; i++) {\n disabledDates[i] = disabledDates[i].trim()\n const rangeMatch = DATE_RANGE_RX.exec(disabledDates[i])\n if (rangeMatch) {\n disabledDates[i] = {from: rangeMatch[1], to: rangeMatch[2]}\n }\n }\n }\n else if ( disabledDates) {\n disabledDates = [].concat(disabledDates)\n for(let i=0; i< disabledDates.length; i++) {\n if (disabledDates[i] instanceof Date) {\n disabledDates[i] = stringifyConfigDate(disabledDates[i])\n }\n else if (typeof disabledDates[i] === 'object') {\n if (disabledDates[i].from instanceof Date) {\n disabledDates[i].from = stringifyConfigDate(disabledDates[i].from)\n }\n if (disabledDates[i].to instanceof Date) {\n disabledDates[i].to = stringifyConfigDate(disabledDates[i].to)\n }\n }\n }\n }\n return disabledDates ? {disabledDates: [].concat(disabledDates)} : {}\n}\n\nfunction isDateDisabled(disabledDates, date, dateStr = toISODate(date)) {\n let disabled = false\n if (disabledDates) {\n for(let i=0; i< disabledDates.length; i++) {\n switch (typeof disabledDates[i]) {\n case 'string': {\n disabled = disabledDates[i] === dateStr\n break\n }\n case 'object': {\n const from = disabledDates[i].from\n const to = disabledDates[i].to\n disabled = (from && dateStr >= from && to && dateStr <= to)\n break\n }\n case 'function': {\n disabled = disabledDates[i](date)\n break\n }\n }\n if (disabled) break\n }\n }\n return disabled\n}\n\n\nexport {\n isValidDateInstance,\n parseDate,\n toISODate,\n toReadableDate,\n isDateDisabled,\n configureDates\n}\n\n","import {getLocale} from './localization'\nimport {CSS_NS, NAMESPACE} from '../../../shared/constants'\nimport {dispatchEvent} from '../../../shared/eventDispatcher'\nimport onDOMChanges from '../../../shared/onDOMChanges'\nimport { instances } from '../../../shared/instances'\nimport { isValidDateInstance,\n parseDate,\n toISODate,\n toReadableDate,\n configureDates,\n isDateDisabled } from './date-utils'\n\nconst CALENDAR_CLASS = `${CSS_NS}calendar`\nconst CALENDAR_MONTH_CLASS = `${CALENDAR_CLASS}__month`\nconst CALENDAR_HEADER_CLASS = `${CALENDAR_CLASS}__header`\nconst CALENDAR_FOOTER_CLASS = `${CALENDAR_CLASS}__footer`\nconst CALENDAR_BODY_CLASS = `${CALENDAR_CLASS}__body`\nconst SELECTABLE_DAY_CLASS = `${CALENDAR_CLASS}__select-date`\nconst SELECTED_DAY_CLASS = `${SELECTABLE_DAY_CLASS}--selected`\nconst CURRENT_DATE_CLASS = `${SELECTABLE_DAY_CLASS}--current-date`\nconst DUE_DATE_CLASS = `${SELECTABLE_DAY_CLASS}--due-date`\nconst DAY_OUTSIDE_MONTH_CLASS = `${SELECTABLE_DAY_CLASS}--outside-month`\nconst INSTANCE_KEY = `${NAMESPACE}Calendar`\nconst REMOVED_EVENT = 'calendar:removed'\nconst HOOKS = {\n onDayCreated: \"onDayCreated\"\n}\nconst defaultConfig = {\n today: true\n}\n\nclass _CalendarInstance {\n constructor(element) {\n this._element = element\n element.dataset.enhancedCalendar = true\n init(this)\n\n this._handlers = [\n [element, 'keydown', keyHandler(this)],\n [element, 'click', actionHandler(this)],\n [document, REMOVED_EVENT, calendarRemovedHandler(this)]\n ]\n bindHandlers(this, true)\n instances.set(element, INSTANCE_KEY , this)\n }\n\n render() {\n renderCalendarTemplate(this)\n renderMonth(this)\n enableNavigationButtons(this)\n updateCalendarDays(this)\n this._rendered = true\n }\n\n get selectedDate() {\n return this.state.selectedDate\n }\n\n set selectedDate(date) {\n let selectedDate = isValidDateInstance(date) ? date : null\n const activeDate = constrainActiveDate(this, selectedDate || new Date())\n if (selectedDate) {\n const selDateStr = toISODate(selectedDate)\n const activeDateStr = toISODate(activeDate)\n if (selDateStr !== activeDateStr) {\n //selected date is not in range. keep current value\n selectedDate = this.state.selectedDate\n }\n }\n this.state = {...this.state, selectedDate: selectedDate, activeDate: activeDate }\n }\n\n get state() {\n return this._state\n }\n\n set state(newState) {\n this._state = newState\n if (this._rendered) {\n this.render()\n }\n }\n\n setConfig(config = {}) {\n const min = config.min\n const max = config.max\n const currentOptions = this._config || {}\n this._config = {...currentOptions, ...config, ...configureDates(config, this._locale.format)}\n if (this._rendered) {\n this.render()\n }\n }\n\n setActiveDate(date) {\n const activeDate = constrainActiveDate(this, date || new Date())\n this.state = {...this.state, activeDate: activeDate}\n }\n\n /**\n * Focuses on the selected date; otherwise first button\n */\n focus() {\n const calElement = this._element\n const focusOn = calElement && (calElement.querySelector(`[data-date][tabIndex=\"0\"]`) || calElement.querySelector(`[data-action]`))\n if (focusOn) {\n setTimeout(() => focusOn.focus(), 0)\n }\n }\n\n addHook(hookName, callback) {\n const hooks = this._hooks || (this._hooks = {})\n const hookArray = hooks[hookName] || (hooks[hookName] = [])\n hookArray.push(callback)\n }\n\n removeHook(hookName, callback) {\n const hookArray = this._hooks && this._hooks[hookName]\n if (hookArray) {\n const i = hookArray.indexOf(callback)\n if (i > -1) {\n hookArray.splice(i,1)\n }\n }\n }\n\n destroy() {\n if (this._element) {\n bindHandlers(this, false)\n instances.remove(this._element, INSTANCE_KEY)\n delete this._element\n }\n }\n}\n\nfunction init(calendar) {\n const element = calendar._element\n calendar._locale = getLocale(element)\n\n //initialize config with default config and data attributes\n const config = {...defaultConfig}\n for(let data in element.dataset) {\n if (data !== 'enhancedCalendar') {\n const value = element.dataset[data]\n config[data] = value === 'true' ? true : value === 'false' ? false : value\n }\n }\n calendar.setConfig(config)\n calendar.setActiveDate(new Date())\n}\n\nfunction constrainActiveDate(calendar, activeDate) {\n const {min, max} = calendar._config\n if (min || max ) {\n const activeDateStr = toISODate(activeDate)\n const { format } = calendar._locale.format\n if (max && activeDateStr > max) {\n activeDate = parseDate(max, format)\n }\n else if (min && activeDateStr < min) {\n activeDate = parseDate(min, format)\n }\n }\n return activeDate\n}\n\n////////////////////////////////////////////////\n// - Render functions\n\nfunction renderCalendarTemplate(calendar) {\n const container = calendar._element\n const prevConfig = calendar._prevConfig || {}\n const { min, max, today } = calendar._config\n let calendarMonth = container.querySelector(`.${CALENDAR_MONTH_CLASS}`)\n if (calendarMonth && prevConfig.min === min && prevConfig.max === max && prevConfig.today === today ) {\n return\n }\n\n calendar._prevConfig = calendar._config\n const {lang, firstDayOfWeek} = calendar._locale\n container.querySelector(`.${CALENDAR_BODY_CLASS}`)\n\n const week = [0, 1, 2, 3, 4, 5, 6]\n const hdrCells = week.map(ix => {\n const day = (ix + firstDayOfWeek) % 7\n return `
' + this.sectionElements[i].getAttribute('data-summary') + '
'\n }\n summaryText += `\n