access-nyc-patterns
Version:
User Interface Patterns for Benefits Access
1,792 lines (1,546 loc) • 124 kB
JavaScript
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