UNPKG

access-nyc-patterns

Version:

User Interface Patterns for Benefits Access

1,792 lines (1,546 loc) 124 kB
var AccessNyc = (function () { 'use strict'; /** * The Simple Toggle class. This will toggle the class 'active' and 'hidden' * on target elements, determined by a click event on a selected link or * element. This will also toggle the aria-hidden attribute for targeted * elements to support screen readers. Target settings and other functionality * can be controlled through data attributes. * * This uses the .matches() method which will require a polyfill for IE * https://polyfill.io/v2/docs/features/#Element_prototype_matches * * @class */ var Toggle = function Toggle(s) { var this$1 = this; var body = document.querySelector('body'); s = !s ? {} : s; this._settings = { selector: s.selector ? s.selector : Toggle.selector, namespace: s.namespace ? s.namespace : Toggle.namespace, inactiveClass: s.inactiveClass ? s.inactiveClass : Toggle.inactiveClass, activeClass: s.activeClass ? s.activeClass : Toggle.activeClass, before: s.before ? s.before : false, after: s.after ? s.after : false }; body.addEventListener('click', function (event) { if (!event.target.matches(this$1._settings.selector)) { return; } this$1._toggle(event); }); return this; }; /** * Logs constants to the debugger * @param{object} eventThe main click event * @return {object} The class */ Toggle.prototype._toggle = function _toggle(event) { var this$1 = this; var el = event.target; var target = false; event.preventDefault(); /** Anchor Links */ target = el.hasAttribute('href') ? document.querySelector(el.getAttribute('href')) : target; /** Toggle Controls */ target = el.hasAttribute('aria-controls') ? document.querySelector("#" + el.getAttribute('aria-controls')) : target; /** Main Functionality */ if (!target) { return this; } this.elementToggle(el, target); /** Undo */ if (el.dataset[this._settings.namespace + "Undo"]) { var undo = document.querySelector(el.dataset[this._settings.namespace + "Undo"]); undo.addEventListener('click', function (event) { event.preventDefault(); this$1.elementToggle(el, target); undo.removeEventListener('click'); }); } return this; }; /** * The main toggling method * @param{object} el The current element to toggle active * @param{object} target The target element to toggle active/hidden * @return {object} The class */ Toggle.prototype.elementToggle = function elementToggle(el, target) { var this$1 = this; var i = 0; var attr = ''; var value = ''; // Get other toggles that might control the same element var others = document.querySelectorAll("[aria-controls=\"" + el.getAttribute('aria-controls') + "\"]"); /** * Toggling before hook. */ if (this._settings.before) { this._settings.before(this); } /** * Toggle Element and Target classes */ if (this._settings.activeClass) { el.classList.toggle(this._settings.activeClass); target.classList.toggle(this._settings.activeClass); // If there are other toggles that control the same element if (others) { others.forEach(function (other) { if (other !== el) { other.classList.toggle(this$1._settings.activeClass); } }); } } if (this._settings.inactiveClass) { target.classList.toggle(this._settings.inactiveClass); } /** * Target Element Aria Attributes */ for (i = 0; i < Toggle.targetAriaRoles.length; i++) { attr = Toggle.targetAriaRoles[i]; value = target.getAttribute(attr); if (value != '' && value) { target.setAttribute(attr, value === 'true' ? 'false' : 'true'); } } /** * Jump Links */ if (el.hasAttribute('href')) { // Reset the history state, this will clear out // the hash when the jump item is toggled closed. history.pushState('', '', window.location.pathname + window.location.search); // Target element toggle. if (target.classList.contains(this._settings.activeClass)) { window.location.hash = el.getAttribute('href'); target.setAttribute('tabindex', '-1'); target.focus({ preventScroll: true }); } else { target.removeAttribute('tabindex'); } } /** * Toggle Element (including multi toggles) Aria Attributes */ for (i = 0; i < Toggle.elAriaRoles.length; i++) { attr = Toggle.elAriaRoles[i]; value = el.getAttribute(attr); if (value != '' && value) { el.setAttribute(attr, value === 'true' ? 'false' : 'true'); } // If there are other toggles that control the same element if (others) { others.forEach(function (other) { if (other !== el && other.getAttribute(attr)) { other.setAttribute(attr, value === 'true' ? 'false' : 'true'); } }); } } /** * Toggling complete hook. */ if (this._settings.after) { this._settings.after(this); } return this; }; /** @type {String} The main selector to add the toggling function to */ Toggle.selector = '[data-js*="toggle"]'; /** @type {String} The namespace for our data attribute settings */ Toggle.namespace = 'toggle'; /** @type {String} The hide class */ Toggle.inactiveClass = 'hidden'; /** @type {String} The active class */ Toggle.activeClass = 'active'; /** @type {Array} Aria roles to toggle true/false on the toggling element */ Toggle.elAriaRoles = ['aria-pressed', 'aria-expanded']; /** @type {Array} Aria roles to toggle true/false on the target element */ Toggle.targetAriaRoles = ['aria-hidden']; /** * The Icon module * @class */ var Icons = function Icons(path) { path = path ? path : Icons.path; fetch(path).then(function (response) { if (response.ok) { return response.text(); } })["catch"](function (error) { }).then(function (data) { var sprite = document.createElement('div'); sprite.innerHTML = data; sprite.setAttribute('aria-hidden', true); sprite.setAttribute('style', 'display: none;'); document.body.appendChild(sprite); }); return this; }; /** @type {String} The path of the icon file */ Icons.path = 'icons.svg'; /** * JaroWinkler function. * https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance * @param {string} s1 string one. * @param {string} s2 second string. * @return {number} amount of matches. */ function jaro(s1, s2) { var assign; var shorter; var longer; assign = s1.length > s2.length ? [s1, s2] : [s2, s1], longer = assign[0], shorter = assign[1]; var matchingWindow = Math.floor(longer.length / 2) - 1; var shorterMatches = []; var longerMatches = []; for (var i = 0; i < shorter.length; i++) { var ch = shorter[i]; var windowStart = Math.max(0, i - matchingWindow); var windowEnd = Math.min(i + matchingWindow + 1, longer.length); for (var j = windowStart; j < windowEnd; j++) { if (longerMatches[j] === undefined && ch === longer[j]) { shorterMatches[i] = longerMatches[j] = ch; break; } } } var shorterMatchesString = shorterMatches.join(''); var longerMatchesString = longerMatches.join(''); var numMatches = shorterMatchesString.length; var transpositions = 0; for (var i$1 = 0; i$1 < shorterMatchesString.length; i$1++) { if (shorterMatchesString[i$1] !== longerMatchesString[i$1]) { transpositions++; } } return numMatches > 0 ? (numMatches / shorter.length + numMatches / longer.length + (numMatches - Math.floor(transpositions / 2)) / numMatches) / 3.0 : 0; } /** * @param {string} s1 string one. * @param {string} s2 second string. * @param {number} prefixScalingFactor * @return {number} jaroSimilarity */ function jaroWinkler (s1, s2, prefixScalingFactor) { if (prefixScalingFactor === void 0) prefixScalingFactor = 0.2; var jaroSimilarity = jaro(s1, s2); var commonPrefixLength = 0; for (var i = 0; i < s1.length; i++) { if (s1[i] === s2[i]) { commonPrefixLength++; } else { break; } } return jaroSimilarity + Math.min(commonPrefixLength, 4) * prefixScalingFactor * (1 - jaroSimilarity); } function memoize (fn) { var cache = {}; return function () { var args = [], len = arguments.length; while (len--) { args[len] = arguments[len]; } var key = JSON.stringify(args); return cache[key] || (cache[key] = fn.apply(void 0, args)); }; } /* eslint-env browser */ /** * Autocomplete for autocomplete. * Forked and modified from https://github.com/xavi/miss-plete */ var Autocomplete = function Autocomplete(settings) { var this$1 = this; if (settings === void 0) settings = {}; this.settings = { 'selector': settings.selector, // required 'options': settings.options, // required 'classname': settings.classname, // required 'selected': settings.hasOwnProperty('selected') ? settings.selected : false, 'score': settings.hasOwnProperty('score') ? settings.score : memoize(Autocomplete.score), 'listItem': settings.hasOwnProperty('listItem') ? settings.listItem : Autocomplete.listItem, 'getSiblingIndex': settings.hasOwnProperty('getSiblingIndex') ? settings.getSiblingIndex : Autocomplete.getSiblingIndex }; this.scoredOptions = null; this.container = null; this.ul = null; this.highlighted = -1; this.SELECTORS = Autocomplete.selectors; this.STRINGS = Autocomplete.strings; this.MAX_ITEMS = Autocomplete.maxItems; window.addEventListener('keydown', function (e) { this$1.keydownEvent(e); }); window.addEventListener('keyup', function (e) { this$1.keyupEvent(e); }); window.addEventListener('input', function (e) { this$1.inputEvent(e); }); var body = document.querySelector('body'); body.addEventListener('focus', function (e) { this$1.focusEvent(e); }, true); body.addEventListener('blur', function (e) { this$1.blurEvent(e); }, true); return this; }; /** * EVENTS */ /** * The input focus event * @param {object}eventThe event object */ Autocomplete.prototype.focusEvent = function focusEvent(event) { if (!event.target.matches(this.settings.selector)) { return; } this.input = event.target; if (this.input.value === '') { this.message('INIT'); } }; /** * The input keydown event * @param {object}eventThe event object */ Autocomplete.prototype.keydownEvent = function keydownEvent(event) { if (!event.target.matches(this.settings.selector)) { return; } this.input = event.target; if (this.ul) { switch (event.keyCode) { case 13: this.keyEnter(event); break; case 27: this.keyEscape(event); break; case 40: this.keyDown(event); break; case 38: this.keyUp(event); break; } } }; /** * The input keyup event * @param {object}eventThe event object */ Autocomplete.prototype.keyupEvent = function keyupEvent(event) { if (!event.target.matches(this.settings.selector)) { return; } this.input = event.target; }; /** * The input event * @param {object}eventThe event object */ Autocomplete.prototype.inputEvent = function inputEvent(event) { var this$1 = this; if (!event.target.matches(this.settings.selector)) { return; } this.input = event.target; if (this.input.value.length > 0) { this.scoredOptions = this.settings.options.map(function (option) { return this$1.settings.score(this$1.input.value, option); }).sort(function (a, b) { return b.score - a.score; }); } else { this.scoredOptions = []; } this.dropdown(); }; /** * The input blur event * @param {object}eventThe event object */ Autocomplete.prototype.blurEvent = function blurEvent(event) { if (event.target === window || !event.target.matches(this.settings.selector)) { return; } this.input = event.target; if (this.input.dataset.persistDropdown === 'true') { return; } this.remove(); this.highlighted = -1; }; /** * KEY INPUT EVENTS */ /** * What happens when the user presses the down arrow * @param {object}eventThe event object * @return{object} The Class */ Autocomplete.prototype.keyDown = function keyDown(event) { event.preventDefault(); this.highlight(this.highlighted < this.ul.children.length - 1 ? this.highlighted + 1 : -1); return this; }; /** * What happens when the user presses the up arrow * @param {object}eventThe event object * @return{object} The Class */ Autocomplete.prototype.keyUp = function keyUp(event) { event.preventDefault(); this.highlight(this.highlighted > -1 ? this.highlighted - 1 : this.ul.children.length - 1); return this; }; /** * What happens when the user presses the enter key * @param {object}eventThe event object * @return{object} The Class */ Autocomplete.prototype.keyEnter = function keyEnter(event) { this.selected(); return this; }; /** * What happens when the user presses the escape key * @param {object}eventThe event object * @return{object} The Class */ Autocomplete.prototype.keyEscape = function keyEscape(event) { this.remove(); return this; }; /** * STATIC */ /** * It must return an object with at least the properties 'score' * and 'displayValue.' Default is a Jaro–Winkler similarity function. * @param{array}value * @param{array}synonyms * @return {int} Score or displayValue */ Autocomplete.score = function score(value, synonyms) { var closestSynonym = null; synonyms.forEach(function (synonym) { var similarity = jaroWinkler(synonym.trim().toLowerCase(), value.trim().toLowerCase()); if (closestSynonym === null || similarity > closestSynonym.similarity) { closestSynonym = { similarity: similarity, value: synonym }; if (similarity === 1) { return; } } }); return { score: closestSynonym.similarity, displayValue: synonyms[0] }; }; /** * List item for dropdown list. * @param{Number}scoredOption * @param{Number}index * @return {string}The a list item <li>. */ Autocomplete.listItem = function listItem(scoredOption, index) { var li = index > this.MAX_ITEMS ? null : document.createElement('li'); li.setAttribute('role', 'option'); li.setAttribute('tabindex', '-1'); li.setAttribute('aria-selected', 'false'); li && li.appendChild(document.createTextNode(scoredOption.displayValue)); return li; }; /** * Get index of previous element. * @param{array} node * @return {number}index of previous element. */ Autocomplete.getSiblingIndex = function getSiblingIndex(node) { var index = -1; var n = node; do { index++; n = n.previousElementSibling; } while (n); return index; }; /** * PUBLIC METHODS */ /** * Display options as a list. * @return{object} The Class */ Autocomplete.prototype.dropdown = function dropdown() { var this$1 = this; var documentFragment = document.createDocumentFragment(); this.scoredOptions.every(function (scoredOption, i) { var listItem = this$1.settings.listItem(scoredOption, i); listItem && documentFragment.appendChild(listItem); return !!listItem; }); this.remove(); this.highlighted = -1; if (documentFragment.hasChildNodes()) { var newUl = document.createElement('ul'); newUl.setAttribute('role', 'listbox'); newUl.setAttribute('tabindex', '0'); newUl.setAttribute('id', this.SELECTORS.OPTIONS); newUl.addEventListener('mouseover', function (event) { if (event.target.tagName === 'LI') { this$1.highlight(this$1.settings.getSiblingIndex(event.target)); } }); newUl.addEventListener('mousedown', function (event) { return event.preventDefault(); }); newUl.addEventListener('click', function (event) { if (event.target.tagName === 'LI') { this$1.selected(); } }); newUl.appendChild(documentFragment); // See CSS to understand why the <ul> has to be wrapped in a <div> var newContainer = document.createElement('div'); newContainer.className = this.settings.classname; newContainer.appendChild(newUl); this.input.setAttribute('aria-expanded', 'true'); // Inserts the dropdown just after the <input> element this.input.parentNode.insertBefore(newContainer, this.input.nextSibling); this.container = newContainer; this.ul = newUl; this.message('TYPING', this.settings.options.length); } return this; }; /** * Highlight new option selected. * @param {Number}newIndex * @return{object}The Class */ Autocomplete.prototype.highlight = function highlight(newIndex) { if (newIndex > -1 && newIndex < this.ul.children.length) { // If any option already selected, then unselect it if (this.highlighted !== -1) { this.ul.children[this.highlighted].classList.remove(this.SELECTORS.HIGHLIGHT); this.ul.children[this.highlighted].removeAttribute('aria-selected'); this.ul.children[this.highlighted].removeAttribute('id'); this.input.removeAttribute('aria-activedescendant'); } this.highlighted = newIndex; if (this.highlighted !== -1) { this.ul.children[this.highlighted].classList.add(this.SELECTORS.HIGHLIGHT); this.ul.children[this.highlighted].setAttribute('aria-selected', 'true'); this.ul.children[this.highlighted].setAttribute('id', this.SELECTORS.ACTIVE_DESCENDANT); this.input.setAttribute('aria-activedescendant', this.SELECTORS.ACTIVE_DESCENDANT); } } return this; }; /** * Selects an option from a list of items. * @return{object} The Class */ Autocomplete.prototype.selected = function selected() { if (this.highlighted !== -1) { this.input.value = this.scoredOptions[this.highlighted].displayValue; this.remove(); this.message('SELECTED', this.input.value); if (window.innerWidth <= 768) { this.input.scrollIntoView(true); } } // User provided callback method for selected option. if (this.settings.selected) { this.settings.selected(this.input.value, this); } return this; }; /** * Remove dropdown list once a list item is selected. * @return{object} The Class */ Autocomplete.prototype.remove = function remove() { this.container && this.container.remove(); this.input.setAttribute('aria-expanded', 'false'); this.container = null; this.ul = null; return this; }; /** * Messaging that is passed to the screen reader * @param {string}key The Key of the message to write * @param {string}variableA variable to provide to the string. * @return{object} The Class */ Autocomplete.prototype.message = function message(key, variable) { var this$1 = this; if (key === void 0) key = false; if (variable === void 0) variable = ''; if (!key) { return this; } var messages = { 'INIT': function INIT() { return this$1.STRINGS.DIRECTIONS_TYPE; }, 'TYPING': function TYPING() { return [this$1.STRINGS.OPTION_AVAILABLE.replace('{{ NUMBER }}', variable), this$1.STRINGS.DIRECTIONS_REVIEW].join('. '); }, 'SELECTED': function SELECTED() { return [this$1.STRINGS.OPTION_SELECTED.replace('{{ VALUE }}', variable), this$1.STRINGS.DIRECTIONS_TYPE].join('. '); } }; document.querySelector("#" + this.input.getAttribute('aria-describedby')).innerHTML = messages[key](); return this; }; /** Selectors for the Autocomplete class. */ Autocomplete.selectors = { 'HIGHLIGHT': 'input-autocomplete__highlight', 'OPTIONS': 'input-autocomplete__options', 'ACTIVE_DESCENDANT': 'input-autocomplete__selected', 'SCREEN_READER_ONLY': 'sr-only' }; /** */ Autocomplete.strings = { 'DIRECTIONS_TYPE': 'Start typing to generate a list of potential input options', 'DIRECTIONS_REVIEW': ['Keyboard users can use the up and down arrows to ', 'review options and press enter to select an option'].join(''), 'OPTION_AVAILABLE': '{{ NUMBER }} options available', 'OPTION_SELECTED': '{{ VALUE }} selected' }; /** Maximum amount of results to be returned. */ Autocomplete.maxItems = 5; /** * The InputAutocomplete class. */ var InputAutocomplete = function InputAutocomplete(settings) { if (settings === void 0) settings = {}; this.library = new Autocomplete({ options: settings.hasOwnProperty('options') ? settings.options : InputAutocomplete.options, selected: settings.hasOwnProperty('selected') ? settings.selected : false, selector: settings.hasOwnProperty('selector') ? settings.selector : InputAutocomplete.selector, classname: settings.hasOwnProperty('classname') ? settings.classname : InputAutocomplete.classname }); return this; }; /** * Setter for the Autocomplete options * @param{object} reset Set of array options for the Autocomplete class * @return {object} InputAutocomplete object with new options. */ InputAutocomplete.prototype.options = function options(reset) { this.library.settings.options = reset; return this; }; /** * Setter for the Autocomplete strings * @param{object}localizedStringsObject containing strings. * @return {object} Autocomplete strings */ InputAutocomplete.prototype.strings = function strings(localizedStrings) { Object.assign(this.library.STRINGS, localizedStrings); return this; }; /** @type {array} Default options for the autocomplete class */ InputAutocomplete.options = []; /** @type {string} The search box dom selector */ InputAutocomplete.selector = '[data-js="input-autocomplete__input"]'; /** @type {string} The classname for the dropdown element */ InputAutocomplete.classname = 'input-autocomplete__dropdown'; /** * The Accordion module * @class */ var Accordion = function Accordion() { this._toggle = new Toggle({ selector: Accordion.selector, namespace: Accordion.namespace, inactiveClass: Accordion.inactiveClass }); return this; }; /** * The dom selector for the module * @type {String} */ Accordion.selector = '[data-js*="accordion"]'; /** * The namespace for the components JS options * @type {String} */ Accordion.namespace = 'accordion'; /** * The incactive class name * @type {String} */ Accordion.inactiveClass = 'inactive'; /** * The Filter module * @class */ var Filter = function Filter() { this._toggle = new Toggle({ selector: Filter.selector, namespace: Filter.namespace, inactiveClass: Filter.inactiveClass }); return this; }; /** * The dom selector for the module * @type {String} */ Filter.selector = '[data-js*="filter"]'; /** * The namespace for the components JS options * @type {String} */ Filter.namespace = 'filter'; /** * The incactive class name * @type {String} */ Filter.inactiveClass = 'inactive'; /** Detect free variable `global` from Node.js. */ var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; /** Detect free variable `self`. */ var freeSelf = typeof self == 'object' && self && self.Object === Object && self; /** Used as a reference to the global object. */ var root = freeGlobal || freeSelf || Function('return this')(); /** Built-in value references. */ var Symbol = root.Symbol; /** Used for built-in method references. */ var objectProto = Object.prototype; /** Used to check objects for own properties. */ var hasOwnProperty = objectProto.hasOwnProperty; /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) * of values. */ var nativeObjectToString = objectProto.toString; /** Built-in value references. */ var symToStringTag = Symbol ? Symbol.toStringTag : undefined; /** * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. * * @private * @param {*} value The value to query. * @returns {string} Returns the raw `toStringTag`. */ function getRawTag(value) { var isOwn = hasOwnProperty.call(value, symToStringTag), tag = value[symToStringTag]; try { value[symToStringTag] = undefined; var unmasked = true; } catch (e) {} var result = nativeObjectToString.call(value); if (unmasked) { if (isOwn) { value[symToStringTag] = tag; } else { delete value[symToStringTag]; } } return result; } /** Used for built-in method references. */ var objectProto$1 = Object.prototype; /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) * of values. */ var nativeObjectToString$1 = objectProto$1.toString; /** * Converts `value` to a string using `Object.prototype.toString`. * * @private * @param {*} value The value to convert. * @returns {string} Returns the converted string. */ function objectToString(value) { return nativeObjectToString$1.call(value); } /** `Object#toString` result references. */ var nullTag = '[object Null]', undefinedTag = '[object Undefined]'; /** Built-in value references. */ var symToStringTag$1 = Symbol ? Symbol.toStringTag : undefined; /** * The base implementation of `getTag` without fallbacks for buggy environments. * * @private * @param {*} value The value to query. * @returns {string} Returns the `toStringTag`. */ function baseGetTag(value) { if (value == null) { return value === undefined ? undefinedTag : nullTag; } return (symToStringTag$1 && symToStringTag$1 in Object(value)) ? getRawTag(value) : objectToString(value); } /** * Checks if `value` is the * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an object, else `false`. * @example * * _.isObject({}); * // => true * * _.isObject([1, 2, 3]); * // => true * * _.isObject(_.noop); * // => true * * _.isObject(null); * // => false */ function isObject(value) { var type = typeof value; return value != null && (type == 'object' || type == 'function'); } /** `Object#toString` result references. */ var asyncTag = '[object AsyncFunction]', funcTag = '[object Function]', genTag = '[object GeneratorFunction]', proxyTag = '[object Proxy]'; /** * Checks if `value` is classified as a `Function` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a function, else `false`. * @example * * _.isFunction(_); * // => true * * _.isFunction(/abc/); * // => false */ function isFunction(value) { if (!isObject(value)) { return false; } // The use of `Object#toString` avoids issues with the `typeof` operator // in Safari 9 which returns 'object' for typed arrays and other constructors. var tag = baseGetTag(value); return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; } /** Used to detect overreaching core-js shims. */ var coreJsData = root['__core-js_shared__']; /** Used to detect methods masquerading as native. */ var maskSrcKey = (function() { var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); return uid ? ('Symbol(src)_1.' + uid) : ''; }()); /** * Checks if `func` has its source masked. * * @private * @param {Function} func The function to check. * @returns {boolean} Returns `true` if `func` is masked, else `false`. */ function isMasked(func) { return !!maskSrcKey && (maskSrcKey in func); } /** Used for built-in method references. */ var funcProto = Function.prototype; /** Used to resolve the decompiled source of functions. */ var funcToString = funcProto.toString; /** * Converts `func` to its source code. * * @private * @param {Function} func The function to convert. * @returns {string} Returns the source code. */ function toSource(func) { if (func != null) { try { return funcToString.call(func); } catch (e) {} try { return (func + ''); } catch (e) {} } return ''; } /** * Used to match `RegExp` * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). */ var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; /** Used to detect host constructors (Safari). */ var reIsHostCtor = /^\[object .+?Constructor\]$/; /** Used for built-in method references. */ var funcProto$1 = Function.prototype, objectProto$2 = Object.prototype; /** Used to resolve the decompiled source of functions. */ var funcToString$1 = funcProto$1.toString; /** Used to check objects for own properties. */ var hasOwnProperty$1 = objectProto$2.hasOwnProperty; /** Used to detect if a method is native. */ var reIsNative = RegExp('^' + funcToString$1.call(hasOwnProperty$1).replace(reRegExpChar, '\\$&') .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' ); /** * The base implementation of `_.isNative` without bad shim checks. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a native function, * else `false`. */ function baseIsNative(value) { if (!isObject(value) || isMasked(value)) { return false; } var pattern = isFunction(value) ? reIsNative : reIsHostCtor; return pattern.test(toSource(value)); } /** * Gets the value at `key` of `object`. * * @private * @param {Object} [object] The object to query. * @param {string} key The key of the property to get. * @returns {*} Returns the property value. */ function getValue(object, key) { return object == null ? undefined : object[key]; } /** * Gets the native function at `key` of `object`. * * @private * @param {Object} object The object to query. * @param {string} key The key of the method to get. * @returns {*} Returns the function if it's native, else `undefined`. */ function getNative(object, key) { var value = getValue(object, key); return baseIsNative(value) ? value : undefined; } var defineProperty = (function() { try { var func = getNative(Object, 'defineProperty'); func({}, '', {}); return func; } catch (e) {} }()); /** * The base implementation of `assignValue` and `assignMergeValue` without * value checks. * * @private * @param {Object} object The object to modify. * @param {string} key The key of the property to assign. * @param {*} value The value to assign. */ function baseAssignValue(object, key, value) { if (key == '__proto__' && defineProperty) { defineProperty(object, key, { 'configurable': true, 'enumerable': true, 'value': value, 'writable': true }); } else { object[key] = value; } } /** * Performs a * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * comparison between two values to determine if they are equivalent. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * * var object = { 'a': 1 }; * var other = { 'a': 1 }; * * _.eq(object, object); * // => true * * _.eq(object, other); * // => false * * _.eq('a', 'a'); * // => true * * _.eq('a', Object('a')); * // => false * * _.eq(NaN, NaN); * // => true */ function eq(value, other) { return value === other || (value !== value && other !== other); } /** Used for built-in method references. */ var objectProto$3 = Object.prototype; /** Used to check objects for own properties. */ var hasOwnProperty$2 = objectProto$3.hasOwnProperty; /** * Assigns `value` to `key` of `object` if the existing value is not equivalent * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. * * @private * @param {Object} object The object to modify. * @param {string} key The key of the property to assign. * @param {*} value The value to assign. */ function assignValue(object, key, value) { var objValue = object[key]; if (!(hasOwnProperty$2.call(object, key) && eq(objValue, value)) || (value === undefined && !(key in object))) { baseAssignValue(object, key, value); } } /** * Copies properties of `source` to `object`. * * @private * @param {Object} source The object to copy properties from. * @param {Array} props The property identifiers to copy. * @param {Object} [object={}] The object to copy properties to. * @param {Function} [customizer] The function to customize copied values. * @returns {Object} Returns `object`. */ function copyObject(source, props, object, customizer) { var isNew = !object; object || (object = {}); var index = -1, length = props.length; while (++index < length) { var key = props[index]; var newValue = customizer ? customizer(object[key], source[key], key, object, source) : undefined; if (newValue === undefined) { newValue = source[key]; } if (isNew) { baseAssignValue(object, key, newValue); } else { assignValue(object, key, newValue); } } return object; } /** * This method returns the first argument it receives. * * @static * @since 0.1.0 * @memberOf _ * @category Util * @param {*} value Any value. * @returns {*} Returns `value`. * @example * * var object = { 'a': 1 }; * * console.log(_.identity(object) === object); * // => true */ function identity(value) { return value; } /** * A faster alternative to `Function#apply`, this function invokes `func` * with the `this` binding of `thisArg` and the arguments of `args`. * * @private * @param {Function} func The function to invoke. * @param {*} thisArg The `this` binding of `func`. * @param {Array} args The arguments to invoke `func` with. * @returns {*} Returns the result of `func`. */ function apply(func, thisArg, args) { switch (args.length) { case 0: return func.call(thisArg); case 1: return func.call(thisArg, args[0]); case 2: return func.call(thisArg, args[0], args[1]); case 3: return func.call(thisArg, args[0], args[1], args[2]); } return func.apply(thisArg, args); } /* Built-in method references for those with the same name as other `lodash` methods. */ var nativeMax = Math.max; /** * A specialized version of `baseRest` which transforms the rest array. * * @private * @param {Function} func The function to apply a rest parameter to. * @param {number} [start=func.length-1] The start position of the rest parameter. * @param {Function} transform The rest array transform. * @returns {Function} Returns the new function. */ function overRest(func, start, transform) { start = nativeMax(start === undefined ? (func.length - 1) : start, 0); return function() { var args = arguments, index = -1, length = nativeMax(args.length - start, 0), array = Array(length); while (++index < length) { array[index] = args[start + index]; } index = -1; var otherArgs = Array(start + 1); while (++index < start) { otherArgs[index] = args[index]; } otherArgs[start] = transform(array); return apply(func, this, otherArgs); }; } /** * Creates a function that returns `value`. * * @static * @memberOf _ * @since 2.4.0 * @category Util * @param {*} value The value to return from the new function. * @returns {Function} Returns the new constant function. * @example * * var objects = _.times(2, _.constant({ 'a': 1 })); * * console.log(objects); * // => [{ 'a': 1 }, { 'a': 1 }] * * console.log(objects[0] === objects[1]); * // => true */ function constant(value) { return function() { return value; }; } /** * The base implementation of `setToString` without support for hot loop shorting. * * @private * @param {Function} func The function to modify. * @param {Function} string The `toString` result. * @returns {Function} Returns `func`. */ var baseSetToString = !defineProperty ? identity : function(func, string) { return defineProperty(func, 'toString', { 'configurable': true, 'enumerable': false, 'value': constant(string), 'writable': true }); }; /** Used to detect hot functions by number of calls within a span of milliseconds. */ var HOT_COUNT = 800, HOT_SPAN = 16; /* Built-in method references for those with the same name as other `lodash` methods. */ var nativeNow = Date.now; /** * Creates a function that'll short out and invoke `identity` instead * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN` * milliseconds. * * @private * @param {Function} func The function to restrict. * @returns {Function} Returns the new shortable function. */ function shortOut(func) { var count = 0, lastCalled = 0; return function() { var stamp = nativeNow(), remaining = HOT_SPAN - (stamp - lastCalled); lastCalled = stamp; if (remaining > 0) { if (++count >= HOT_COUNT) { return arguments[0]; } } else { count = 0; } return func.apply(undefined, arguments); }; } /** * Sets the `toString` method of `func` to return `string`. * * @private * @param {Function} func The function to modify. * @param {Function} string The `toString` result. * @returns {Function} Returns `func`. */ var setToString = shortOut(baseSetToString); /** * The base implementation of `_.rest` which doesn't validate or coerce arguments. * * @private * @param {Function} func The function to apply a rest parameter to. * @param {number} [start=func.length-1] The start position of the rest parameter. * @returns {Function} Returns the new function. */ function baseRest(func, start) { return setToString(overRest(func, start, identity), func + ''); } /** Used as references for various `Number` constants. */ var MAX_SAFE_INTEGER = 9007199254740991; /** * Checks if `value` is a valid array-like length. * * **Note:** This method is loosely based on * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. * @example * * _.isLength(3); * // => true * * _.isLength(Number.MIN_VALUE); * // => false * * _.isLength(Infinity); * // => false * * _.isLength('3'); * // => false */ function isLength(value) { return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; } /** * Checks if `value` is array-like. A value is considered array-like if it's * not a function and has a `value.length` that's an integer greater than or * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is array-like, else `false`. * @example * * _.isArrayLike([1, 2, 3]); * // => true * * _.isArrayLike(document.body.children); * // => true * * _.isArrayLike('abc'); * // => true * * _.isArrayLike(_.noop); * // => false */ function isArrayLike(value) { return value != null && isLength(value.length) && !isFunction(value); } /** Used as references for various `Number` constants. */ var MAX_SAFE_INTEGER$1 = 9007199254740991; /** Used to detect unsigned integer values. */ var reIsUint = /^(?:0|[1-9]\d*)$/; /** * Checks if `value` is a valid array-like index. * * @private * @param {*} value The value to check. * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. */ function isIndex(value, length) { var type = typeof value; length = length == null ? MAX_SAFE_INTEGER$1 : length; return !!length && (type == 'number' || (type != 'symbol' && reIsUint.test(value))) && (value > -1 && value % 1 == 0 && value < length); } /** * Checks if the given arguments are from an iteratee call. * * @private * @param {*} value The potential iteratee value argument. * @param {*} index The potential iteratee index or key argument. * @param {*} object The potential iteratee object argument. * @returns {boolean} Returns `true` if the arguments are from an iteratee call, * else `false`. */ function isIterateeCall(value, index, object) { if (!isObject(object)) { return false; } var type = typeof index; if (type == 'number' ? (isArrayLike(object) && isIndex(index, object.length)) : (type == 'string' && index in object) ) { return eq(object[index], value); } return false; } /** * Creates a function like `_.assign`. * * @private * @param {Function} assigner The function to assign values. * @returns {Function} Returns the new assigner function. */ function createAssigner(assigner) { return baseRest(function(object, sources) { var index = -1, length = sources.length, customizer = length > 1 ? sources[length - 1] : undefined, guard = length > 2 ? sources[2] : undefined; customizer = (assigner.length > 3 && typeof customizer == 'function') ? (length--, customizer) : undefined; if (guard && isIterateeCall(sources[0], sources[1], guard)) { customizer = length < 3 ? undefined : customizer; length = 1; } object = Object(object); while (++index < length) { var source = sources[index]; if (source) { assigner(object, source, index, customizer); } } return object; }); } /** * The base implementation of `_.times` without support for iteratee shorthands * or max array length checks. * * @private * @param {number} n The number of times to invoke `iteratee`. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns the array of results. */ function baseTimes(n, iteratee) { var index = -1, result = Array(n); while (++index < n) { result[index] = iteratee(index); } return result; } /** * Checks if `value` is object-like. A value is object-like if it's not `null` * and has a `typeof` result of "object". * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is object-like, else `false`. * @example * * _.isObjectLike({}); * // => true * * _.isObjectLike([1, 2, 3]); * // => true * * _.isObjectLike(_.noop); * // => false * * _.isObjectLike(null); * // => false */ function isObjectLike(value) { return value != null && typeof value == 'object'; } /** `Object#toString` result references. */ var argsTag = '[object Arguments]'; /** * The base implementation of `_.isArguments`. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an `arguments` object, */ function baseIsArguments(value) { return isObjectLike(value) && baseGetTag(value) == argsTag; } /** Used for built-in method references. */ var objectProto$4 = Object.prototype; /** Used to check objects for own properties. */ var hasOwnProperty$3 = objectProto$4.hasOwnProperty; /** Built-in value references. */ var propertyIsEnumerable = objectProto$4.propertyIsEnumerable; /** * Checks if `value` is likely an `arguments` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an `arguments` object, * else `false`. * @example * * _.isArguments(function() { return arguments; }()); * // => true * * _.isArguments([1, 2, 3]); * // => false */ var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) { return isObjectLike(value) && hasOwnProperty$3.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); }; /** * Checks if `value` is classified as an `Array` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array, else `false`. * @example * * _.isArray([1, 2, 3]); * // => true * * _.isArray(document.body.children); * // => false * * _.isArray('abc'); * // => false * * _.isArray(_.noop); * // => false */ var isArray = Array.isArray; /** * This method returns `false`. * * @static * @memberOf _ * @since 4.13.0 * @category Util * @returns {boolean} Returns `false`. * @example * * _.times(2, _.stubFalse); * // => [false, false] */ function stubFalse() { return false; } /** Detect free variable `exports`. */ var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; /** Detect free variable `module`. */ var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; /** Detect the popular CommonJS extension `module.exports`. */ var moduleExports = freeModule && freeModule.exports === freeExports; /** Built-in value references. */ var Buffer = moduleExports ? root.Buffer : undefined; /* Built-in method references for those with the same name as other `lodash` methods. */ var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined; /** * Checks if `value` is a buffer. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. * @example * * _.isBuffer(new Buffer(2)); * // => true * * _.isBuffer(new Uint8Array(2)); * // => false */ var isBuffer = nativeIsBuffer || stubFalse; /** `Object#toString` result references. */ var argsTag$1 = '[object Arguments]', arrayTag = '[object Array]', boolTag = '[object Boolean]', dateTag = '[object Date]', errorTag = '[object Error]', funcTag$1 = '[object Function]', mapTag = '[object Map]', numberTag = '[object Number]', objectTag = '[object Object]', regexpTag = '[object RegExp]', setTag = '[object Set]', stringTag = '[object String]', weakMapTag = '[object WeakMap]'; var arrayBufferTag = '[object ArrayBuffer]', dataViewTag = '[object DataView]', float32Tag = '[object Float32Array]', float64Tag = '[object Float64Array]', int8Tag = '[object Int8Array]', int16Tag = '[object Int16Array]', int32Tag = '[object Int32Array]', uint8Tag = '[object Uint8Array]', uint8ClampedTag = '[object Uint8ClampedArray]', uint16Tag = '[object Uint16Array]', uint32Tag = '[object Uint32Array]'; /** Used to identify `toStringTag` values of typed arrays. */ var typedArrayTags = {}; typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8Cla