{"version":3,"file":"sohoxi.js.map","sources":["../../components/utils/debug.js","../../components/utils/dom.js","../../components/utils/behaviors.js","../../components/utils/breakpoints.js","../../components/utils/debounced-resize.js","../../components/utils/debounced-resize.jquery.js","../../components/utils/environment.js","../../components/utils/utils.js","../../components/locale/locale.js","../../components/utils/highlight.js","../../components/arrange/arrange.js","../../components/arrange/arrange.jquery.js","../../components/drag/drag.js","../../components/drag/drag.jquery.js","../../components/place/place.js","../../components/place/place.jquery.js","../../components/personalize/personalize.js","../../components/personalize/personalize.bootstrap.js","../../components/personalize/personalize.jquery.js","../../components/icons/icons.js","../../components/icons/icons.jquery.js","../../components/button/button.js","../../components/button/button.jquery.js","../../components/hyperlinks/hyperlinks.js","../../components/hyperlinks/hyperlinks.jquery.js","../../components/about/about.js","../../components/about/about.jquery.js","../../components/utils/animations.js","../../components/accordion/accordion.js","../../components/accordion/accordion.jquery.js","../../components/utils/lifecycle.js","../../components/listfilter/listfilter.js","../../components/tmpl/tmpl.js","../../components/popupmenu/popupmenu.js","../../components/popupmenu/popupmenu.jquery.js","../../components/autocomplete/autocomplete.js","../../components/autocomplete/autocomplete.jquery.js","../../components/searchfield/searchfield.js","../../components/searchfield/searchfield.jquery.js","../../components/applicationmenu/applicationmenu.js","../../components/applicationmenu/applicationmenu.jquery.js","../../components/blockgrid/blockgrid.js","../../components/blockgrid/blockgrid.jquery.js","../../components/busyindicator/busyindicator.js","../../components/busyindicator/busyindicator.jquery.js","../../components/charts/charts.js","../../components/bullet/bullet.js","../../components/completion-chart/completion-chart.js","../../components/sparkline/sparkline.js","../../components/line/line.js","../../components/column/column.js","../../components/bar/bar.js","../../components/pie/pie.js","../../components/radar/radar.js","../../components/charts/charts.jquery.js","../../components/circlepager/circlepager.js","../../components/circlepager/circlepager.jquery.js","../../components/colorpicker/colorpicker.js","../../components/colorpicker/colorpicker.jquery.js","../../components/expandablearea/expandablearea.js","../../components/expandablearea/expandablearea.jquery.js","../../components/compositeform/compositeform.js","../../components/compositeform/compositeform.jquery.js","../../components/contextualactionpanel/contextualactionpanel.js","../../components/contextualactionpanel/contextualactionpanel.jquery.js","../../components/utils/string.js","../../components/mask/masks.js","../../components/mask/mask-api.js","../../components/mask/mask-input.js","../../components/mask/mask-input.jquery.js","../../components/tooltip/tooltip.js","../../components/tooltip/tooltip.jquery.js","../../components/popover/popover.jquery.js","../../components/dropdown/dropdown.js","../../components/dropdown/dropdown.jquery.js","../../components/timepicker/timepicker.js","../../components/timepicker/timepicker.jquery.js","../../components/validation/validation.js","../../components/utils/renderloop.js","../../components/toast/toast.js","../../components/toast/toast.jquery.js","../../components/validation/validator.js","../../components/validation/validation.jquery.js","../../components/validation/validation.utils.js","../../components/datepicker/datepicker.js","../../components/datepicker/datepicker.jquery.js","../../components/editor/editor.js","../../components/editor/editor.jquery.js","../../components/emptymessage/emptymessage.js","../../components/emptymessage/emptymessage.jquery.js","../../components/form/form.js","../../components/hierarchy/hierarchy.js","../../components/hierarchy/hierarchy.jquery.js","../../components/field-filter/field-filter.js","../../components/field-filter/field-filter.jquery.js","../../components/field-options/field-options.js","../../components/field-options/field-options.jquery.js","../../components/fileupload/fileupload.js","../../components/fileupload/fileupload.jquery.js","../../components/fileupload-advanced/fileupload-advanced.js","../../components/fileupload-advanced/fileupload-advanced.jquery.js","../../components/homepage/homepage.js","../../components/homepage/homepage.jquery.js","../../components/pager/pager.js","../../components/pager/pager.jquery.js","../../components/listview/listview.js","../../components/listview/listview.jquery.js","../../components/listbuilder/listbuilder.js","../../components/listbuilder/listbuilder.jquery.js","../../components/modal/modal.js","../../components/message/message.js","../../components/message/message.jquery.js","../../components/modal/modal.jquery.js","../../components/multiselect/multiselect.js","../../components/multiselect/multiselect.jquery.js","../../components/progress/progress.js","../../components/progress/progress.jquery.js","../../components/popdown/popdown.js","../../components/popdown/popdown.jquery.js","../../components/rating/rating.js","../../components/rating/rating.jquery.js","../../components/signin/signin.js","../../components/signin/signin.jquery.js","../../components/slider/slider.js","../../components/slider/slider.jquery.js","../../components/spinbox/spinbox.js","../../components/spinbox/spinbox.jquery.js","../../components/splitter/splitter.js","../../components/splitter/splitter.jquery.js","../../components/swaplist/swaplist.js","../../components/swaplist/swaplist.jquery.js","../../components/scrollaction/scrollaction.js","../../components/scrollaction/scrollaction.jquery.js","../../components/stepchart/stepchart.js","../../components/stepchart/stepchart.jquery.js","../../components/stepprocess/stepprocess.js","../../components/stepprocess/stepprocess.jquery.js","../../components/tabs/tabs.js","../../components/tabs/tabs.jquery.js","../../components/tag/tag.js","../../components/tag/tag.jquery.js","../../components/textarea/textarea.js","../../components/textarea/textarea.jquery.js","../../components/toolbarsearchfield/toolbarsearchfield.js","../../components/toolbarsearchfield/toolbarsearchfield.jquery.js","../../components/toolbar/toolbar.js","../../components/toolbar/toolbar.jquery.js","../../components/trackdirty/trackdirty.js","../../components/trackdirty/trackdirty.jquery.js","../../components/tree/tree.js","../../components/tree/tree.jquery.js","../../components/wizard/wizard.js","../../components/wizard/wizard.jquery.js","../../components/zoom/zoom.js","../../components/zoom/zoom.jquery.js","../../components/datagrid/datagrid.formatters.js","../../components/utils/excel.js","../../components/datagrid/datagrid.groupby.js","../../components/datagrid/datagrid.editors.js","../../components/datagrid/datagrid.js","../../components/datagrid/datagrid.jquery.js","../../components/header/header.js","../../components/header/header.jquery.js","../../components/list-detail/list-detail.js","../../components/list-detail/list-detail.jquery.js","../../components/lookup/lookup.js","../../components/lookup/lookup.jquery.js","../../components/tabs-multi/multi-tabs.js","../../components/tabs-multi/multi-tabs.jquery.js","../../components/components.jquery.js","../../components/initialize/initialize.js","../../components/initialize/initialize.jquery.js","../../components/utils/base.js","../../components/personalize/personalize.hooks.js","../../components/components.js","../../components/index.js"],"sourcesContent":["/* eslint-disable no-console */\n\n// Easy flag for determining whether or not time will be logged to the console.\nexport const enableTimeLogging = false;\n\n/**\n * Start the logging timer\n * @param {string} label Provide a way to match a timing operation.\n * @returns {void}\n */\nexport function logTimeStart(label) {\n if (enableTimeLogging) {\n console.time(label);\n }\n}\n\n/**\n * End the logging timer and print the result\n * @param {string} label End this matching timing operation\n * @returns {void}\n */\nexport function logTimeEnd(label) {\n if (enableTimeLogging) {\n console.timeEnd(label);\n }\n}\n\n// Easy flag for allowing console debugging\nexport const enableConsoleLogging = false;\n\n/**\n * Simple wrapper for `console.[whatever]` to abstract out console access.\n * @param {string} type console display type\n * @param {string} message message type\n * @returns {void}\n */\nexport function log(type, message) {\n if (!console) { // eslint-disable-line\n return;\n }\n\n if (typeof !console[type] !== 'function') { // eslint-disable-line\n type = 'log';\n }\n\n console[type](`${message}`); // eslint-disable-line\n}\n","\nconst DOM = {};\n\n/**\n * Returns an array containing an element's attributes.\n * @param {HTMLElement|SVGElement} element the element whose attributes are being accessed\n * @returns {object} list of attributes in name/value pairs.\n */\nDOM.getAttributes = function getAttributes(element) {\n if (!element || (!(element instanceof HTMLElement) && !(element instanceof SVGElement))) {\n return {};\n }\n\n return element.attributes;\n};\n\n/**\n * Adding, removing, and testing for classes\n * @param {HTMLElement} element the element to test\n * @returns {boolean} whether or not a className exists\n */\nDOM.classNameExists = function classNameExists(element) {\n const cn = element.className;\n return cn && cn.length > 0;\n};\n\n/**\n * Checks the contents of a string for the existence of a particular substring.\n * @param {string} classNameString a string to test\n * @param {string} targetContents the contents that need to exist inside the `classNameString`\n * @returns {boolean} whether or not a className exists\n */\nDOM.classNameHas = function has(classNameString, targetContents) {\n return classNameString.indexOf(targetContents) > -1;\n};\n\n/**\n * @param {HTMLElement} el a element being checked.\n * @param {string} className a string representing a class name to check for.\n * @returns {boolean} whether or not the element's class attribute contains the string.\n */\nDOM.hasClass = function hasClass(el, className) {\n return el.classList ? el.classList.contains(className) : new RegExp(`\\\\b${className}\\\\b`).test(el.className);\n};\n\n/**\n * @param {HTMLElement} el a element being checked.\n * @param {string} className a string representing a class name.\n */\nDOM.addClass = function addClass(el, className) {\n if (el.classList) {\n el.classList.add(className);\n } else if (!DOM.hasClass(el, className)) {\n el.className += ` ${className}`;\n }\n};\n\n/**\n * Checks if an element is valid\n * @param {HTMLElement|SVGElement|jQuery[]} el The element being checked\n * @returns {boolean} represents all values normally contained by a DOMRect or ClientRect\n */\nDOM.isElement = function isElement(el) {\n if ((el instanceof HTMLElement) || (el instanceof SVGElement) || (el instanceof $ && el.length)) {\n return true;\n }\n return false;\n};\n\n/**\n * Runs the generic _getBoundingClientRect()_ method on an element, but returns its results\n * as a plain object instead of a ClientRect\n * @param {HTMLElement|SVGElement|jQuery[]} el The element being manipulated\n * @returns {object} represents all values normally contained by a DOMRect or ClientRect\n */\nDOM.getDimensions = function getDimensions(el) {\n if (!DOM.isElement(el)) {\n return {};\n }\n\n if (el instanceof $) {\n if (!el.length) {\n return {};\n }\n\n el = el[0];\n }\n\n const rect = el.getBoundingClientRect();\n const rectObj = {};\n\n for (let prop in rect) { // eslint-disable-line\n if (!isNaN(rect[prop])) {\n rectObj[prop] = rect[prop];\n }\n }\n\n return rectObj;\n};\n\nexport { DOM };\n","import { DOM } from './dom';\n\n/**\n * HideFocus Behavior\n * Only shows the focus state on key entry (tabs or arrows).\n * @param {HTMLElement|SVGElement} element the base element\n * @returns {HideFocus} component instance\n */\nfunction HideFocus(element) {\n return this.init(element);\n}\n\nHideFocus.prototype = {\n init(element) {\n if (!this.element && (element instanceof HTMLElement || element instanceof SVGElement)) {\n this.element = element;\n }\n\n const $el = $(element);\n let isClick = false;\n let isFocused = false;\n let labelClicked = false;\n\n // Checkbox, Radio buttons or Switch\n if ($el.is('.checkbox, .radio, .switch')) {\n let label = $el.next();\n if (label.is('[type=\"hidden\"]')) {\n label = label.next();\n }\n this.label = label[0];\n\n $el.addClass('hide-focus')\n .on('focusin.hide-focus', (e) => {\n if (!isClick && !isFocused && !labelClicked) {\n $el.removeClass('hide-focus');\n $el.triggerHandler('hidefocusremove', [e]);\n }\n isClick = false;\n isFocused = true;\n labelClicked = false;\n })\n .on('focusout.hide-focus', (e) => {\n $el.addClass('hide-focus');\n labelClicked = label.is(labelClicked);\n isClick = false;\n isFocused = false;\n $el.triggerHandler('hidefocusadd', [e]);\n });\n\n label.on('mousedown.hide-focus', function (e) {\n labelClicked = this;\n isClick = true;\n $el.addClass('hide-focus');\n $el.triggerHandler('hidefocusadd', [e]);\n });\n } else {\n // All other elements (ie. Hyperlinks)\n $el.addClass('hide-focus')\n .on('mousedown.hide-focus touchstart.hide-focus', (e) => {\n isClick = true;\n $el.addClass('hide-focus');\n $el.triggerHandler('hidefocusadd', [e]);\n })\n .on('focusin.hide-focus', (e) => {\n if (!isClick && !isFocused) {\n $el.removeClass('hide-focus');\n $el.triggerHandler('hidefocusremove', [e]);\n }\n isClick = false;\n isFocused = true;\n })\n .on('focusout.hide-focus', (e) => {\n $el.addClass('hide-focus');\n isClick = false;\n isFocused = false;\n $el.triggerHandler('hidefocusadd', [e]);\n });\n }\n\n return this;\n },\n\n updated() {\n return this\n .teardown()\n .init();\n },\n\n teardown() {\n if (this.label) {\n $(this.label).off('mousedown.hide-focus');\n }\n\n const elemEvents = [\n 'focusin.hide-focus',\n 'focusout.hide-focus',\n 'mousedown.hide-focus',\n 'touchstart.hide-focus'\n ];\n $(this.element).off(elemEvents.join(' '));\n\n return this;\n }\n};\n\n/**\n * jQuery component wrapper for the HideFocus behavior\n * @returns {jQuery[]} components being acted on\n */\n$.fn.hideFocus = function () {\n return this.each(function () {\n let instance = $.data(this, 'hidefocus');\n if (instance) {\n instance.updated();\n } else {\n instance = $.data(this, 'hidefocus', new HideFocus(this));\n instance.destroy = function destroy() {\n this.teardown();\n $.removeData(this, 'hidefocus');\n };\n }\n });\n};\n\n/**\n * Allows for the smooth scrolling of an element's content area.\n * @param {HTMLElement|SVGElement|jQuery[]} el The element being manipulated.\n * @param {number} target target distance.\n * @param {number} duration the time that will be needed for the scrolling to complete.\n * @returns {$.Deferred} promise that resolved when scrolling completes.\n */\nfunction smoothScrollTo(el, target, duration) {\n const dfd = $.Deferred();\n\n if (!DOM.isElement(el)) {\n // Not a workable element\n return dfd.reject();\n }\n\n // Strip the jQuery\n if (el instanceof $ && el.length) {\n el = el[0];\n }\n\n // undefined (not zero) target should instantly resolve\n if (target === undefined || target === null) {\n return dfd.resolve();\n }\n\n if (isNaN(duration)) {\n duration = 0;\n }\n\n target = Math.round(target);\n duration = Math.round(duration);\n\n if (duration < 0) {\n // bad duration\n return dfd.fail();\n }\n\n if (duration === 0) {\n el.scrollLeft += target;\n return dfd.resolve();\n }\n\n const startTime = Date.now();\n const endTime = startTime + duration;\n const startLeft = el.scrollLeft;\n const distance = target;\n\n // based on http://en.wikipedia.org/wiki/Smoothstep\n function smoothStep(start, end, point) {\n if (point <= start) { return 0; }\n if (point >= end) { return 1; }\n const x = (point - start) / (end - start); // interpolation\n return x * x * (3 - 2 * x);\n }\n\n // This is to keep track of where the element's scrollLeft is\n // supposed to be, based on what we're doing\n let previousLeft = el.scrollLeft;\n\n // This is like a think function from a game loop\n function scrollFrame() {\n if (el.scrollLeft !== previousLeft) {\n // interrupted\n dfd.reject();\n return;\n }\n\n // set the scrollLeft for this frame\n const now = Date.now();\n const point = smoothStep(startTime, endTime, now);\n const frameLeft = Math.round(startLeft + (distance * point));\n el.scrollLeft = frameLeft;\n\n // check if we're done!\n if (now >= endTime) {\n dfd.resolve();\n return;\n }\n\n // If we were supposed to scroll but didn't, then we\n // probably hit the limit, so consider it done; not\n // interrupted.\n if (el.scrollLeft === previousLeft && el.scrollLeft !== frameLeft) {\n dfd.resolve();\n return;\n }\n previousLeft = el.scrollLeft;\n\n // schedule next frame for execution\n setTimeout(scrollFrame, 0);\n }\n\n // boostrap the animation process\n setTimeout(scrollFrame, 0);\n\n return dfd;\n}\n\n/**\n * Binds the Soho Behavior _smoothScrollTo()_ to a jQuery selector\n * @param {number} target target distance to scroll the element\n * @param {number} duration the time that will be needed for the scrolling to complete.\n * @returns {$.Deferred} promise that resolved when scrolling completes.\n */\n$.fn.smoothScroll = function (target, duration) {\n return smoothScrollTo(this, target, duration);\n};\n\n/**\n * Uses 'requestAnimationFrame' or 'setTimeout' to defer a function.\n * @param {function} callback the callback that runs on a deferment.\n * @param {number} timer how long to delay before running the callback.\n * @returns {function} either `requestAnimationFrame` or `setTimeout`\n */\nfunction defer(callback, timer) {\n const deferMethod = typeof window.requestAnimationFrame !== 'undefined' ? window.requestAnimationFrame : setTimeout;\n return deferMethod(callback, timer);\n}\n\nexport { HideFocus, smoothScrollTo, defer };\n","import { DOM } from './dom';\n\n// =================================================================\n// Soho JS-level Breakpoint Access\n// NOTE: these should match whatever the breakpoints are in \"/sass/_config.scss\"\n// =================================================================\nconst breakpoints = {\n phone: 320,\n slim: 400,\n phablet: 610,\n 'phone-to-tablet': 767,\n 'wide-tablet': 968,\n 'tablet-to-desktop': 1280,\n desktop: 1024,\n 'desktop-to-extralarge': 1600\n};\n\n/**\n * Get the name of the current CSS breakpoint by checking the popuplated 'content' value of the\n *
tag's `::after` pseudo-element. These names should be reflected in the breakpoints object\n * above.\n * @returns {string} name of the current breakpoint\n */\nbreakpoints.current = function () {\n const afterElement = window.getComputedStyle ? window.getComputedStyle(document.body, ':after') : false;\n if (!afterElement) {\n return '';\n }\n return (afterElement.getPropertyValue('content') || '').replace(/\"/g, '');\n};\n\n/**\n * @param {string} breakpoint matches one of the entries in the \"Soho.breakpoints\" object.\n * @returns {boolean} whether or not the window is currently wider than the breakpoint provided.\n */\nbreakpoints.isAbove = function isAbove(breakpoint) {\n const bp = breakpoints[breakpoint];\n if (!bp) {\n return false;\n }\n\n const windowWidth = $(window).width();\n return windowWidth > bp - 1;\n};\n\n/**\n * @param {string} breakpoint matches one of the entries in the \"Soho.breakpoints\" object.\n * @returns {boolean} whether or not the window is currently more narrow\n * than the breakpoint provided.\n */\nbreakpoints.isBelow = function isBelow(breakpoint) {\n const bp = breakpoints[breakpoint];\n if (!bp) {\n return false;\n }\n\n const windowWidth = $(window).width();\n return windowWidth < bp;\n};\n\n/**\n * Compares the last-stored breakpoint with a check on the \"current\" breakpoint to see if the\n * breakpoint has changed.\n * @returns {void}\n */\nbreakpoints.compare = function compare() {\n if (!this.last) {\n this.last = '';\n }\n\n const cur = this.current();\n if (this.last !== cur) {\n $('body').triggerHandler('breakpoint-change', [{\n previous: this.last,\n current: cur\n }]);\n this.last = cur;\n }\n};\n\n/**\n * Checks an element for Soho visibility classes and determines whether or not\n * should be hidden based on those values at the current breakpoint.\n * NOTE: this method does NOT determine if the element is ACTUALLY hidden with a\n * `display: none;` or `visibility: hidden;` rule. It determines whether or not a CSS\n * visibility rule alone would hide the element.\n * @param {HTMLElement} element the element being checked.\n * @returns {boolean} whether or not the element is hidden at this breakpoint.\n */\nbreakpoints.isHidden = function (element) {\n if (!element || !DOM.isElement(element)) {\n return false;\n }\n\n // If there are no CSS classes on the element, return false.\n const cl = element.classList;\n if (!cl.length) {\n return false;\n }\n\n // If it's always hidden, always return true.\n if (cl.contains('hidden')) {\n return true;\n }\n\n const bp = this.current();\n const map = {\n phonedown: 'xs',\n phone: 'sm',\n tablet: 'md',\n desktop: 'lg',\n extralarge: 'xl',\n };\n const size = map[bp];\n const hiddenClassName = `hidden-${size}`;\n const visibleClassName = `visible-${size}-`;\n\n // Should be hidden on this breakpoint\n if (cl.contains(hiddenClassName)) {\n return true;\n }\n\n // If explicitly visible, return\n if (cl.toString().indexOf(visibleClassName) > -1) {\n return false;\n }\n\n // Simply return false if none of these thing are found\n return false;\n};\n\n/**\n * jQuery wrapper for `Soho.breakpoints.isHidden()`\n * NOTE: if a jQuery selector with multiple elements is passed to this function,\n * it will only operate on the first one.\n * This method is NOT chainable.\n * @returns {boolean} whether or not the element is hidden at this breakpoint.\n */\n$.fn.isHiddenAtBreakpoint = function () {\n if (!this.length) {\n return false;\n }\n return breakpoints.isHidden($(this).first()[0]);\n};\n\nexport { breakpoints };\n","/**\n * Debounce method\n * @param {function} func the callback function to be run on a stagger.\n * @param {number} [threshold] the amount of time in CPU ticks to delay.\n * @param {boolean} [execAsap] if true, executes the callback immediately\n * instead of waiting for the threshold to complete.\n * @returns {void}\n */\nfunction debounce(func, threshold, execAsap) {\n let timeout;\n\n return function debounced(...args) {\n const obj = this;\n function delayed() {\n if (!execAsap) {\n func.apply(obj, args);\n }\n timeout = null;\n }\n\n if (timeout) {\n clearTimeout(timeout);\n } else if (execAsap) {\n func.apply(obj, args);\n }\n\n timeout = setTimeout(delayed, threshold || 250);\n };\n}\n\nexport { debounce };\n","import { debounce } from './debounced-resize';\n\nconst debouncedResizeName = 'debouncedResize';\n\n/**\n * Bind the smartResize method to $.fn()\n * @param {function} fn the callback function to be bound on debounced resize\n * @returns {void}\n */\n$.fn[debouncedResizeName] = function (fn) {\n if (fn) {\n return this.bind('resize', debounce(fn));\n }\n return this.trigger(debouncedResizeName);\n};\n","import { version as SOHO_XI_VERSION } from '../../package.json';\nimport { breakpoints } from './breakpoints';\n\n// jQuery Components\nimport './debounced-resize.jquery';\n\n/**\n * @class {Environment}\n */\nconst Environment = {\n\n browser: {},\n\n os: {},\n\n rtl: $('html').attr('dir') === 'rtl',\n\n /**\n * Builds run-time environment settings\n */\n set() {\n $('html').attr('data-sohoxi-version', SOHO_XI_VERSION);\n this.addBrowserClasses();\n this.addGlobalResize();\n },\n\n /**\n * Global Classes for browser, version and device as needed.\n */\n addBrowserClasses() {\n const ua = navigator.userAgent || navigator.vendor || window.opera;\n const html = $('html');\n let cssClasses = ''; // User-agent string\n\n if (ua.indexOf('Safari') !== -1 &&\n ua.indexOf('Chrome') === -1 &&\n ua.indexOf('Android') === -1) {\n cssClasses += 'is-safari ';\n this.browser.name = 'safari';\n }\n\n if (ua.indexOf('Chrome') !== -1) {\n cssClasses += 'is-chrome ';\n this.browser.name = 'chrome';\n }\n\n if (ua.indexOf('Mac OS X') !== -1) {\n cssClasses += 'is-mac ';\n this.os.name = 'Mac OS X';\n }\n\n if (ua.indexOf('Firefox') > 0) {\n cssClasses += 'is-firefox ';\n this.browser.name = 'firefox';\n }\n\n // Class-based detection for IE\n if (ua.match(/Edge\\//)) {\n cssClasses += 'ie ie-edge ';\n this.browser.name = 'edge';\n }\n if (ua.match(/Trident/)) {\n cssClasses += 'ie ';\n this.browser.name = 'ie';\n }\n if (navigator.appVersion.indexOf('MSIE 8.0') > -1 ||\n ua.indexOf('MSIE 8.0') > -1 ||\n document.documentMode === 8) {\n cssClasses += 'ie8 ';\n this.browser.version = '8';\n }\n if (navigator.appVersion.indexOf('MSIE 9.0') > -1) {\n cssClasses += 'ie9 ';\n this.browser.version = '9';\n }\n if (navigator.appVersion.indexOf('MSIE 10.0') > -1) {\n cssClasses += 'ie10 ';\n this.browser.version = '10';\n } else if (ua.match(/Trident\\/7\\./)) {\n cssClasses += 'ie11 ';\n this.browser.version = '11';\n }\n\n // Class-based detection for iOS\n // /iPhone|iPod|iPad|Silk|Android|BlackBerry|Opera Mini|IEMobile/\n if ((/iPhone|iPod|iPad/).test(ua)) {\n cssClasses += 'ios ';\n this.os.name = 'ios';\n\n const iDevices = ['iPod', 'iPad', 'iPhone'];\n for (let i = 0; i < iDevices.length; i++) {\n if (new RegExp(iDevices[i]).test(ua)) {\n cssClasses += `${iDevices[i].toLowerCase()} `;\n this.device = iDevices[i];\n }\n }\n }\n\n if ((/Android/.test(ua))) {\n cssClasses += 'android ';\n this.os.name = 'android';\n }\n\n html.addClass(cssClasses);\n },\n\n /**\n * Setup a global resize event trigger for controls to listen to\n */\n addGlobalResize() {\n // Global resize event\n $(window).debouncedResize(() => {\n $('body').triggerHandler('resize', [window]);\n breakpoints.compare();\n });\n\n // Also detect whenenver a load or orientation change occurs\n $(window).on('orientationchange load', () => breakpoints.compare());\n }\n};\n\n/**\n *\n */\nEnvironment.pasteEvent = (function getPasteEvent() {\n const el = document.createElement('input');\n const name = 'onpaste';\n el.setAttribute(name, '');\n return ((typeof el[name] === 'function') ? 'paste' : 'input');\n}());\n\n/**\n * Automatically set up the environment by virtue of including this script\n */\nEnvironment.set();\n\nexport { Environment };\n","import { defer } from './behaviors';\nimport { Environment as env } from './environment';\nimport { DOM } from './dom';\n\n/**\n * Used for changing the stacking order of jQuery events. This is needed to override certain\n * Events invoked by other plugins http://stackoverflow.com/questions/2360655\n * @private\n * @param {string} name the event name\n * @param {function} fn callback function that will be called during the supplied event name\n * @returns {void}\n */\n$.fn.bindFirst = function (name, fn) {\n this.on(name, fn);\n this.each(function () {\n const handlers = $._data(this, 'events')[name.split('.')[0]]; // eslint-disable-line\n // take out the handler we just inserted from the end\n const handler = handlers.pop();\n // move it at the beginning\n handlers.splice(0, 0, handler);\n });\n};\n\n/**\n * @private\n * uniqueIdCount is a baseline unique number that will be used when generating\n * uniqueIds for elements and components.\n */\nexport let uniqueIdCount = 0; // eslint-disable-line\n\n/**\n * Generates a unique ID for an element based on the element's configuration, any\n * Soho components that are generated against it, and provided prefixes/suffixes.\n * @private\n * @param {string} [className] CSS classname (will be interpreted automatically\n * if it's not provided)\n * @param {string} [prefix] optional prefix\n * @param {string} [suffix] optional suffix\n * @returns {string} the compiled uniqueID\n */\n$.fn.uniqueId = function (className, prefix, suffix) {\n const predefinedId = $(this).attr('id');\n\n if (predefinedId && $(`#${predefinedId}`).length < 2) {\n return predefinedId;\n }\n\n prefix = (!prefix ? '' : `${prefix}-`);\n suffix = (!suffix ? '' : `-${suffix}`);\n className = (!className ? $(this).attr('class') : className);\n\n const str = prefix + className + uniqueIdCount + suffix;\n uniqueIdCount += 1;\n return str;\n};\n\n/**\n * Detect whether or not a text string represents a valid CSS property. This check\n * includes an attempt at checking for vendor-prefixed versions of the CSS property\n * provided.\n * @private\n * @param {string} prop a possible CSS property\n * @returns {string|null} If the property exists, it will be returned in string format.\n * If the property doesn't exist, a null result is returned.\n */\n$.fn.cssPropSupport = function (prop) {\n if (!prop) {\n return null;\n }\n\n const el = $('')[0];\n const propStr = prop.toString();\n const prefixes = ['Moz', 'Webkit', 'O', 'ms'];\n const capitalizedProp = propStr.charAt(0).toUpperCase() + propStr.substr(1);\n\n if (prop in el.style) {\n $(el).remove();\n return prop;\n }\n\n for (let i = 0; i < prefixes.length; i++) {\n const vendorProp = prefixes[i] + capitalizedProp;\n if (vendorProp in el.style) {\n $(el).remove();\n return vendorProp;\n }\n }\n\n $(el).remove();\n return null;\n};\n\n/**\n * Returns the name of the TransitionEnd event.\n * @private\n * @returns {string} a (possibly) vendor-adjusted CSS transition property name.\n */\n$.fn.transitionEndName = function () {\n const prop = $.fn.cssPropSupport('transition');\n const eventNames = {\n WebkitTransition: 'webkitTransitionEnd',\n MozTransition: 'transitionend',\n MSTransition: 'msTransitionEnd',\n OTransition: 'oTransitionEnd',\n transition: 'transitionend'\n };\n\n return eventNames[prop] || null;\n};\n\n/**\n * Checks to see if a provided element is visible based on its CSS `visibility` property.\n * @private\n * @param {HTMLElement} element the element being checked.\n * @returns {boolean} whether or not the element is visible.\n */\nfunction visible(element) {\n return $.expr.filters.visible(element) &&\n !$(element).parents().addBack().filter(function () { return $.css(this, 'visibility') === 'hidden'; }).length;\n}\n\n/**\n * From jQueryUI Core: https://github.com/jquery/jquery-ui/blob/24756a978a977d7abbef5e5bce403837a01d964f/ui/jquery.ui.core.js#L93\n * Adapted from: http://stackoverflow.com/questions/7668525/is-there-a-jquery-selector-to-get-all-elements-that-can-get-focus\n * Adds the ':focusable' selector to Sizzle to allow for the selection of elements\n * that can currently be focused.\n * @private\n * @param {HTMLElement} element the element being checked\n * @returns {boolean} whether or not the element is focusable.\n */\nfunction focusable(element) {\n let map;\n let mapName;\n let img;\n const nodeName = element.nodeName.toLowerCase();\n const isTabIndexNotNaN = !isNaN($.attr(element, 'tabindex'));\n\n if (nodeName === 'area') {\n map = element.parentNode;\n mapName = map.name;\n if (!element.href || !mapName || map.nodeName.toLowerCase() !== 'map') {\n return false;\n }\n img = $(`img[usemap=#${mapName}]`)[0];\n return !!img && visible(img);\n }\n\n // The element and all of its ancestors must be visible.\n // Return out fast if this isn't the case.\n if (!visible(element)) {\n return false;\n }\n\n const match = /input|select|textarea|button|object/.test(nodeName);\n if (match) {\n return !element.disabled;\n }\n if (nodeName === 'a') {\n return (element.href !== undefined || isTabIndexNotNaN);\n }\n return isTabIndexNotNaN;\n}\n\n// Adds a `:focusable` selector to jQuery's selector library.\n$.extend($.expr[':'], {\n focusable(element) {\n return focusable(element, !isNaN($.attr(element, 'tabindex')));\n }\n});\n\n/**\n * Returns a key/value list of currently attached event listeners\n * @private\n * @returns {object} containing list of event names as keys, and event listener functions as values.\n */\n$.fn.listEvents = function () {\n let data = {};\n\n this.each(function () {\n data = $._data(this, 'events'); // eslint-disable-line\n });\n\n return data;\n};\n\nconst utils = {};\n\n/**\n * Grabs an attribute from an HTMLElement containing stringified JSON syntax,\n * and interprets it into options.\n * @private\n * @param {HTMLElement} element the element whose settings are being interpreted\n * @param {string} [attr] optional different attribute to parse for settings\n * @returns {object} a list of interpreted settings for this element\n */\nutils.parseSettings = function parseSettings(element, attr) {\n let options = {};\n if (!element ||\n (!(element instanceof HTMLElement) && !(element instanceof $)) ||\n (element instanceof $ && !element.length)) {\n return options;\n }\n\n if (element instanceof $) {\n element = element[0];\n }\n\n // Use `data-options` as a default.\n attr = attr || 'data-options';\n\n const str = element.getAttribute(attr);\n if (!str || typeof str !== 'string' || str.indexOf('{') === -1) {\n return options;\n }\n\n // replace single to double quotes, since single-quotes may be necessary\n // due to entry in markup.\n function replaceDoubleQuotes(changedStr) {\n return changedStr.replace(/'/g, '\"');\n }\n\n // Manually parse a string more in-depth\n function manualParse(changedStr) {\n // get keys\n let regex = /({|,)(?:\\s*)(?:')?([A-Za-z_$\\.][A-Za-z0-9_ \\-\\.$]*)(?:')?(?:\\s*):/g; // eslint-disable-line\n\n // add double quotes to keys\n changedStr = changedStr.replace(regex, '$1\\\"$2\\\":'); // eslint-disable-line\n\n // get strings in values\n regex = /:(?:\\s*)(?!(true|false|null|undefined))([A-Za-z_$\\.#][A-Za-z0-9_ \\-\\.$]*)/g; // eslint-disable-line\n\n // add double quotes to strings in values\n changedStr = changedStr.replace(regex, ':\\\"$2\\\"'); // eslint-disable-line\n changedStr = replaceDoubleQuotes(changedStr);\n return changedStr;\n }\n\n try {\n options = JSON.parse(replaceDoubleQuotes(str));\n } catch (err) {\n options = JSON.parse(manualParse(str));\n }\n\n return options;\n};\n\n/**\n * Deprecate `utils.parseOptions` in favor of `utils.parseSettings`\n * @private\n * @deprecated\n * TODO: Remove in 4.4.1 ?\n */\nutils.parseOptions = utils.parseSettings;\n\n/**\n* jQuery Behavior Wrapper for `utils.parseOptions`.\n* @deprecated\n* @private\n* @param {HTMLElement|jQuery[]} element the element whose options are being parsed\n* @param {string} [attr] an optional alternate attribute name to use when obtaining settings\n* @returns {Object|Object[]} an object representation of parsed settings.\n*/\n$.fn.parseOptions = function (element, attr) {\n const results = [];\n const isCalledDirectly = (element instanceof HTMLElement ||\n element instanceof SVGElement || element instanceof $);\n let targets = this;\n\n if (isCalledDirectly) {\n targets = $(element);\n } else {\n attr = element;\n element = undefined;\n }\n\n targets.each(function (i, item) {\n results.push({\n element: this,\n options: utils.parseOptions(item, attr)\n });\n });\n\n if (results.length === 1) {\n return results[0].options;\n }\n return results;\n};\n\n/**\n * Timer - can be used for play/pause or stop for given time.\n * Use as new instance [ var timer = new $.fn.timer(function() {}, 6000); ]\n * then can be listen events as:\n * [ $(timer.event).on('update', function(e, data){console.log(data.counter)}); ]\n * or can access as [ timer.cancel(); -or- timer.pause(); -or- timer.resume(); ]\n * @private\n * @param {function} [callback] method that will run on each timer update\n * @param {number} delay amount of time between timer ticks\n * @returns {object} containing methods that can be run on the timer\n */\n$.fn.timer = function (callback, delay) {\n const self = $(this);\n const speed = 10;\n let interval;\n let counter = 0;\n\n function cancel() {\n self.triggerHandler('cancel');\n clearInterval(interval);\n counter = 0;\n }\n\n function pause() {\n self.triggerHandler('pause');\n clearInterval(interval);\n }\n\n function update() {\n interval = setInterval(function () {\n counter += speed;\n self.triggerHandler('update', [{ counter }]);\n if (counter > delay) {\n self.triggerHandler('timeout');\n callback.apply(arguments); // eslint-disable-line\n clearInterval(interval);\n counter = 0;\n }\n }, speed);\n }\n\n function resume() {\n self.triggerHandler('resume');\n update();\n }\n\n update();\n\n return {\n event: this,\n cancel,\n pause,\n resume\n };\n};\n\n/**\n * Copies a string to the clipboard. Must be called from within an event handler such as click.\n * May return false if it failed, but this is not always\n * possible. Browser support for Chrome 43+, Firefox 42+, Edge and IE 10+.\n * No Safari support, as of (Nov. 2015). Returns false.\n * IE: The clipboard feature may be disabled by an adminstrator. By default a prompt is\n * shown the first time the clipboard is used (per session).\n * @private\n * @param {string} text incoming text content\n * @returns {string|boolean} copied text, or a false result if there was an error\n */\n$.copyToClipboard = function (text) { // eslint-disable-line\n if (window.clipboardData && window.clipboardData.setData) {\n // IE specific code path to prevent textarea being shown while dialog is visible.\n return window.clipboardData.setData('Text', text);\n } else if (document.queryCommandSupported && document.queryCommandSupported('copy')) {\n const textarea = document.createElement('textarea');\n textarea.textContent = text;\n textarea.style.position = 'fixed'; // Prevent scrolling to bottom of page in MS Edge.\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand('copy'); // Security exception may be thrown by some browsers.\n } catch (ex) {\n // console.warn('Copy to clipboard failed.', ex);\n return false;\n } finally {\n document.body.removeChild(textarea);\n }\n }\n};\n\n/**\n * Escapes HTML, replacing special characters with encoded symbols.\n * @private\n * @param {string} value HTML in string form\n * @returns {string} the modified value\n */\n$.escapeHTML = function (value) {\n let newValue = value;\n if (typeof value === 'string') {\n newValue = newValue.replace(/&/g, '&');\n newValue = newValue.replace(//g, '>');\n }\n return newValue;\n};\n\n/**\n * Un-escapes HTML, replacing encoded symbols with special characters.\n * @private\n * @param {string} value HTML in string form\n * @returns {string} the modified value\n */\n$.unescapeHTML = function (value) {\n let newValue = value;\n if (typeof value === 'string') {\n newValue = newValue.replace(/</g, '<').replace(/>/g, '>');\n newValue = newValue.replace(/&/g, '&');\n }\n return newValue;\n};\n\n/**\n * Remove Script tags and all onXXX functions\n * @private\n * @param {string} html HTML in string form\n * @returns {string} the modified value\n */\n$.sanitizeHTML = function (html) {\n let santizedHtml = html.replace(/