diff --git a/dist/influxframework-datatable.cjs.js b/dist/influxframework-datatable.cjs.js
new file mode 100644
index 0000000..87e402f
--- /dev/null
+++ b/dist/influxframework-datatable.cjs.js
@@ -0,0 +1,5983 @@
+'use strict';
+
+function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
+
+var Sortable = _interopDefault(require('sortablejs'));
+
+function $(expr, con) {
+ return typeof expr === 'string' ?
+ (con || document).querySelector(expr) :
+ expr || null;
+}
+
+$.each = (expr, con) => {
+ return typeof expr === 'string' ?
+ Array.from((con || document).querySelectorAll(expr)) :
+ expr || null;
+};
+
+$.create = (tag, o) => {
+ let element = document.createElement(tag);
+
+ for (let i in o) {
+ let val = o[i];
+
+ if (i === 'inside') {
+ $(val).appendChild(element);
+ } else
+ if (i === 'around') {
+ let ref = $(val);
+ ref.parentNode.insertBefore(element, ref);
+ element.appendChild(ref);
+ } else
+ if (i === 'styles') {
+ if (typeof val === 'object') {
+ Object.keys(val).map(prop => {
+ element.style[prop] = val[prop];
+ });
+ }
+ } else
+ if (i in element) {
+ element[i] = val;
+ } else {
+ element.setAttribute(i, val);
+ }
+ }
+
+ return element;
+};
+
+$.on = (element, event, selector, callback) => {
+ if (!callback) {
+ callback = selector;
+ $.bind(element, event, callback);
+ } else {
+ $.delegate(element, event, selector, callback);
+ }
+};
+
+$.off = (element, event, handler) => {
+ element.removeEventListener(event, handler);
+};
+
+$.bind = (element, event, callback) => {
+ event.split(/\s+/).forEach(function (event) {
+ element.addEventListener(event, callback);
+ });
+};
+
+$.delegate = (element, event, selector, callback) => {
+ element.addEventListener(event, function (e) {
+ const delegatedTarget = e.target.closest(selector);
+ if (delegatedTarget) {
+ e.delegatedTarget = delegatedTarget;
+ callback.call(this, e, delegatedTarget);
+ }
+ });
+};
+
+$.unbind = (element, o) => {
+ if (element) {
+ for (let event in o) {
+ let callback = o[event];
+
+ event.split(/\s+/).forEach(function (event) {
+ element.removeEventListener(event, callback);
+ });
+ }
+ }
+};
+
+$.fire = (target, type, properties) => {
+ let evt = document.createEvent('HTMLEvents');
+
+ evt.initEvent(type, true, true);
+
+ for (let j in properties) {
+ evt[j] = properties[j];
+ }
+
+ return target.dispatchEvent(evt);
+};
+
+$.data = (element, attrs) => { // eslint-disable-line
+ if (!attrs) {
+ return element.dataset;
+ }
+
+ for (const attr in attrs) {
+ element.dataset[attr] = attrs[attr];
+ }
+};
+
+$.style = (elements, styleMap) => { // eslint-disable-line
+
+ if (typeof styleMap === 'string') {
+ return $.getStyle(elements, styleMap);
+ }
+
+ if (!Array.isArray(elements)) {
+ elements = [elements];
+ }
+
+ elements.map(element => {
+ for (const prop in styleMap) {
+ element.style[prop] = styleMap[prop];
+ }
+ });
+};
+
+$.removeStyle = (elements, styleProps) => {
+ if (!Array.isArray(elements)) {
+ elements = [elements];
+ }
+
+ if (!Array.isArray(styleProps)) {
+ styleProps = [styleProps];
+ }
+
+ elements.map(element => {
+ for (const prop of styleProps) {
+ element.style[prop] = '';
+ }
+ });
+};
+
+$.getStyle = (element, prop) => {
+ if (!prop) {
+ return getComputedStyle(element);
+ }
+
+ let val = getComputedStyle(element)[prop];
+
+ if (['width', 'height'].includes(prop)) {
+ val = parseFloat(val);
+ }
+
+ return val;
+};
+
+$.closest = (selector, element) => {
+ if (!element) return null;
+
+ if (element.matches(selector)) {
+ return element;
+ }
+
+ return $.closest(selector, element.parentNode);
+};
+
+$.inViewport = (el, parentEl) => {
+ const {
+ top,
+ left,
+ bottom,
+ right
+ } = el.getBoundingClientRect();
+ const {
+ top: pTop,
+ left: pLeft,
+ bottom: pBottom,
+ right: pRight
+ } = parentEl.getBoundingClientRect();
+
+ return top >= pTop && left >= pLeft && bottom <= pBottom && right <= pRight;
+};
+
+$.scrollTop = function scrollTop(element, pixels) {
+ requestAnimationFrame(() => {
+ element.scrollTop = pixels;
+ });
+};
+
+$.scrollbarSize = function scrollbarSize() {
+ if (!$.scrollBarSizeValue) {
+ $.scrollBarSizeValue = getScrollBarSize();
+ }
+ return $.scrollBarSizeValue;
+};
+
+function getScrollBarSize() {
+ // assume scrollbar width and height would be the same
+
+ // Create the measurement node
+ const scrollDiv = document.createElement('div');
+ $.style(scrollDiv, {
+ width: '100px',
+ height: '100px',
+ overflow: 'scroll',
+ position: 'absolute',
+ top: '-9999px'
+ });
+ document.body.appendChild(scrollDiv);
+
+ // Get the scrollbar width
+ const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
+
+ // Delete the DIV
+ document.body.removeChild(scrollDiv);
+
+ return scrollbarWidth;
+}
+
+$.hasVerticalOverflow = function (element) {
+ return element.scrollHeight > element.offsetHeight + 10;
+};
+
+$.hasHorizontalOverflow = function (element) {
+ return element.scrollWidth > element.offsetWidth + 10;
+};
+
+$.measureTextWidth = function (text) {
+ const div = document.createElement('div');
+ div.style.position = 'absolute';
+ div.style.visibility = 'hidden';
+ div.style.height = 'auto';
+ div.style.width = 'auto';
+ div.style.whiteSpace = 'nowrap';
+ div.innerText = text;
+ document.body.appendChild(div);
+ return div.clientWidth + 1;
+};
+
+/**
+ * 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');
+}
+
+var isObject_1 = isObject;
+
+var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+function commonjsRequire () {
+ throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs');
+}
+
+function unwrapExports (x) {
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
+}
+
+function createCommonjsModule(fn, module) {
+ return module = { exports: {} }, fn(module, module.exports), module.exports;
+}
+
+/** Detect free variable `global` from Node.js. */
+var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
+
+var _freeGlobal = freeGlobal;
+
+/** 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')();
+
+var _root = root;
+
+/**
+ * Gets the timestamp of the number of milliseconds that have elapsed since
+ * the Unix epoch (1 January 1970 00:00:00 UTC).
+ *
+ * @static
+ * @memberOf _
+ * @since 2.4.0
+ * @category Date
+ * @returns {number} Returns the timestamp.
+ * @example
+ *
+ * _.defer(function(stamp) {
+ * console.log(_.now() - stamp);
+ * }, _.now());
+ * // => Logs the number of milliseconds it took for the deferred invocation.
+ */
+var now = function() {
+ return _root.Date.now();
+};
+
+var now_1 = now;
+
+/** Used to match a single whitespace character. */
+var reWhitespace = /\s/;
+
+/**
+ * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
+ * character of `string`.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @returns {number} Returns the index of the last non-whitespace character.
+ */
+function trimmedEndIndex(string) {
+ var index = string.length;
+
+ while (index-- && reWhitespace.test(string.charAt(index))) {}
+ return index;
+}
+
+var _trimmedEndIndex = trimmedEndIndex;
+
+/** Used to match leading whitespace. */
+var reTrimStart = /^\s+/;
+
+/**
+ * The base implementation of `_.trim`.
+ *
+ * @private
+ * @param {string} string The string to trim.
+ * @returns {string} Returns the trimmed string.
+ */
+function baseTrim(string) {
+ return string
+ ? string.slice(0, _trimmedEndIndex(string) + 1).replace(reTrimStart, '')
+ : string;
+}
+
+var _baseTrim = baseTrim;
+
+/** Built-in value references. */
+var Symbol = _root.Symbol;
+
+var _Symbol = 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;
+ } catch (e) {}
+
+ var result = nativeObjectToString.call(value);
+ {
+ if (isOwn) {
+ value[symToStringTag] = tag;
+ } else {
+ delete value[symToStringTag];
+ }
+ }
+ return result;
+}
+
+var _getRawTag = getRawTag;
+
+/** 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);
+}
+
+var _objectToString = objectToString;
+
+/** `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);
+}
+
+var _baseGetTag = baseGetTag;
+
+/**
+ * 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';
+}
+
+var isObjectLike_1 = isObjectLike;
+
+/** `Object#toString` result references. */
+var symbolTag = '[object Symbol]';
+
+/**
+ * Checks if `value` is classified as a `Symbol` primitive or object.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
+ * @example
+ *
+ * _.isSymbol(Symbol.iterator);
+ * // => true
+ *
+ * _.isSymbol('abc');
+ * // => false
+ */
+function isSymbol(value) {
+ return typeof value == 'symbol' ||
+ (isObjectLike_1(value) && _baseGetTag(value) == symbolTag);
+}
+
+var isSymbol_1 = isSymbol;
+
+/** Used as references for various `Number` constants. */
+var NAN = 0 / 0;
+
+/** Used to detect bad signed hexadecimal string values. */
+var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
+
+/** Used to detect binary string values. */
+var reIsBinary = /^0b[01]+$/i;
+
+/** Used to detect octal string values. */
+var reIsOctal = /^0o[0-7]+$/i;
+
+/** Built-in method references without a dependency on `root`. */
+var freeParseInt = parseInt;
+
+/**
+ * Converts `value` to a number.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to process.
+ * @returns {number} Returns the number.
+ * @example
+ *
+ * _.toNumber(3.2);
+ * // => 3.2
+ *
+ * _.toNumber(Number.MIN_VALUE);
+ * // => 5e-324
+ *
+ * _.toNumber(Infinity);
+ * // => Infinity
+ *
+ * _.toNumber('3.2');
+ * // => 3.2
+ */
+function toNumber(value) {
+ if (typeof value == 'number') {
+ return value;
+ }
+ if (isSymbol_1(value)) {
+ return NAN;
+ }
+ if (isObject_1(value)) {
+ var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
+ value = isObject_1(other) ? (other + '') : other;
+ }
+ if (typeof value != 'string') {
+ return value === 0 ? value : +value;
+ }
+ value = _baseTrim(value);
+ var isBinary = reIsBinary.test(value);
+ return (isBinary || reIsOctal.test(value))
+ ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
+ : (reIsBadHex.test(value) ? NAN : +value);
+}
+
+var toNumber_1 = toNumber;
+
+/** Error message constants. */
+var FUNC_ERROR_TEXT = 'Expected a function';
+
+/* Built-in method references for those with the same name as other `lodash` methods. */
+var nativeMax = Math.max,
+ nativeMin = Math.min;
+
+/**
+ * Creates a debounced function that delays invoking `func` until after `wait`
+ * milliseconds have elapsed since the last time the debounced function was
+ * invoked. The debounced function comes with a `cancel` method to cancel
+ * delayed `func` invocations and a `flush` method to immediately invoke them.
+ * Provide `options` to indicate whether `func` should be invoked on the
+ * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
+ * with the last arguments provided to the debounced function. Subsequent
+ * calls to the debounced function return the result of the last `func`
+ * invocation.
+ *
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is
+ * invoked on the trailing edge of the timeout only if the debounced function
+ * is invoked more than once during the `wait` timeout.
+ *
+ * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
+ * until to the next tick, similar to `setTimeout` with a timeout of `0`.
+ *
+ * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
+ * for details over the differences between `_.debounce` and `_.throttle`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to debounce.
+ * @param {number} [wait=0] The number of milliseconds to delay.
+ * @param {Object} [options={}] The options object.
+ * @param {boolean} [options.leading=false]
+ * Specify invoking on the leading edge of the timeout.
+ * @param {number} [options.maxWait]
+ * The maximum time `func` is allowed to be delayed before it's invoked.
+ * @param {boolean} [options.trailing=true]
+ * Specify invoking on the trailing edge of the timeout.
+ * @returns {Function} Returns the new debounced function.
+ * @example
+ *
+ * // Avoid costly calculations while the window size is in flux.
+ * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
+ *
+ * // Invoke `sendMail` when clicked, debouncing subsequent calls.
+ * jQuery(element).on('click', _.debounce(sendMail, 300, {
+ * 'leading': true,
+ * 'trailing': false
+ * }));
+ *
+ * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
+ * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
+ * var source = new EventSource('/stream');
+ * jQuery(source).on('message', debounced);
+ *
+ * // Cancel the trailing debounced invocation.
+ * jQuery(window).on('popstate', debounced.cancel);
+ */
+function debounce(func, wait, options) {
+ var lastArgs,
+ lastThis,
+ maxWait,
+ result,
+ timerId,
+ lastCallTime,
+ lastInvokeTime = 0,
+ leading = false,
+ maxing = false,
+ trailing = true;
+
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ wait = toNumber_1(wait) || 0;
+ if (isObject_1(options)) {
+ leading = !!options.leading;
+ maxing = 'maxWait' in options;
+ maxWait = maxing ? nativeMax(toNumber_1(options.maxWait) || 0, wait) : maxWait;
+ trailing = 'trailing' in options ? !!options.trailing : trailing;
+ }
+
+ function invokeFunc(time) {
+ var args = lastArgs,
+ thisArg = lastThis;
+
+ lastArgs = lastThis = undefined;
+ lastInvokeTime = time;
+ result = func.apply(thisArg, args);
+ return result;
+ }
+
+ function leadingEdge(time) {
+ // Reset any `maxWait` timer.
+ lastInvokeTime = time;
+ // Start the timer for the trailing edge.
+ timerId = setTimeout(timerExpired, wait);
+ // Invoke the leading edge.
+ return leading ? invokeFunc(time) : result;
+ }
+
+ function remainingWait(time) {
+ var timeSinceLastCall = time - lastCallTime,
+ timeSinceLastInvoke = time - lastInvokeTime,
+ timeWaiting = wait - timeSinceLastCall;
+
+ return maxing
+ ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
+ : timeWaiting;
+ }
+
+ function shouldInvoke(time) {
+ var timeSinceLastCall = time - lastCallTime,
+ timeSinceLastInvoke = time - lastInvokeTime;
+
+ // Either this is the first call, activity has stopped and we're at the
+ // trailing edge, the system time has gone backwards and we're treating
+ // it as the trailing edge, or we've hit the `maxWait` limit.
+ return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
+ (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
+ }
+
+ function timerExpired() {
+ var time = now_1();
+ if (shouldInvoke(time)) {
+ return trailingEdge(time);
+ }
+ // Restart the timer.
+ timerId = setTimeout(timerExpired, remainingWait(time));
+ }
+
+ function trailingEdge(time) {
+ timerId = undefined;
+
+ // Only invoke if we have `lastArgs` which means `func` has been
+ // debounced at least once.
+ if (trailing && lastArgs) {
+ return invokeFunc(time);
+ }
+ lastArgs = lastThis = undefined;
+ return result;
+ }
+
+ function cancel() {
+ if (timerId !== undefined) {
+ clearTimeout(timerId);
+ }
+ lastInvokeTime = 0;
+ lastArgs = lastCallTime = lastThis = timerId = undefined;
+ }
+
+ function flush() {
+ return timerId === undefined ? result : trailingEdge(now_1());
+ }
+
+ function debounced() {
+ var time = now_1(),
+ isInvoking = shouldInvoke(time);
+
+ lastArgs = arguments;
+ lastThis = this;
+ lastCallTime = time;
+
+ if (isInvoking) {
+ if (timerId === undefined) {
+ return leadingEdge(lastCallTime);
+ }
+ if (maxing) {
+ // Handle invocations in a tight loop.
+ clearTimeout(timerId);
+ timerId = setTimeout(timerExpired, wait);
+ return invokeFunc(lastCallTime);
+ }
+ }
+ if (timerId === undefined) {
+ timerId = setTimeout(timerExpired, wait);
+ }
+ return result;
+ }
+ debounced.cancel = cancel;
+ debounced.flush = flush;
+ return debounced;
+}
+
+var debounce_1 = debounce;
+
+/** Error message constants. */
+var FUNC_ERROR_TEXT$1 = 'Expected a function';
+
+/**
+ * Creates a throttled function that only invokes `func` at most once per
+ * every `wait` milliseconds. The throttled function comes with a `cancel`
+ * method to cancel delayed `func` invocations and a `flush` method to
+ * immediately invoke them. Provide `options` to indicate whether `func`
+ * should be invoked on the leading and/or trailing edge of the `wait`
+ * timeout. The `func` is invoked with the last arguments provided to the
+ * throttled function. Subsequent calls to the throttled function return the
+ * result of the last `func` invocation.
+ *
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is
+ * invoked on the trailing edge of the timeout only if the throttled function
+ * is invoked more than once during the `wait` timeout.
+ *
+ * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
+ * until to the next tick, similar to `setTimeout` with a timeout of `0`.
+ *
+ * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
+ * for details over the differences between `_.throttle` and `_.debounce`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to throttle.
+ * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
+ * @param {Object} [options={}] The options object.
+ * @param {boolean} [options.leading=true]
+ * Specify invoking on the leading edge of the timeout.
+ * @param {boolean} [options.trailing=true]
+ * Specify invoking on the trailing edge of the timeout.
+ * @returns {Function} Returns the new throttled function.
+ * @example
+ *
+ * // Avoid excessively updating the position while scrolling.
+ * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
+ *
+ * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
+ * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
+ * jQuery(element).on('click', throttled);
+ *
+ * // Cancel the trailing throttled invocation.
+ * jQuery(window).on('popstate', throttled.cancel);
+ */
+function throttle(func, wait, options) {
+ var leading = true,
+ trailing = true;
+
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT$1);
+ }
+ if (isObject_1(options)) {
+ leading = 'leading' in options ? !!options.leading : leading;
+ trailing = 'trailing' in options ? !!options.trailing : trailing;
+ }
+ return debounce_1(func, wait, {
+ 'leading': leading,
+ 'maxWait': wait,
+ 'trailing': trailing
+ });
+}
+
+var throttle_1 = throttle;
+
+/** `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_1(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;
+}
+
+var isFunction_1 = isFunction;
+
+/** Used to detect overreaching core-js shims. */
+var coreJsData = _root['__core-js_shared__'];
+
+var _coreJsData = coreJsData;
+
+/** 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);
+}
+
+var _isMasked = isMasked;
+
+/** 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 '';
+}
+
+var _toSource = toSource;
+
+/**
+ * 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_1(value) || _isMasked(value)) {
+ return false;
+ }
+ var pattern = isFunction_1(value) ? reIsNative : reIsHostCtor;
+ return pattern.test(_toSource(value));
+}
+
+var _baseIsNative = baseIsNative;
+
+/**
+ * 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];
+}
+
+var _getValue = getValue;
+
+/**
+ * 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 _getNative = getNative;
+
+/* Built-in method references that are verified to be native. */
+var nativeCreate = _getNative(Object, 'create');
+
+var _nativeCreate = nativeCreate;
+
+/**
+ * Removes all key-value entries from the hash.
+ *
+ * @private
+ * @name clear
+ * @memberOf Hash
+ */
+function hashClear() {
+ this.__data__ = _nativeCreate ? _nativeCreate(null) : {};
+ this.size = 0;
+}
+
+var _hashClear = hashClear;
+
+/**
+ * Removes `key` and its value from the hash.
+ *
+ * @private
+ * @name delete
+ * @memberOf Hash
+ * @param {Object} hash The hash to modify.
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+function hashDelete(key) {
+ var result = this.has(key) && delete this.__data__[key];
+ this.size -= result ? 1 : 0;
+ return result;
+}
+
+var _hashDelete = hashDelete;
+
+/** Used to stand-in for `undefined` hash values. */
+var HASH_UNDEFINED = '__lodash_hash_undefined__';
+
+/** Used for built-in method references. */
+var objectProto$3 = Object.prototype;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty$2 = objectProto$3.hasOwnProperty;
+
+/**
+ * Gets the hash value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf Hash
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+function hashGet(key) {
+ var data = this.__data__;
+ if (_nativeCreate) {
+ var result = data[key];
+ return result === HASH_UNDEFINED ? undefined : result;
+ }
+ return hasOwnProperty$2.call(data, key) ? data[key] : undefined;
+}
+
+var _hashGet = hashGet;
+
+/** Used for built-in method references. */
+var objectProto$4 = Object.prototype;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty$3 = objectProto$4.hasOwnProperty;
+
+/**
+ * Checks if a hash value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf Hash
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+function hashHas(key) {
+ var data = this.__data__;
+ return _nativeCreate ? (data[key] !== undefined) : hasOwnProperty$3.call(data, key);
+}
+
+var _hashHas = hashHas;
+
+/** Used to stand-in for `undefined` hash values. */
+var HASH_UNDEFINED$1 = '__lodash_hash_undefined__';
+
+/**
+ * Sets the hash `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf Hash
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the hash instance.
+ */
+function hashSet(key, value) {
+ var data = this.__data__;
+ this.size += this.has(key) ? 0 : 1;
+ data[key] = (_nativeCreate && value === undefined) ? HASH_UNDEFINED$1 : value;
+ return this;
+}
+
+var _hashSet = hashSet;
+
+/**
+ * Creates a hash object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function Hash(entries) {
+ var index = -1,
+ length = entries == null ? 0 : entries.length;
+
+ this.clear();
+ while (++index < length) {
+ var entry = entries[index];
+ this.set(entry[0], entry[1]);
+ }
+}
+
+// Add methods to `Hash`.
+Hash.prototype.clear = _hashClear;
+Hash.prototype['delete'] = _hashDelete;
+Hash.prototype.get = _hashGet;
+Hash.prototype.has = _hashHas;
+Hash.prototype.set = _hashSet;
+
+var _Hash = Hash;
+
+/**
+ * Removes all key-value entries from the list cache.
+ *
+ * @private
+ * @name clear
+ * @memberOf ListCache
+ */
+function listCacheClear() {
+ this.__data__ = [];
+ this.size = 0;
+}
+
+var _listCacheClear = listCacheClear;
+
+/**
+ * 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);
+}
+
+var eq_1 = eq;
+
+/**
+ * Gets the index at which the `key` is found in `array` of key-value pairs.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} key The key to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+function assocIndexOf(array, key) {
+ var length = array.length;
+ while (length--) {
+ if (eq_1(array[length][0], key)) {
+ return length;
+ }
+ }
+ return -1;
+}
+
+var _assocIndexOf = assocIndexOf;
+
+/** Used for built-in method references. */
+var arrayProto = Array.prototype;
+
+/** Built-in value references. */
+var splice = arrayProto.splice;
+
+/**
+ * Removes `key` and its value from the list cache.
+ *
+ * @private
+ * @name delete
+ * @memberOf ListCache
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+function listCacheDelete(key) {
+ var data = this.__data__,
+ index = _assocIndexOf(data, key);
+
+ if (index < 0) {
+ return false;
+ }
+ var lastIndex = data.length - 1;
+ if (index == lastIndex) {
+ data.pop();
+ } else {
+ splice.call(data, index, 1);
+ }
+ --this.size;
+ return true;
+}
+
+var _listCacheDelete = listCacheDelete;
+
+/**
+ * Gets the list cache value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf ListCache
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+function listCacheGet(key) {
+ var data = this.__data__,
+ index = _assocIndexOf(data, key);
+
+ return index < 0 ? undefined : data[index][1];
+}
+
+var _listCacheGet = listCacheGet;
+
+/**
+ * Checks if a list cache value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf ListCache
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+function listCacheHas(key) {
+ return _assocIndexOf(this.__data__, key) > -1;
+}
+
+var _listCacheHas = listCacheHas;
+
+/**
+ * Sets the list cache `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf ListCache
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the list cache instance.
+ */
+function listCacheSet(key, value) {
+ var data = this.__data__,
+ index = _assocIndexOf(data, key);
+
+ if (index < 0) {
+ ++this.size;
+ data.push([key, value]);
+ } else {
+ data[index][1] = value;
+ }
+ return this;
+}
+
+var _listCacheSet = listCacheSet;
+
+/**
+ * Creates an list cache object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function ListCache(entries) {
+ var index = -1,
+ length = entries == null ? 0 : entries.length;
+
+ this.clear();
+ while (++index < length) {
+ var entry = entries[index];
+ this.set(entry[0], entry[1]);
+ }
+}
+
+// Add methods to `ListCache`.
+ListCache.prototype.clear = _listCacheClear;
+ListCache.prototype['delete'] = _listCacheDelete;
+ListCache.prototype.get = _listCacheGet;
+ListCache.prototype.has = _listCacheHas;
+ListCache.prototype.set = _listCacheSet;
+
+var _ListCache = ListCache;
+
+/* Built-in method references that are verified to be native. */
+var Map = _getNative(_root, 'Map');
+
+var _Map = Map;
+
+/**
+ * Removes all key-value entries from the map.
+ *
+ * @private
+ * @name clear
+ * @memberOf MapCache
+ */
+function mapCacheClear() {
+ this.size = 0;
+ this.__data__ = {
+ 'hash': new _Hash,
+ 'map': new (_Map || _ListCache),
+ 'string': new _Hash
+ };
+}
+
+var _mapCacheClear = mapCacheClear;
+
+/**
+ * Checks if `value` is suitable for use as unique object key.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
+ */
+function isKeyable(value) {
+ var type = typeof value;
+ return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
+ ? (value !== '__proto__')
+ : (value === null);
+}
+
+var _isKeyable = isKeyable;
+
+/**
+ * Gets the data for `map`.
+ *
+ * @private
+ * @param {Object} map The map to query.
+ * @param {string} key The reference key.
+ * @returns {*} Returns the map data.
+ */
+function getMapData(map, key) {
+ var data = map.__data__;
+ return _isKeyable(key)
+ ? data[typeof key == 'string' ? 'string' : 'hash']
+ : data.map;
+}
+
+var _getMapData = getMapData;
+
+/**
+ * Removes `key` and its value from the map.
+ *
+ * @private
+ * @name delete
+ * @memberOf MapCache
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+function mapCacheDelete(key) {
+ var result = _getMapData(this, key)['delete'](key);
+ this.size -= result ? 1 : 0;
+ return result;
+}
+
+var _mapCacheDelete = mapCacheDelete;
+
+/**
+ * Gets the map value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf MapCache
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+function mapCacheGet(key) {
+ return _getMapData(this, key).get(key);
+}
+
+var _mapCacheGet = mapCacheGet;
+
+/**
+ * Checks if a map value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf MapCache
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+function mapCacheHas(key) {
+ return _getMapData(this, key).has(key);
+}
+
+var _mapCacheHas = mapCacheHas;
+
+/**
+ * Sets the map `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf MapCache
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the map cache instance.
+ */
+function mapCacheSet(key, value) {
+ var data = _getMapData(this, key),
+ size = data.size;
+
+ data.set(key, value);
+ this.size += data.size == size ? 0 : 1;
+ return this;
+}
+
+var _mapCacheSet = mapCacheSet;
+
+/**
+ * Creates a map cache object to store key-value pairs.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function MapCache(entries) {
+ var index = -1,
+ length = entries == null ? 0 : entries.length;
+
+ this.clear();
+ while (++index < length) {
+ var entry = entries[index];
+ this.set(entry[0], entry[1]);
+ }
+}
+
+// Add methods to `MapCache`.
+MapCache.prototype.clear = _mapCacheClear;
+MapCache.prototype['delete'] = _mapCacheDelete;
+MapCache.prototype.get = _mapCacheGet;
+MapCache.prototype.has = _mapCacheHas;
+MapCache.prototype.set = _mapCacheSet;
+
+var _MapCache = MapCache;
+
+/** Used to stand-in for `undefined` hash values. */
+var HASH_UNDEFINED$2 = '__lodash_hash_undefined__';
+
+/**
+ * Adds `value` to the array cache.
+ *
+ * @private
+ * @name add
+ * @memberOf SetCache
+ * @alias push
+ * @param {*} value The value to cache.
+ * @returns {Object} Returns the cache instance.
+ */
+function setCacheAdd(value) {
+ this.__data__.set(value, HASH_UNDEFINED$2);
+ return this;
+}
+
+var _setCacheAdd = setCacheAdd;
+
+/**
+ * Checks if `value` is in the array cache.
+ *
+ * @private
+ * @name has
+ * @memberOf SetCache
+ * @param {*} value The value to search for.
+ * @returns {number} Returns `true` if `value` is found, else `false`.
+ */
+function setCacheHas(value) {
+ return this.__data__.has(value);
+}
+
+var _setCacheHas = setCacheHas;
+
+/**
+ *
+ * Creates an array cache object to store unique values.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [values] The values to cache.
+ */
+function SetCache(values) {
+ var index = -1,
+ length = values == null ? 0 : values.length;
+
+ this.__data__ = new _MapCache;
+ while (++index < length) {
+ this.add(values[index]);
+ }
+}
+
+// Add methods to `SetCache`.
+SetCache.prototype.add = SetCache.prototype.push = _setCacheAdd;
+SetCache.prototype.has = _setCacheHas;
+
+var _SetCache = SetCache;
+
+/**
+ * The base implementation of `_.findIndex` and `_.findLastIndex` without
+ * support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Function} predicate The function invoked per iteration.
+ * @param {number} fromIndex The index to search from.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+function baseFindIndex(array, predicate, fromIndex, fromRight) {
+ var length = array.length,
+ index = fromIndex + (fromRight ? 1 : -1);
+
+ while ((fromRight ? index-- : ++index < length)) {
+ if (predicate(array[index], index, array)) {
+ return index;
+ }
+ }
+ return -1;
+}
+
+var _baseFindIndex = baseFindIndex;
+
+/**
+ * The base implementation of `_.isNaN` without support for number objects.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
+ */
+function baseIsNaN(value) {
+ return value !== value;
+}
+
+var _baseIsNaN = baseIsNaN;
+
+/**
+ * A specialized version of `_.indexOf` which performs strict equality
+ * comparisons of values, i.e. `===`.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} fromIndex The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+function strictIndexOf(array, value, fromIndex) {
+ var index = fromIndex - 1,
+ length = array.length;
+
+ while (++index < length) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+}
+
+var _strictIndexOf = strictIndexOf;
+
+/**
+ * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} fromIndex The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+function baseIndexOf(array, value, fromIndex) {
+ return value === value
+ ? _strictIndexOf(array, value, fromIndex)
+ : _baseFindIndex(array, _baseIsNaN, fromIndex);
+}
+
+var _baseIndexOf = baseIndexOf;
+
+/**
+ * A specialized version of `_.includes` for arrays without support for
+ * specifying an index to search from.
+ *
+ * @private
+ * @param {Array} [array] The array to inspect.
+ * @param {*} target The value to search for.
+ * @returns {boolean} Returns `true` if `target` is found, else `false`.
+ */
+function arrayIncludes(array, value) {
+ var length = array == null ? 0 : array.length;
+ return !!length && _baseIndexOf(array, value, 0) > -1;
+}
+
+var _arrayIncludes = arrayIncludes;
+
+/**
+ * This function is like `arrayIncludes` except that it accepts a comparator.
+ *
+ * @private
+ * @param {Array} [array] The array to inspect.
+ * @param {*} target The value to search for.
+ * @param {Function} comparator The comparator invoked per element.
+ * @returns {boolean} Returns `true` if `target` is found, else `false`.
+ */
+function arrayIncludesWith(array, value, comparator) {
+ var index = -1,
+ length = array == null ? 0 : array.length;
+
+ while (++index < length) {
+ if (comparator(value, array[index])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+var _arrayIncludesWith = arrayIncludesWith;
+
+/**
+ * Checks if a `cache` value for `key` exists.
+ *
+ * @private
+ * @param {Object} cache The cache to query.
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+function cacheHas(cache, key) {
+ return cache.has(key);
+}
+
+var _cacheHas = cacheHas;
+
+/* Built-in method references that are verified to be native. */
+var Set = _getNative(_root, 'Set');
+
+var _Set = Set;
+
+/**
+ * This method returns `undefined`.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.3.0
+ * @category Util
+ * @example
+ *
+ * _.times(2, _.noop);
+ * // => [undefined, undefined]
+ */
+function noop() {
+ // No operation performed.
+}
+
+var noop_1 = noop;
+
+/**
+ * Converts `set` to an array of its values.
+ *
+ * @private
+ * @param {Object} set The set to convert.
+ * @returns {Array} Returns the values.
+ */
+function setToArray(set) {
+ var index = -1,
+ result = Array(set.size);
+
+ set.forEach(function(value) {
+ result[++index] = value;
+ });
+ return result;
+}
+
+var _setToArray = setToArray;
+
+/** Used as references for various `Number` constants. */
+var INFINITY = 1 / 0;
+
+/**
+ * Creates a set object of `values`.
+ *
+ * @private
+ * @param {Array} values The values to add to the set.
+ * @returns {Object} Returns the new set.
+ */
+var createSet = !(_Set && (1 / _setToArray(new _Set([,-0]))[1]) == INFINITY) ? noop_1 : function(values) {
+ return new _Set(values);
+};
+
+var _createSet = createSet;
+
+/** Used as the size to enable large array optimizations. */
+var LARGE_ARRAY_SIZE = 200;
+
+/**
+ * The base implementation of `_.uniqBy` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
+ */
+function baseUniq(array, iteratee, comparator) {
+ var index = -1,
+ includes = _arrayIncludes,
+ length = array.length,
+ isCommon = true,
+ result = [],
+ seen = result;
+
+ if (comparator) {
+ isCommon = false;
+ includes = _arrayIncludesWith;
+ }
+ else if (length >= LARGE_ARRAY_SIZE) {
+ var set = iteratee ? null : _createSet(array);
+ if (set) {
+ return _setToArray(set);
+ }
+ isCommon = false;
+ includes = _cacheHas;
+ seen = new _SetCache;
+ }
+ else {
+ seen = iteratee ? [] : result;
+ }
+ outer:
+ while (++index < length) {
+ var value = array[index],
+ computed = iteratee ? iteratee(value) : value;
+
+ value = (comparator || value !== 0) ? value : 0;
+ if (isCommon && computed === computed) {
+ var seenIndex = seen.length;
+ while (seenIndex--) {
+ if (seen[seenIndex] === computed) {
+ continue outer;
+ }
+ }
+ if (iteratee) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ else if (!includes(seen, computed, comparator)) {
+ if (seen !== result) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ }
+ return result;
+}
+
+var _baseUniq = baseUniq;
+
+/**
+ * Creates a duplicate-free version of an array, using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons, in which only the first occurrence of each element
+ * is kept. The order of result values is determined by the order they occur
+ * in the array.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.uniq([2, 1, 2]);
+ * // => [2, 1]
+ */
+function uniq(array) {
+ return (array && array.length) ? _baseUniq(array) : [];
+}
+
+var uniq_1 = uniq;
+
+function camelCaseToDash(str) {
+ return str.replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`);
+}
+
+function makeDataAttributeString(props) {
+ const keys = Object.keys(props);
+
+ return keys
+ .map((key) => {
+ const _key = camelCaseToDash(key);
+ const val = props[key];
+
+ if (val === undefined) return '';
+ return `data-${_key}="${val}" `;
+ })
+ .join('')
+ .trim();
+}
+
+function copyTextToClipboard(text) {
+ // https://stackoverflow.com/a/30810322/5353542
+ var textArea = document.createElement('textarea');
+
+ //
+ // *** This styling is an extra step which is likely not required. ***
+ //
+ // Why is it here? To ensure:
+ // 1. the element is able to have focus and selection.
+ // 2. if element was to flash render it has minimal visual impact.
+ // 3. less flakyness with selection and copying which **might** occur if
+ // the textarea element is not visible.
+ //
+ // The likelihood is the element won't even render, not even a flash,
+ // so some of these are just precautions. However in IE the element
+ // is visible whilst the popup box asking the user for permission for
+ // the web page to copy to the clipboard.
+ //
+
+ // Place in top-left corner of screen regardless of scroll position.
+ textArea.style.position = 'fixed';
+ textArea.style.top = 0;
+ textArea.style.left = 0;
+
+ // Ensure it has a small width and height. Setting to 1px / 1em
+ // doesn't work as this gives a negative w/h on some browsers.
+ textArea.style.width = '2em';
+ textArea.style.height = '2em';
+
+ // We don't need padding, reducing the size if it does flash render.
+ textArea.style.padding = 0;
+
+ // Clean up any borders.
+ textArea.style.border = 'none';
+ textArea.style.outline = 'none';
+ textArea.style.boxShadow = 'none';
+
+ // Avoid flash of white box if rendered for any reason.
+ textArea.style.background = 'transparent';
+
+ textArea.value = text;
+
+ document.body.appendChild(textArea);
+
+ textArea.select();
+
+ try {
+ document.execCommand('copy');
+ } catch (err) {
+ console.log('Oops, unable to copy');
+ }
+
+ document.body.removeChild(textArea);
+}
+
+function isNumeric(val) {
+ return !isNaN(val);
+}
+
+let throttle$1 = throttle_1;
+
+let debounce$1 = debounce_1;
+
+function nextTick(fn, context = null) {
+ return (...args) => {
+ return new Promise(resolve => {
+ const execute = () => {
+ const out = fn.apply(context, args);
+ resolve(out);
+ };
+ setTimeout(execute);
+ });
+ };
+}
+function linkProperties(target, source, properties) {
+ const props = properties.reduce((acc, prop) => {
+ acc[prop] = {
+ get() {
+ return source[prop];
+ }
+ };
+ return acc;
+ }, {});
+ Object.defineProperties(target, props);
+}
+function isSet(val) {
+ return val !== undefined || val !== null;
+}
+
+function notSet(val) {
+ return !isSet(val);
+}
+
+function isNumber(val) {
+ return !isNaN(val);
+}
+
+function ensureArray(val) {
+ if (!Array.isArray(val)) {
+ return [val];
+ }
+ return val;
+}
+
+function uniq$1(arr) {
+ return uniq_1(arr);
+}
+
+function numberSortAsc(a, b) {
+ return a - b;
+}
+function stripHTML(html) {
+ return html.replace(/<[^>]*>/g, '');
+}
+function format(str, args) {
+ if (!str) return str;
+
+ Object.keys(args).forEach(arg => {
+ let regex = new RegExp(`{(${arg})}`, 'g');
+ str = str.replace(regex, args[arg]);
+ });
+
+ return str;
+}
+
+class DataManager {
+ constructor(options) {
+ this.options = options;
+ this.sortRows = nextTick(this.sortRows, this);
+ this.switchColumn = nextTick(this.switchColumn, this);
+ this.removeColumn = nextTick(this.removeColumn, this);
+ this.options.filterRows = nextTick(this.options.filterRows, this);
+ }
+
+ init(data, columns) {
+ if (!data) {
+ data = this.options.data;
+ }
+ if (columns) {
+ this.options.columns = columns;
+ }
+
+ this.data = data;
+
+ this.rowCount = 0;
+ this.columns = [];
+ this.rows = [];
+
+ this.prepareColumns();
+ this.prepareRows();
+ this.prepareTreeRows();
+ this.prepareRowView();
+ this.prepareNumericColumns();
+ }
+
+ // computed property
+ get currentSort() {
+ const col = this.columns.find(col => col.sortOrder !== 'none');
+ return col || {
+ colIndex: -1,
+ sortOrder: 'none'
+ };
+ }
+
+ prepareColumns() {
+ this.columns = [];
+ this.validateColumns();
+ this.prepareDefaultColumns();
+ this.prepareHeader();
+ }
+
+ prepareDefaultColumns() {
+ if (this.options.checkboxColumn && !this.hasColumnById('_checkbox')) {
+ const cell = {
+ id: '_checkbox',
+ content: this.getCheckboxHTML(),
+ editable: false,
+ resizable: false,
+ sortable: false,
+ focusable: false,
+ dropdown: false,
+ width: 32
+ };
+ this.columns.push(cell);
+ }
+
+ if (this.options.serialNoColumn && !this.hasColumnById('_rowIndex')) {
+ let cell = {
+ id: '_rowIndex',
+ content: '',
+ align: 'center',
+ editable: false,
+ resizable: false,
+ focusable: false,
+ dropdown: false
+ };
+
+ this.columns.push(cell);
+ }
+ }
+
+ prepareHeader() {
+ let columns = this.columns.concat(this.options.columns);
+ const baseCell = {
+ isHeader: 1,
+ editable: true,
+ sortable: true,
+ resizable: true,
+ focusable: true,
+ dropdown: true,
+ width: null,
+ format: (value) => {
+ if (value === null || value === undefined) {
+ return '';
+ }
+ return value + '';
+ }
+ };
+
+ this.columns = columns
+ .map((cell, i) => this.prepareCell(cell, i))
+ .map(col => Object.assign({}, baseCell, col))
+ .map(col => {
+ col.content = col.content || col.name || '';
+ col.id = col.id || col.content;
+ return col;
+ });
+ }
+
+ prepareCell(content, i) {
+ const cell = {
+ content: '',
+ sortOrder: 'none',
+ colIndex: i,
+ column: this.columns[i]
+ };
+
+ if (content !== null && typeof content === 'object') {
+ // passed as column/header
+ Object.assign(cell, content);
+ } else {
+ cell.content = content;
+ }
+
+ return cell;
+ }
+
+ prepareNumericColumns() {
+ const row0 = this.getRow(0);
+ if (!row0) return;
+ this.columns = this.columns.map((column, i) => {
+
+ const cellValue = row0[i].content;
+ if (!column.align && isNumeric(cellValue)) {
+ column.align = 'right';
+ }
+
+ return column;
+ });
+ }
+
+ prepareRows() {
+ this.validateData(this.data);
+
+ this.rows = this.data.map((d, i) => {
+ const index = this._getNextRowCount();
+
+ let row = [];
+ let meta = {
+ rowIndex: index
+ };
+
+ if (Array.isArray(d)) {
+ // row is an array
+ if (this.options.checkboxColumn) {
+ row.push(this.getCheckboxHTML());
+ }
+ if (this.options.serialNoColumn) {
+ row.push((index + 1) + '');
+ }
+ row = row.concat(d);
+
+ while (row.length < this.columns.length) {
+ row.push('');
+ }
+
+ } else {
+ // row is an object
+ for (let col of this.columns) {
+ if (col.id === '_checkbox') {
+ row.push(this.getCheckboxHTML());
+ } else if (col.id === '_rowIndex') {
+ row.push((index + 1) + '');
+ } else {
+ row.push(d[col.id]);
+ }
+ }
+
+ meta.indent = d.indent || 0;
+ }
+
+ return this.prepareRow(row, meta);
+ });
+ }
+
+ prepareTreeRows() {
+ this.rows.forEach((row, i) => {
+ if (isNumber(row.meta.indent)) {
+ // if (i === 36) debugger;
+ const nextRow = this.getRow(i + 1);
+ row.meta.isLeaf = !nextRow ||
+ notSet(nextRow.meta.indent) ||
+ nextRow.meta.indent <= row.meta.indent;
+ row.meta.isTreeNodeClose = false;
+ }
+ });
+ }
+
+ prepareRowView() {
+ // This is order in which rows will be rendered in the table.
+ // When sorting happens, only this.rowViewOrder will change
+ // and not the original this.rows
+ this.rowViewOrder = this.rows.map(row => row.meta.rowIndex);
+ }
+
+ prepareRow(row, meta) {
+ const baseRowCell = {
+ rowIndex: meta.rowIndex,
+ indent: meta.indent
+ };
+
+ row = row
+ .map((cell, i) => this.prepareCell(cell, i))
+ .map(cell => Object.assign({}, baseRowCell, cell));
+
+ // monkey patched in array object
+ row.meta = meta;
+ return row;
+ }
+
+ validateColumns() {
+ const columns = this.options.columns;
+ if (!Array.isArray(columns)) {
+ throw new DataError('`columns` must be an array');
+ }
+
+ columns.forEach((column, i) => {
+ if (typeof column !== 'string' && typeof column !== 'object') {
+ throw new DataError(`column "${i}" must be a string or an object`);
+ }
+ });
+ }
+
+ validateData(data) {
+ if (Array.isArray(data) &&
+ (data.length === 0 || Array.isArray(data[0]) || typeof data[0] === 'object')) {
+ return true;
+ }
+ throw new DataError('`data` must be an array of arrays or objects');
+ }
+
+ appendRows(rows) {
+ this.validateData(rows);
+
+ this.rows.push(...this.prepareRows(rows));
+ }
+
+ sortRows(colIndex, sortOrder = 'none') {
+ colIndex = +colIndex;
+
+ // reset sortOrder and update for colIndex
+ this.getColumns()
+ .map(col => {
+ if (col.colIndex === colIndex) {
+ col.sortOrder = sortOrder;
+ } else {
+ col.sortOrder = 'none';
+ }
+ });
+
+ this._sortRows(colIndex, sortOrder);
+ }
+
+ _sortRows(colIndex, sortOrder) {
+
+ if (this.currentSort.colIndex === colIndex) {
+ // reverse the array if only sortOrder changed
+ if (
+ (this.currentSort.sortOrder === 'asc' && sortOrder === 'desc') ||
+ (this.currentSort.sortOrder === 'desc' && sortOrder === 'asc')
+ ) {
+ this.reverseArray(this.rowViewOrder);
+ this.currentSort.sortOrder = sortOrder;
+ return;
+ }
+ }
+
+ this.rowViewOrder.sort((a, b) => {
+ const aIndex = a;
+ const bIndex = b;
+
+ let aContent = this.getCell(colIndex, a).content;
+ let bContent = this.getCell(colIndex, b).content;
+ aContent = aContent == null ? '' : aContent;
+ bContent = bContent == null ? '' : bContent;
+
+ if (sortOrder === 'none') {
+ return aIndex - bIndex;
+ } else if (sortOrder === 'asc') {
+ if (aContent < bContent) return -1;
+ if (aContent > bContent) return 1;
+ if (aContent === bContent) return 0;
+ } else if (sortOrder === 'desc') {
+ if (aContent < bContent) return 1;
+ if (aContent > bContent) return -1;
+ if (aContent === bContent) return 0;
+ }
+ return 0;
+ });
+
+ if (this.hasColumnById('_rowIndex')) {
+ // update row index
+ const srNoColIndex = this.getColumnIndexById('_rowIndex');
+ this.rows.forEach((row, index) => {
+ const viewIndex = this.rowViewOrder.indexOf(index);
+ const cell = row[srNoColIndex];
+ cell.content = (viewIndex + 1) + '';
+ });
+ }
+ }
+
+ reverseArray(array) {
+ let left = null;
+ let right = null;
+ let length = array.length;
+
+ for (left = 0, right = length - 1; left < right; left += 1, right -= 1) {
+ const temporary = array[left];
+
+ array[left] = array[right];
+ array[right] = temporary;
+ }
+ }
+
+ switchColumn(index1, index2) {
+ // update columns
+ const temp = this.columns[index1];
+ this.columns[index1] = this.columns[index2];
+ this.columns[index2] = temp;
+
+ this.columns[index1].colIndex = index1;
+ this.columns[index2].colIndex = index2;
+
+ // update rows
+ this.rows.forEach(row => {
+ const newCell1 = Object.assign({}, row[index1], {
+ colIndex: index2
+ });
+ const newCell2 = Object.assign({}, row[index2], {
+ colIndex: index1
+ });
+
+ row[index2] = newCell1;
+ row[index1] = newCell2;
+ });
+ }
+
+ removeColumn(index) {
+ index = +index;
+ const filter = cell => cell.colIndex !== index;
+ const map = (cell, i) => Object.assign({}, cell, {
+ colIndex: i
+ });
+ // update columns
+ this.columns = this.columns
+ .filter(filter)
+ .map(map);
+
+ // update rows
+ this.rows.forEach(row => {
+ // remove cell
+ row.splice(index, 1);
+ // update colIndex
+ row.forEach((cell, i) => {
+ cell.colIndex = i;
+ });
+ });
+ }
+
+ updateRow(row, rowIndex) {
+ if (row.length < this.columns.length) {
+ if (this.hasColumnById('_rowIndex')) {
+ const val = (rowIndex + 1) + '';
+
+ row = [val].concat(row);
+ }
+
+ if (this.hasColumnById('_checkbox')) {
+ const val = '';
+
+ row = [val].concat(row);
+ }
+ }
+
+ const _row = this.prepareRow(row, {rowIndex});
+ const index = this.rows.findIndex(row => row[0].rowIndex === rowIndex);
+ this.rows[index] = _row;
+
+ return _row;
+ }
+
+ updateCell(colIndex, rowIndex, options) {
+ let cell;
+ if (typeof colIndex === 'object') {
+ // cell object was passed,
+ // must have colIndex, rowIndex
+ cell = colIndex;
+ colIndex = cell.colIndex;
+ rowIndex = cell.rowIndex;
+ // the object passed must be merged with original cell
+ options = cell;
+ }
+ cell = this.getCell(colIndex, rowIndex);
+
+ // mutate object directly
+ for (let key in options) {
+ const newVal = options[key];
+ if (newVal !== undefined) {
+ cell[key] = newVal;
+ }
+ }
+
+ return cell;
+ }
+
+ updateColumn(colIndex, keyValPairs) {
+ const column = this.getColumn(colIndex);
+ for (let key in keyValPairs) {
+ const newVal = keyValPairs[key];
+ if (newVal !== undefined) {
+ column[key] = newVal;
+ }
+ }
+ return column;
+ }
+
+ filterRows(filters) {
+ return this.options.filterRows(this.rows, filters)
+ .then(result => {
+ if (!result) {
+ result = this.getAllRowIndices();
+ }
+
+ if (!result.then) {
+ result = Promise.resolve(result);
+ }
+
+ return result.then(rowsToShow => {
+ this._filteredRows = rowsToShow;
+
+ const rowsToHide = this.getAllRowIndices()
+ .filter(index => !rowsToShow.includes(index));
+
+ return {
+ rowsToHide,
+ rowsToShow
+ };
+ });
+ });
+ }
+
+ getFilteredRowIndices() {
+ return this._filteredRows || this.getAllRowIndices();
+ }
+
+ getAllRowIndices() {
+ return this.rows.map(row => row.meta.rowIndex);
+ }
+
+ getRowCount() {
+ return this.rowCount;
+ }
+
+ _getNextRowCount() {
+ const val = this.rowCount;
+
+ this.rowCount++;
+ return val;
+ }
+
+ getRows(start, end) {
+ return this.rows.slice(start, end);
+ }
+
+ getRowsForView(start, end) {
+ const rows = this.rowViewOrder.map(i => this.rows[i]);
+ return rows.slice(start, end);
+ }
+
+ getColumns(skipStandardColumns) {
+ let columns = this.columns;
+
+ if (skipStandardColumns) {
+ columns = columns.slice(this.getStandardColumnCount());
+ }
+
+ return columns;
+ }
+
+ getStandardColumnCount() {
+ if (this.options.checkboxColumn && this.options.serialNoColumn) {
+ return 2;
+ }
+
+ if (this.options.checkboxColumn || this.options.serialNoColumn) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ getColumnCount(skipStandardColumns) {
+ let val = this.columns.length;
+
+ if (skipStandardColumns) {
+ val = val - this.getStandardColumnCount();
+ }
+
+ return val;
+ }
+
+ getColumn(colIndex) {
+ colIndex = +colIndex;
+
+ if (colIndex < 0) {
+ // negative indexes
+ colIndex = this.columns.length + colIndex;
+ }
+
+ return this.columns.find(col => col.colIndex === colIndex);
+ }
+
+ getColumnById(id) {
+ return this.columns.find(col => col.id === id);
+ }
+
+ getRow(rowIndex) {
+ rowIndex = +rowIndex;
+ return this.rows[rowIndex];
+ }
+
+ getCell(colIndex, rowIndex) {
+ rowIndex = +rowIndex;
+ colIndex = +colIndex;
+ return this.getRow(rowIndex)[colIndex];
+ }
+
+ getChildren(parentRowIndex) {
+ parentRowIndex = +parentRowIndex;
+ const parentIndent = this.getRow(parentRowIndex).meta.indent;
+ const out = [];
+
+ for (let i = parentRowIndex + 1; i < this.rowCount; i++) {
+ const row = this.getRow(i);
+ if (isNaN(row.meta.indent)) continue;
+
+ if (row.meta.indent > parentIndent) {
+ out.push(i);
+ }
+
+ if (row.meta.indent === parentIndent) {
+ break;
+ }
+ }
+
+ return out;
+ }
+
+ getImmediateChildren(parentRowIndex) {
+ parentRowIndex = +parentRowIndex;
+ const parentIndent = this.getRow(parentRowIndex).meta.indent;
+ const out = [];
+ const childIndent = parentIndent + 1;
+
+ for (let i = parentRowIndex + 1; i < this.rowCount; i++) {
+ const row = this.getRow(i);
+ if (isNaN(row.meta.indent) || row.meta.indent > childIndent) continue;
+
+ if (row.meta.indent === childIndent) {
+ out.push(i);
+ }
+
+ if (row.meta.indent === parentIndent) {
+ break;
+ }
+ }
+
+ return out;
+ }
+
+ get() {
+ return {
+ columns: this.columns,
+ rows: this.rows
+ };
+ }
+
+ /**
+ * Returns the original data which was passed
+ * based on rowIndex
+ * @param {Number} rowIndex
+ * @returns Array|Object
+ * @memberof DataManager
+ */
+ getData(rowIndex) {
+ return this.data[rowIndex];
+ }
+
+ hasColumn(name) {
+ return Boolean(this.columns.find(col => col.content === name));
+ }
+
+ hasColumnById(id) {
+ return Boolean(this.columns.find(col => col.id === id));
+ }
+
+ getColumnIndex(name) {
+ return this.columns.findIndex(col => col.content === name);
+ }
+
+ getColumnIndexById(id) {
+ return this.columns.findIndex(col => col.id === id);
+ }
+
+ getCheckboxHTML() {
+ return '';
+ }
+}
+
+// Custom Errors
+class DataError extends TypeError {}
+
+/* eslint-disable max-len */
+
+// Icons from https://feathericons.com/
+
+let icons = {
+ chevronDown: '',
+ chevronRight: ''
+};
+
+class CellManager {
+ constructor(instance) {
+ this.instance = instance;
+ linkProperties(this, this.instance, [
+ 'wrapper',
+ 'options',
+ 'style',
+ 'header',
+ 'bodyScrollable',
+ 'columnmanager',
+ 'rowmanager',
+ 'datamanager',
+ 'keyboard'
+ ]);
+
+ this.bindEvents();
+ }
+
+ bindEvents() {
+ this.bindFocusCell();
+ this.bindEditCell();
+ this.bindKeyboardSelection();
+ this.bindCopyCellContents();
+ this.bindMouseEvents();
+ this.bindTreeEvents();
+ }
+
+ bindFocusCell() {
+ this.bindKeyboardNav();
+ }
+
+ bindEditCell() {
+ this.$editingCell = null;
+
+ $.on(this.bodyScrollable, 'dblclick', '.dt-cell', (e, cell) => {
+ this.activateEditing(cell);
+ });
+
+ this.keyboard.on('enter', () => {
+ if (this.$focusedCell && !this.$editingCell) {
+ // enter keypress on focused cell
+ this.activateEditing(this.$focusedCell);
+ } else if (this.$editingCell) {
+ // enter keypress on editing cell
+ this.deactivateEditing();
+ }
+ });
+ }
+
+ bindKeyboardNav() {
+ const focusLastCell = (direction) => {
+ if (!this.$focusedCell || this.$editingCell) {
+ return false;
+ }
+
+ let $cell = this.$focusedCell;
+ const {
+ rowIndex,
+ colIndex
+ } = $.data($cell);
+
+ if (direction === 'left') {
+ $cell = this.getLeftMostCell$(rowIndex);
+ } else if (direction === 'right') {
+ $cell = this.getRightMostCell$(rowIndex);
+ } else if (direction === 'up') {
+ $cell = this.getTopMostCell$(colIndex);
+ } else if (direction === 'down') {
+ $cell = this.getBottomMostCell$(colIndex);
+ }
+
+ this.focusCell($cell);
+ return true;
+ };
+
+ ['left', 'right', 'up', 'down', 'tab', 'shift+tab']
+ .map(direction => this.keyboard.on(direction, () => this.focusCellInDirection(direction)));
+
+ ['left', 'right', 'up', 'down']
+ .map(direction => this.keyboard.on(`ctrl+${direction}`, () => focusLastCell(direction)));
+
+ this.keyboard.on('esc', () => {
+ this.deactivateEditing(false);
+ this.columnmanager.toggleFilter(false);
+ });
+
+ if (this.options.inlineFilters) {
+ this.keyboard.on('ctrl+f', (e) => {
+ const $cell = $.closest('.dt-cell', e.target);
+ const { colIndex } = $.data($cell);
+
+ this.activateFilter(colIndex);
+ return true;
+ });
+
+ $.on(this.header, 'focusin', '.dt-filter', () => {
+ this.unfocusCell(this.$focusedCell);
+ });
+ }
+ }
+
+ bindKeyboardSelection() {
+ const getNextSelectionCursor = (direction) => {
+ let $selectionCursor = this.getSelectionCursor();
+
+ if (direction === 'left') {
+ $selectionCursor = this.getLeftCell$($selectionCursor);
+ } else if (direction === 'right') {
+ $selectionCursor = this.getRightCell$($selectionCursor);
+ } else if (direction === 'up') {
+ $selectionCursor = this.getAboveCell$($selectionCursor);
+ } else if (direction === 'down') {
+ $selectionCursor = this.getBelowCell$($selectionCursor);
+ }
+
+ return $selectionCursor;
+ };
+
+ ['left', 'right', 'up', 'down']
+ .map(direction =>
+ this.keyboard.on(`shift+${direction}`, () => this.selectArea(getNextSelectionCursor(direction))));
+ }
+
+ bindCopyCellContents() {
+ this.keyboard.on('ctrl+c', () => {
+ const noOfCellsCopied = this.copyCellContents(this.$focusedCell, this.$selectionCursor);
+ const message = this.instance.translate('{count} cells copied', {
+ count: noOfCellsCopied
+ });
+
+ if (noOfCellsCopied) {
+ this.instance.showToastMessage(message, 2);
+ }
+ });
+
+ if (this.options.pasteFromClipboard) {
+ this.keyboard.on('ctrl+v', (e) => {
+ // hack
+ // https://stackoverflow.com/a/2177059/5353542
+ this.instance.pasteTarget.focus();
+
+ setTimeout(() => {
+ const data = this.instance.pasteTarget.value;
+ this.instance.pasteTarget.value = '';
+ this.pasteContentInCell(data);
+ }, 10);
+
+ return false;
+ });
+ }
+ }
+
+ bindMouseEvents() {
+ let mouseDown = null;
+
+ $.on(this.bodyScrollable, 'mousedown', '.dt-cell', (e) => {
+ mouseDown = true;
+ this.focusCell($(e.delegatedTarget));
+ });
+
+ $.on(this.bodyScrollable, 'mouseup', () => {
+ mouseDown = false;
+ });
+
+ const selectArea = (e) => {
+ if (!mouseDown) return;
+ this.selectArea($(e.delegatedTarget));
+ };
+
+ $.on(this.bodyScrollable, 'mousemove', '.dt-cell', throttle$1(selectArea, 50));
+ }
+
+ bindTreeEvents() {
+ $.on(this.bodyScrollable, 'click', '.dt-tree-node__toggle', (e, $toggle) => {
+ const $cell = $.closest('.dt-cell', $toggle);
+ const { rowIndex } = $.data($cell);
+
+ if ($cell.classList.contains('dt-cell--tree-close')) {
+ this.rowmanager.openSingleNode(rowIndex);
+ } else {
+ this.rowmanager.closeSingleNode(rowIndex);
+ }
+ });
+ }
+
+ focusCell($cell, {
+ skipClearSelection = 0,
+ skipDOMFocus = 0,
+ skipScrollToCell = 0
+ } = {}) {
+ if (!$cell) return;
+
+ // don't focus if already editing cell
+ if ($cell === this.$editingCell) return;
+
+ const {
+ colIndex,
+ isHeader
+ } = $.data($cell);
+ if (isHeader) {
+ return;
+ }
+
+ const column = this.columnmanager.getColumn(colIndex);
+ if (column.focusable === false) {
+ return;
+ }
+
+ if (!skipScrollToCell) {
+ this.scrollToCell($cell);
+ }
+
+ this.deactivateEditing();
+ if (!skipClearSelection) {
+ this.clearSelection();
+ }
+
+ if (this.$focusedCell) {
+ this.$focusedCell.classList.remove('dt-cell--focus');
+ }
+
+ this.$focusedCell = $cell;
+ $cell.classList.add('dt-cell--focus');
+
+ if (!skipDOMFocus) {
+ // so that keyboard nav works
+ $cell.focus();
+ }
+
+ this.highlightRowColumnHeader($cell);
+ }
+
+ unfocusCell($cell) {
+ if (!$cell) return;
+
+ // remove cell border
+ $cell.classList.remove('dt-cell--focus');
+ this.$focusedCell = null;
+
+ // reset header background
+ if (this.lastHeaders) {
+ this.lastHeaders.forEach(header => header && header.classList.remove('dt-cell--highlight'));
+ }
+ }
+
+ highlightRowColumnHeader($cell) {
+ const {
+ colIndex,
+ rowIndex
+ } = $.data($cell);
+
+ const srNoColIndex = this.datamanager.getColumnIndexById('_rowIndex');
+ const colHeaderSelector = `.dt-cell--header-${colIndex}`;
+ const rowHeaderSelector = `.dt-cell--${srNoColIndex}-${rowIndex}`;
+
+ if (this.lastHeaders) {
+ this.lastHeaders.forEach(header => header && header.classList.remove('dt-cell--highlight'));
+ }
+
+ const colHeader = $(colHeaderSelector, this.wrapper);
+ const rowHeader = $(rowHeaderSelector, this.wrapper);
+
+ this.lastHeaders = [colHeader, rowHeader];
+ this.lastHeaders.forEach(header => header && header.classList.add('dt-cell--highlight'));
+ }
+
+ selectAreaOnClusterChanged() {
+ if (!(this.$focusedCell && this.$selectionCursor)) return;
+ const {
+ colIndex,
+ rowIndex
+ } = $.data(this.$selectionCursor);
+ const $cell = this.getCell$(colIndex, rowIndex);
+
+ if (!$cell || $cell === this.$selectionCursor) return;
+
+ // selectArea needs $focusedCell
+ const fCell = $.data(this.$focusedCell);
+ this.$focusedCell = this.getCell$(fCell.colIndex, fCell.rowIndex);
+
+ this.selectArea($cell);
+ }
+
+ focusCellOnClusterChanged() {
+ if (!this.$focusedCell) return;
+
+ const {
+ colIndex,
+ rowIndex
+ } = $.data(this.$focusedCell);
+ const $cell = this.getCell$(colIndex, rowIndex);
+
+ if (!$cell) return;
+ // this function is called after hyperlist renders the rows after scroll,
+ // focusCell calls clearSelection which resets the area selection
+ // so a flag to skip it
+ // we also skip DOM focus and scroll to cell
+ // because it fights with the user scroll
+ this.focusCell($cell, {
+ skipClearSelection: 1,
+ skipDOMFocus: 1,
+ skipScrollToCell: 1
+ });
+ }
+
+ selectArea($selectionCursor) {
+ if (!this.$focusedCell) return;
+
+ if (this._selectArea(this.$focusedCell, $selectionCursor)) {
+ // valid selection
+ this.$selectionCursor = $selectionCursor;
+ }
+ }
+
+ _selectArea($cell1, $cell2) {
+ if ($cell1 === $cell2) return false;
+
+ const cells = this.getCellsInRange($cell1, $cell2);
+ if (!cells) return false;
+
+ this.clearSelection();
+ this._selectedCells = cells.map(index => this.getCell$(...index));
+ requestAnimationFrame(() => {
+ this._selectedCells.map($cell => $cell.classList.add('dt-cell--highlight'));
+ });
+ return true;
+ }
+
+ getCellsInRange($cell1, $cell2) {
+ let colIndex1, rowIndex1, colIndex2, rowIndex2;
+
+ if (typeof $cell1 === 'number') {
+ [colIndex1, rowIndex1, colIndex2, rowIndex2] = arguments;
+ } else
+ if (typeof $cell1 === 'object') {
+ if (!($cell1 && $cell2)) {
+ return false;
+ }
+
+ const cell1 = $.data($cell1);
+ const cell2 = $.data($cell2);
+
+ colIndex1 = +cell1.colIndex;
+ rowIndex1 = +cell1.rowIndex;
+ colIndex2 = +cell2.colIndex;
+ rowIndex2 = +cell2.rowIndex;
+ }
+
+ if (rowIndex1 > rowIndex2) {
+ [rowIndex1, rowIndex2] = [rowIndex2, rowIndex1];
+ }
+
+ if (colIndex1 > colIndex2) {
+ [colIndex1, colIndex2] = [colIndex2, colIndex1];
+ }
+
+ if (this.isStandardCell(colIndex1) || this.isStandardCell(colIndex2)) {
+ return false;
+ }
+
+ const cells = [];
+ let colIndex = colIndex1;
+ let rowIndex = rowIndex1;
+ const rowIndices = [];
+
+ while (rowIndex <= rowIndex2) {
+ rowIndices.push(rowIndex);
+ rowIndex += 1;
+ }
+
+ rowIndices.map((rowIndex) => {
+ while (colIndex <= colIndex2) {
+ cells.push([colIndex, rowIndex]);
+ colIndex++;
+ }
+ colIndex = colIndex1;
+ });
+
+ return cells;
+ }
+
+ clearSelection() {
+ (this._selectedCells || [])
+ .forEach($cell => $cell.classList.remove('dt-cell--highlight'));
+
+ this._selectedCells = [];
+ this.$selectionCursor = null;
+ }
+
+ getSelectionCursor() {
+ return this.$selectionCursor || this.$focusedCell;
+ }
+
+ activateEditing($cell) {
+ this.focusCell($cell);
+ const {
+ rowIndex,
+ colIndex
+ } = $.data($cell);
+
+ const col = this.columnmanager.getColumn(colIndex);
+ if (col && (col.editable === false || col.focusable === false)) {
+ return;
+ }
+
+ const cell = this.getCell(colIndex, rowIndex);
+ if (cell && cell.editable === false) {
+ return;
+ }
+
+ if (this.$editingCell) {
+ const {
+ _rowIndex,
+ _colIndex
+ } = $.data(this.$editingCell);
+
+ if (rowIndex === _rowIndex && colIndex === _colIndex) {
+ // editing the same cell
+ return;
+ }
+ }
+
+ this.$editingCell = $cell;
+ $cell.classList.add('dt-cell--editing');
+
+ const $editCell = $('.dt-cell__edit', $cell);
+ $editCell.innerHTML = '';
+
+ const editor = this.getEditor(colIndex, rowIndex, cell.content, $editCell);
+
+ if (editor) {
+ this.currentCellEditor = editor;
+ // initialize editing input with cell value
+ editor.initValue(cell.content, rowIndex, col);
+ }
+ }
+
+ deactivateEditing(submitValue = true) {
+ if (submitValue) {
+ this.submitEditing();
+ }
+ // keep focus on the cell so that keyboard navigation works
+ if (this.$focusedCell) this.$focusedCell.focus();
+
+ if (!this.$editingCell) return;
+ this.$editingCell.classList.remove('dt-cell--editing');
+ this.$editingCell = null;
+ }
+
+ getEditor(colIndex, rowIndex, value, parent) {
+ const column = this.datamanager.getColumn(colIndex);
+ const row = this.datamanager.getRow(rowIndex);
+ const data = this.datamanager.getData(rowIndex);
+ let editor = this.options.getEditor ?
+ this.options.getEditor(colIndex, rowIndex, value, parent, column, row, data) :
+ this.getDefaultEditor(parent);
+
+ if (editor === false) {
+ // explicitly returned false
+ return false;
+ }
+ if (editor === undefined) {
+ // didn't return editor, fallback to default
+ editor = this.getDefaultEditor(parent);
+ }
+
+ return editor;
+ }
+
+ getDefaultEditor(parent) {
+ const $input = $.create('input', {
+ class: 'dt-input',
+ type: 'text',
+ inside: parent
+ });
+
+ return {
+ initValue(value) {
+ $input.focus();
+ $input.value = value;
+ },
+ getValue() {
+ return $input.value;
+ },
+ setValue(value) {
+ $input.value = value;
+ }
+ };
+ }
+
+ submitEditing() {
+ let promise = Promise.resolve();
+ if (!this.$editingCell) return promise;
+
+ const $cell = this.$editingCell;
+ const {
+ rowIndex,
+ colIndex
+ } = $.data($cell);
+ const col = this.datamanager.getColumn(colIndex);
+
+ if ($cell) {
+ const editor = this.currentCellEditor;
+
+ if (editor) {
+ let valuePromise = editor.getValue();
+
+ // convert to stubbed Promise
+ if (!valuePromise.then) {
+ valuePromise = Promise.resolve(valuePromise);
+ }
+
+ promise = valuePromise.then((value) => {
+ const oldValue = this.getCell(colIndex, rowIndex).content;
+
+ if (oldValue === value) return false;
+
+ const done = editor.setValue(value, rowIndex, col);
+
+ // update cell immediately
+ this.updateCell(colIndex, rowIndex, value, true);
+ $cell.focus();
+
+ if (done && done.then) {
+ // revert to oldValue if promise fails
+ done.catch((e) => {
+ console.log(e);
+ this.updateCell(colIndex, rowIndex, oldValue);
+ });
+ }
+ return done;
+ });
+ }
+ }
+
+ this.currentCellEditor = null;
+ return promise;
+ }
+
+ copyCellContents($cell1, $cell2) {
+ if (!$cell2 && $cell1) {
+ // copy only focusedCell
+ const {
+ colIndex,
+ rowIndex
+ } = $.data($cell1);
+ const cell = this.getCell(colIndex, rowIndex);
+ copyTextToClipboard(cell.content);
+ return 1;
+ }
+ const cells = this.getCellsInRange($cell1, $cell2);
+
+ if (!cells) return 0;
+
+ const rows = cells
+ // get cell objects
+ .map(index => this.getCell(...index))
+ // convert to array of rows
+ .reduce((acc, curr) => {
+ const rowIndex = curr.rowIndex;
+
+ acc[rowIndex] = acc[rowIndex] || [];
+ acc[rowIndex].push(curr.content);
+
+ return acc;
+ }, []);
+
+ const values = rows
+ // join values by tab
+ .map(row => row.join('\t'))
+ // join rows by newline
+ .join('\n');
+
+ copyTextToClipboard(values);
+
+ // return no of cells copied
+ return rows.reduce((total, row) => total + row.length, 0);
+ }
+
+ pasteContentInCell(data) {
+ if (!this.$focusedCell) return;
+
+ const matrix = data
+ .split('\n')
+ .map(row => row.split('\t'))
+ .filter(row => row.length && row.every(it => it));
+
+ let { colIndex, rowIndex } = $.data(this.$focusedCell);
+
+ let focusedCell = {
+ colIndex: +colIndex,
+ rowIndex: +rowIndex
+ };
+
+ matrix.forEach((row, i) => {
+ let rowIndex = i + focusedCell.rowIndex;
+ row.forEach((cell, j) => {
+ let colIndex = j + focusedCell.colIndex;
+ this.updateCell(colIndex, rowIndex, cell, true);
+ });
+ });
+ }
+
+ activateFilter(colIndex) {
+ this.columnmanager.toggleFilter();
+ this.columnmanager.focusFilter(colIndex);
+
+ if (!this.columnmanager.isFilterShown) {
+ // put focus back on cell
+ this.$focusedCell && this.$focusedCell.focus();
+ }
+ }
+
+ updateCell(colIndex, rowIndex, value, refreshHtml = false) {
+ const cell = this.datamanager.updateCell(colIndex, rowIndex, {
+ content: value
+ });
+ this.refreshCell(cell, refreshHtml);
+ }
+
+ refreshCell(cell, refreshHtml = false) {
+ const $cell = $(this.selector(cell.colIndex, cell.rowIndex), this.bodyScrollable);
+ $cell.innerHTML = this.getCellContent(cell, refreshHtml);
+ }
+
+ toggleTreeButton(rowIndex, flag) {
+ const colIndex = this.columnmanager.getFirstColumnIndex();
+ const $cell = this.getCell$(colIndex, rowIndex);
+ if ($cell) {
+ $cell.classList[flag ? 'remove' : 'add']('dt-cell--tree-close');
+ }
+ }
+
+ isStandardCell(colIndex) {
+ // Standard cells are in Sr. No and Checkbox column
+ return colIndex < this.columnmanager.getFirstColumnIndex();
+ }
+
+ focusCellInDirection(direction) {
+ if (!this.$focusedCell || (this.$editingCell && ['left', 'right', 'up', 'down'].includes(direction))) {
+ return false;
+ } else if (this.$editingCell && ['tab', 'shift+tab'].includes(direction)) {
+ this.deactivateEditing();
+ }
+
+ let $cell = this.$focusedCell;
+
+ if (direction === 'left' || direction === 'shift+tab') {
+ $cell = this.getLeftCell$($cell);
+ } else if (direction === 'right' || direction === 'tab') {
+ $cell = this.getRightCell$($cell);
+ } else if (direction === 'up') {
+ $cell = this.getAboveCell$($cell);
+ } else if (direction === 'down') {
+ $cell = this.getBelowCell$($cell);
+ }
+
+ if (!$cell) {
+ return false;
+ }
+
+ const {
+ colIndex
+ } = $.data($cell);
+ const column = this.columnmanager.getColumn(colIndex);
+
+ if (!column.focusable) {
+ let $prevFocusedCell = this.$focusedCell;
+ this.unfocusCell($prevFocusedCell);
+ this.$focusedCell = $cell;
+ let ret = this.focusCellInDirection(direction);
+ if (!ret) {
+ this.focusCell($prevFocusedCell);
+ }
+ return ret;
+ }
+
+ this.focusCell($cell);
+ return true;
+ }
+
+ getCell$(colIndex, rowIndex) {
+ return $(this.selector(colIndex, rowIndex), this.bodyScrollable);
+ }
+
+ getAboveCell$($cell) {
+ const {
+ colIndex
+ } = $.data($cell);
+
+ let $aboveRow = $cell.parentElement.previousElementSibling;
+ while ($aboveRow && $aboveRow.classList.contains('dt-row--hide')) {
+ $aboveRow = $aboveRow.previousElementSibling;
+ }
+
+ if (!$aboveRow) return $cell;
+ return $(`.dt-cell--col-${colIndex}`, $aboveRow);
+ }
+
+ getBelowCell$($cell) {
+ const {
+ colIndex
+ } = $.data($cell);
+
+ let $belowRow = $cell.parentElement.nextElementSibling;
+ while ($belowRow && $belowRow.classList.contains('dt-row--hide')) {
+ $belowRow = $belowRow.nextElementSibling;
+ }
+
+ if (!$belowRow) return $cell;
+ return $(`.dt-cell--col-${colIndex}`, $belowRow);
+ }
+
+ getLeftCell$($cell) {
+ return $cell.previousElementSibling;
+ }
+
+ getRightCell$($cell) {
+ return $cell.nextElementSibling;
+ }
+
+ getLeftMostCell$(rowIndex) {
+ return this.getCell$(this.columnmanager.getFirstColumnIndex(), rowIndex);
+ }
+
+ getRightMostCell$(rowIndex) {
+ return this.getCell$(this.columnmanager.getLastColumnIndex(), rowIndex);
+ }
+
+ getTopMostCell$(colIndex) {
+ return this.getCell$(colIndex, this.rowmanager.getFirstRowIndex());
+ }
+
+ getBottomMostCell$(colIndex) {
+ return this.getCell$(colIndex, this.rowmanager.getLastRowIndex());
+ }
+
+ getCell(colIndex, rowIndex) {
+ return this.instance.datamanager.getCell(colIndex, rowIndex);
+ }
+
+ getRowHeight() {
+ return $.style($('.dt-row', this.bodyScrollable), 'height');
+ }
+
+ scrollToCell($cell) {
+ if ($.inViewport($cell, this.bodyScrollable)) return false;
+
+ const {
+ rowIndex
+ } = $.data($cell);
+ this.rowmanager.scrollToRow(rowIndex);
+ return false;
+ }
+
+ getRowCountPerPage() {
+ return Math.ceil(this.instance.getViewportHeight() / this.getRowHeight());
+ }
+
+ getCellHTML(cell) {
+ const {
+ rowIndex,
+ colIndex,
+ isHeader,
+ isFilter,
+ isTotalRow
+ } = cell;
+ const dataAttr = makeDataAttributeString({
+ rowIndex,
+ colIndex,
+ isHeader,
+ isFilter,
+ isTotalRow
+ });
+
+ const row = this.datamanager.getRow(rowIndex);
+
+ const isBodyCell = !(isHeader || isFilter || isTotalRow);
+
+ const className = [
+ 'dt-cell',
+ 'dt-cell--col-' + colIndex,
+ isBodyCell ? `dt-cell--${colIndex}-${rowIndex}` : '',
+ isBodyCell ? 'dt-cell--row-' + rowIndex : '',
+ isHeader ? 'dt-cell--header' : '',
+ isHeader ? `dt-cell--header-${colIndex}` : '',
+ isFilter ? 'dt-cell--filter' : '',
+ isBodyCell && (row && row.meta.isTreeNodeClose) ? 'dt-cell--tree-close' : ''
+ ].join(' ');
+
+ return `
+
+ ${this.getCellContent(cell)}
+
+ `;
+ }
+
+ getCellContent(cell, refreshHtml = false) {
+ const {
+ isHeader,
+ isFilter,
+ colIndex
+ } = cell;
+
+ const editable = !isHeader && cell.editable !== false;
+ const editCellHTML = editable ? this.getEditCellHTML(colIndex) : '';
+
+ const sortable = isHeader && cell.sortable !== false;
+ const sortIndicator = sortable ?
+ `
+ ${this.options.sortIndicator[cell.sortOrder]}
+ ` :
+ '';
+
+ const resizable = isHeader && cell.resizable !== false;
+ const resizeColumn = resizable ? '' : '';
+
+ const hasDropdown = isHeader && cell.dropdown !== false;
+ const dropdown = hasDropdown ? this.columnmanager.getDropdownHTML() : '';
+
+ let customFormatter = CellManager.getCustomCellFormatter(cell);
+ let contentHTML;
+ if (isHeader || isFilter || !customFormatter) {
+ contentHTML = cell.content;
+ } else {
+ if (!cell.html || refreshHtml) {
+ const row = this.datamanager.getRow(cell.rowIndex);
+ const data = this.datamanager.getData(cell.rowIndex);
+ contentHTML = customFormatter(cell.content, row, cell.column, data);
+ } else {
+ contentHTML = cell.html;
+ }
+ }
+
+ cell.html = contentHTML;
+
+ if (this.options.treeView && !(isHeader || isFilter) && cell.indent !== undefined) {
+ const nextRow = this.datamanager.getRow(cell.rowIndex + 1);
+ const addToggle = nextRow && nextRow.meta.indent > cell.indent;
+ const leftPadding = 20;
+ const unit = 'px';
+
+ // Add toggle and indent in the first column
+ const firstColumnIndex = this.datamanager.getColumnIndexById('_rowIndex') + 1;
+ if (firstColumnIndex === cell.colIndex) {
+ const padding = ((cell.indent || 0)) * leftPadding;
+ const toggleHTML = addToggle ?
+ `
+ ${icons.chevronDown}
+ ${icons.chevronRight}
+ ` : '';
+ contentHTML = `
+ ${toggleHTML}
+ ${contentHTML}
+ `;
+ }
+ }
+
+ const className = [
+ 'dt-cell__content',
+ isHeader ? `dt-cell__content--header-${colIndex}` : `dt-cell__content--col-${colIndex}`
+ ].join(' ');
+
+ return `
+
+ ${contentHTML}
+ ${sortIndicator}
+ ${resizeColumn}
+ ${dropdown}
+
+ ${editCellHTML}
+ `;
+ }
+
+ getEditCellHTML(colIndex) {
+ return ``;
+ }
+
+ selector(colIndex, rowIndex) {
+ return `.dt-cell--${colIndex}-${rowIndex}`;
+ }
+
+ static getCustomCellFormatter(cell) {
+ return cell.format || (cell.column && cell.column.format) || null;
+ }
+}
+
+class ColumnManager {
+ constructor(instance) {
+ this.instance = instance;
+
+ linkProperties(this, this.instance, [
+ 'options',
+ 'fireEvent',
+ 'header',
+ 'datamanager',
+ 'cellmanager',
+ 'style',
+ 'wrapper',
+ 'rowmanager',
+ 'bodyScrollable',
+ 'bodyRenderer'
+ ]);
+
+ this.bindEvents();
+ }
+
+ renderHeader() {
+ this.header.innerHTML = '';
+ this.refreshHeader();
+ }
+
+ refreshHeader() {
+ const columns = this.datamanager.getColumns();
+
+ // refresh html
+ $('div', this.header).innerHTML = this.getHeaderHTML(columns);
+
+ this.$filterRow = $('.dt-row-filter', this.header);
+ if (this.$filterRow) {
+ $.style(this.$filterRow, { display: 'none' });
+ }
+ // reset columnMap
+ this.$columnMap = [];
+ this.bindMoveColumn();
+ }
+
+ getHeaderHTML(columns) {
+ let html = this.rowmanager.getRowHTML(columns, {
+ isHeader: 1
+ });
+ if (this.options.inlineFilters) {
+ html += this.rowmanager.getRowHTML(columns, {
+ isFilter: 1
+ });
+ }
+ return html;
+ }
+
+ bindEvents() {
+ this.bindDropdown();
+ this.bindResizeColumn();
+ this.bindPerfectColumnWidth();
+ this.bindFilter();
+ }
+
+ bindDropdown() {
+ let toggleClass = '.dt-dropdown__toggle';
+ let dropdownClass = '.dt-dropdown__list';
+
+ // attach the dropdown list to container
+ this.instance.dropdownContainer.innerHTML = this.getDropdownListHTML();
+ this.$dropdownList = this.instance.dropdownContainer.firstElementChild;
+
+ $.on(this.header, 'click', toggleClass, e => {
+ this.openDropdown(e);
+ });
+
+ const deactivateDropdownOnBodyClick = (e) => {
+ const selector = [
+ toggleClass, toggleClass + ' *',
+ dropdownClass, dropdownClass + ' *'
+ ].join(',');
+ if (e.target.matches(selector)) return;
+ deactivateDropdown();
+ };
+ $.on(document.body, 'click', deactivateDropdownOnBodyClick);
+ document.addEventListener('scroll', deactivateDropdown, true);
+
+ this.instance.on('onDestroy', () => {
+ $.off(document.body, 'click', deactivateDropdownOnBodyClick);
+ $.off(document, 'scroll', deactivateDropdown);
+ });
+
+ $.on(this.$dropdownList, 'click', '.dt-dropdown__list-item', (e, $item) => {
+ if (!this._dropdownActiveColIndex) return;
+ const dropdownItems = this.options.headerDropdown;
+ const { index } = $.data($item);
+ const colIndex = this._dropdownActiveColIndex;
+ let callback = dropdownItems[index].action;
+
+ callback && callback.call(this.instance, this.getColumn(colIndex));
+ this.hideDropdown();
+ });
+
+ const _this = this;
+ function deactivateDropdown(e) {
+ _this.hideDropdown();
+ }
+
+ this.hideDropdown();
+ }
+
+ openDropdown(e) {
+ if (!this._dropdownWidth) {
+ $.style(this.$dropdownList, { display: '' });
+ this._dropdownWidth = $.style(this.$dropdownList, 'width');
+ }
+ $.style(this.$dropdownList, {
+ display: '',
+ left: (e.clientX - this._dropdownWidth + 4) + 'px',
+ top: (e.clientY + 4) + 'px'
+ });
+ const $cell = $.closest('.dt-cell', e.target);
+ const { colIndex } = $.data($cell);
+ this._dropdownActiveColIndex = colIndex;
+ }
+
+ hideDropdown() {
+ $.style(this.$dropdownList, {
+ display: 'none'
+ });
+ this._dropdownActiveColIndex = null;
+ }
+
+ bindResizeColumn() {
+ let isDragging = false;
+ let $resizingCell, startWidth, startX;
+
+ $.on(this.header, 'mousedown', '.dt-cell .dt-cell__resize-handle', (e, $handle) => {
+ document.body.classList.add('dt-resize');
+ const $cell = $handle.parentNode.parentNode;
+ $resizingCell = $cell;
+ const {
+ colIndex
+ } = $.data($resizingCell);
+ const col = this.getColumn(colIndex);
+
+ if (col && col.resizable === false) {
+ return;
+ }
+
+ isDragging = true;
+ startWidth = $.style($('.dt-cell__content', $resizingCell), 'width');
+ startX = e.pageX;
+ });
+
+ const onMouseup = (e) => {
+ document.body.classList.remove('dt-resize');
+ if (!$resizingCell) return;
+ isDragging = false;
+
+ const {
+ colIndex
+ } = $.data($resizingCell);
+ this.setColumnWidth(colIndex);
+ this.style.setBodyStyle();
+ $resizingCell = null;
+ };
+ $.on(document.body, 'mouseup', onMouseup);
+ this.instance.on('onDestroy', () => {
+ $.off(document.body, 'mouseup', onMouseup);
+ });
+
+ const onMouseMove = (e) => {
+ if (!isDragging) return;
+ let delta = e.pageX - startX;
+ if (this.options.direction === 'rtl') {
+ delta = -1 * delta;
+ }
+ const finalWidth = startWidth + delta;
+ const {
+ colIndex
+ } = $.data($resizingCell);
+
+ let columnMinWidth = this.options.minimumColumnWidth;
+ if (columnMinWidth > finalWidth) {
+ // don't resize past 30 pixels
+ return;
+ }
+ this.datamanager.updateColumn(colIndex, {
+ width: finalWidth
+ });
+ this.setColumnHeaderWidth(colIndex);
+ };
+ $.on(document.body, 'mousemove', onMouseMove);
+ this.instance.on('onDestroy', () => {
+ $.off(document.body, 'mousemove', onMouseMove);
+ });
+ }
+
+ bindPerfectColumnWidth() {
+ $.on(this.header, 'dblclick', '.dt-cell .dt-cell__resize-handle', (e, $handle) => {
+ const $cell = $handle.parentNode.parentNode;
+ const { colIndex } = $.data($cell);
+
+ let longestCell = this.bodyRenderer.visibleRows
+ .map(d => d[colIndex])
+ .reduce((acc, curr) => acc.content.length > curr.content.length ? acc : curr);
+
+ let $longestCellHTML = this.cellmanager.getCellHTML(longestCell);
+ let $div = document.createElement('div');
+ $div.innerHTML = $longestCellHTML;
+ let cellText = $div.querySelector('.dt-cell__content').textContent;
+
+ let {
+ borderLeftWidth,
+ borderRightWidth,
+ paddingLeft,
+ paddingRight
+ } = $.getStyle(this.bodyScrollable.querySelector('.dt-cell__content'));
+
+ let padding = [borderLeftWidth, borderRightWidth, paddingLeft, paddingRight]
+ .map(parseFloat)
+ .reduce((sum, val) => sum + val);
+
+ let width = $.measureTextWidth(cellText) + padding;
+ this.datamanager.updateColumn(colIndex, { width });
+ this.setColumnHeaderWidth(colIndex);
+ this.setColumnWidth(colIndex);
+ });
+ }
+
+ bindMoveColumn() {
+ if (this.options.disableReorderColumn) return;
+
+ const $parent = $('.dt-row', this.header);
+
+ this.sortable = Sortable.create($parent, {
+ onEnd: (e) => {
+ const {
+ oldIndex,
+ newIndex
+ } = e;
+ const $draggedCell = e.item;
+ const {
+ colIndex
+ } = $.data($draggedCell);
+ if (+colIndex === newIndex) return;
+
+ this.switchColumn(oldIndex, newIndex);
+ },
+ preventOnFilter: false,
+ filter: '.dt-cell__resize-handle, .dt-dropdown',
+ chosenClass: 'dt-cell--dragging',
+ animation: 150
+ });
+ }
+
+ sortColumn(colIndex, nextSortOrder) {
+ this.instance.freeze();
+ this.sortRows(colIndex, nextSortOrder)
+ .then(() => {
+ this.refreshHeader();
+ return this.rowmanager.refreshRows();
+ })
+ .then(() => this.instance.unfreeze())
+ .then(() => {
+ this.fireEvent('onSortColumn', this.getColumn(colIndex));
+ });
+ }
+
+ removeColumn(colIndex) {
+ const removedCol = this.getColumn(colIndex);
+ this.instance.freeze();
+ this.datamanager.removeColumn(colIndex)
+ .then(() => {
+ this.refreshHeader();
+ return this.rowmanager.refreshRows();
+ })
+ .then(() => this.instance.unfreeze())
+ .then(() => {
+ this.fireEvent('onRemoveColumn', removedCol);
+ });
+ }
+
+ switchColumn(oldIndex, newIndex) {
+ this.instance.freeze();
+ this.datamanager.switchColumn(oldIndex, newIndex)
+ .then(() => {
+ this.refreshHeader();
+ return this.rowmanager.refreshRows();
+ })
+ .then(() => {
+ this.setColumnWidth(oldIndex);
+ this.setColumnWidth(newIndex);
+ this.instance.unfreeze();
+ })
+ .then(() => {
+ this.fireEvent('onSwitchColumn',
+ this.getColumn(oldIndex), this.getColumn(newIndex)
+ );
+ });
+ }
+
+ toggleFilter(flag) {
+ if (!this.options.inlineFilters) return;
+
+ let showFilter;
+ if (flag === undefined) {
+ showFilter = !this.isFilterShown;
+ } else {
+ showFilter = flag;
+ }
+
+ if (showFilter) {
+ $.style(this.$filterRow, { display: '' });
+ } else {
+ $.style(this.$filterRow, { display: 'none' });
+ }
+
+ this.isFilterShown = showFilter;
+ this.style.setBodyStyle();
+ }
+
+ focusFilter(colIndex) {
+ if (!this.isFilterShown) return;
+
+ const $filterInput = $(`.dt-cell--col-${colIndex} .dt-filter`, this.$filterRow);
+ $filterInput.focus();
+ }
+
+ bindFilter() {
+ if (!this.options.inlineFilters) return;
+ const handler = e => {
+ this.applyFilter(this.getAppliedFilters());
+ };
+ $.on(this.header, 'keydown', '.dt-filter', debounce$1(handler, 300));
+ }
+
+ applyFilter(filters) {
+ this.datamanager.filterRows(filters)
+ .then(({
+ rowsToShow
+ }) => {
+ this.rowmanager.showRows(rowsToShow);
+ });
+ }
+
+ getAppliedFilters() {
+ const filters = {};
+ $.each('.dt-filter', this.header).map((input) => {
+ const value = input.value;
+ if (value) {
+ filters[input.dataset.colIndex] = value;
+ }
+ });
+ return filters;
+ }
+
+ applyDefaultSortOrder() {
+ // sort rows if any 1 column has a default sortOrder set
+ const columnsToSort = this.getColumns().filter(col => col.sortOrder !== 'none');
+
+ if (columnsToSort.length === 1) {
+ const column = columnsToSort[0];
+ this.sortColumn(column.colIndex, column.sortOrder);
+ }
+ }
+
+ sortRows(colIndex, sortOrder) {
+ return this.datamanager.sortRows(colIndex, sortOrder);
+ }
+
+ getColumn(colIndex) {
+ return this.datamanager.getColumn(colIndex);
+ }
+
+ getColumns() {
+ return this.datamanager.getColumns();
+ }
+
+ setColumnWidth(colIndex, width) {
+ colIndex = +colIndex;
+
+ let columnWidth = width || this.getColumn(colIndex).width;
+
+ const selector = [
+ `.dt-cell__content--col-${colIndex}`,
+ `.dt-cell__edit--col-${colIndex}`
+ ].join(', ');
+
+ const styles = {
+ width: columnWidth + 'px'
+ };
+
+ this.style.setStyle(selector, styles);
+ }
+
+ setColumnHeaderWidth(colIndex) {
+ colIndex = +colIndex;
+ this.$columnMap = this.$columnMap || [];
+ const selector = `.dt-cell__content--header-${colIndex}`;
+ const {
+ width
+ } = this.getColumn(colIndex);
+
+ let $column = this.$columnMap[colIndex];
+ if (!$column) {
+ $column = this.header.querySelector(selector);
+ this.$columnMap[colIndex] = $column;
+ }
+
+ $column.style.width = width + 'px';
+ }
+
+ getColumnMinWidth(colIndex) {
+ colIndex = +colIndex;
+ return this.getColumn(colIndex).minWidth || 24;
+ }
+
+ getFirstColumnIndex() {
+ return this.datamanager.getColumnIndexById('_rowIndex') + 1;
+ }
+
+ getHeaderCell$(colIndex) {
+ return $(`.dt-cell--header-${colIndex}`, this.header);
+ }
+
+ getLastColumnIndex() {
+ return this.datamanager.getColumnCount() - 1;
+ }
+
+ getDropdownHTML() {
+ const { dropdownButton } = this.options;
+
+ return `
+
+ `;
+ }
+
+ getDropdownListHTML() {
+ const { headerDropdown: dropdownItems } = this.options;
+
+ return `
+
+ ${dropdownItems.map((d, i) => `
+
${d.label}
+ `).join('')}
+
+ `;
+ }
+}
+
+class RowManager {
+ constructor(instance) {
+ this.instance = instance;
+ linkProperties(this, this.instance, [
+ 'options',
+ 'fireEvent',
+ 'wrapper',
+ 'bodyScrollable',
+ 'bodyRenderer',
+ 'style'
+ ]);
+
+ this.bindEvents();
+ this.refreshRows = nextTick(this.refreshRows, this);
+ }
+
+ get datamanager() {
+ return this.instance.datamanager;
+ }
+
+ get cellmanager() {
+ return this.instance.cellmanager;
+ }
+
+ bindEvents() {
+ this.bindCheckbox();
+ }
+
+ bindCheckbox() {
+ if (!this.options.checkboxColumn) return;
+
+ // map of checked rows
+ this.checkMap = [];
+
+ $.on(this.wrapper, 'click', '.dt-cell--col-0 [type="checkbox"]', (e, $checkbox) => {
+ const $cell = $checkbox.closest('.dt-cell');
+ const {
+ rowIndex,
+ isHeader
+ } = $.data($cell);
+ const checked = $checkbox.checked;
+
+ if (isHeader) {
+ this.checkAll(checked);
+ } else {
+ this.checkRow(rowIndex, checked);
+ }
+ });
+ }
+
+ refreshRows() {
+ this.instance.renderBody();
+ this.instance.setDimensions();
+ }
+
+ refreshRow(row, rowIndex) {
+ const _row = this.datamanager.updateRow(row, rowIndex);
+
+ _row.forEach(cell => {
+ this.cellmanager.refreshCell(cell, true);
+ });
+ }
+
+ getCheckedRows() {
+ if (!this.checkMap) {
+ return [];
+ }
+
+ let out = [];
+ for (let rowIndex in this.checkMap) {
+ const checked = this.checkMap[rowIndex];
+ if (checked === 1) {
+ out.push(rowIndex);
+ }
+ }
+
+ return out;
+ }
+
+ highlightCheckedRows() {
+ this.getCheckedRows()
+ .map(rowIndex => this.checkRow(rowIndex, true));
+ }
+
+ checkRow(rowIndex, toggle) {
+ const value = toggle ? 1 : 0;
+ const selector = rowIndex => `.dt-cell--0-${rowIndex} [type="checkbox"]`;
+ // update internal map
+ this.checkMap[rowIndex] = value;
+ // set checkbox value explicitly
+ $.each(selector(rowIndex), this.bodyScrollable)
+ .map(input => {
+ input.checked = toggle;
+ });
+ // highlight row
+ this.highlightRow(rowIndex, toggle);
+ this.showCheckStatus();
+ this.fireEvent('onCheckRow', this.datamanager.getRow(rowIndex));
+ }
+
+ checkAll(toggle) {
+ const value = toggle ? 1 : 0;
+
+ // update internal map
+ if (toggle) {
+ this.checkMap = Array.from(Array(this.getTotalRows())).map(c => value);
+ } else {
+ this.checkMap = [];
+ }
+ // set checkbox value
+ $.each('.dt-cell--col-0 [type="checkbox"]', this.bodyScrollable)
+ .map(input => {
+ input.checked = toggle;
+ });
+ // highlight all
+ this.highlightAll(toggle);
+ this.showCheckStatus();
+ this.fireEvent('onCheckRow');
+ }
+
+ showCheckStatus() {
+ if (!this.options.checkedRowStatus) return;
+ const checkedRows = this.getCheckedRows();
+ const count = checkedRows.length;
+ if (count > 0) {
+ let message = this.instance.translate('{count} rows selected', {
+ count: count
+ });
+ this.bodyRenderer.showToastMessage(message);
+ } else {
+ this.bodyRenderer.clearToastMessage();
+ }
+ }
+
+ highlightRow(rowIndex, toggle = true) {
+ const $row = this.getRow$(rowIndex);
+ if (!$row) return;
+
+ if (!toggle && this.bodyScrollable.classList.contains('dt-scrollable--highlight-all')) {
+ $row.classList.add('dt-row--unhighlight');
+ return;
+ }
+
+ if (toggle && $row.classList.contains('dt-row--unhighlight')) {
+ $row.classList.remove('dt-row--unhighlight');
+ }
+
+ this._highlightedRows = this._highlightedRows || {};
+
+ if (toggle) {
+ $row.classList.add('dt-row--highlight');
+ this._highlightedRows[rowIndex] = $row;
+ } else {
+ $row.classList.remove('dt-row--highlight');
+ delete this._highlightedRows[rowIndex];
+ }
+ }
+
+ highlightAll(toggle = true) {
+ if (toggle) {
+ this.bodyScrollable.classList.add('dt-scrollable--highlight-all');
+ } else {
+ this.bodyScrollable.classList.remove('dt-scrollable--highlight-all');
+ for (const rowIndex in this._highlightedRows) {
+ const $row = this._highlightedRows[rowIndex];
+ $row.classList.remove('dt-row--highlight');
+ }
+ this._highlightedRows = {};
+ }
+ }
+
+ showRows(rowIndices) {
+ rowIndices = ensureArray(rowIndices);
+ const rows = rowIndices.map(rowIndex => this.datamanager.getRow(rowIndex));
+ this.bodyRenderer.renderRows(rows);
+ }
+
+ showAllRows() {
+ const rowIndices = this.datamanager.getAllRowIndices();
+ this.showRows(rowIndices);
+ }
+
+ getChildrenToShowForNode(rowIndex) {
+ const row = this.datamanager.getRow(rowIndex);
+ row.meta.isTreeNodeClose = false;
+
+ return this.datamanager.getImmediateChildren(rowIndex);
+ }
+
+ openSingleNode(rowIndex) {
+ const childrenToShow = this.getChildrenToShowForNode(rowIndex);
+ const visibleRowIndices = this.bodyRenderer.visibleRowIndices;
+ const rowsToShow = uniq$1([...childrenToShow, ...visibleRowIndices]).sort(numberSortAsc);
+
+ this.showRows(rowsToShow);
+ }
+
+ getChildrenToHideForNode(rowIndex) {
+ const row = this.datamanager.getRow(rowIndex);
+ row.meta.isTreeNodeClose = true;
+
+ const rowsToHide = this.datamanager.getChildren(rowIndex);
+ rowsToHide.forEach(rowIndex => {
+ const row = this.datamanager.getRow(rowIndex);
+ if (!row.meta.isLeaf) {
+ row.meta.isTreeNodeClose = true;
+ }
+ });
+
+ return rowsToHide;
+ }
+
+ closeSingleNode(rowIndex) {
+ const rowsToHide = this.getChildrenToHideForNode(rowIndex);
+ const visibleRows = this.bodyRenderer.visibleRowIndices;
+ const rowsToShow = visibleRows
+ .filter(rowIndex => !rowsToHide.includes(rowIndex))
+ .sort(numberSortAsc);
+
+ this.showRows(rowsToShow);
+ }
+
+ expandAllNodes() {
+ let rows = this.datamanager.getRows();
+ let rootNodes = rows.filter(row => !row.meta.isLeaf);
+
+ const childrenToShow = rootNodes.map(row => this.getChildrenToShowForNode(row.meta.rowIndex)).flat();
+ const visibleRowIndices = this.bodyRenderer.visibleRowIndices;
+ const rowsToShow = uniq$1([...childrenToShow, ...visibleRowIndices]).sort(numberSortAsc);
+
+ this.showRows(rowsToShow);
+ }
+
+ collapseAllNodes() {
+ let rows = this.datamanager.getRows();
+ let rootNodes = rows.filter(row => row.meta.indent === 0);
+
+ const rowsToHide = rootNodes.map(row => this.getChildrenToHideForNode(row.meta.rowIndex)).flat();
+ const visibleRows = this.bodyRenderer.visibleRowIndices;
+ const rowsToShow = visibleRows
+ .filter(rowIndex => !rowsToHide.includes(rowIndex))
+ .sort(numberSortAsc);
+
+ this.showRows(rowsToShow);
+ }
+
+ setTreeDepth(depth) {
+ let rows = this.datamanager.getRows();
+
+ const rowsToOpen = rows.filter(row => row.meta.indent < depth);
+ const rowsToClose = rows.filter(row => row.meta.indent >= depth);
+ const rowsToHide = rowsToClose.filter(row => row.meta.indent > depth);
+
+ rowsToClose.forEach(row => {
+ if (!row.meta.isLeaf) {
+ row.meta.isTreeNodeClose = true;
+ }
+ });
+ rowsToOpen.forEach(row => {
+ if (!row.meta.isLeaf) {
+ row.meta.isTreeNodeClose = false;
+ }
+ });
+
+ const rowsToShow = rows
+ .filter(row => !rowsToHide.includes(row))
+ .map(row => row.meta.rowIndex)
+ .sort(numberSortAsc);
+ this.showRows(rowsToShow);
+ }
+
+ getRow$(rowIndex) {
+ return $(this.selector(rowIndex), this.bodyScrollable);
+ }
+
+ getTotalRows() {
+ return this.datamanager.getRowCount();
+ }
+
+ getFirstRowIndex() {
+ return 0;
+ }
+
+ getLastRowIndex() {
+ return this.datamanager.getRowCount() - 1;
+ }
+
+ scrollToRow(rowIndex) {
+ rowIndex = +rowIndex;
+ this._lastScrollTo = this._lastScrollTo || 0;
+ const $row = this.getRow$(rowIndex);
+ if ($.inViewport($row, this.bodyScrollable)) return;
+
+ const {
+ height
+ } = $row.getBoundingClientRect();
+ const {
+ top,
+ bottom
+ } = this.bodyScrollable.getBoundingClientRect();
+ const rowsInView = Math.floor((bottom - top) / height);
+
+ let offset = 0;
+ if (rowIndex > this._lastScrollTo) {
+ offset = height * ((rowIndex + 1) - rowsInView);
+ } else {
+ offset = height * ((rowIndex + 1) - 1);
+ }
+
+ this._lastScrollTo = rowIndex;
+ $.scrollTop(this.bodyScrollable, offset);
+ }
+
+ getRowHTML(row, props) {
+ const dataAttr = makeDataAttributeString(props);
+ let rowIdentifier = props.rowIndex;
+
+ if (props.isFilter) {
+ row = row.map(cell => (Object.assign({}, cell, {
+ content: this.getFilterInput({
+ colIndex: cell.colIndex,
+ name: cell.name
+ }),
+ isFilter: 1,
+ isHeader: undefined,
+ editable: false
+ })));
+
+ rowIdentifier = 'filter';
+ }
+
+ if (props.isHeader) {
+ rowIdentifier = 'header';
+ }
+
+ return `
+
+ ${row.map(cell => this.cellmanager.getCellHTML(cell)).join('')}
+
+ `;
+ }
+
+ getFilterInput(props) {
+ let title = `title="Filter based on ${props.name || 'Index'}"`;
+ const dataAttr = makeDataAttributeString(props);
+ return ``;
+ }
+
+ selector(rowIndex) {
+ return `.dt-row-${rowIndex}`;
+ }
+}
+
+var hyperlist = createCommonjsModule(function (module, exports) {
+(function(f){{module.exports=f();}})(function(){return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof commonjsRequire&&commonjsRequire;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t);}return n[i].exports}for(var u="function"==typeof commonjsRequire&&commonjsRequire,i=0;i _this._averageHeight) {
+ var rendered = _this._renderChunk();
+
+ _this._lastRepaint = scrollTop;
+
+ if (rendered !== false && typeof config.afterRender === 'function') {
+ config.afterRender();
+ }
+ }
+ };
+
+ render();
+ }
+
+ _createClass(HyperList, [{
+ key: 'destroy',
+ value: function destroy() {
+ window.cancelAnimationFrame(this._renderAnimationFrame);
+ }
+ }, {
+ key: 'refresh',
+ value: function refresh(element, userProvidedConfig) {
+ var _scrollerStyle;
+
+ Object.assign(this._config, defaultConfig, userProvidedConfig);
+
+ if (!element || element.nodeType !== 1) {
+ throw new Error('HyperList requires a valid DOM Node container');
+ }
+
+ this._element = element;
+
+ var config = this._config;
+
+ var scroller = this._scroller || config.scroller || document.createElement(config.scrollerTagName || 'tr');
+
+ // Default configuration option `useFragment` to `true`.
+ if (typeof config.useFragment !== 'boolean') {
+ this._config.useFragment = true;
+ }
+
+ if (!config.generate) {
+ throw new Error('Missing required `generate` function');
+ }
+
+ if (!isNumber(config.total)) {
+ throw new Error('Invalid required `total` value, expected number');
+ }
+
+ if (!Array.isArray(config.itemHeight) && !isNumber(config.itemHeight)) {
+ throw new Error('\n Invalid required `itemHeight` value, expected number or array\n '.trim());
+ } else if (isNumber(config.itemHeight)) {
+ this._itemHeights = Array(config.total).fill(config.itemHeight);
+ } else {
+ this._itemHeights = config.itemHeight;
+ }
+
+ // Width and height should be coerced to string representations. Either in
+ // `%` or `px`.
+ Object.keys(defaultConfig).filter(function (prop) {
+ return prop in config;
+ }).forEach(function (prop) {
+ var value = config[prop];
+ var isValueNumber = isNumber(value);
+
+ if (value && typeof value !== 'string' && typeof value !== 'number') {
+ var msg = 'Invalid optional `' + prop + '`, expected string or number';
+ throw new Error(msg);
+ } else if (isValueNumber) {
+ config[prop] = value + 'px';
+ }
+ });
+
+ var isHoriz = Boolean(config.horizontal);
+ var value = config[isHoriz ? 'width' : 'height'];
+
+ if (value) {
+ var isValueNumber = isNumber(value);
+ var isValuePercent = isValueNumber ? false : value.slice(-1) === '%';
+ // Compute the containerHeight as number
+ var numberValue = isValueNumber ? value : parseInt(value.replace(/px|%/, ''), 10);
+ var innerSize = window[isHoriz ? 'innerWidth' : 'innerHeight'];
+
+ if (isValuePercent) {
+ this._containerSize = innerSize * numberValue / 100;
+ } else {
+ this._containerSize = isNumber(value) ? value : numberValue;
+ }
+ }
+
+ var scrollContainer = config.scrollContainer;
+ var scrollerHeight = config.itemHeight * config.total;
+ var maxElementHeight = this._maxElementHeight;
+
+ if (scrollerHeight > maxElementHeight) {
+ console.warn(['HyperList: The maximum element height', maxElementHeight + 'px has', 'been exceeded; please reduce your item height.'].join(' '));
+ }
+
+ // Decorate the container element with styles that will match
+ // the user supplied configuration.
+ var elementStyle = {
+ width: '' + config.width,
+ height: scrollContainer ? scrollerHeight + 'px' : '' + config.height,
+ overflow: scrollContainer ? 'none' : 'auto',
+ position: 'relative'
+ };
+
+ HyperList.mergeStyle(element, elementStyle);
+
+ if (scrollContainer) {
+ HyperList.mergeStyle(config.scrollContainer, { overflow: 'auto' });
+ }
+
+ var scrollerStyle = (_scrollerStyle = {
+ opacity: '0',
+ position: 'absolute'
+ }, _defineProperty(_scrollerStyle, isHoriz ? 'height' : 'width', '1px'), _defineProperty(_scrollerStyle, isHoriz ? 'width' : 'height', scrollerHeight + 'px'), _scrollerStyle);
+
+ HyperList.mergeStyle(scroller, scrollerStyle);
+
+ // Only append the scroller element once.
+ if (!this._scroller) {
+ element.appendChild(scroller);
+ }
+
+ var padding = this._computeScrollPadding();
+ this._scrollPaddingBottom = padding.bottom;
+ this._scrollPaddingTop = padding.top;
+
+ // Set the scroller instance.
+ this._scroller = scroller;
+ this._scrollHeight = this._computeScrollHeight();
+
+ // Reuse the item positions if refreshed, otherwise set to empty array.
+ this._itemPositions = this._itemPositions || Array(config.total).fill(0);
+
+ // Each index in the array should represent the position in the DOM.
+ this._computePositions(0);
+
+ // Render after refreshing. Force render if we're calling refresh manually.
+ this._renderChunk(this._lastRepaint !== null);
+
+ if (typeof config.afterRender === 'function') {
+ config.afterRender();
+ }
+ }
+ }, {
+ key: '_getRow',
+ value: function _getRow(i) {
+ var config = this._config;
+ var item = config.generate(i);
+ var height = item.height;
+
+ if (height !== undefined && isNumber(height)) {
+ item = item.element;
+
+ // The height isn't the same as predicted, compute positions again
+ if (height !== this._itemHeights[i]) {
+ this._itemHeights[i] = height;
+ this._computePositions(i);
+ this._scrollHeight = this._computeScrollHeight(i);
+ }
+ } else {
+ height = this._itemHeights[i];
+ }
+
+ if (!item || item.nodeType !== 1) {
+ throw new Error('Generator did not return a DOM Node for index: ' + i);
+ }
+
+ addClass(item, config.rowClassName || 'vrow');
+
+ var top = this._itemPositions[i] + this._scrollPaddingTop;
+
+ HyperList.mergeStyle(item, _defineProperty({
+ position: 'absolute'
+ }, config.horizontal ? 'left' : 'top', top + 'px'));
+
+ return item;
+ }
+ }, {
+ key: '_getScrollPosition',
+ value: function _getScrollPosition() {
+ var config = this._config;
+
+ if (typeof config.overrideScrollPosition === 'function') {
+ return config.overrideScrollPosition();
+ }
+
+ return this._element[config.horizontal ? 'scrollLeft' : 'scrollTop'];
+ }
+ }, {
+ key: '_renderChunk',
+ value: function _renderChunk(force) {
+ var config = this._config;
+ var element = this._element;
+ var scrollTop = this._getScrollPosition();
+ var total = config.total;
+
+ var from = config.reverse ? this._getReverseFrom(scrollTop) : this._getFrom(scrollTop) - 1;
+
+ if (from < 0 || from - this._screenItemsLen < 0) {
+ from = 0;
+ }
+
+ if (!force && this._lastFrom === from) {
+ return false;
+ }
+
+ this._lastFrom = from;
+
+ var to = from + this._cachedItemsLen;
+
+ if (to > total || to + this._cachedItemsLen > total) {
+ to = total;
+ }
+
+ // Append all the new rows in a document fragment that we will later append
+ // to the parent node
+ var fragment = config.useFragment ? document.createDocumentFragment() : []
+ // Sometimes you'll pass fake elements to this tool and Fragments require
+ // real elements.
+
+
+ // The element that forces the container to scroll.
+ ;var scroller = this._scroller;
+
+ // Keep the scroller in the list of children.
+ fragment[config.useFragment ? 'appendChild' : 'push'](scroller);
+
+ for (var i = from; i < to; i++) {
+ var row = this._getRow(i);
+
+ fragment[config.useFragment ? 'appendChild' : 'push'](row);
+ }
+
+ if (config.applyPatch) {
+ return config.applyPatch(element, fragment);
+ }
+
+ element.innerHTML = '';
+ element.appendChild(fragment);
+ }
+ }, {
+ key: '_computePositions',
+ value: function _computePositions() {
+ var from = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
+
+ var config = this._config;
+ var total = config.total;
+ var reverse = config.reverse;
+
+ if (from < 1 && !reverse) {
+ from = 1;
+ }
+
+ for (var i = from; i < total; i++) {
+ if (reverse) {
+ if (i === 0) {
+ this._itemPositions[0] = this._scrollHeight - this._itemHeights[0];
+ } else {
+ this._itemPositions[i] = this._itemPositions[i - 1] - this._itemHeights[i];
+ }
+ } else {
+ this._itemPositions[i] = this._itemHeights[i - 1] + this._itemPositions[i - 1];
+ }
+ }
+ }
+ }, {
+ key: '_computeScrollHeight',
+ value: function _computeScrollHeight() {
+ var _HyperList$mergeStyle2,
+ _this2 = this;
+
+ var config = this._config;
+ var isHoriz = Boolean(config.horizontal);
+ var total = config.total;
+ var scrollHeight = this._itemHeights.reduce(function (a, b) {
+ return a + b;
+ }, 0) + this._scrollPaddingBottom + this._scrollPaddingTop;
+
+ HyperList.mergeStyle(this._scroller, (_HyperList$mergeStyle2 = {
+ opacity: 0,
+ position: 'absolute',
+ top: '0px'
+ }, _defineProperty(_HyperList$mergeStyle2, isHoriz ? 'height' : 'width', '1px'), _defineProperty(_HyperList$mergeStyle2, isHoriz ? 'width' : 'height', scrollHeight + 'px'), _HyperList$mergeStyle2));
+
+ // Calculate the height median
+ var sortedItemHeights = this._itemHeights.slice(0).sort(function (a, b) {
+ return a - b;
+ });
+ var middle = Math.floor(total / 2);
+ var averageHeight = total % 2 === 0 ? (sortedItemHeights[middle] + sortedItemHeights[middle - 1]) / 2 : sortedItemHeights[middle];
+
+ var clientProp = isHoriz ? 'clientWidth' : 'clientHeight';
+ var element = config.scrollContainer ? config.scrollContainer : this._element;
+ var containerHeight = element[clientProp] ? element[clientProp] : this._containerSize;
+ this._screenItemsLen = Math.ceil(containerHeight / averageHeight);
+ this._containerSize = containerHeight;
+
+ // Cache 3 times the number of items that fit in the container viewport.
+ this._cachedItemsLen = Math.max(this._cachedItemsLen || 0, this._screenItemsLen * 3);
+ this._averageHeight = averageHeight;
+
+ if (config.reverse) {
+ window.requestAnimationFrame(function () {
+ if (isHoriz) {
+ _this2._element.scrollLeft = scrollHeight;
+ } else {
+ _this2._element.scrollTop = scrollHeight;
+ }
+ });
+ }
+
+ return scrollHeight;
+ }
+ }, {
+ key: '_computeScrollPadding',
+ value: function _computeScrollPadding() {
+ var config = this._config;
+ var isHoriz = Boolean(config.horizontal);
+ var isReverse = config.reverse;
+ var styles = window.getComputedStyle(this._element);
+
+ var padding = function padding(location) {
+ var cssValue = styles.getPropertyValue('padding-' + location);
+ return parseInt(cssValue, 10) || 0;
+ };
+
+ if (isHoriz && isReverse) {
+ return {
+ bottom: padding('left'),
+ top: padding('right')
+ };
+ } else if (isHoriz) {
+ return {
+ bottom: padding('right'),
+ top: padding('left')
+ };
+ } else if (isReverse) {
+ return {
+ bottom: padding('top'),
+ top: padding('bottom')
+ };
+ } else {
+ return {
+ bottom: padding('bottom'),
+ top: padding('top')
+ };
+ }
+ }
+ }, {
+ key: '_getFrom',
+ value: function _getFrom(scrollTop) {
+ var i = 0;
+
+ while (this._itemPositions[i] < scrollTop) {
+ i++;
+ }
+
+ return i;
+ }
+ }, {
+ key: '_getReverseFrom',
+ value: function _getReverseFrom(scrollTop) {
+ var i = this._config.total - 1;
+
+ while (i > 0 && this._itemPositions[i] < scrollTop + this._containerSize) {
+ i--;
+ }
+
+ return i;
+ }
+ }]);
+
+ return HyperList;
+}();
+
+exports.default = HyperList;
+module.exports = exports['default'];
+
+},{}]},{},[1])(1)
+});
+});
+
+var HyperList = unwrapExports(hyperlist);
+
+class BodyRenderer {
+ constructor(instance) {
+ this.instance = instance;
+ this.options = instance.options;
+ this.datamanager = instance.datamanager;
+ this.rowmanager = instance.rowmanager;
+ this.cellmanager = instance.cellmanager;
+ this.bodyScrollable = instance.bodyScrollable;
+ this.footer = this.instance.footer;
+ this.log = instance.log;
+ }
+
+ renderRows(rows) {
+ this.visibleRows = rows;
+ this.visibleRowIndices = rows.map(row => row.meta.rowIndex);
+
+ if (rows.length === 0) {
+ this.bodyScrollable.innerHTML = this.getNoDataHTML();
+ return;
+ }
+
+ const rowViewOrder = this.datamanager.rowViewOrder.map(index => {
+ if (this.visibleRowIndices.includes(index)) {
+ return index;
+ }
+ return null;
+ }).filter(index => index !== null);
+
+ const computedStyle = getComputedStyle(this.bodyScrollable);
+
+ let config = {
+ width: computedStyle.width,
+ height: computedStyle.height,
+ itemHeight: this.options.cellHeight,
+ total: rows.length,
+ generate: (index) => {
+ const el = document.createElement('div');
+ const rowIndex = rowViewOrder[index];
+ const row = this.datamanager.getRow(rowIndex);
+ const rowHTML = this.rowmanager.getRowHTML(row, row.meta);
+ el.innerHTML = rowHTML;
+ return el.children[0];
+ },
+ afterRender: () => {
+ this.restoreState();
+ }
+ };
+
+ if (!this.hyperlist) {
+ this.hyperlist = new HyperList(this.bodyScrollable, config);
+ } else {
+ this.hyperlist.refresh(this.bodyScrollable, config);
+ }
+
+ this.renderFooter();
+ }
+
+ render() {
+ const rows = this.datamanager.getRowsForView();
+ this.renderRows(rows);
+ // setDimensions requires atleast 1 row to exist in dom
+ this.instance.setDimensions();
+ }
+
+ renderFooter() {
+ if (!this.options.showTotalRow) return;
+
+ const totalRow = this.getTotalRow();
+ let html = this.rowmanager.getRowHTML(totalRow, { isTotalRow: 1, rowIndex: 'totalRow' });
+
+ this.footer.innerHTML = html;
+ }
+
+ getTotalRow() {
+ const columns = this.datamanager.getColumns();
+ const totalRowTemplate = columns.map(col => {
+ let content = null;
+ if (['_rowIndex', '_checkbox'].includes(col.id)) {
+ content = '';
+ }
+ return {
+ content,
+ isTotalRow: 1,
+ colIndex: col.colIndex,
+ column: col
+ };
+ });
+
+ const totalRow = totalRowTemplate.map((cell, i) => {
+ if (cell.content === '') return cell;
+
+ if (this.options.hooks.columnTotal) {
+ const columnValues = this.visibleRows.map(row => row[i].content);
+ const result = this.options.hooks.columnTotal.call(this.instance, columnValues, cell);
+ if (result != null) {
+ cell.content = result;
+ return cell;
+ }
+ }
+
+ cell.content = this.visibleRows.reduce((acc, prevRow) => {
+ const prevCell = prevRow[i];
+ if (typeof prevCell.content === 'number') {
+ if (acc == null) acc = 0;
+ return acc + prevCell.content;
+ }
+ return acc;
+ }, cell.content);
+
+ return cell;
+ });
+
+ return totalRow;
+ }
+
+ restoreState() {
+ this.rowmanager.highlightCheckedRows();
+ this.cellmanager.selectAreaOnClusterChanged();
+ this.cellmanager.focusCellOnClusterChanged();
+ }
+
+ showToastMessage(message, hideAfter) {
+ this.instance.toastMessage.innerHTML = this.getToastMessageHTML(message);
+
+ if (hideAfter) {
+ setTimeout(() => {
+ this.clearToastMessage();
+ }, hideAfter * 1000);
+ }
+ }
+
+ clearToastMessage() {
+ this.instance.toastMessage.innerHTML = '';
+ }
+
+ getNoDataHTML() {
+ return `${this.options.noDataMessage}
`;
+ }
+
+ getToastMessageHTML(message) {
+ return `${message}`;
+ }
+}
+
+class Style {
+ constructor(instance) {
+ this.instance = instance;
+
+ linkProperties(this, this.instance, [
+ 'options', 'datamanager', 'columnmanager',
+ 'header', 'footer', 'bodyScrollable', 'datatableWrapper',
+ 'getColumn', 'bodyRenderer'
+ ]);
+
+ this.scopeClass = 'dt-instance-' + instance.constructor.instances;
+ instance.datatableWrapper.classList.add(this.scopeClass);
+
+ const styleEl = document.createElement('style');
+ instance.wrapper.insertBefore(styleEl, instance.datatableWrapper);
+ this.styleEl = styleEl;
+
+ this.bindResizeWindow();
+ this.bindScrollHeader();
+ }
+
+ get stylesheet() {
+ return this.styleEl.sheet;
+ }
+
+ bindResizeWindow() {
+ this.onWindowResize = this.onWindowResize.bind(this);
+ this.onWindowResize = throttle$1(this.onWindowResize, 300);
+
+ if (this.options.layout === 'fluid') {
+ $.on(window, 'resize', this.onWindowResize);
+ }
+ }
+
+ bindScrollHeader() {
+ this._settingHeaderPosition = false;
+
+ $.on(this.bodyScrollable, 'scroll', (e) => {
+ if (this._settingHeaderPosition) return;
+
+ this._settingHeaderPosition = true;
+
+ requestAnimationFrame(() => {
+ const left = -e.target.scrollLeft;
+
+ $.style(this.header, {
+ transform: `translateX(${left}px)`
+ });
+ $.style(this.footer, {
+ transform: `translateX(${left}px)`
+ });
+ this._settingHeaderPosition = false;
+ });
+ });
+ }
+
+ onWindowResize() {
+ this.distributeRemainingWidth();
+ this.refreshColumnWidth();
+ this.setBodyStyle();
+ }
+
+ destroy() {
+ this.styleEl.remove();
+ $.off(window, 'resize', this.onWindowResize);
+ }
+
+ setStyle(selector, styleObject) {
+ if (selector.includes(',')) {
+ selector.split(',')
+ .map(s => s.trim())
+ .forEach(selector => {
+ this.setStyle(selector, styleObject);
+ });
+ return;
+ }
+
+ selector = selector.trim();
+ if (!selector) return;
+
+ this._styleRulesMap = this._styleRulesMap || {};
+ const prefixedSelector = this._getPrefixedSelector(selector);
+
+ if (this._styleRulesMap[prefixedSelector]) {
+ this.removeStyle(selector);
+
+ // merge with old styleobject
+ styleObject = Object.assign({}, this._styleRulesMap[prefixedSelector], styleObject);
+ }
+
+ const styleString = this._getRuleString(styleObject);
+ const ruleString = `${prefixedSelector} { ${styleString} }`;
+
+ this._styleRulesMap[prefixedSelector] = styleObject;
+ this.stylesheet.insertRule(ruleString);
+ }
+
+ removeStyle(selector) {
+ if (selector.includes(',')) {
+ selector.split(',')
+ .map(s => s.trim())
+ .forEach(selector => {
+ this.removeStyle(selector);
+ });
+ return;
+ }
+
+ selector = selector.trim();
+ if (!selector) return;
+
+ // find and remove
+ const prefixedSelector = this._getPrefixedSelector(selector);
+ const index = Array.from(this.stylesheet.cssRules)
+ .findIndex(rule => rule.selectorText === prefixedSelector);
+
+ if (index === -1) return;
+ this.stylesheet.deleteRule(index);
+ }
+
+ _getPrefixedSelector(selector) {
+ return `.${this.scopeClass} ${selector}`;
+ }
+
+ _getRuleString(styleObject) {
+ return Object.keys(styleObject)
+ .map(prop => {
+ let dashed = prop;
+ if (!prop.includes('-')) {
+ dashed = camelCaseToDash(prop);
+ }
+ return `${dashed}:${styleObject[prop]};`;
+ })
+ .join('');
+ }
+
+ setDimensions() {
+ this.setCellHeight();
+ this.setupMinWidth();
+ this.setupNaturalColumnWidth();
+ this.setupColumnWidth();
+ this.distributeRemainingWidth();
+ this.setColumnStyle();
+ this.setBodyStyle();
+ }
+
+ setCellHeight() {
+ this.setStyle('.dt-cell', {
+ height: this.options.cellHeight + 'px'
+ });
+ }
+
+ setupMinWidth() {
+ $.each('.dt-cell--header', this.header).map(col => {
+ const { colIndex } = $.data(col);
+ const column = this.getColumn(colIndex);
+
+ if (!column.minWidth) {
+ const width = $.style($('.dt-cell__content', col), 'width');
+ // only set this once
+ column.minWidth = width;
+ }
+ });
+ }
+
+ setupNaturalColumnWidth() {
+ if (!$('.dt-row')) return;
+
+ $.each('.dt-row-header .dt-cell', this.header).map($headerCell => {
+ const { colIndex } = $.data($headerCell);
+ const column = this.datamanager.getColumn(colIndex);
+ let width = $.style($('.dt-cell__content', $headerCell), 'width');
+ if (typeof width === 'number' && width >= this.options.minimumColumnWidth) {
+ column.naturalWidth = width;
+ } else {
+ column.naturalWidth = this.options.minimumColumnWidth;
+ }
+ });
+
+ // set initial width as naturally calculated by table's first row
+ $.each('.dt-row-0 .dt-cell', this.bodyScrollable).map($cell => {
+ const {
+ colIndex
+ } = $.data($cell);
+ const column = this.datamanager.getColumn(colIndex);
+
+ let naturalWidth = $.style($('.dt-cell__content', $cell), 'width');
+
+ if (typeof naturalWidth === 'number' && naturalWidth >= column.naturalWidth) {
+ column.naturalWidth = naturalWidth;
+ } else {
+ column.naturalWidth = column.naturalWidth;
+ }
+ });
+ }
+
+ setupColumnWidth() {
+ if (this.options.layout === 'ratio') {
+ let totalWidth = $.style(this.datatableWrapper, 'width');
+
+ if (this.options.serialNoColumn) {
+ const rowIndexColumn = this.datamanager.getColumnById('_rowIndex');
+ totalWidth = totalWidth - rowIndexColumn.width - 1;
+ }
+
+ if (this.options.checkboxColumn) {
+ const rowIndexColumn = this.datamanager.getColumnById('_checkbox');
+ totalWidth = totalWidth - rowIndexColumn.width - 1;
+ }
+
+ const totalParts = this.datamanager.getColumns()
+ .map(column => {
+ if (column.id === '_rowIndex' || column.id === '_checkbox') {
+ return 0;
+ }
+ if (!column.width) {
+ column.width = 1;
+ }
+ column.ratioWidth = parseInt(column.width, 10);
+ return column.ratioWidth;
+ })
+ .reduce((a, c) => a + c);
+
+ const onePart = totalWidth / totalParts;
+
+ this.datamanager.getColumns()
+ .map(column => {
+ if (column.id === '_rowIndex' || column.id === '_checkbox') return;
+ column.width = Math.floor(onePart * column.ratioWidth) - 1;
+ });
+ } else {
+ this.datamanager.getColumns()
+ .map(column => {
+ if (!column.width) {
+ column.width = column.naturalWidth;
+ }
+ if (column.id === '_rowIndex') {
+ column.width = this.getRowIndexColumnWidth();
+ }
+ if (column.width < this.options.minimumColumnWidth) {
+ column.width = this.options.minimumColumnWidth;
+ }
+ });
+ }
+ }
+
+ distributeRemainingWidth() {
+ if (this.options.layout !== 'fluid') return;
+
+ const wrapperWidth = $.style(this.instance.datatableWrapper, 'width');
+ let firstRow = $('.dt-row', this.bodyScrollable);
+ let firstRowWidth = wrapperWidth;
+ if (!firstRow) {
+ let headerRow = $('.dt-row', this.instance.header);
+ let cellWidths = Array.from(headerRow.children)
+ .map(cell => cell.offsetWidth);
+ firstRowWidth = cellWidths.reduce((sum, a) => sum + a, 0);
+ } else {
+ firstRowWidth = $.style(firstRow, 'width');
+ }
+ const resizableColumns = this.datamanager.getColumns().filter(col => col.resizable);
+ const deltaWidth = (wrapperWidth - firstRowWidth) / resizableColumns.length;
+
+ resizableColumns.map(col => {
+ const width = $.style(this.getColumnHeaderElement(col.colIndex), 'width');
+ let finalWidth = Math.floor(width + deltaWidth) - 2;
+
+ this.datamanager.updateColumn(col.colIndex, {
+ width: finalWidth
+ });
+ });
+ }
+
+ setColumnStyle() {
+ // align columns
+ this.datamanager.getColumns()
+ .map(column => {
+ // alignment
+ if (!column.align) {
+ column.align = 'left';
+ }
+ if (!['left', 'center', 'right'].includes(column.align)) {
+ column.align = 'left';
+ }
+ this.setStyle(`.dt-cell--col-${column.colIndex}`, {
+ 'text-align': column.align
+ });
+
+ // width
+ this.columnmanager.setColumnHeaderWidth(column.colIndex);
+ this.columnmanager.setColumnWidth(column.colIndex);
+ });
+ }
+
+ refreshColumnWidth() {
+ this.datamanager.getColumns()
+ .map(column => {
+ this.columnmanager.setColumnHeaderWidth(column.colIndex);
+ this.columnmanager.setColumnWidth(column.colIndex);
+ });
+ }
+
+ setBodyStyle() {
+ const bodyWidth = $.style(this.datatableWrapper, 'width');
+ const firstRow = $('.dt-row', this.bodyScrollable);
+ if (!firstRow) return;
+ const rowWidth = $.style(firstRow, 'width');
+
+ let width = bodyWidth > rowWidth ? rowWidth : bodyWidth;
+ $.style(this.bodyScrollable, {
+ width: width + 'px'
+ });
+
+ // remove the body height, so that it resets to it's original
+ $.removeStyle(this.bodyScrollable, 'height');
+
+ // when there are less rows than the container
+ // adapt the container height
+ let bodyHeight = $.getStyle(this.bodyScrollable, 'height');
+ const scrollHeight = (this.bodyRenderer.hyperlist || {})._scrollHeight || Infinity;
+ const hasHorizontalOverflow = $.hasHorizontalOverflow(this.bodyScrollable);
+
+ let height;
+
+ if (scrollHeight < bodyHeight) {
+ height = scrollHeight;
+
+ // account for scrollbar size when
+ // there is horizontal overflow
+ if (hasHorizontalOverflow) {
+ height += $.scrollbarSize();
+ }
+
+ $.style(this.bodyScrollable, {
+ height: height + 'px'
+ });
+ }
+
+ const verticalOverflow = this.bodyScrollable.scrollHeight - this.bodyScrollable.offsetHeight;
+ if (verticalOverflow < $.scrollbarSize()) {
+ // if verticalOverflow is less than scrollbar size
+ // then most likely scrollbar is causing the scroll
+ // which is not needed
+ $.style(this.bodyScrollable, {
+ overflowY: 'hidden'
+ });
+ }
+
+ if (this.options.layout === 'fluid') {
+ $.style(this.bodyScrollable, {
+ overflowX: 'hidden'
+ });
+ }
+ }
+
+ getColumnHeaderElement(colIndex) {
+ colIndex = +colIndex;
+ if (colIndex < 0) return null;
+ return $(`.dt-cell--col-${colIndex}`, this.header);
+ }
+
+ getRowIndexColumnWidth() {
+ const rowCount = this.datamanager.getRowCount();
+ const padding = 22;
+ return $.measureTextWidth(rowCount + '') + padding;
+ }
+}
+
+const KEYCODES = {
+ 13: 'enter',
+ 91: 'meta',
+ 16: 'shift',
+ 17: 'ctrl',
+ 18: 'alt',
+ 37: 'left',
+ 38: 'up',
+ 39: 'right',
+ 40: 'down',
+ 9: 'tab',
+ 27: 'esc',
+ 67: 'c',
+ 70: 'f',
+ 86: 'v'
+};
+
+class Keyboard {
+ constructor(element) {
+ this.listeners = {};
+ $.on(element, 'keydown', this.handler.bind(this));
+ }
+
+ handler(e) {
+ let key = KEYCODES[e.keyCode];
+
+ if (e.shiftKey && key !== 'shift') {
+ key = 'shift+' + key;
+ }
+
+ if ((e.ctrlKey && key !== 'ctrl') || (e.metaKey && key !== 'meta')) {
+ key = 'ctrl+' + key;
+ }
+
+ const listeners = this.listeners[key];
+
+ if (listeners && listeners.length > 0) {
+ for (let listener of listeners) {
+ const preventBubbling = listener(e);
+ if (preventBubbling === undefined || preventBubbling === true) {
+ e.preventDefault();
+ }
+ }
+ }
+ }
+
+ on(key, listener) {
+ const keys = key.split(',').map(k => k.trim());
+
+ keys.map(key => {
+ this.listeners[key] = this.listeners[key] || [];
+ this.listeners[key].push(listener);
+ });
+ }
+}
+
+var en = {
+ "Sort Ascending": "Sort Ascending",
+ "Sort Descending": "Sort Descending",
+ "Reset sorting": "Reset sorting",
+ "Remove column": "Remove column",
+ "No Data": "No Data",
+ "{count} cells copied": {"1":"{count} cell copied","default":"{count} cells copied"},
+ "{count} rows selected": {"1":"{count} row selected","default":"{count} rows selected"}
+};
+
+var de = {
+ "Sort Ascending": "Aufsteigend sortieren",
+ "Sort Descending": "Absteigend sortieren",
+ "Reset sorting": "Sortierung zurücksetzen",
+ "Remove column": "Spalte entfernen",
+ "No Data": "Keine Daten",
+ "{count} cells copied": {"1":"{count} Zelle kopiert","default":"{count} Zellen kopiert"},
+ "{count} rows selected": {"1":"{count} Zeile ausgewählt","default":"{count} Zeilen ausgewählt"}
+};
+
+var fr = {
+ "Sort Ascending": "Trier par ordre croissant",
+ "Sort Descending": "Trier par ordre décroissant",
+ "Reset sorting": "Réinitialiser le tri",
+ "Remove column": "Supprimer colonne",
+ "No Data": "Pas de données",
+ "{count} cells copied": {"1":"{count} cellule copiée","default":"{count} cellules copiées"},
+ "{count} rows selected": {"1":"{count} ligne sélectionnée","default":"{count} lignes sélectionnées"}
+};
+
+var it = {
+ "Sort Ascending": "Ordinamento ascendente",
+ "Sort Descending": "Ordinamento decrescente",
+ "Reset sorting": "Azzeramento ordinamento",
+ "Remove column": "Rimuovi colonna",
+ "No Data": "Nessun dato",
+ "{count} cells copied": {"1":"Copiato {count} cella","default":"{count} celle copiate"},
+ "{count} rows selected": {"1":"{count} linea selezionata","default":"{count} linee selezionate"}
+};
+
+function getTranslations() {
+ return {
+ en,
+ de,
+ fr,
+ it,
+ };
+}
+
+class TranslationManager {
+ constructor(language) {
+ this.language = language;
+ this.translations = getTranslations();
+ }
+
+ addTranslations(translations) {
+ this.translations = Object.assign(this.translations, translations);
+ }
+
+ translate(sourceText, args) {
+ let translation = (this.translations[this.language] &&
+ this.translations[this.language][sourceText]) || sourceText;
+
+ if (typeof translation === 'object') {
+ translation = args && args.count ?
+ this.getPluralizedTranslation(translation, args.count) :
+ sourceText;
+ }
+
+ return format(translation, args || {});
+ }
+
+ getPluralizedTranslation(translations, count) {
+ return translations[count] || translations['default'];
+ }
+}
+
+function filterRows(rows, filters) {
+ let filteredRowIndices = [];
+
+ if (Object.keys(filters).length === 0) {
+ return rows.map(row => row.meta.rowIndex);
+ }
+
+ for (let colIndex in filters) {
+ const keyword = filters[colIndex];
+
+ const filteredRows = filteredRowIndices.length ?
+ filteredRowIndices.map(i => rows[i]) :
+ rows;
+
+ const cells = filteredRows.map(row => row[colIndex]);
+
+ let filter = guessFilter(keyword);
+ let filterMethod = getFilterMethod(rows, filter);
+
+ if (filterMethod) {
+ filteredRowIndices = filterMethod(filter.text, cells);
+ } else {
+ filteredRowIndices = cells.map(cell => cell.rowIndex);
+ }
+ }
+
+ return filteredRowIndices;
+}
+function getFilterMethod(rows, filter) {
+ const getFormattedValue = cell => {
+ let formatter = CellManager.getCustomCellFormatter(cell);
+ if (formatter && cell.content) {
+ cell.html = formatter(cell.content, rows[cell.rowIndex], cell.column, rows[cell.rowIndex]);
+ return stripHTML(cell.html);
+ }
+ return cell.content || '';
+ };
+
+ const stringCompareValue = cell =>
+ String(stripHTML(cell.html || '') || getFormattedValue(cell)).toLowerCase();
+
+ const numberCompareValue = cell => parseFloat(cell.content);
+
+ const getCompareValues = (cell, keyword) => {
+ if (cell.column.compareValue) {
+ const compareValues = cell.column.compareValue(cell, keyword);
+ if (compareValues && Array.isArray(compareValues)) return compareValues;
+ }
+
+ // check if it can be converted to number
+ const float = numberCompareValue(cell);
+ if (!isNaN(float)) {
+ return [float, keyword];
+ }
+
+ return [stringCompareValue(cell), keyword];
+ };
+
+ let filterMethodMap = {
+ contains(keyword, cells) {
+ return cells
+ .filter(cell => {
+ const hay = stringCompareValue(cell);
+ const needle = (keyword || '').toLowerCase();
+ return !needle || hay.includes(needle);
+ })
+ .map(cell => cell.rowIndex);
+ },
+
+ greaterThan(keyword, cells) {
+ return cells
+ .filter(cell => {
+ const [compareValue, keywordValue] = getCompareValues(cell, keyword);
+ return compareValue > keywordValue;
+ })
+ .map(cell => cell.rowIndex);
+ },
+
+ lessThan(keyword, cells) {
+ return cells
+ .filter(cell => {
+ const [compareValue, keywordValue] = getCompareValues(cell, keyword);
+ return compareValue < keywordValue;
+ })
+ .map(cell => cell.rowIndex);
+ },
+
+ equals(keyword, cells) {
+ return cells
+ .filter(cell => {
+ const value = parseFloat(cell.content);
+ return value === keyword;
+ })
+ .map(cell => cell.rowIndex);
+ },
+
+ notEquals(keyword, cells) {
+ return cells
+ .filter(cell => {
+ const value = parseFloat(cell.content);
+ return value !== keyword;
+ })
+ .map(cell => cell.rowIndex);
+ },
+
+ range(rangeValues, cells) {
+ return cells
+ .filter(cell => {
+ const values1 = getCompareValues(cell, rangeValues[0]);
+ const values2 = getCompareValues(cell, rangeValues[1]);
+ const value = values1[0];
+ return value >= values1[1] && value <= values2[1];
+ })
+ .map(cell => cell.rowIndex);
+ },
+
+ containsNumber(keyword, cells) {
+ return cells
+ .filter(cell => {
+ let number = parseFloat(keyword, 10);
+ let string = keyword;
+ let hayNumber = numberCompareValue(cell);
+ let hayString = stringCompareValue(cell);
+
+ return number === hayNumber || hayString.includes(string);
+ })
+ .map(cell => cell.rowIndex);
+ }
+ };
+
+ return filterMethodMap[filter.type];
+}
+
+function guessFilter(keyword = '') {
+ if (keyword.length === 0) return {};
+
+ let compareString = keyword;
+
+ if (['>', '<', '='].includes(compareString[0])) {
+ compareString = keyword.slice(1);
+ } else if (compareString.startsWith('!=')) {
+ compareString = keyword.slice(2);
+ }
+
+ if (keyword.startsWith('>')) {
+ if (compareString) {
+ return {
+ type: 'greaterThan',
+ text: compareString.trim()
+ };
+ }
+ }
+
+ if (keyword.startsWith('<')) {
+ if (compareString) {
+ return {
+ type: 'lessThan',
+ text: compareString.trim()
+ };
+ }
+ }
+
+ if (keyword.startsWith('=')) {
+ if (isNumber(compareString)) {
+ return {
+ type: 'equals',
+ text: Number(keyword.slice(1).trim())
+ };
+ }
+ }
+
+ if (isNumber(compareString)) {
+ return {
+ type: 'containsNumber',
+ text: compareString
+ };
+ }
+
+ if (keyword.startsWith('!=')) {
+ if (isNumber(compareString)) {
+ return {
+ type: 'notEquals',
+ text: Number(keyword.slice(2).trim())
+ };
+ }
+ }
+
+ if (keyword.split(':').length === 2) {
+ compareString = keyword.split(':');
+ return {
+ type: 'range',
+ text: compareString.map(v => v.trim())
+ };
+ }
+
+ return {
+ type: 'contains',
+ text: compareString.toLowerCase()
+ };
+}
+
+function getDefaultOptions(instance) {
+ return {
+ columns: [],
+ data: [],
+ dropdownButton: icons.chevronDown,
+ headerDropdown: [
+ {
+ label: instance.translate('Sort Ascending'),
+ action: function (column) {
+ this.sortColumn(column.colIndex, 'asc');
+ }
+ },
+ {
+ label: instance.translate('Sort Descending'),
+ action: function (column) {
+ this.sortColumn(column.colIndex, 'desc');
+ }
+ },
+ {
+ label: instance.translate('Reset sorting'),
+ action: function (column) {
+ this.sortColumn(column.colIndex, 'none');
+ }
+ },
+ {
+ label: instance.translate('Remove column'),
+ action: function (column) {
+ this.removeColumn(column.colIndex);
+ }
+ }
+ ],
+ events: {
+ onRemoveColumn(column) {},
+ onSwitchColumn(column1, column2) {},
+ onSortColumn(column) {},
+ onCheckRow(row) {},
+ onDestroy() {}
+ },
+ hooks: {
+ columnTotal: null
+ },
+ sortIndicator: {
+ asc: '↑',
+ desc: '↓',
+ none: ''
+ },
+ overrideComponents: {
+ // ColumnManager: CustomColumnManager
+ },
+ filterRows: filterRows,
+ freezeMessage: '',
+ getEditor: null,
+ serialNoColumn: true,
+ checkboxColumn: false,
+ clusterize: true,
+ logs: false,
+ layout: 'fixed', // fixed, fluid, ratio
+ noDataMessage: instance.translate('No Data'),
+ cellHeight: 40,
+ minimumColumnWidth: 30,
+ inlineFilters: false,
+ treeView: false,
+ checkedRowStatus: true,
+ dynamicRowHeight: false,
+ pasteFromClipboard: false,
+ showTotalRow: false,
+ direction: 'ltr',
+ disableReorderColumn: false
+ };
+}
+
+let defaultComponents = {
+ DataManager,
+ CellManager,
+ ColumnManager,
+ RowManager,
+ BodyRenderer,
+ Style,
+ Keyboard
+};
+
+class DataTable {
+ constructor(wrapper, options) {
+ DataTable.instances++;
+
+ if (typeof wrapper === 'string') {
+ // css selector
+ wrapper = document.querySelector(wrapper);
+ }
+ this.wrapper = wrapper;
+ if (!(this.wrapper instanceof HTMLElement)) {
+ throw new Error('Invalid argument given for `wrapper`');
+ }
+
+ this.initializeTranslations(options);
+ this.setDefaultOptions();
+ this.buildOptions(options);
+ this.prepare();
+ this.initializeComponents();
+
+ if (this.options.data) {
+ this.refresh();
+ this.columnmanager.applyDefaultSortOrder();
+ }
+ }
+
+ initializeTranslations(options) {
+ this.language = options.language || 'en';
+ this.translationManager = new TranslationManager(this.language);
+
+ if (options.translations) {
+ this.translationManager.addTranslations(options.translations);
+ }
+ }
+
+ setDefaultOptions() {
+ this.DEFAULT_OPTIONS = getDefaultOptions(this);
+ }
+
+ buildOptions(options) {
+ this.options = this.options || {};
+
+ this.options = Object.assign(
+ {}, this.DEFAULT_OPTIONS,
+ this.options || {}, options
+ );
+
+ options.headerDropdown = options.headerDropdown || [];
+ this.options.headerDropdown = [
+ ...this.DEFAULT_OPTIONS.headerDropdown,
+ ...options.headerDropdown
+ ];
+
+ // custom user events
+ this.events = Object.assign(
+ {}, this.DEFAULT_OPTIONS.events,
+ this.options.events || {},
+ options.events || {}
+ );
+ this.fireEvent = this.fireEvent.bind(this);
+ }
+
+ prepare() {
+ this.prepareDom();
+ this.unfreeze();
+ }
+
+ initializeComponents() {
+ let components = Object.assign({}, defaultComponents, this.options.overrideComponents);
+ let {
+ Style: Style$$1,
+ Keyboard: Keyboard$$1,
+ DataManager: DataManager$$1,
+ RowManager: RowManager$$1,
+ ColumnManager: ColumnManager$$1,
+ CellManager: CellManager$$1,
+ BodyRenderer: BodyRenderer$$1
+ } = components;
+
+ this.style = new Style$$1(this);
+ this.keyboard = new Keyboard$$1(this.wrapper);
+ this.datamanager = new DataManager$$1(this.options);
+ this.rowmanager = new RowManager$$1(this);
+ this.columnmanager = new ColumnManager$$1(this);
+ this.cellmanager = new CellManager$$1(this);
+ this.bodyRenderer = new BodyRenderer$$1(this);
+ }
+
+ prepareDom() {
+ this.wrapper.innerHTML = `
+
+
+
+
+
+
+ ${this.options.freezeMessage}
+
+
+
+
+
+
+ `;
+
+ this.datatableWrapper = $('.datatable', this.wrapper);
+ this.header = $('.dt-header', this.wrapper);
+ this.footer = $('.dt-footer', this.wrapper);
+ this.bodyScrollable = $('.dt-scrollable', this.wrapper);
+ this.freezeContainer = $('.dt-freeze', this.wrapper);
+ this.toastMessage = $('.dt-toast', this.wrapper);
+ this.pasteTarget = $('.dt-paste-target', this.wrapper);
+ this.dropdownContainer = $('.dt-dropdown-container', this.wrapper);
+ }
+
+ refresh(data, columns) {
+ this.datamanager.init(data, columns);
+ this.render();
+ this.setDimensions();
+ }
+
+ destroy() {
+ this.wrapper.innerHTML = '';
+ this.style.destroy();
+ this.fireEvent('onDestroy');
+ }
+
+ appendRows(rows) {
+ this.datamanager.appendRows(rows);
+ this.rowmanager.refreshRows();
+ }
+
+ refreshRow(row, rowIndex) {
+ this.rowmanager.refreshRow(row, rowIndex);
+ }
+
+ render() {
+ this.renderHeader();
+ this.renderBody();
+ }
+
+ renderHeader() {
+ this.columnmanager.renderHeader();
+ }
+
+ renderBody() {
+ this.bodyRenderer.render();
+ }
+
+ setDimensions() {
+ this.style.setDimensions();
+ }
+
+ showToastMessage(message, hideAfter) {
+ this.bodyRenderer.showToastMessage(message, hideAfter);
+ }
+
+ clearToastMessage() {
+ this.bodyRenderer.clearToastMessage();
+ }
+
+ getColumn(colIndex) {
+ return this.datamanager.getColumn(colIndex);
+ }
+
+ getColumns() {
+ return this.datamanager.getColumns();
+ }
+
+ getRows() {
+ return this.datamanager.getRows();
+ }
+
+ getCell(colIndex, rowIndex) {
+ return this.datamanager.getCell(colIndex, rowIndex);
+ }
+
+ getColumnHeaderElement(colIndex) {
+ return this.columnmanager.getColumnHeaderElement(colIndex);
+ }
+
+ getViewportHeight() {
+ if (!this.viewportHeight) {
+ this.viewportHeight = $.style(this.bodyScrollable, 'height');
+ }
+
+ return this.viewportHeight;
+ }
+
+ sortColumn(colIndex, sortOrder) {
+ this.columnmanager.sortColumn(colIndex, sortOrder);
+ }
+
+ removeColumn(colIndex) {
+ this.columnmanager.removeColumn(colIndex);
+ }
+
+ scrollToLastColumn() {
+ this.datatableWrapper.scrollLeft = 9999;
+ }
+
+ freeze() {
+ $.style(this.freezeContainer, {
+ display: ''
+ });
+ }
+
+ unfreeze() {
+ $.style(this.freezeContainer, {
+ display: 'none'
+ });
+ }
+
+ updateOptions(options) {
+ this.buildOptions(options);
+ }
+
+ fireEvent(eventName, ...args) {
+ // fire internalEventHandlers if any
+ // and then user events
+ const handlers = [
+ ...(this._internalEventHandlers[eventName] || []),
+ this.events[eventName]
+ ].filter(Boolean);
+
+ for (let handler of handlers) {
+ handler.apply(this, args);
+ }
+ }
+
+ on(event, handler) {
+ this._internalEventHandlers = this._internalEventHandlers || {};
+ this._internalEventHandlers[event] = this._internalEventHandlers[event] || [];
+ this._internalEventHandlers[event].push(handler);
+ }
+
+ log() {
+ if (this.options.logs) {
+ console.log.apply(console, arguments);
+ }
+ }
+
+ translate(str, args) {
+ return this.translationManager.translate(str, args);
+ }
+}
+
+DataTable.instances = 0;
+
+var name = "influxframework-datatable";
+var version = "0.0.0-development";
+var description = "A modern datatable library for the web";
+var main = "dist/influxframework-datatable.cjs.js";
+var unpkg = "dist/influxframework-datatable.min.js";
+var jsdelivr = "dist/influxframework-datatable.min.js";
+var scripts = {"start":"yarn run dev","build":"rollup -c && NODE_ENV=production rollup -c","dev":"rollup -c -w","cy:server":"http-server -p 8989","cy:open":"cypress open","cy:run":"cypress run","test":"start-server-and-test cy:server http://localhost:8989 cy:run","test-local":"start-server-and-test cy:server http://localhost:8989 cy:open","travis-deploy-once":"travis-deploy-once","semantic-release":"semantic-release","lint":"eslint src","lint-and-build":"yarn lint && yarn build","commit":"npx git-cz"};
+var files = ["dist","src"];
+var devDependencies = {"autoprefixer":"^9.0.0","chai":"3.5.0","cypress":"^9.2.0","cz-conventional-changelog":"^2.1.0","deepmerge":"^2.0.1","eslint":"^5.0.1","eslint-config-airbnb":"^16.1.0","eslint-config-airbnb-base":"^12.1.0","eslint-plugin-import":"^2.11.0","http-server":"^0.11.1","mocha":"3.3.0","postcss-custom-properties":"^7.0.0","postcss-nested":"^3.0.0","rollup":"^0.59.4","rollup-plugin-commonjs":"^8.3.0","rollup-plugin-eslint":"^4.0.0","rollup-plugin-json":"^2.3.0","rollup-plugin-node-resolve":"^3.0.3","rollup-plugin-postcss":"^1.2.8","rollup-plugin-uglify-es":"^0.0.1","semantic-release":"^17.1.1","start-server-and-test":"^1.4.1","travis-deploy-once":"^5.0.1"};
+var repository = {"type":"git","url":"https://github.com/influxframework/datatable.git"};
+var keywords = ["datatable","data","grid","table"];
+var author = "Faris Ansari";
+var license = "MIT";
+var bugs = {"url":"https://github.com/influxframework/datatable/issues"};
+var homepage = "https://influxframework.com/datatable";
+var dependencies = {"hyperlist":"^1.0.0-beta","lodash":"^4.17.5","sortablejs":"^1.7.0"};
+var config = {"commitizen":{"path":"cz-conventional-changelog"}};
+var packageJson = {
+ name: name,
+ version: version,
+ description: description,
+ main: main,
+ unpkg: unpkg,
+ jsdelivr: jsdelivr,
+ scripts: scripts,
+ files: files,
+ devDependencies: devDependencies,
+ repository: repository,
+ keywords: keywords,
+ author: author,
+ license: license,
+ bugs: bugs,
+ homepage: homepage,
+ dependencies: dependencies,
+ config: config
+};
+
+DataTable.__version__ = packageJson.version;
+
+module.exports = DataTable;
diff --git a/dist/influxframework-datatable.css b/dist/influxframework-datatable.css
new file mode 100644
index 0000000..815b1df
--- /dev/null
+++ b/dist/influxframework-datatable.css
@@ -0,0 +1,327 @@
+:root {
+ --dt-border-color: #d1d8dd;
+ --dt-primary-color: rgb(82, 146, 247);
+ --dt-light-bg: #f5f7fa;
+ --dt-light-red: #FD8B8B;
+ --dt-light-yellow: #fffce7;
+ --dt-orange: rgb(255, 160, 10);
+ --dt-text-color: #000000;
+ --dt-text-light: #dfe2e5;
+ --dt-spacer-1: 0.25rem;
+ --dt-spacer-2: 0.5rem;
+ --dt-spacer-3: 1rem;
+ --dt-border-radius: 3px;
+ --dt-cell-bg: #fff;
+ --dt-focus-border-width: 2px;
+ --dt-selection-highlight-color: #fffce7;
+ --dt-selection-highlight-color: var(--dt-light-yellow);
+ --dt-toast-message-border: none;
+ --dt-header-cell-bg: #fff;
+ --dt-header-cell-bg: var(--dt-cell-bg);
+}
+
+.datatable *, .datatable *::after, .datatable *::before {
+ box-sizing: border-box;
+ }
+
+.datatable {
+ position: relative;
+ overflow: hidden;
+}
+
+.dt-scrollable {
+ height: 40vw;
+ overflow: auto;
+ border-top: 2px solid #d1d8dd;
+ border-top: 2px solid var(--dt-border-color);
+}
+
+.dt-scrollable--highlight-all {
+ background-color: #fffce7;
+ background-color: var(--dt-selection-highlight-color);
+ }
+
+.dt-scrollable__no-data {
+ text-align: center;
+ padding: 1rem;
+ padding: var(--dt-spacer-3);
+ border-left: 1px solid #d1d8dd;
+ border-left: 1px solid var(--dt-border-color);
+ border-right: 1px solid #d1d8dd;
+ border-right: 1px solid var(--dt-border-color);
+ }
+
+.dt-row {
+ display: flex;
+}
+
+.dt-row--highlight .dt-cell {
+ background-color: #fffce7;
+ background-color: var(--dt-selection-highlight-color);
+ }
+
+.dt-row--unhighlight .dt-cell {
+ background-color: #fff;
+ background-color: var(--dt-cell-bg);
+ }
+
+.dt-row--hide {
+ display: none;
+ }
+
+.dt-row:last-child:not(.dt-row-filter) {
+ border-bottom: 1px solid #d1d8dd;
+ border-bottom: 1px solid var(--dt-border-color);
+ }
+
+.dt-cell {
+ border: 1px solid #d1d8dd;
+ border: 1px solid var(--dt-border-color);
+ border-bottom: none;
+ border-right: none;
+ position: relative;
+ outline: none;
+ padding: 0;
+ background-color: #fff;
+ background-color: var(--dt-cell-bg);
+ color: #000000;
+ color: var(--dt-text-color);
+ /*
+ Fix for firefox and Edge
+ https://stackoverflow.com/a/16337203
+ firefox paints td background over border
+ */
+ background-clip: padding-box;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+}
+
+.dt-cell__content {
+ padding: 0.5rem;
+ padding: var(--dt-spacer-2);
+ border: 2px solid transparent;
+ border: var(--dt-focus-border-width) solid transparent;
+ height: 100%;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ }
+
+.dt-cell__edit {
+ display: none;
+ padding: 0.5rem;
+ padding: var(--dt-spacer-2);
+ background-color: #fff;
+ background-color: var(--dt-cell-bg);
+ border: 2px solid rgb(255, 160, 10);
+ border: var(--dt-focus-border-width) solid var(--dt-orange);
+ z-index: 1;
+ height: 100%;
+ }
+
+.dt-cell__resize-handle {
+ opacity: 0;
+ position: absolute;
+ right: -3px;
+ top: 0;
+ width: 5px;
+ height: 100%;
+ cursor: col-resize;
+ z-index: 1;
+ }
+
+.dt-cell--editing .dt-cell__content {
+ display: none;
+ }
+
+.dt-cell--editing .dt-cell__edit {
+ display: block;
+ }
+
+.dt-cell--focus .dt-cell__content {
+ border-color: rgb(82, 146, 247);
+ border-color: var(--dt-primary-color);
+ }
+
+.dt-cell--highlight {
+ background-color: #f5f7fa;
+ background-color: var(--dt-light-bg);
+ }
+
+.dt-cell--dragging {
+ background-color: #f5f7fa;
+ background-color: var(--dt-light-bg);
+ }
+
+.dt-cell--header {
+ background-color: #fff;
+ background-color: var(--dt-header-cell-bg);
+ }
+
+.dt-cell--header:last-child {
+ border-right: 1px solid #d1d8dd;
+ border-right: 1px solid var(--dt-border-color);
+ }
+
+.dt-cell--header .dt-cell__content {
+ padding-right: 1rem;
+ padding-right: var(--dt-spacer-3);
+ font-weight: bold;
+ }
+
+.dt-cell--header:hover .dt-dropdown__toggle {
+ opacity: 1;
+ }
+
+.dt-cell--tree-close .icon-open {
+ display: none;
+ }
+
+.dt-cell--tree-close .icon-close {
+ display: flex;
+ }
+
+.dt-cell:last-child {
+ border-right: 1px solid #d1d8dd;
+ border-right: 1px solid var(--dt-border-color);
+ }
+
+.datatable[dir=rtl] .dt-cell__resize-handle {
+ right: unset;
+ left: -3px;
+}
+
+.icon-open, .icon-close {
+ width: 16px;
+ height: 16px;
+}
+
+.icon-open {
+ display: flex;
+}
+
+.icon-close {
+ display: none;
+}
+
+.dt-dropdown {
+ position: absolute;
+ right: 10px;
+ display: inline-flex;
+ vertical-align: top;
+ text-align: left;
+ font-weight: normal;
+ cursor: pointer;
+}
+
+.dt-dropdown__toggle {
+ opacity: 0;
+ background-color: #fff;
+ background-color: var(--dt-header-cell-bg);
+ }
+
+.dt-dropdown__list {
+ position: fixed;
+ min-width: 8rem;
+ z-index: 1;
+ cursor: pointer;
+ background-color: #fff;
+ background-color: var(--dt-cell-bg);
+ border-radius: 3px;
+ border-radius: var(--dt-border-radius);
+ padding: 0.5rem 0;
+ padding: var(--dt-spacer-2) 0;
+ box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
+ }
+
+.dt-dropdown__list-item {
+ padding: 0.5rem 1rem;
+ padding: var(--dt-spacer-2) var(--dt-spacer-3);
+ }
+
+.dt-dropdown__list-item:hover {
+ background-color: #f5f7fa;
+ background-color: var(--dt-light-bg);
+ }
+
+.dt-dropdown--active .dt-dropdown__list {
+ display: block;
+ }
+
+.dt-tree-node {
+ display: flex;
+ align-items: center;
+ position: relative;
+}
+
+.dt-tree-node__toggle {
+ display: inline-block;
+ cursor: pointer;
+ margin-right: 0.2rem;
+ }
+
+.dt-toast {
+ position: absolute;
+ bottom: 1rem;
+ bottom: var(--dt-spacer-3);
+ left: 50%;
+ transform: translateX(-50%);
+}
+
+.dt-toast__message {
+ display: inline-block;
+ background-color: rgba(0, 0, 0, 0.8);
+ color: #dfe2e5;
+ color: var(--dt-text-light);
+ border-radius: 3px;
+ border-radius: var(--dt-border-radius);
+ padding: 0.5rem 1rem;
+ padding: var(--dt-spacer-2) var(--dt-spacer-3);
+ border: none;
+ border: var(--dt-toast-message-border);
+ }
+
+.dt-input {
+ outline: none;
+ width: 100%;
+ border: none;
+ overflow: visible;
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+ background-color: inherit;
+ color: inherit;
+ margin: 0;
+ padding: 0;
+}
+
+.dt-freeze {
+ display: flex;
+ justify-content: center;
+ align-content: center;
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background-color: #f5f7fa;
+ background-color: var(--dt-light-bg);
+ opacity: 0.5;
+ font-size: 2em;
+}
+
+.dt-freeze__message {
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ }
+
+.dt-paste-target {
+ position: fixed;
+ left: -999em;
+}
+
+body.dt-resize {
+ cursor: col-resize;
+}
diff --git a/dist/influxframework-datatable.js b/dist/influxframework-datatable.js
new file mode 100644
index 0000000..494a63a
--- /dev/null
+++ b/dist/influxframework-datatable.js
@@ -0,0 +1,6404 @@
+var DataTable = (function (Sortable) {
+ 'use strict';
+
+ Sortable = Sortable && Sortable.hasOwnProperty('default') ? Sortable['default'] : Sortable;
+
+ function $(expr, con) {
+ return typeof expr === 'string' ?
+ (con || document).querySelector(expr) :
+ expr || null;
+ }
+
+ $.each = (expr, con) => {
+ return typeof expr === 'string' ?
+ Array.from((con || document).querySelectorAll(expr)) :
+ expr || null;
+ };
+
+ $.create = (tag, o) => {
+ let element = document.createElement(tag);
+
+ for (let i in o) {
+ let val = o[i];
+
+ if (i === 'inside') {
+ $(val).appendChild(element);
+ } else
+ if (i === 'around') {
+ let ref = $(val);
+ ref.parentNode.insertBefore(element, ref);
+ element.appendChild(ref);
+ } else
+ if (i === 'styles') {
+ if (typeof val === 'object') {
+ Object.keys(val).map(prop => {
+ element.style[prop] = val[prop];
+ });
+ }
+ } else
+ if (i in element) {
+ element[i] = val;
+ } else {
+ element.setAttribute(i, val);
+ }
+ }
+
+ return element;
+ };
+
+ $.on = (element, event, selector, callback) => {
+ if (!callback) {
+ callback = selector;
+ $.bind(element, event, callback);
+ } else {
+ $.delegate(element, event, selector, callback);
+ }
+ };
+
+ $.off = (element, event, handler) => {
+ element.removeEventListener(event, handler);
+ };
+
+ $.bind = (element, event, callback) => {
+ event.split(/\s+/).forEach(function (event) {
+ element.addEventListener(event, callback);
+ });
+ };
+
+ $.delegate = (element, event, selector, callback) => {
+ element.addEventListener(event, function (e) {
+ const delegatedTarget = e.target.closest(selector);
+ if (delegatedTarget) {
+ e.delegatedTarget = delegatedTarget;
+ callback.call(this, e, delegatedTarget);
+ }
+ });
+ };
+
+ $.unbind = (element, o) => {
+ if (element) {
+ for (let event in o) {
+ let callback = o[event];
+
+ event.split(/\s+/).forEach(function (event) {
+ element.removeEventListener(event, callback);
+ });
+ }
+ }
+ };
+
+ $.fire = (target, type, properties) => {
+ let evt = document.createEvent('HTMLEvents');
+
+ evt.initEvent(type, true, true);
+
+ for (let j in properties) {
+ evt[j] = properties[j];
+ }
+
+ return target.dispatchEvent(evt);
+ };
+
+ $.data = (element, attrs) => { // eslint-disable-line
+ if (!attrs) {
+ return element.dataset;
+ }
+
+ for (const attr in attrs) {
+ element.dataset[attr] = attrs[attr];
+ }
+ };
+
+ $.style = (elements, styleMap) => { // eslint-disable-line
+
+ if (typeof styleMap === 'string') {
+ return $.getStyle(elements, styleMap);
+ }
+
+ if (!Array.isArray(elements)) {
+ elements = [elements];
+ }
+
+ elements.map(element => {
+ for (const prop in styleMap) {
+ element.style[prop] = styleMap[prop];
+ }
+ });
+ };
+
+ $.removeStyle = (elements, styleProps) => {
+ if (!Array.isArray(elements)) {
+ elements = [elements];
+ }
+
+ if (!Array.isArray(styleProps)) {
+ styleProps = [styleProps];
+ }
+
+ elements.map(element => {
+ for (const prop of styleProps) {
+ element.style[prop] = '';
+ }
+ });
+ };
+
+ $.getStyle = (element, prop) => {
+ if (!prop) {
+ return getComputedStyle(element);
+ }
+
+ let val = getComputedStyle(element)[prop];
+
+ if (['width', 'height'].includes(prop)) {
+ val = parseFloat(val);
+ }
+
+ return val;
+ };
+
+ $.closest = (selector, element) => {
+ if (!element) return null;
+
+ if (element.matches(selector)) {
+ return element;
+ }
+
+ return $.closest(selector, element.parentNode);
+ };
+
+ $.inViewport = (el, parentEl) => {
+ const {
+ top,
+ left,
+ bottom,
+ right
+ } = el.getBoundingClientRect();
+ const {
+ top: pTop,
+ left: pLeft,
+ bottom: pBottom,
+ right: pRight
+ } = parentEl.getBoundingClientRect();
+
+ return top >= pTop && left >= pLeft && bottom <= pBottom && right <= pRight;
+ };
+
+ $.scrollTop = function scrollTop(element, pixels) {
+ requestAnimationFrame(() => {
+ element.scrollTop = pixels;
+ });
+ };
+
+ $.scrollbarSize = function scrollbarSize() {
+ if (!$.scrollBarSizeValue) {
+ $.scrollBarSizeValue = getScrollBarSize();
+ }
+ return $.scrollBarSizeValue;
+ };
+
+ function getScrollBarSize() {
+ // assume scrollbar width and height would be the same
+
+ // Create the measurement node
+ const scrollDiv = document.createElement('div');
+ $.style(scrollDiv, {
+ width: '100px',
+ height: '100px',
+ overflow: 'scroll',
+ position: 'absolute',
+ top: '-9999px'
+ });
+ document.body.appendChild(scrollDiv);
+
+ // Get the scrollbar width
+ const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
+
+ // Delete the DIV
+ document.body.removeChild(scrollDiv);
+
+ return scrollbarWidth;
+ }
+
+ $.hasVerticalOverflow = function (element) {
+ return element.scrollHeight > element.offsetHeight + 10;
+ };
+
+ $.hasHorizontalOverflow = function (element) {
+ return element.scrollWidth > element.offsetWidth + 10;
+ };
+
+ $.measureTextWidth = function (text) {
+ const div = document.createElement('div');
+ div.style.position = 'absolute';
+ div.style.visibility = 'hidden';
+ div.style.height = 'auto';
+ div.style.width = 'auto';
+ div.style.whiteSpace = 'nowrap';
+ div.innerText = text;
+ document.body.appendChild(div);
+ return div.clientWidth + 1;
+ };
+
+ /**
+ * 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');
+ }
+
+ var isObject_1 = isObject;
+
+ var isObject$1 = /*#__PURE__*/Object.freeze({
+ default: isObject_1,
+ __moduleExports: isObject_1
+ });
+
+ var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+ function commonjsRequire () {
+ throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs');
+ }
+
+ function unwrapExports (x) {
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
+ }
+
+ function createCommonjsModule(fn, module) {
+ return module = { exports: {} }, fn(module, module.exports), module.exports;
+ }
+
+ /** Detect free variable `global` from Node.js. */
+ var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
+
+ var _freeGlobal = freeGlobal;
+
+ var _freeGlobal$1 = /*#__PURE__*/Object.freeze({
+ default: _freeGlobal,
+ __moduleExports: _freeGlobal
+ });
+
+ var freeGlobal$1 = ( _freeGlobal$1 && _freeGlobal ) || _freeGlobal$1;
+
+ /** 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$1 || freeSelf || Function('return this')();
+
+ var _root = root;
+
+ var _root$1 = /*#__PURE__*/Object.freeze({
+ default: _root,
+ __moduleExports: _root
+ });
+
+ var root$1 = ( _root$1 && _root ) || _root$1;
+
+ /**
+ * Gets the timestamp of the number of milliseconds that have elapsed since
+ * the Unix epoch (1 January 1970 00:00:00 UTC).
+ *
+ * @static
+ * @memberOf _
+ * @since 2.4.0
+ * @category Date
+ * @returns {number} Returns the timestamp.
+ * @example
+ *
+ * _.defer(function(stamp) {
+ * console.log(_.now() - stamp);
+ * }, _.now());
+ * // => Logs the number of milliseconds it took for the deferred invocation.
+ */
+ var now = function() {
+ return root$1.Date.now();
+ };
+
+ var now_1 = now;
+
+ var now$1 = /*#__PURE__*/Object.freeze({
+ default: now_1,
+ __moduleExports: now_1
+ });
+
+ /** Used to match a single whitespace character. */
+ var reWhitespace = /\s/;
+
+ /**
+ * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
+ * character of `string`.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @returns {number} Returns the index of the last non-whitespace character.
+ */
+ function trimmedEndIndex(string) {
+ var index = string.length;
+
+ while (index-- && reWhitespace.test(string.charAt(index))) {}
+ return index;
+ }
+
+ var _trimmedEndIndex = trimmedEndIndex;
+
+ var _trimmedEndIndex$1 = /*#__PURE__*/Object.freeze({
+ default: _trimmedEndIndex,
+ __moduleExports: _trimmedEndIndex
+ });
+
+ var trimmedEndIndex$1 = ( _trimmedEndIndex$1 && _trimmedEndIndex ) || _trimmedEndIndex$1;
+
+ /** Used to match leading whitespace. */
+ var reTrimStart = /^\s+/;
+
+ /**
+ * The base implementation of `_.trim`.
+ *
+ * @private
+ * @param {string} string The string to trim.
+ * @returns {string} Returns the trimmed string.
+ */
+ function baseTrim(string) {
+ return string
+ ? string.slice(0, trimmedEndIndex$1(string) + 1).replace(reTrimStart, '')
+ : string;
+ }
+
+ var _baseTrim = baseTrim;
+
+ var _baseTrim$1 = /*#__PURE__*/Object.freeze({
+ default: _baseTrim,
+ __moduleExports: _baseTrim
+ });
+
+ /** Built-in value references. */
+ var Symbol = root$1.Symbol;
+
+ var _Symbol = Symbol;
+
+ var _Symbol$1 = /*#__PURE__*/Object.freeze({
+ default: _Symbol,
+ __moduleExports: _Symbol
+ });
+
+ var Symbol$1 = ( _Symbol$1 && _Symbol ) || _Symbol$1;
+
+ /** 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$1 ? Symbol$1.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;
+ } catch (e) {}
+
+ var result = nativeObjectToString.call(value);
+ {
+ if (isOwn) {
+ value[symToStringTag] = tag;
+ } else {
+ delete value[symToStringTag];
+ }
+ }
+ return result;
+ }
+
+ var _getRawTag = getRawTag;
+
+ var _getRawTag$1 = /*#__PURE__*/Object.freeze({
+ default: _getRawTag,
+ __moduleExports: _getRawTag
+ });
+
+ /** 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);
+ }
+
+ var _objectToString = objectToString;
+
+ var _objectToString$1 = /*#__PURE__*/Object.freeze({
+ default: _objectToString,
+ __moduleExports: _objectToString
+ });
+
+ var getRawTag$1 = ( _getRawTag$1 && _getRawTag ) || _getRawTag$1;
+
+ var objectToString$1 = ( _objectToString$1 && _objectToString ) || _objectToString$1;
+
+ /** `Object#toString` result references. */
+ var nullTag = '[object Null]',
+ undefinedTag = '[object Undefined]';
+
+ /** Built-in value references. */
+ var symToStringTag$1 = Symbol$1 ? Symbol$1.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$1(value)
+ : objectToString$1(value);
+ }
+
+ var _baseGetTag = baseGetTag;
+
+ var _baseGetTag$1 = /*#__PURE__*/Object.freeze({
+ default: _baseGetTag,
+ __moduleExports: _baseGetTag
+ });
+
+ /**
+ * 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';
+ }
+
+ var isObjectLike_1 = isObjectLike;
+
+ var isObjectLike$1 = /*#__PURE__*/Object.freeze({
+ default: isObjectLike_1,
+ __moduleExports: isObjectLike_1
+ });
+
+ var baseGetTag$1 = ( _baseGetTag$1 && _baseGetTag ) || _baseGetTag$1;
+
+ var isObjectLike$2 = ( isObjectLike$1 && isObjectLike_1 ) || isObjectLike$1;
+
+ /** `Object#toString` result references. */
+ var symbolTag = '[object Symbol]';
+
+ /**
+ * Checks if `value` is classified as a `Symbol` primitive or object.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
+ * @example
+ *
+ * _.isSymbol(Symbol.iterator);
+ * // => true
+ *
+ * _.isSymbol('abc');
+ * // => false
+ */
+ function isSymbol(value) {
+ return typeof value == 'symbol' ||
+ (isObjectLike$2(value) && baseGetTag$1(value) == symbolTag);
+ }
+
+ var isSymbol_1 = isSymbol;
+
+ var isSymbol$1 = /*#__PURE__*/Object.freeze({
+ default: isSymbol_1,
+ __moduleExports: isSymbol_1
+ });
+
+ var baseTrim$1 = ( _baseTrim$1 && _baseTrim ) || _baseTrim$1;
+
+ var isObject$2 = ( isObject$1 && isObject_1 ) || isObject$1;
+
+ var isSymbol$2 = ( isSymbol$1 && isSymbol_1 ) || isSymbol$1;
+
+ /** Used as references for various `Number` constants. */
+ var NAN = 0 / 0;
+
+ /** Used to detect bad signed hexadecimal string values. */
+ var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
+
+ /** Used to detect binary string values. */
+ var reIsBinary = /^0b[01]+$/i;
+
+ /** Used to detect octal string values. */
+ var reIsOctal = /^0o[0-7]+$/i;
+
+ /** Built-in method references without a dependency on `root`. */
+ var freeParseInt = parseInt;
+
+ /**
+ * Converts `value` to a number.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to process.
+ * @returns {number} Returns the number.
+ * @example
+ *
+ * _.toNumber(3.2);
+ * // => 3.2
+ *
+ * _.toNumber(Number.MIN_VALUE);
+ * // => 5e-324
+ *
+ * _.toNumber(Infinity);
+ * // => Infinity
+ *
+ * _.toNumber('3.2');
+ * // => 3.2
+ */
+ function toNumber(value) {
+ if (typeof value == 'number') {
+ return value;
+ }
+ if (isSymbol$2(value)) {
+ return NAN;
+ }
+ if (isObject$2(value)) {
+ var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
+ value = isObject$2(other) ? (other + '') : other;
+ }
+ if (typeof value != 'string') {
+ return value === 0 ? value : +value;
+ }
+ value = baseTrim$1(value);
+ var isBinary = reIsBinary.test(value);
+ return (isBinary || reIsOctal.test(value))
+ ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
+ : (reIsBadHex.test(value) ? NAN : +value);
+ }
+
+ var toNumber_1 = toNumber;
+
+ var toNumber$1 = /*#__PURE__*/Object.freeze({
+ default: toNumber_1,
+ __moduleExports: toNumber_1
+ });
+
+ var now$2 = ( now$1 && now_1 ) || now$1;
+
+ var toNumber$2 = ( toNumber$1 && toNumber_1 ) || toNumber$1;
+
+ /** Error message constants. */
+ var FUNC_ERROR_TEXT = 'Expected a function';
+
+ /* Built-in method references for those with the same name as other `lodash` methods. */
+ var nativeMax = Math.max,
+ nativeMin = Math.min;
+
+ /**
+ * Creates a debounced function that delays invoking `func` until after `wait`
+ * milliseconds have elapsed since the last time the debounced function was
+ * invoked. The debounced function comes with a `cancel` method to cancel
+ * delayed `func` invocations and a `flush` method to immediately invoke them.
+ * Provide `options` to indicate whether `func` should be invoked on the
+ * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
+ * with the last arguments provided to the debounced function. Subsequent
+ * calls to the debounced function return the result of the last `func`
+ * invocation.
+ *
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is
+ * invoked on the trailing edge of the timeout only if the debounced function
+ * is invoked more than once during the `wait` timeout.
+ *
+ * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
+ * until to the next tick, similar to `setTimeout` with a timeout of `0`.
+ *
+ * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
+ * for details over the differences between `_.debounce` and `_.throttle`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to debounce.
+ * @param {number} [wait=0] The number of milliseconds to delay.
+ * @param {Object} [options={}] The options object.
+ * @param {boolean} [options.leading=false]
+ * Specify invoking on the leading edge of the timeout.
+ * @param {number} [options.maxWait]
+ * The maximum time `func` is allowed to be delayed before it's invoked.
+ * @param {boolean} [options.trailing=true]
+ * Specify invoking on the trailing edge of the timeout.
+ * @returns {Function} Returns the new debounced function.
+ * @example
+ *
+ * // Avoid costly calculations while the window size is in flux.
+ * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
+ *
+ * // Invoke `sendMail` when clicked, debouncing subsequent calls.
+ * jQuery(element).on('click', _.debounce(sendMail, 300, {
+ * 'leading': true,
+ * 'trailing': false
+ * }));
+ *
+ * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
+ * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
+ * var source = new EventSource('/stream');
+ * jQuery(source).on('message', debounced);
+ *
+ * // Cancel the trailing debounced invocation.
+ * jQuery(window).on('popstate', debounced.cancel);
+ */
+ function debounce(func, wait, options) {
+ var lastArgs,
+ lastThis,
+ maxWait,
+ result,
+ timerId,
+ lastCallTime,
+ lastInvokeTime = 0,
+ leading = false,
+ maxing = false,
+ trailing = true;
+
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ wait = toNumber$2(wait) || 0;
+ if (isObject$2(options)) {
+ leading = !!options.leading;
+ maxing = 'maxWait' in options;
+ maxWait = maxing ? nativeMax(toNumber$2(options.maxWait) || 0, wait) : maxWait;
+ trailing = 'trailing' in options ? !!options.trailing : trailing;
+ }
+
+ function invokeFunc(time) {
+ var args = lastArgs,
+ thisArg = lastThis;
+
+ lastArgs = lastThis = undefined;
+ lastInvokeTime = time;
+ result = func.apply(thisArg, args);
+ return result;
+ }
+
+ function leadingEdge(time) {
+ // Reset any `maxWait` timer.
+ lastInvokeTime = time;
+ // Start the timer for the trailing edge.
+ timerId = setTimeout(timerExpired, wait);
+ // Invoke the leading edge.
+ return leading ? invokeFunc(time) : result;
+ }
+
+ function remainingWait(time) {
+ var timeSinceLastCall = time - lastCallTime,
+ timeSinceLastInvoke = time - lastInvokeTime,
+ timeWaiting = wait - timeSinceLastCall;
+
+ return maxing
+ ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
+ : timeWaiting;
+ }
+
+ function shouldInvoke(time) {
+ var timeSinceLastCall = time - lastCallTime,
+ timeSinceLastInvoke = time - lastInvokeTime;
+
+ // Either this is the first call, activity has stopped and we're at the
+ // trailing edge, the system time has gone backwards and we're treating
+ // it as the trailing edge, or we've hit the `maxWait` limit.
+ return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
+ (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
+ }
+
+ function timerExpired() {
+ var time = now$2();
+ if (shouldInvoke(time)) {
+ return trailingEdge(time);
+ }
+ // Restart the timer.
+ timerId = setTimeout(timerExpired, remainingWait(time));
+ }
+
+ function trailingEdge(time) {
+ timerId = undefined;
+
+ // Only invoke if we have `lastArgs` which means `func` has been
+ // debounced at least once.
+ if (trailing && lastArgs) {
+ return invokeFunc(time);
+ }
+ lastArgs = lastThis = undefined;
+ return result;
+ }
+
+ function cancel() {
+ if (timerId !== undefined) {
+ clearTimeout(timerId);
+ }
+ lastInvokeTime = 0;
+ lastArgs = lastCallTime = lastThis = timerId = undefined;
+ }
+
+ function flush() {
+ return timerId === undefined ? result : trailingEdge(now$2());
+ }
+
+ function debounced() {
+ var time = now$2(),
+ isInvoking = shouldInvoke(time);
+
+ lastArgs = arguments;
+ lastThis = this;
+ lastCallTime = time;
+
+ if (isInvoking) {
+ if (timerId === undefined) {
+ return leadingEdge(lastCallTime);
+ }
+ if (maxing) {
+ // Handle invocations in a tight loop.
+ clearTimeout(timerId);
+ timerId = setTimeout(timerExpired, wait);
+ return invokeFunc(lastCallTime);
+ }
+ }
+ if (timerId === undefined) {
+ timerId = setTimeout(timerExpired, wait);
+ }
+ return result;
+ }
+ debounced.cancel = cancel;
+ debounced.flush = flush;
+ return debounced;
+ }
+
+ var debounce_1 = debounce;
+
+ var debounce$1 = /*#__PURE__*/Object.freeze({
+ default: debounce_1,
+ __moduleExports: debounce_1
+ });
+
+ var debounce$2 = ( debounce$1 && debounce_1 ) || debounce$1;
+
+ /** Error message constants. */
+ var FUNC_ERROR_TEXT$1 = 'Expected a function';
+
+ /**
+ * Creates a throttled function that only invokes `func` at most once per
+ * every `wait` milliseconds. The throttled function comes with a `cancel`
+ * method to cancel delayed `func` invocations and a `flush` method to
+ * immediately invoke them. Provide `options` to indicate whether `func`
+ * should be invoked on the leading and/or trailing edge of the `wait`
+ * timeout. The `func` is invoked with the last arguments provided to the
+ * throttled function. Subsequent calls to the throttled function return the
+ * result of the last `func` invocation.
+ *
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is
+ * invoked on the trailing edge of the timeout only if the throttled function
+ * is invoked more than once during the `wait` timeout.
+ *
+ * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
+ * until to the next tick, similar to `setTimeout` with a timeout of `0`.
+ *
+ * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
+ * for details over the differences between `_.throttle` and `_.debounce`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to throttle.
+ * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
+ * @param {Object} [options={}] The options object.
+ * @param {boolean} [options.leading=true]
+ * Specify invoking on the leading edge of the timeout.
+ * @param {boolean} [options.trailing=true]
+ * Specify invoking on the trailing edge of the timeout.
+ * @returns {Function} Returns the new throttled function.
+ * @example
+ *
+ * // Avoid excessively updating the position while scrolling.
+ * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
+ *
+ * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
+ * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
+ * jQuery(element).on('click', throttled);
+ *
+ * // Cancel the trailing throttled invocation.
+ * jQuery(window).on('popstate', throttled.cancel);
+ */
+ function throttle(func, wait, options) {
+ var leading = true,
+ trailing = true;
+
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT$1);
+ }
+ if (isObject$2(options)) {
+ leading = 'leading' in options ? !!options.leading : leading;
+ trailing = 'trailing' in options ? !!options.trailing : trailing;
+ }
+ return debounce$2(func, wait, {
+ 'leading': leading,
+ 'maxWait': wait,
+ 'trailing': trailing
+ });
+ }
+
+ var throttle_1 = throttle;
+
+ /** `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$2(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$1(value);
+ return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
+ }
+
+ var isFunction_1 = isFunction;
+
+ var isFunction$1 = /*#__PURE__*/Object.freeze({
+ default: isFunction_1,
+ __moduleExports: isFunction_1
+ });
+
+ /** Used to detect overreaching core-js shims. */
+ var coreJsData = root$1['__core-js_shared__'];
+
+ var _coreJsData = coreJsData;
+
+ var _coreJsData$1 = /*#__PURE__*/Object.freeze({
+ default: _coreJsData,
+ __moduleExports: _coreJsData
+ });
+
+ var coreJsData$1 = ( _coreJsData$1 && _coreJsData ) || _coreJsData$1;
+
+ /** Used to detect methods masquerading as native. */
+ var maskSrcKey = (function() {
+ var uid = /[^.]+$/.exec(coreJsData$1 && coreJsData$1.keys && coreJsData$1.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);
+ }
+
+ var _isMasked = isMasked;
+
+ var _isMasked$1 = /*#__PURE__*/Object.freeze({
+ default: _isMasked,
+ __moduleExports: _isMasked
+ });
+
+ /** 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 '';
+ }
+
+ var _toSource = toSource;
+
+ var _toSource$1 = /*#__PURE__*/Object.freeze({
+ default: _toSource,
+ __moduleExports: _toSource
+ });
+
+ var isFunction$2 = ( isFunction$1 && isFunction_1 ) || isFunction$1;
+
+ var isMasked$1 = ( _isMasked$1 && _isMasked ) || _isMasked$1;
+
+ var toSource$1 = ( _toSource$1 && _toSource ) || _toSource$1;
+
+ /**
+ * 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$2(value) || isMasked$1(value)) {
+ return false;
+ }
+ var pattern = isFunction$2(value) ? reIsNative : reIsHostCtor;
+ return pattern.test(toSource$1(value));
+ }
+
+ var _baseIsNative = baseIsNative;
+
+ var _baseIsNative$1 = /*#__PURE__*/Object.freeze({
+ default: _baseIsNative,
+ __moduleExports: _baseIsNative
+ });
+
+ /**
+ * 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];
+ }
+
+ var _getValue = getValue;
+
+ var _getValue$1 = /*#__PURE__*/Object.freeze({
+ default: _getValue,
+ __moduleExports: _getValue
+ });
+
+ var baseIsNative$1 = ( _baseIsNative$1 && _baseIsNative ) || _baseIsNative$1;
+
+ var getValue$1 = ( _getValue$1 && _getValue ) || _getValue$1;
+
+ /**
+ * 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$1(object, key);
+ return baseIsNative$1(value) ? value : undefined;
+ }
+
+ var _getNative = getNative;
+
+ var _getNative$1 = /*#__PURE__*/Object.freeze({
+ default: _getNative,
+ __moduleExports: _getNative
+ });
+
+ var getNative$1 = ( _getNative$1 && _getNative ) || _getNative$1;
+
+ /* Built-in method references that are verified to be native. */
+ var nativeCreate = getNative$1(Object, 'create');
+
+ var _nativeCreate = nativeCreate;
+
+ var _nativeCreate$1 = /*#__PURE__*/Object.freeze({
+ default: _nativeCreate,
+ __moduleExports: _nativeCreate
+ });
+
+ var nativeCreate$1 = ( _nativeCreate$1 && _nativeCreate ) || _nativeCreate$1;
+
+ /**
+ * Removes all key-value entries from the hash.
+ *
+ * @private
+ * @name clear
+ * @memberOf Hash
+ */
+ function hashClear() {
+ this.__data__ = nativeCreate$1 ? nativeCreate$1(null) : {};
+ this.size = 0;
+ }
+
+ var _hashClear = hashClear;
+
+ var _hashClear$1 = /*#__PURE__*/Object.freeze({
+ default: _hashClear,
+ __moduleExports: _hashClear
+ });
+
+ /**
+ * Removes `key` and its value from the hash.
+ *
+ * @private
+ * @name delete
+ * @memberOf Hash
+ * @param {Object} hash The hash to modify.
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+ function hashDelete(key) {
+ var result = this.has(key) && delete this.__data__[key];
+ this.size -= result ? 1 : 0;
+ return result;
+ }
+
+ var _hashDelete = hashDelete;
+
+ var _hashDelete$1 = /*#__PURE__*/Object.freeze({
+ default: _hashDelete,
+ __moduleExports: _hashDelete
+ });
+
+ /** Used to stand-in for `undefined` hash values. */
+ var HASH_UNDEFINED = '__lodash_hash_undefined__';
+
+ /** Used for built-in method references. */
+ var objectProto$3 = Object.prototype;
+
+ /** Used to check objects for own properties. */
+ var hasOwnProperty$2 = objectProto$3.hasOwnProperty;
+
+ /**
+ * Gets the hash value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf Hash
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+ function hashGet(key) {
+ var data = this.__data__;
+ if (nativeCreate$1) {
+ var result = data[key];
+ return result === HASH_UNDEFINED ? undefined : result;
+ }
+ return hasOwnProperty$2.call(data, key) ? data[key] : undefined;
+ }
+
+ var _hashGet = hashGet;
+
+ var _hashGet$1 = /*#__PURE__*/Object.freeze({
+ default: _hashGet,
+ __moduleExports: _hashGet
+ });
+
+ /** Used for built-in method references. */
+ var objectProto$4 = Object.prototype;
+
+ /** Used to check objects for own properties. */
+ var hasOwnProperty$3 = objectProto$4.hasOwnProperty;
+
+ /**
+ * Checks if a hash value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf Hash
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function hashHas(key) {
+ var data = this.__data__;
+ return nativeCreate$1 ? (data[key] !== undefined) : hasOwnProperty$3.call(data, key);
+ }
+
+ var _hashHas = hashHas;
+
+ var _hashHas$1 = /*#__PURE__*/Object.freeze({
+ default: _hashHas,
+ __moduleExports: _hashHas
+ });
+
+ /** Used to stand-in for `undefined` hash values. */
+ var HASH_UNDEFINED$1 = '__lodash_hash_undefined__';
+
+ /**
+ * Sets the hash `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf Hash
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the hash instance.
+ */
+ function hashSet(key, value) {
+ var data = this.__data__;
+ this.size += this.has(key) ? 0 : 1;
+ data[key] = (nativeCreate$1 && value === undefined) ? HASH_UNDEFINED$1 : value;
+ return this;
+ }
+
+ var _hashSet = hashSet;
+
+ var _hashSet$1 = /*#__PURE__*/Object.freeze({
+ default: _hashSet,
+ __moduleExports: _hashSet
+ });
+
+ var hashClear$1 = ( _hashClear$1 && _hashClear ) || _hashClear$1;
+
+ var hashDelete$1 = ( _hashDelete$1 && _hashDelete ) || _hashDelete$1;
+
+ var hashGet$1 = ( _hashGet$1 && _hashGet ) || _hashGet$1;
+
+ var hashHas$1 = ( _hashHas$1 && _hashHas ) || _hashHas$1;
+
+ var hashSet$1 = ( _hashSet$1 && _hashSet ) || _hashSet$1;
+
+ /**
+ * Creates a hash object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+ function Hash(entries) {
+ var index = -1,
+ length = entries == null ? 0 : entries.length;
+
+ this.clear();
+ while (++index < length) {
+ var entry = entries[index];
+ this.set(entry[0], entry[1]);
+ }
+ }
+
+ // Add methods to `Hash`.
+ Hash.prototype.clear = hashClear$1;
+ Hash.prototype['delete'] = hashDelete$1;
+ Hash.prototype.get = hashGet$1;
+ Hash.prototype.has = hashHas$1;
+ Hash.prototype.set = hashSet$1;
+
+ var _Hash = Hash;
+
+ var _Hash$1 = /*#__PURE__*/Object.freeze({
+ default: _Hash,
+ __moduleExports: _Hash
+ });
+
+ /**
+ * Removes all key-value entries from the list cache.
+ *
+ * @private
+ * @name clear
+ * @memberOf ListCache
+ */
+ function listCacheClear() {
+ this.__data__ = [];
+ this.size = 0;
+ }
+
+ var _listCacheClear = listCacheClear;
+
+ var _listCacheClear$1 = /*#__PURE__*/Object.freeze({
+ default: _listCacheClear,
+ __moduleExports: _listCacheClear
+ });
+
+ /**
+ * 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);
+ }
+
+ var eq_1 = eq;
+
+ var eq$1 = /*#__PURE__*/Object.freeze({
+ default: eq_1,
+ __moduleExports: eq_1
+ });
+
+ var eq$2 = ( eq$1 && eq_1 ) || eq$1;
+
+ /**
+ * Gets the index at which the `key` is found in `array` of key-value pairs.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} key The key to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function assocIndexOf(array, key) {
+ var length = array.length;
+ while (length--) {
+ if (eq$2(array[length][0], key)) {
+ return length;
+ }
+ }
+ return -1;
+ }
+
+ var _assocIndexOf = assocIndexOf;
+
+ var _assocIndexOf$1 = /*#__PURE__*/Object.freeze({
+ default: _assocIndexOf,
+ __moduleExports: _assocIndexOf
+ });
+
+ var assocIndexOf$1 = ( _assocIndexOf$1 && _assocIndexOf ) || _assocIndexOf$1;
+
+ /** Used for built-in method references. */
+ var arrayProto = Array.prototype;
+
+ /** Built-in value references. */
+ var splice = arrayProto.splice;
+
+ /**
+ * Removes `key` and its value from the list cache.
+ *
+ * @private
+ * @name delete
+ * @memberOf ListCache
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+ function listCacheDelete(key) {
+ var data = this.__data__,
+ index = assocIndexOf$1(data, key);
+
+ if (index < 0) {
+ return false;
+ }
+ var lastIndex = data.length - 1;
+ if (index == lastIndex) {
+ data.pop();
+ } else {
+ splice.call(data, index, 1);
+ }
+ --this.size;
+ return true;
+ }
+
+ var _listCacheDelete = listCacheDelete;
+
+ var _listCacheDelete$1 = /*#__PURE__*/Object.freeze({
+ default: _listCacheDelete,
+ __moduleExports: _listCacheDelete
+ });
+
+ /**
+ * Gets the list cache value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf ListCache
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+ function listCacheGet(key) {
+ var data = this.__data__,
+ index = assocIndexOf$1(data, key);
+
+ return index < 0 ? undefined : data[index][1];
+ }
+
+ var _listCacheGet = listCacheGet;
+
+ var _listCacheGet$1 = /*#__PURE__*/Object.freeze({
+ default: _listCacheGet,
+ __moduleExports: _listCacheGet
+ });
+
+ /**
+ * Checks if a list cache value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf ListCache
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function listCacheHas(key) {
+ return assocIndexOf$1(this.__data__, key) > -1;
+ }
+
+ var _listCacheHas = listCacheHas;
+
+ var _listCacheHas$1 = /*#__PURE__*/Object.freeze({
+ default: _listCacheHas,
+ __moduleExports: _listCacheHas
+ });
+
+ /**
+ * Sets the list cache `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf ListCache
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the list cache instance.
+ */
+ function listCacheSet(key, value) {
+ var data = this.__data__,
+ index = assocIndexOf$1(data, key);
+
+ if (index < 0) {
+ ++this.size;
+ data.push([key, value]);
+ } else {
+ data[index][1] = value;
+ }
+ return this;
+ }
+
+ var _listCacheSet = listCacheSet;
+
+ var _listCacheSet$1 = /*#__PURE__*/Object.freeze({
+ default: _listCacheSet,
+ __moduleExports: _listCacheSet
+ });
+
+ var listCacheClear$1 = ( _listCacheClear$1 && _listCacheClear ) || _listCacheClear$1;
+
+ var listCacheDelete$1 = ( _listCacheDelete$1 && _listCacheDelete ) || _listCacheDelete$1;
+
+ var listCacheGet$1 = ( _listCacheGet$1 && _listCacheGet ) || _listCacheGet$1;
+
+ var listCacheHas$1 = ( _listCacheHas$1 && _listCacheHas ) || _listCacheHas$1;
+
+ var listCacheSet$1 = ( _listCacheSet$1 && _listCacheSet ) || _listCacheSet$1;
+
+ /**
+ * Creates an list cache object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+ function ListCache(entries) {
+ var index = -1,
+ length = entries == null ? 0 : entries.length;
+
+ this.clear();
+ while (++index < length) {
+ var entry = entries[index];
+ this.set(entry[0], entry[1]);
+ }
+ }
+
+ // Add methods to `ListCache`.
+ ListCache.prototype.clear = listCacheClear$1;
+ ListCache.prototype['delete'] = listCacheDelete$1;
+ ListCache.prototype.get = listCacheGet$1;
+ ListCache.prototype.has = listCacheHas$1;
+ ListCache.prototype.set = listCacheSet$1;
+
+ var _ListCache = ListCache;
+
+ var _ListCache$1 = /*#__PURE__*/Object.freeze({
+ default: _ListCache,
+ __moduleExports: _ListCache
+ });
+
+ /* Built-in method references that are verified to be native. */
+ var Map = getNative$1(root$1, 'Map');
+
+ var _Map = Map;
+
+ var _Map$1 = /*#__PURE__*/Object.freeze({
+ default: _Map,
+ __moduleExports: _Map
+ });
+
+ var Hash$1 = ( _Hash$1 && _Hash ) || _Hash$1;
+
+ var ListCache$1 = ( _ListCache$1 && _ListCache ) || _ListCache$1;
+
+ var Map$1 = ( _Map$1 && _Map ) || _Map$1;
+
+ /**
+ * Removes all key-value entries from the map.
+ *
+ * @private
+ * @name clear
+ * @memberOf MapCache
+ */
+ function mapCacheClear() {
+ this.size = 0;
+ this.__data__ = {
+ 'hash': new Hash$1,
+ 'map': new (Map$1 || ListCache$1),
+ 'string': new Hash$1
+ };
+ }
+
+ var _mapCacheClear = mapCacheClear;
+
+ var _mapCacheClear$1 = /*#__PURE__*/Object.freeze({
+ default: _mapCacheClear,
+ __moduleExports: _mapCacheClear
+ });
+
+ /**
+ * Checks if `value` is suitable for use as unique object key.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
+ */
+ function isKeyable(value) {
+ var type = typeof value;
+ return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
+ ? (value !== '__proto__')
+ : (value === null);
+ }
+
+ var _isKeyable = isKeyable;
+
+ var _isKeyable$1 = /*#__PURE__*/Object.freeze({
+ default: _isKeyable,
+ __moduleExports: _isKeyable
+ });
+
+ var isKeyable$1 = ( _isKeyable$1 && _isKeyable ) || _isKeyable$1;
+
+ /**
+ * Gets the data for `map`.
+ *
+ * @private
+ * @param {Object} map The map to query.
+ * @param {string} key The reference key.
+ * @returns {*} Returns the map data.
+ */
+ function getMapData(map, key) {
+ var data = map.__data__;
+ return isKeyable$1(key)
+ ? data[typeof key == 'string' ? 'string' : 'hash']
+ : data.map;
+ }
+
+ var _getMapData = getMapData;
+
+ var _getMapData$1 = /*#__PURE__*/Object.freeze({
+ default: _getMapData,
+ __moduleExports: _getMapData
+ });
+
+ var getMapData$1 = ( _getMapData$1 && _getMapData ) || _getMapData$1;
+
+ /**
+ * Removes `key` and its value from the map.
+ *
+ * @private
+ * @name delete
+ * @memberOf MapCache
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+ function mapCacheDelete(key) {
+ var result = getMapData$1(this, key)['delete'](key);
+ this.size -= result ? 1 : 0;
+ return result;
+ }
+
+ var _mapCacheDelete = mapCacheDelete;
+
+ var _mapCacheDelete$1 = /*#__PURE__*/Object.freeze({
+ default: _mapCacheDelete,
+ __moduleExports: _mapCacheDelete
+ });
+
+ /**
+ * Gets the map value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf MapCache
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+ function mapCacheGet(key) {
+ return getMapData$1(this, key).get(key);
+ }
+
+ var _mapCacheGet = mapCacheGet;
+
+ var _mapCacheGet$1 = /*#__PURE__*/Object.freeze({
+ default: _mapCacheGet,
+ __moduleExports: _mapCacheGet
+ });
+
+ /**
+ * Checks if a map value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf MapCache
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function mapCacheHas(key) {
+ return getMapData$1(this, key).has(key);
+ }
+
+ var _mapCacheHas = mapCacheHas;
+
+ var _mapCacheHas$1 = /*#__PURE__*/Object.freeze({
+ default: _mapCacheHas,
+ __moduleExports: _mapCacheHas
+ });
+
+ /**
+ * Sets the map `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf MapCache
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the map cache instance.
+ */
+ function mapCacheSet(key, value) {
+ var data = getMapData$1(this, key),
+ size = data.size;
+
+ data.set(key, value);
+ this.size += data.size == size ? 0 : 1;
+ return this;
+ }
+
+ var _mapCacheSet = mapCacheSet;
+
+ var _mapCacheSet$1 = /*#__PURE__*/Object.freeze({
+ default: _mapCacheSet,
+ __moduleExports: _mapCacheSet
+ });
+
+ var mapCacheClear$1 = ( _mapCacheClear$1 && _mapCacheClear ) || _mapCacheClear$1;
+
+ var mapCacheDelete$1 = ( _mapCacheDelete$1 && _mapCacheDelete ) || _mapCacheDelete$1;
+
+ var mapCacheGet$1 = ( _mapCacheGet$1 && _mapCacheGet ) || _mapCacheGet$1;
+
+ var mapCacheHas$1 = ( _mapCacheHas$1 && _mapCacheHas ) || _mapCacheHas$1;
+
+ var mapCacheSet$1 = ( _mapCacheSet$1 && _mapCacheSet ) || _mapCacheSet$1;
+
+ /**
+ * Creates a map cache object to store key-value pairs.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+ function MapCache(entries) {
+ var index = -1,
+ length = entries == null ? 0 : entries.length;
+
+ this.clear();
+ while (++index < length) {
+ var entry = entries[index];
+ this.set(entry[0], entry[1]);
+ }
+ }
+
+ // Add methods to `MapCache`.
+ MapCache.prototype.clear = mapCacheClear$1;
+ MapCache.prototype['delete'] = mapCacheDelete$1;
+ MapCache.prototype.get = mapCacheGet$1;
+ MapCache.prototype.has = mapCacheHas$1;
+ MapCache.prototype.set = mapCacheSet$1;
+
+ var _MapCache = MapCache;
+
+ var _MapCache$1 = /*#__PURE__*/Object.freeze({
+ default: _MapCache,
+ __moduleExports: _MapCache
+ });
+
+ /** Used to stand-in for `undefined` hash values. */
+ var HASH_UNDEFINED$2 = '__lodash_hash_undefined__';
+
+ /**
+ * Adds `value` to the array cache.
+ *
+ * @private
+ * @name add
+ * @memberOf SetCache
+ * @alias push
+ * @param {*} value The value to cache.
+ * @returns {Object} Returns the cache instance.
+ */
+ function setCacheAdd(value) {
+ this.__data__.set(value, HASH_UNDEFINED$2);
+ return this;
+ }
+
+ var _setCacheAdd = setCacheAdd;
+
+ var _setCacheAdd$1 = /*#__PURE__*/Object.freeze({
+ default: _setCacheAdd,
+ __moduleExports: _setCacheAdd
+ });
+
+ /**
+ * Checks if `value` is in the array cache.
+ *
+ * @private
+ * @name has
+ * @memberOf SetCache
+ * @param {*} value The value to search for.
+ * @returns {number} Returns `true` if `value` is found, else `false`.
+ */
+ function setCacheHas(value) {
+ return this.__data__.has(value);
+ }
+
+ var _setCacheHas = setCacheHas;
+
+ var _setCacheHas$1 = /*#__PURE__*/Object.freeze({
+ default: _setCacheHas,
+ __moduleExports: _setCacheHas
+ });
+
+ var MapCache$1 = ( _MapCache$1 && _MapCache ) || _MapCache$1;
+
+ var setCacheAdd$1 = ( _setCacheAdd$1 && _setCacheAdd ) || _setCacheAdd$1;
+
+ var setCacheHas$1 = ( _setCacheHas$1 && _setCacheHas ) || _setCacheHas$1;
+
+ /**
+ *
+ * Creates an array cache object to store unique values.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [values] The values to cache.
+ */
+ function SetCache(values) {
+ var index = -1,
+ length = values == null ? 0 : values.length;
+
+ this.__data__ = new MapCache$1;
+ while (++index < length) {
+ this.add(values[index]);
+ }
+ }
+
+ // Add methods to `SetCache`.
+ SetCache.prototype.add = SetCache.prototype.push = setCacheAdd$1;
+ SetCache.prototype.has = setCacheHas$1;
+
+ var _SetCache = SetCache;
+
+ var _SetCache$1 = /*#__PURE__*/Object.freeze({
+ default: _SetCache,
+ __moduleExports: _SetCache
+ });
+
+ /**
+ * The base implementation of `_.findIndex` and `_.findLastIndex` without
+ * support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Function} predicate The function invoked per iteration.
+ * @param {number} fromIndex The index to search from.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function baseFindIndex(array, predicate, fromIndex, fromRight) {
+ var length = array.length,
+ index = fromIndex + (fromRight ? 1 : -1);
+
+ while ((fromRight ? index-- : ++index < length)) {
+ if (predicate(array[index], index, array)) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ var _baseFindIndex = baseFindIndex;
+
+ var _baseFindIndex$1 = /*#__PURE__*/Object.freeze({
+ default: _baseFindIndex,
+ __moduleExports: _baseFindIndex
+ });
+
+ /**
+ * The base implementation of `_.isNaN` without support for number objects.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
+ */
+ function baseIsNaN(value) {
+ return value !== value;
+ }
+
+ var _baseIsNaN = baseIsNaN;
+
+ var _baseIsNaN$1 = /*#__PURE__*/Object.freeze({
+ default: _baseIsNaN,
+ __moduleExports: _baseIsNaN
+ });
+
+ /**
+ * A specialized version of `_.indexOf` which performs strict equality
+ * comparisons of values, i.e. `===`.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} fromIndex The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function strictIndexOf(array, value, fromIndex) {
+ var index = fromIndex - 1,
+ length = array.length;
+
+ while (++index < length) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ var _strictIndexOf = strictIndexOf;
+
+ var _strictIndexOf$1 = /*#__PURE__*/Object.freeze({
+ default: _strictIndexOf,
+ __moduleExports: _strictIndexOf
+ });
+
+ var baseFindIndex$1 = ( _baseFindIndex$1 && _baseFindIndex ) || _baseFindIndex$1;
+
+ var baseIsNaN$1 = ( _baseIsNaN$1 && _baseIsNaN ) || _baseIsNaN$1;
+
+ var strictIndexOf$1 = ( _strictIndexOf$1 && _strictIndexOf ) || _strictIndexOf$1;
+
+ /**
+ * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} fromIndex The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function baseIndexOf(array, value, fromIndex) {
+ return value === value
+ ? strictIndexOf$1(array, value, fromIndex)
+ : baseFindIndex$1(array, baseIsNaN$1, fromIndex);
+ }
+
+ var _baseIndexOf = baseIndexOf;
+
+ var _baseIndexOf$1 = /*#__PURE__*/Object.freeze({
+ default: _baseIndexOf,
+ __moduleExports: _baseIndexOf
+ });
+
+ var baseIndexOf$1 = ( _baseIndexOf$1 && _baseIndexOf ) || _baseIndexOf$1;
+
+ /**
+ * A specialized version of `_.includes` for arrays without support for
+ * specifying an index to search from.
+ *
+ * @private
+ * @param {Array} [array] The array to inspect.
+ * @param {*} target The value to search for.
+ * @returns {boolean} Returns `true` if `target` is found, else `false`.
+ */
+ function arrayIncludes(array, value) {
+ var length = array == null ? 0 : array.length;
+ return !!length && baseIndexOf$1(array, value, 0) > -1;
+ }
+
+ var _arrayIncludes = arrayIncludes;
+
+ var _arrayIncludes$1 = /*#__PURE__*/Object.freeze({
+ default: _arrayIncludes,
+ __moduleExports: _arrayIncludes
+ });
+
+ /**
+ * This function is like `arrayIncludes` except that it accepts a comparator.
+ *
+ * @private
+ * @param {Array} [array] The array to inspect.
+ * @param {*} target The value to search for.
+ * @param {Function} comparator The comparator invoked per element.
+ * @returns {boolean} Returns `true` if `target` is found, else `false`.
+ */
+ function arrayIncludesWith(array, value, comparator) {
+ var index = -1,
+ length = array == null ? 0 : array.length;
+
+ while (++index < length) {
+ if (comparator(value, array[index])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ var _arrayIncludesWith = arrayIncludesWith;
+
+ var _arrayIncludesWith$1 = /*#__PURE__*/Object.freeze({
+ default: _arrayIncludesWith,
+ __moduleExports: _arrayIncludesWith
+ });
+
+ /**
+ * Checks if a `cache` value for `key` exists.
+ *
+ * @private
+ * @param {Object} cache The cache to query.
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function cacheHas(cache, key) {
+ return cache.has(key);
+ }
+
+ var _cacheHas = cacheHas;
+
+ var _cacheHas$1 = /*#__PURE__*/Object.freeze({
+ default: _cacheHas,
+ __moduleExports: _cacheHas
+ });
+
+ /* Built-in method references that are verified to be native. */
+ var Set = getNative$1(root$1, 'Set');
+
+ var _Set = Set;
+
+ var _Set$1 = /*#__PURE__*/Object.freeze({
+ default: _Set,
+ __moduleExports: _Set
+ });
+
+ /**
+ * This method returns `undefined`.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.3.0
+ * @category Util
+ * @example
+ *
+ * _.times(2, _.noop);
+ * // => [undefined, undefined]
+ */
+ function noop() {
+ // No operation performed.
+ }
+
+ var noop_1 = noop;
+
+ var noop$1 = /*#__PURE__*/Object.freeze({
+ default: noop_1,
+ __moduleExports: noop_1
+ });
+
+ /**
+ * Converts `set` to an array of its values.
+ *
+ * @private
+ * @param {Object} set The set to convert.
+ * @returns {Array} Returns the values.
+ */
+ function setToArray(set) {
+ var index = -1,
+ result = Array(set.size);
+
+ set.forEach(function(value) {
+ result[++index] = value;
+ });
+ return result;
+ }
+
+ var _setToArray = setToArray;
+
+ var _setToArray$1 = /*#__PURE__*/Object.freeze({
+ default: _setToArray,
+ __moduleExports: _setToArray
+ });
+
+ var Set$1 = ( _Set$1 && _Set ) || _Set$1;
+
+ var noop$2 = ( noop$1 && noop_1 ) || noop$1;
+
+ var setToArray$1 = ( _setToArray$1 && _setToArray ) || _setToArray$1;
+
+ /** Used as references for various `Number` constants. */
+ var INFINITY = 1 / 0;
+
+ /**
+ * Creates a set object of `values`.
+ *
+ * @private
+ * @param {Array} values The values to add to the set.
+ * @returns {Object} Returns the new set.
+ */
+ var createSet = !(Set$1 && (1 / setToArray$1(new Set$1([,-0]))[1]) == INFINITY) ? noop$2 : function(values) {
+ return new Set$1(values);
+ };
+
+ var _createSet = createSet;
+
+ var _createSet$1 = /*#__PURE__*/Object.freeze({
+ default: _createSet,
+ __moduleExports: _createSet
+ });
+
+ var SetCache$1 = ( _SetCache$1 && _SetCache ) || _SetCache$1;
+
+ var arrayIncludes$1 = ( _arrayIncludes$1 && _arrayIncludes ) || _arrayIncludes$1;
+
+ var arrayIncludesWith$1 = ( _arrayIncludesWith$1 && _arrayIncludesWith ) || _arrayIncludesWith$1;
+
+ var cacheHas$1 = ( _cacheHas$1 && _cacheHas ) || _cacheHas$1;
+
+ var createSet$1 = ( _createSet$1 && _createSet ) || _createSet$1;
+
+ /** Used as the size to enable large array optimizations. */
+ var LARGE_ARRAY_SIZE = 200;
+
+ /**
+ * The base implementation of `_.uniqBy` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
+ */
+ function baseUniq(array, iteratee, comparator) {
+ var index = -1,
+ includes = arrayIncludes$1,
+ length = array.length,
+ isCommon = true,
+ result = [],
+ seen = result;
+
+ if (comparator) {
+ isCommon = false;
+ includes = arrayIncludesWith$1;
+ }
+ else if (length >= LARGE_ARRAY_SIZE) {
+ var set = iteratee ? null : createSet$1(array);
+ if (set) {
+ return setToArray$1(set);
+ }
+ isCommon = false;
+ includes = cacheHas$1;
+ seen = new SetCache$1;
+ }
+ else {
+ seen = iteratee ? [] : result;
+ }
+ outer:
+ while (++index < length) {
+ var value = array[index],
+ computed = iteratee ? iteratee(value) : value;
+
+ value = (comparator || value !== 0) ? value : 0;
+ if (isCommon && computed === computed) {
+ var seenIndex = seen.length;
+ while (seenIndex--) {
+ if (seen[seenIndex] === computed) {
+ continue outer;
+ }
+ }
+ if (iteratee) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ else if (!includes(seen, computed, comparator)) {
+ if (seen !== result) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ }
+ return result;
+ }
+
+ var _baseUniq = baseUniq;
+
+ var _baseUniq$1 = /*#__PURE__*/Object.freeze({
+ default: _baseUniq,
+ __moduleExports: _baseUniq
+ });
+
+ var baseUniq$1 = ( _baseUniq$1 && _baseUniq ) || _baseUniq$1;
+
+ /**
+ * Creates a duplicate-free version of an array, using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons, in which only the first occurrence of each element
+ * is kept. The order of result values is determined by the order they occur
+ * in the array.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.uniq([2, 1, 2]);
+ * // => [2, 1]
+ */
+ function uniq(array) {
+ return (array && array.length) ? baseUniq$1(array) : [];
+ }
+
+ var uniq_1 = uniq;
+
+ function camelCaseToDash(str) {
+ return str.replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`);
+ }
+
+ function makeDataAttributeString(props) {
+ const keys = Object.keys(props);
+
+ return keys
+ .map((key) => {
+ const _key = camelCaseToDash(key);
+ const val = props[key];
+
+ if (val === undefined) return '';
+ return `data-${_key}="${val}" `;
+ })
+ .join('')
+ .trim();
+ }
+
+ function copyTextToClipboard(text) {
+ // https://stackoverflow.com/a/30810322/5353542
+ var textArea = document.createElement('textarea');
+
+ //
+ // *** This styling is an extra step which is likely not required. ***
+ //
+ // Why is it here? To ensure:
+ // 1. the element is able to have focus and selection.
+ // 2. if element was to flash render it has minimal visual impact.
+ // 3. less flakyness with selection and copying which **might** occur if
+ // the textarea element is not visible.
+ //
+ // The likelihood is the element won't even render, not even a flash,
+ // so some of these are just precautions. However in IE the element
+ // is visible whilst the popup box asking the user for permission for
+ // the web page to copy to the clipboard.
+ //
+
+ // Place in top-left corner of screen regardless of scroll position.
+ textArea.style.position = 'fixed';
+ textArea.style.top = 0;
+ textArea.style.left = 0;
+
+ // Ensure it has a small width and height. Setting to 1px / 1em
+ // doesn't work as this gives a negative w/h on some browsers.
+ textArea.style.width = '2em';
+ textArea.style.height = '2em';
+
+ // We don't need padding, reducing the size if it does flash render.
+ textArea.style.padding = 0;
+
+ // Clean up any borders.
+ textArea.style.border = 'none';
+ textArea.style.outline = 'none';
+ textArea.style.boxShadow = 'none';
+
+ // Avoid flash of white box if rendered for any reason.
+ textArea.style.background = 'transparent';
+
+ textArea.value = text;
+
+ document.body.appendChild(textArea);
+
+ textArea.select();
+
+ try {
+ document.execCommand('copy');
+ } catch (err) {
+ console.log('Oops, unable to copy');
+ }
+
+ document.body.removeChild(textArea);
+ }
+
+ function isNumeric(val) {
+ return !isNaN(val);
+ }
+
+ let throttle$1 = throttle_1;
+
+ let debounce$3 = debounce_1;
+
+ function nextTick(fn, context = null) {
+ return (...args) => {
+ return new Promise(resolve => {
+ const execute = () => {
+ const out = fn.apply(context, args);
+ resolve(out);
+ };
+ setTimeout(execute);
+ });
+ };
+ }
+ function linkProperties(target, source, properties) {
+ const props = properties.reduce((acc, prop) => {
+ acc[prop] = {
+ get() {
+ return source[prop];
+ }
+ };
+ return acc;
+ }, {});
+ Object.defineProperties(target, props);
+ }
+ function isSet(val) {
+ return val !== undefined || val !== null;
+ }
+
+ function notSet(val) {
+ return !isSet(val);
+ }
+
+ function isNumber(val) {
+ return !isNaN(val);
+ }
+
+ function ensureArray(val) {
+ if (!Array.isArray(val)) {
+ return [val];
+ }
+ return val;
+ }
+
+ function uniq$1(arr) {
+ return uniq_1(arr);
+ }
+
+ function numberSortAsc(a, b) {
+ return a - b;
+ }
+ function stripHTML(html) {
+ return html.replace(/<[^>]*>/g, '');
+ }
+ function format(str, args) {
+ if (!str) return str;
+
+ Object.keys(args).forEach(arg => {
+ let regex = new RegExp(`{(${arg})}`, 'g');
+ str = str.replace(regex, args[arg]);
+ });
+
+ return str;
+ }
+
+ class DataManager {
+ constructor(options) {
+ this.options = options;
+ this.sortRows = nextTick(this.sortRows, this);
+ this.switchColumn = nextTick(this.switchColumn, this);
+ this.removeColumn = nextTick(this.removeColumn, this);
+ this.options.filterRows = nextTick(this.options.filterRows, this);
+ }
+
+ init(data, columns) {
+ if (!data) {
+ data = this.options.data;
+ }
+ if (columns) {
+ this.options.columns = columns;
+ }
+
+ this.data = data;
+
+ this.rowCount = 0;
+ this.columns = [];
+ this.rows = [];
+
+ this.prepareColumns();
+ this.prepareRows();
+ this.prepareTreeRows();
+ this.prepareRowView();
+ this.prepareNumericColumns();
+ }
+
+ // computed property
+ get currentSort() {
+ const col = this.columns.find(col => col.sortOrder !== 'none');
+ return col || {
+ colIndex: -1,
+ sortOrder: 'none'
+ };
+ }
+
+ prepareColumns() {
+ this.columns = [];
+ this.validateColumns();
+ this.prepareDefaultColumns();
+ this.prepareHeader();
+ }
+
+ prepareDefaultColumns() {
+ if (this.options.checkboxColumn && !this.hasColumnById('_checkbox')) {
+ const cell = {
+ id: '_checkbox',
+ content: this.getCheckboxHTML(),
+ editable: false,
+ resizable: false,
+ sortable: false,
+ focusable: false,
+ dropdown: false,
+ width: 32
+ };
+ this.columns.push(cell);
+ }
+
+ if (this.options.serialNoColumn && !this.hasColumnById('_rowIndex')) {
+ let cell = {
+ id: '_rowIndex',
+ content: '',
+ align: 'center',
+ editable: false,
+ resizable: false,
+ focusable: false,
+ dropdown: false
+ };
+
+ this.columns.push(cell);
+ }
+ }
+
+ prepareHeader() {
+ let columns = this.columns.concat(this.options.columns);
+ const baseCell = {
+ isHeader: 1,
+ editable: true,
+ sortable: true,
+ resizable: true,
+ focusable: true,
+ dropdown: true,
+ width: null,
+ format: (value) => {
+ if (value === null || value === undefined) {
+ return '';
+ }
+ return value + '';
+ }
+ };
+
+ this.columns = columns
+ .map((cell, i) => this.prepareCell(cell, i))
+ .map(col => Object.assign({}, baseCell, col))
+ .map(col => {
+ col.content = col.content || col.name || '';
+ col.id = col.id || col.content;
+ return col;
+ });
+ }
+
+ prepareCell(content, i) {
+ const cell = {
+ content: '',
+ sortOrder: 'none',
+ colIndex: i,
+ column: this.columns[i]
+ };
+
+ if (content !== null && typeof content === 'object') {
+ // passed as column/header
+ Object.assign(cell, content);
+ } else {
+ cell.content = content;
+ }
+
+ return cell;
+ }
+
+ prepareNumericColumns() {
+ const row0 = this.getRow(0);
+ if (!row0) return;
+ this.columns = this.columns.map((column, i) => {
+
+ const cellValue = row0[i].content;
+ if (!column.align && isNumeric(cellValue)) {
+ column.align = 'right';
+ }
+
+ return column;
+ });
+ }
+
+ prepareRows() {
+ this.validateData(this.data);
+
+ this.rows = this.data.map((d, i) => {
+ const index = this._getNextRowCount();
+
+ let row = [];
+ let meta = {
+ rowIndex: index
+ };
+
+ if (Array.isArray(d)) {
+ // row is an array
+ if (this.options.checkboxColumn) {
+ row.push(this.getCheckboxHTML());
+ }
+ if (this.options.serialNoColumn) {
+ row.push((index + 1) + '');
+ }
+ row = row.concat(d);
+
+ while (row.length < this.columns.length) {
+ row.push('');
+ }
+
+ } else {
+ // row is an object
+ for (let col of this.columns) {
+ if (col.id === '_checkbox') {
+ row.push(this.getCheckboxHTML());
+ } else if (col.id === '_rowIndex') {
+ row.push((index + 1) + '');
+ } else {
+ row.push(d[col.id]);
+ }
+ }
+
+ meta.indent = d.indent || 0;
+ }
+
+ return this.prepareRow(row, meta);
+ });
+ }
+
+ prepareTreeRows() {
+ this.rows.forEach((row, i) => {
+ if (isNumber(row.meta.indent)) {
+ // if (i === 36) debugger;
+ const nextRow = this.getRow(i + 1);
+ row.meta.isLeaf = !nextRow ||
+ notSet(nextRow.meta.indent) ||
+ nextRow.meta.indent <= row.meta.indent;
+ row.meta.isTreeNodeClose = false;
+ }
+ });
+ }
+
+ prepareRowView() {
+ // This is order in which rows will be rendered in the table.
+ // When sorting happens, only this.rowViewOrder will change
+ // and not the original this.rows
+ this.rowViewOrder = this.rows.map(row => row.meta.rowIndex);
+ }
+
+ prepareRow(row, meta) {
+ const baseRowCell = {
+ rowIndex: meta.rowIndex,
+ indent: meta.indent
+ };
+
+ row = row
+ .map((cell, i) => this.prepareCell(cell, i))
+ .map(cell => Object.assign({}, baseRowCell, cell));
+
+ // monkey patched in array object
+ row.meta = meta;
+ return row;
+ }
+
+ validateColumns() {
+ const columns = this.options.columns;
+ if (!Array.isArray(columns)) {
+ throw new DataError('`columns` must be an array');
+ }
+
+ columns.forEach((column, i) => {
+ if (typeof column !== 'string' && typeof column !== 'object') {
+ throw new DataError(`column "${i}" must be a string or an object`);
+ }
+ });
+ }
+
+ validateData(data) {
+ if (Array.isArray(data) &&
+ (data.length === 0 || Array.isArray(data[0]) || typeof data[0] === 'object')) {
+ return true;
+ }
+ throw new DataError('`data` must be an array of arrays or objects');
+ }
+
+ appendRows(rows) {
+ this.validateData(rows);
+
+ this.rows.push(...this.prepareRows(rows));
+ }
+
+ sortRows(colIndex, sortOrder = 'none') {
+ colIndex = +colIndex;
+
+ // reset sortOrder and update for colIndex
+ this.getColumns()
+ .map(col => {
+ if (col.colIndex === colIndex) {
+ col.sortOrder = sortOrder;
+ } else {
+ col.sortOrder = 'none';
+ }
+ });
+
+ this._sortRows(colIndex, sortOrder);
+ }
+
+ _sortRows(colIndex, sortOrder) {
+
+ if (this.currentSort.colIndex === colIndex) {
+ // reverse the array if only sortOrder changed
+ if (
+ (this.currentSort.sortOrder === 'asc' && sortOrder === 'desc') ||
+ (this.currentSort.sortOrder === 'desc' && sortOrder === 'asc')
+ ) {
+ this.reverseArray(this.rowViewOrder);
+ this.currentSort.sortOrder = sortOrder;
+ return;
+ }
+ }
+
+ this.rowViewOrder.sort((a, b) => {
+ const aIndex = a;
+ const bIndex = b;
+
+ let aContent = this.getCell(colIndex, a).content;
+ let bContent = this.getCell(colIndex, b).content;
+ aContent = aContent == null ? '' : aContent;
+ bContent = bContent == null ? '' : bContent;
+
+ if (sortOrder === 'none') {
+ return aIndex - bIndex;
+ } else if (sortOrder === 'asc') {
+ if (aContent < bContent) return -1;
+ if (aContent > bContent) return 1;
+ if (aContent === bContent) return 0;
+ } else if (sortOrder === 'desc') {
+ if (aContent < bContent) return 1;
+ if (aContent > bContent) return -1;
+ if (aContent === bContent) return 0;
+ }
+ return 0;
+ });
+
+ if (this.hasColumnById('_rowIndex')) {
+ // update row index
+ const srNoColIndex = this.getColumnIndexById('_rowIndex');
+ this.rows.forEach((row, index) => {
+ const viewIndex = this.rowViewOrder.indexOf(index);
+ const cell = row[srNoColIndex];
+ cell.content = (viewIndex + 1) + '';
+ });
+ }
+ }
+
+ reverseArray(array) {
+ let left = null;
+ let right = null;
+ let length = array.length;
+
+ for (left = 0, right = length - 1; left < right; left += 1, right -= 1) {
+ const temporary = array[left];
+
+ array[left] = array[right];
+ array[right] = temporary;
+ }
+ }
+
+ switchColumn(index1, index2) {
+ // update columns
+ const temp = this.columns[index1];
+ this.columns[index1] = this.columns[index2];
+ this.columns[index2] = temp;
+
+ this.columns[index1].colIndex = index1;
+ this.columns[index2].colIndex = index2;
+
+ // update rows
+ this.rows.forEach(row => {
+ const newCell1 = Object.assign({}, row[index1], {
+ colIndex: index2
+ });
+ const newCell2 = Object.assign({}, row[index2], {
+ colIndex: index1
+ });
+
+ row[index2] = newCell1;
+ row[index1] = newCell2;
+ });
+ }
+
+ removeColumn(index) {
+ index = +index;
+ const filter = cell => cell.colIndex !== index;
+ const map = (cell, i) => Object.assign({}, cell, {
+ colIndex: i
+ });
+ // update columns
+ this.columns = this.columns
+ .filter(filter)
+ .map(map);
+
+ // update rows
+ this.rows.forEach(row => {
+ // remove cell
+ row.splice(index, 1);
+ // update colIndex
+ row.forEach((cell, i) => {
+ cell.colIndex = i;
+ });
+ });
+ }
+
+ updateRow(row, rowIndex) {
+ if (row.length < this.columns.length) {
+ if (this.hasColumnById('_rowIndex')) {
+ const val = (rowIndex + 1) + '';
+
+ row = [val].concat(row);
+ }
+
+ if (this.hasColumnById('_checkbox')) {
+ const val = '';
+
+ row = [val].concat(row);
+ }
+ }
+
+ const _row = this.prepareRow(row, {rowIndex});
+ const index = this.rows.findIndex(row => row[0].rowIndex === rowIndex);
+ this.rows[index] = _row;
+
+ return _row;
+ }
+
+ updateCell(colIndex, rowIndex, options) {
+ let cell;
+ if (typeof colIndex === 'object') {
+ // cell object was passed,
+ // must have colIndex, rowIndex
+ cell = colIndex;
+ colIndex = cell.colIndex;
+ rowIndex = cell.rowIndex;
+ // the object passed must be merged with original cell
+ options = cell;
+ }
+ cell = this.getCell(colIndex, rowIndex);
+
+ // mutate object directly
+ for (let key in options) {
+ const newVal = options[key];
+ if (newVal !== undefined) {
+ cell[key] = newVal;
+ }
+ }
+
+ return cell;
+ }
+
+ updateColumn(colIndex, keyValPairs) {
+ const column = this.getColumn(colIndex);
+ for (let key in keyValPairs) {
+ const newVal = keyValPairs[key];
+ if (newVal !== undefined) {
+ column[key] = newVal;
+ }
+ }
+ return column;
+ }
+
+ filterRows(filters) {
+ return this.options.filterRows(this.rows, filters)
+ .then(result => {
+ if (!result) {
+ result = this.getAllRowIndices();
+ }
+
+ if (!result.then) {
+ result = Promise.resolve(result);
+ }
+
+ return result.then(rowsToShow => {
+ this._filteredRows = rowsToShow;
+
+ const rowsToHide = this.getAllRowIndices()
+ .filter(index => !rowsToShow.includes(index));
+
+ return {
+ rowsToHide,
+ rowsToShow
+ };
+ });
+ });
+ }
+
+ getFilteredRowIndices() {
+ return this._filteredRows || this.getAllRowIndices();
+ }
+
+ getAllRowIndices() {
+ return this.rows.map(row => row.meta.rowIndex);
+ }
+
+ getRowCount() {
+ return this.rowCount;
+ }
+
+ _getNextRowCount() {
+ const val = this.rowCount;
+
+ this.rowCount++;
+ return val;
+ }
+
+ getRows(start, end) {
+ return this.rows.slice(start, end);
+ }
+
+ getRowsForView(start, end) {
+ const rows = this.rowViewOrder.map(i => this.rows[i]);
+ return rows.slice(start, end);
+ }
+
+ getColumns(skipStandardColumns) {
+ let columns = this.columns;
+
+ if (skipStandardColumns) {
+ columns = columns.slice(this.getStandardColumnCount());
+ }
+
+ return columns;
+ }
+
+ getStandardColumnCount() {
+ if (this.options.checkboxColumn && this.options.serialNoColumn) {
+ return 2;
+ }
+
+ if (this.options.checkboxColumn || this.options.serialNoColumn) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ getColumnCount(skipStandardColumns) {
+ let val = this.columns.length;
+
+ if (skipStandardColumns) {
+ val = val - this.getStandardColumnCount();
+ }
+
+ return val;
+ }
+
+ getColumn(colIndex) {
+ colIndex = +colIndex;
+
+ if (colIndex < 0) {
+ // negative indexes
+ colIndex = this.columns.length + colIndex;
+ }
+
+ return this.columns.find(col => col.colIndex === colIndex);
+ }
+
+ getColumnById(id) {
+ return this.columns.find(col => col.id === id);
+ }
+
+ getRow(rowIndex) {
+ rowIndex = +rowIndex;
+ return this.rows[rowIndex];
+ }
+
+ getCell(colIndex, rowIndex) {
+ rowIndex = +rowIndex;
+ colIndex = +colIndex;
+ return this.getRow(rowIndex)[colIndex];
+ }
+
+ getChildren(parentRowIndex) {
+ parentRowIndex = +parentRowIndex;
+ const parentIndent = this.getRow(parentRowIndex).meta.indent;
+ const out = [];
+
+ for (let i = parentRowIndex + 1; i < this.rowCount; i++) {
+ const row = this.getRow(i);
+ if (isNaN(row.meta.indent)) continue;
+
+ if (row.meta.indent > parentIndent) {
+ out.push(i);
+ }
+
+ if (row.meta.indent === parentIndent) {
+ break;
+ }
+ }
+
+ return out;
+ }
+
+ getImmediateChildren(parentRowIndex) {
+ parentRowIndex = +parentRowIndex;
+ const parentIndent = this.getRow(parentRowIndex).meta.indent;
+ const out = [];
+ const childIndent = parentIndent + 1;
+
+ for (let i = parentRowIndex + 1; i < this.rowCount; i++) {
+ const row = this.getRow(i);
+ if (isNaN(row.meta.indent) || row.meta.indent > childIndent) continue;
+
+ if (row.meta.indent === childIndent) {
+ out.push(i);
+ }
+
+ if (row.meta.indent === parentIndent) {
+ break;
+ }
+ }
+
+ return out;
+ }
+
+ get() {
+ return {
+ columns: this.columns,
+ rows: this.rows
+ };
+ }
+
+ /**
+ * Returns the original data which was passed
+ * based on rowIndex
+ * @param {Number} rowIndex
+ * @returns Array|Object
+ * @memberof DataManager
+ */
+ getData(rowIndex) {
+ return this.data[rowIndex];
+ }
+
+ hasColumn(name) {
+ return Boolean(this.columns.find(col => col.content === name));
+ }
+
+ hasColumnById(id) {
+ return Boolean(this.columns.find(col => col.id === id));
+ }
+
+ getColumnIndex(name) {
+ return this.columns.findIndex(col => col.content === name);
+ }
+
+ getColumnIndexById(id) {
+ return this.columns.findIndex(col => col.id === id);
+ }
+
+ getCheckboxHTML() {
+ return '';
+ }
+ }
+
+ // Custom Errors
+ class DataError extends TypeError {}
+
+ /* eslint-disable max-len */
+
+ // Icons from https://feathericons.com/
+
+ let icons = {
+ chevronDown: '',
+ chevronRight: ''
+ };
+
+ class CellManager {
+ constructor(instance) {
+ this.instance = instance;
+ linkProperties(this, this.instance, [
+ 'wrapper',
+ 'options',
+ 'style',
+ 'header',
+ 'bodyScrollable',
+ 'columnmanager',
+ 'rowmanager',
+ 'datamanager',
+ 'keyboard'
+ ]);
+
+ this.bindEvents();
+ }
+
+ bindEvents() {
+ this.bindFocusCell();
+ this.bindEditCell();
+ this.bindKeyboardSelection();
+ this.bindCopyCellContents();
+ this.bindMouseEvents();
+ this.bindTreeEvents();
+ }
+
+ bindFocusCell() {
+ this.bindKeyboardNav();
+ }
+
+ bindEditCell() {
+ this.$editingCell = null;
+
+ $.on(this.bodyScrollable, 'dblclick', '.dt-cell', (e, cell) => {
+ this.activateEditing(cell);
+ });
+
+ this.keyboard.on('enter', () => {
+ if (this.$focusedCell && !this.$editingCell) {
+ // enter keypress on focused cell
+ this.activateEditing(this.$focusedCell);
+ } else if (this.$editingCell) {
+ // enter keypress on editing cell
+ this.deactivateEditing();
+ }
+ });
+ }
+
+ bindKeyboardNav() {
+ const focusLastCell = (direction) => {
+ if (!this.$focusedCell || this.$editingCell) {
+ return false;
+ }
+
+ let $cell = this.$focusedCell;
+ const {
+ rowIndex,
+ colIndex
+ } = $.data($cell);
+
+ if (direction === 'left') {
+ $cell = this.getLeftMostCell$(rowIndex);
+ } else if (direction === 'right') {
+ $cell = this.getRightMostCell$(rowIndex);
+ } else if (direction === 'up') {
+ $cell = this.getTopMostCell$(colIndex);
+ } else if (direction === 'down') {
+ $cell = this.getBottomMostCell$(colIndex);
+ }
+
+ this.focusCell($cell);
+ return true;
+ };
+
+ ['left', 'right', 'up', 'down', 'tab', 'shift+tab']
+ .map(direction => this.keyboard.on(direction, () => this.focusCellInDirection(direction)));
+
+ ['left', 'right', 'up', 'down']
+ .map(direction => this.keyboard.on(`ctrl+${direction}`, () => focusLastCell(direction)));
+
+ this.keyboard.on('esc', () => {
+ this.deactivateEditing(false);
+ this.columnmanager.toggleFilter(false);
+ });
+
+ if (this.options.inlineFilters) {
+ this.keyboard.on('ctrl+f', (e) => {
+ const $cell = $.closest('.dt-cell', e.target);
+ const { colIndex } = $.data($cell);
+
+ this.activateFilter(colIndex);
+ return true;
+ });
+
+ $.on(this.header, 'focusin', '.dt-filter', () => {
+ this.unfocusCell(this.$focusedCell);
+ });
+ }
+ }
+
+ bindKeyboardSelection() {
+ const getNextSelectionCursor = (direction) => {
+ let $selectionCursor = this.getSelectionCursor();
+
+ if (direction === 'left') {
+ $selectionCursor = this.getLeftCell$($selectionCursor);
+ } else if (direction === 'right') {
+ $selectionCursor = this.getRightCell$($selectionCursor);
+ } else if (direction === 'up') {
+ $selectionCursor = this.getAboveCell$($selectionCursor);
+ } else if (direction === 'down') {
+ $selectionCursor = this.getBelowCell$($selectionCursor);
+ }
+
+ return $selectionCursor;
+ };
+
+ ['left', 'right', 'up', 'down']
+ .map(direction =>
+ this.keyboard.on(`shift+${direction}`, () => this.selectArea(getNextSelectionCursor(direction))));
+ }
+
+ bindCopyCellContents() {
+ this.keyboard.on('ctrl+c', () => {
+ const noOfCellsCopied = this.copyCellContents(this.$focusedCell, this.$selectionCursor);
+ const message = this.instance.translate('{count} cells copied', {
+ count: noOfCellsCopied
+ });
+
+ if (noOfCellsCopied) {
+ this.instance.showToastMessage(message, 2);
+ }
+ });
+
+ if (this.options.pasteFromClipboard) {
+ this.keyboard.on('ctrl+v', (e) => {
+ // hack
+ // https://stackoverflow.com/a/2177059/5353542
+ this.instance.pasteTarget.focus();
+
+ setTimeout(() => {
+ const data = this.instance.pasteTarget.value;
+ this.instance.pasteTarget.value = '';
+ this.pasteContentInCell(data);
+ }, 10);
+
+ return false;
+ });
+ }
+ }
+
+ bindMouseEvents() {
+ let mouseDown = null;
+
+ $.on(this.bodyScrollable, 'mousedown', '.dt-cell', (e) => {
+ mouseDown = true;
+ this.focusCell($(e.delegatedTarget));
+ });
+
+ $.on(this.bodyScrollable, 'mouseup', () => {
+ mouseDown = false;
+ });
+
+ const selectArea = (e) => {
+ if (!mouseDown) return;
+ this.selectArea($(e.delegatedTarget));
+ };
+
+ $.on(this.bodyScrollable, 'mousemove', '.dt-cell', throttle$1(selectArea, 50));
+ }
+
+ bindTreeEvents() {
+ $.on(this.bodyScrollable, 'click', '.dt-tree-node__toggle', (e, $toggle) => {
+ const $cell = $.closest('.dt-cell', $toggle);
+ const { rowIndex } = $.data($cell);
+
+ if ($cell.classList.contains('dt-cell--tree-close')) {
+ this.rowmanager.openSingleNode(rowIndex);
+ } else {
+ this.rowmanager.closeSingleNode(rowIndex);
+ }
+ });
+ }
+
+ focusCell($cell, {
+ skipClearSelection = 0,
+ skipDOMFocus = 0,
+ skipScrollToCell = 0
+ } = {}) {
+ if (!$cell) return;
+
+ // don't focus if already editing cell
+ if ($cell === this.$editingCell) return;
+
+ const {
+ colIndex,
+ isHeader
+ } = $.data($cell);
+ if (isHeader) {
+ return;
+ }
+
+ const column = this.columnmanager.getColumn(colIndex);
+ if (column.focusable === false) {
+ return;
+ }
+
+ if (!skipScrollToCell) {
+ this.scrollToCell($cell);
+ }
+
+ this.deactivateEditing();
+ if (!skipClearSelection) {
+ this.clearSelection();
+ }
+
+ if (this.$focusedCell) {
+ this.$focusedCell.classList.remove('dt-cell--focus');
+ }
+
+ this.$focusedCell = $cell;
+ $cell.classList.add('dt-cell--focus');
+
+ if (!skipDOMFocus) {
+ // so that keyboard nav works
+ $cell.focus();
+ }
+
+ this.highlightRowColumnHeader($cell);
+ }
+
+ unfocusCell($cell) {
+ if (!$cell) return;
+
+ // remove cell border
+ $cell.classList.remove('dt-cell--focus');
+ this.$focusedCell = null;
+
+ // reset header background
+ if (this.lastHeaders) {
+ this.lastHeaders.forEach(header => header && header.classList.remove('dt-cell--highlight'));
+ }
+ }
+
+ highlightRowColumnHeader($cell) {
+ const {
+ colIndex,
+ rowIndex
+ } = $.data($cell);
+
+ const srNoColIndex = this.datamanager.getColumnIndexById('_rowIndex');
+ const colHeaderSelector = `.dt-cell--header-${colIndex}`;
+ const rowHeaderSelector = `.dt-cell--${srNoColIndex}-${rowIndex}`;
+
+ if (this.lastHeaders) {
+ this.lastHeaders.forEach(header => header && header.classList.remove('dt-cell--highlight'));
+ }
+
+ const colHeader = $(colHeaderSelector, this.wrapper);
+ const rowHeader = $(rowHeaderSelector, this.wrapper);
+
+ this.lastHeaders = [colHeader, rowHeader];
+ this.lastHeaders.forEach(header => header && header.classList.add('dt-cell--highlight'));
+ }
+
+ selectAreaOnClusterChanged() {
+ if (!(this.$focusedCell && this.$selectionCursor)) return;
+ const {
+ colIndex,
+ rowIndex
+ } = $.data(this.$selectionCursor);
+ const $cell = this.getCell$(colIndex, rowIndex);
+
+ if (!$cell || $cell === this.$selectionCursor) return;
+
+ // selectArea needs $focusedCell
+ const fCell = $.data(this.$focusedCell);
+ this.$focusedCell = this.getCell$(fCell.colIndex, fCell.rowIndex);
+
+ this.selectArea($cell);
+ }
+
+ focusCellOnClusterChanged() {
+ if (!this.$focusedCell) return;
+
+ const {
+ colIndex,
+ rowIndex
+ } = $.data(this.$focusedCell);
+ const $cell = this.getCell$(colIndex, rowIndex);
+
+ if (!$cell) return;
+ // this function is called after hyperlist renders the rows after scroll,
+ // focusCell calls clearSelection which resets the area selection
+ // so a flag to skip it
+ // we also skip DOM focus and scroll to cell
+ // because it fights with the user scroll
+ this.focusCell($cell, {
+ skipClearSelection: 1,
+ skipDOMFocus: 1,
+ skipScrollToCell: 1
+ });
+ }
+
+ selectArea($selectionCursor) {
+ if (!this.$focusedCell) return;
+
+ if (this._selectArea(this.$focusedCell, $selectionCursor)) {
+ // valid selection
+ this.$selectionCursor = $selectionCursor;
+ }
+ }
+
+ _selectArea($cell1, $cell2) {
+ if ($cell1 === $cell2) return false;
+
+ const cells = this.getCellsInRange($cell1, $cell2);
+ if (!cells) return false;
+
+ this.clearSelection();
+ this._selectedCells = cells.map(index => this.getCell$(...index));
+ requestAnimationFrame(() => {
+ this._selectedCells.map($cell => $cell.classList.add('dt-cell--highlight'));
+ });
+ return true;
+ }
+
+ getCellsInRange($cell1, $cell2) {
+ let colIndex1, rowIndex1, colIndex2, rowIndex2;
+
+ if (typeof $cell1 === 'number') {
+ [colIndex1, rowIndex1, colIndex2, rowIndex2] = arguments;
+ } else
+ if (typeof $cell1 === 'object') {
+ if (!($cell1 && $cell2)) {
+ return false;
+ }
+
+ const cell1 = $.data($cell1);
+ const cell2 = $.data($cell2);
+
+ colIndex1 = +cell1.colIndex;
+ rowIndex1 = +cell1.rowIndex;
+ colIndex2 = +cell2.colIndex;
+ rowIndex2 = +cell2.rowIndex;
+ }
+
+ if (rowIndex1 > rowIndex2) {
+ [rowIndex1, rowIndex2] = [rowIndex2, rowIndex1];
+ }
+
+ if (colIndex1 > colIndex2) {
+ [colIndex1, colIndex2] = [colIndex2, colIndex1];
+ }
+
+ if (this.isStandardCell(colIndex1) || this.isStandardCell(colIndex2)) {
+ return false;
+ }
+
+ const cells = [];
+ let colIndex = colIndex1;
+ let rowIndex = rowIndex1;
+ const rowIndices = [];
+
+ while (rowIndex <= rowIndex2) {
+ rowIndices.push(rowIndex);
+ rowIndex += 1;
+ }
+
+ rowIndices.map((rowIndex) => {
+ while (colIndex <= colIndex2) {
+ cells.push([colIndex, rowIndex]);
+ colIndex++;
+ }
+ colIndex = colIndex1;
+ });
+
+ return cells;
+ }
+
+ clearSelection() {
+ (this._selectedCells || [])
+ .forEach($cell => $cell.classList.remove('dt-cell--highlight'));
+
+ this._selectedCells = [];
+ this.$selectionCursor = null;
+ }
+
+ getSelectionCursor() {
+ return this.$selectionCursor || this.$focusedCell;
+ }
+
+ activateEditing($cell) {
+ this.focusCell($cell);
+ const {
+ rowIndex,
+ colIndex
+ } = $.data($cell);
+
+ const col = this.columnmanager.getColumn(colIndex);
+ if (col && (col.editable === false || col.focusable === false)) {
+ return;
+ }
+
+ const cell = this.getCell(colIndex, rowIndex);
+ if (cell && cell.editable === false) {
+ return;
+ }
+
+ if (this.$editingCell) {
+ const {
+ _rowIndex,
+ _colIndex
+ } = $.data(this.$editingCell);
+
+ if (rowIndex === _rowIndex && colIndex === _colIndex) {
+ // editing the same cell
+ return;
+ }
+ }
+
+ this.$editingCell = $cell;
+ $cell.classList.add('dt-cell--editing');
+
+ const $editCell = $('.dt-cell__edit', $cell);
+ $editCell.innerHTML = '';
+
+ const editor = this.getEditor(colIndex, rowIndex, cell.content, $editCell);
+
+ if (editor) {
+ this.currentCellEditor = editor;
+ // initialize editing input with cell value
+ editor.initValue(cell.content, rowIndex, col);
+ }
+ }
+
+ deactivateEditing(submitValue = true) {
+ if (submitValue) {
+ this.submitEditing();
+ }
+ // keep focus on the cell so that keyboard navigation works
+ if (this.$focusedCell) this.$focusedCell.focus();
+
+ if (!this.$editingCell) return;
+ this.$editingCell.classList.remove('dt-cell--editing');
+ this.$editingCell = null;
+ }
+
+ getEditor(colIndex, rowIndex, value, parent) {
+ const column = this.datamanager.getColumn(colIndex);
+ const row = this.datamanager.getRow(rowIndex);
+ const data = this.datamanager.getData(rowIndex);
+ let editor = this.options.getEditor ?
+ this.options.getEditor(colIndex, rowIndex, value, parent, column, row, data) :
+ this.getDefaultEditor(parent);
+
+ if (editor === false) {
+ // explicitly returned false
+ return false;
+ }
+ if (editor === undefined) {
+ // didn't return editor, fallback to default
+ editor = this.getDefaultEditor(parent);
+ }
+
+ return editor;
+ }
+
+ getDefaultEditor(parent) {
+ const $input = $.create('input', {
+ class: 'dt-input',
+ type: 'text',
+ inside: parent
+ });
+
+ return {
+ initValue(value) {
+ $input.focus();
+ $input.value = value;
+ },
+ getValue() {
+ return $input.value;
+ },
+ setValue(value) {
+ $input.value = value;
+ }
+ };
+ }
+
+ submitEditing() {
+ let promise = Promise.resolve();
+ if (!this.$editingCell) return promise;
+
+ const $cell = this.$editingCell;
+ const {
+ rowIndex,
+ colIndex
+ } = $.data($cell);
+ const col = this.datamanager.getColumn(colIndex);
+
+ if ($cell) {
+ const editor = this.currentCellEditor;
+
+ if (editor) {
+ let valuePromise = editor.getValue();
+
+ // convert to stubbed Promise
+ if (!valuePromise.then) {
+ valuePromise = Promise.resolve(valuePromise);
+ }
+
+ promise = valuePromise.then((value) => {
+ const oldValue = this.getCell(colIndex, rowIndex).content;
+
+ if (oldValue === value) return false;
+
+ const done = editor.setValue(value, rowIndex, col);
+
+ // update cell immediately
+ this.updateCell(colIndex, rowIndex, value, true);
+ $cell.focus();
+
+ if (done && done.then) {
+ // revert to oldValue if promise fails
+ done.catch((e) => {
+ console.log(e);
+ this.updateCell(colIndex, rowIndex, oldValue);
+ });
+ }
+ return done;
+ });
+ }
+ }
+
+ this.currentCellEditor = null;
+ return promise;
+ }
+
+ copyCellContents($cell1, $cell2) {
+ if (!$cell2 && $cell1) {
+ // copy only focusedCell
+ const {
+ colIndex,
+ rowIndex
+ } = $.data($cell1);
+ const cell = this.getCell(colIndex, rowIndex);
+ copyTextToClipboard(cell.content);
+ return 1;
+ }
+ const cells = this.getCellsInRange($cell1, $cell2);
+
+ if (!cells) return 0;
+
+ const rows = cells
+ // get cell objects
+ .map(index => this.getCell(...index))
+ // convert to array of rows
+ .reduce((acc, curr) => {
+ const rowIndex = curr.rowIndex;
+
+ acc[rowIndex] = acc[rowIndex] || [];
+ acc[rowIndex].push(curr.content);
+
+ return acc;
+ }, []);
+
+ const values = rows
+ // join values by tab
+ .map(row => row.join('\t'))
+ // join rows by newline
+ .join('\n');
+
+ copyTextToClipboard(values);
+
+ // return no of cells copied
+ return rows.reduce((total, row) => total + row.length, 0);
+ }
+
+ pasteContentInCell(data) {
+ if (!this.$focusedCell) return;
+
+ const matrix = data
+ .split('\n')
+ .map(row => row.split('\t'))
+ .filter(row => row.length && row.every(it => it));
+
+ let { colIndex, rowIndex } = $.data(this.$focusedCell);
+
+ let focusedCell = {
+ colIndex: +colIndex,
+ rowIndex: +rowIndex
+ };
+
+ matrix.forEach((row, i) => {
+ let rowIndex = i + focusedCell.rowIndex;
+ row.forEach((cell, j) => {
+ let colIndex = j + focusedCell.colIndex;
+ this.updateCell(colIndex, rowIndex, cell, true);
+ });
+ });
+ }
+
+ activateFilter(colIndex) {
+ this.columnmanager.toggleFilter();
+ this.columnmanager.focusFilter(colIndex);
+
+ if (!this.columnmanager.isFilterShown) {
+ // put focus back on cell
+ this.$focusedCell && this.$focusedCell.focus();
+ }
+ }
+
+ updateCell(colIndex, rowIndex, value, refreshHtml = false) {
+ const cell = this.datamanager.updateCell(colIndex, rowIndex, {
+ content: value
+ });
+ this.refreshCell(cell, refreshHtml);
+ }
+
+ refreshCell(cell, refreshHtml = false) {
+ const $cell = $(this.selector(cell.colIndex, cell.rowIndex), this.bodyScrollable);
+ $cell.innerHTML = this.getCellContent(cell, refreshHtml);
+ }
+
+ toggleTreeButton(rowIndex, flag) {
+ const colIndex = this.columnmanager.getFirstColumnIndex();
+ const $cell = this.getCell$(colIndex, rowIndex);
+ if ($cell) {
+ $cell.classList[flag ? 'remove' : 'add']('dt-cell--tree-close');
+ }
+ }
+
+ isStandardCell(colIndex) {
+ // Standard cells are in Sr. No and Checkbox column
+ return colIndex < this.columnmanager.getFirstColumnIndex();
+ }
+
+ focusCellInDirection(direction) {
+ if (!this.$focusedCell || (this.$editingCell && ['left', 'right', 'up', 'down'].includes(direction))) {
+ return false;
+ } else if (this.$editingCell && ['tab', 'shift+tab'].includes(direction)) {
+ this.deactivateEditing();
+ }
+
+ let $cell = this.$focusedCell;
+
+ if (direction === 'left' || direction === 'shift+tab') {
+ $cell = this.getLeftCell$($cell);
+ } else if (direction === 'right' || direction === 'tab') {
+ $cell = this.getRightCell$($cell);
+ } else if (direction === 'up') {
+ $cell = this.getAboveCell$($cell);
+ } else if (direction === 'down') {
+ $cell = this.getBelowCell$($cell);
+ }
+
+ if (!$cell) {
+ return false;
+ }
+
+ const {
+ colIndex
+ } = $.data($cell);
+ const column = this.columnmanager.getColumn(colIndex);
+
+ if (!column.focusable) {
+ let $prevFocusedCell = this.$focusedCell;
+ this.unfocusCell($prevFocusedCell);
+ this.$focusedCell = $cell;
+ let ret = this.focusCellInDirection(direction);
+ if (!ret) {
+ this.focusCell($prevFocusedCell);
+ }
+ return ret;
+ }
+
+ this.focusCell($cell);
+ return true;
+ }
+
+ getCell$(colIndex, rowIndex) {
+ return $(this.selector(colIndex, rowIndex), this.bodyScrollable);
+ }
+
+ getAboveCell$($cell) {
+ const {
+ colIndex
+ } = $.data($cell);
+
+ let $aboveRow = $cell.parentElement.previousElementSibling;
+ while ($aboveRow && $aboveRow.classList.contains('dt-row--hide')) {
+ $aboveRow = $aboveRow.previousElementSibling;
+ }
+
+ if (!$aboveRow) return $cell;
+ return $(`.dt-cell--col-${colIndex}`, $aboveRow);
+ }
+
+ getBelowCell$($cell) {
+ const {
+ colIndex
+ } = $.data($cell);
+
+ let $belowRow = $cell.parentElement.nextElementSibling;
+ while ($belowRow && $belowRow.classList.contains('dt-row--hide')) {
+ $belowRow = $belowRow.nextElementSibling;
+ }
+
+ if (!$belowRow) return $cell;
+ return $(`.dt-cell--col-${colIndex}`, $belowRow);
+ }
+
+ getLeftCell$($cell) {
+ return $cell.previousElementSibling;
+ }
+
+ getRightCell$($cell) {
+ return $cell.nextElementSibling;
+ }
+
+ getLeftMostCell$(rowIndex) {
+ return this.getCell$(this.columnmanager.getFirstColumnIndex(), rowIndex);
+ }
+
+ getRightMostCell$(rowIndex) {
+ return this.getCell$(this.columnmanager.getLastColumnIndex(), rowIndex);
+ }
+
+ getTopMostCell$(colIndex) {
+ return this.getCell$(colIndex, this.rowmanager.getFirstRowIndex());
+ }
+
+ getBottomMostCell$(colIndex) {
+ return this.getCell$(colIndex, this.rowmanager.getLastRowIndex());
+ }
+
+ getCell(colIndex, rowIndex) {
+ return this.instance.datamanager.getCell(colIndex, rowIndex);
+ }
+
+ getRowHeight() {
+ return $.style($('.dt-row', this.bodyScrollable), 'height');
+ }
+
+ scrollToCell($cell) {
+ if ($.inViewport($cell, this.bodyScrollable)) return false;
+
+ const {
+ rowIndex
+ } = $.data($cell);
+ this.rowmanager.scrollToRow(rowIndex);
+ return false;
+ }
+
+ getRowCountPerPage() {
+ return Math.ceil(this.instance.getViewportHeight() / this.getRowHeight());
+ }
+
+ getCellHTML(cell) {
+ const {
+ rowIndex,
+ colIndex,
+ isHeader,
+ isFilter,
+ isTotalRow
+ } = cell;
+ const dataAttr = makeDataAttributeString({
+ rowIndex,
+ colIndex,
+ isHeader,
+ isFilter,
+ isTotalRow
+ });
+
+ const row = this.datamanager.getRow(rowIndex);
+
+ const isBodyCell = !(isHeader || isFilter || isTotalRow);
+
+ const className = [
+ 'dt-cell',
+ 'dt-cell--col-' + colIndex,
+ isBodyCell ? `dt-cell--${colIndex}-${rowIndex}` : '',
+ isBodyCell ? 'dt-cell--row-' + rowIndex : '',
+ isHeader ? 'dt-cell--header' : '',
+ isHeader ? `dt-cell--header-${colIndex}` : '',
+ isFilter ? 'dt-cell--filter' : '',
+ isBodyCell && (row && row.meta.isTreeNodeClose) ? 'dt-cell--tree-close' : ''
+ ].join(' ');
+
+ return `
+
+ ${this.getCellContent(cell)}
+
+ `;
+ }
+
+ getCellContent(cell, refreshHtml = false) {
+ const {
+ isHeader,
+ isFilter,
+ colIndex
+ } = cell;
+
+ const editable = !isHeader && cell.editable !== false;
+ const editCellHTML = editable ? this.getEditCellHTML(colIndex) : '';
+
+ const sortable = isHeader && cell.sortable !== false;
+ const sortIndicator = sortable ?
+ `
+ ${this.options.sortIndicator[cell.sortOrder]}
+ ` :
+ '';
+
+ const resizable = isHeader && cell.resizable !== false;
+ const resizeColumn = resizable ? '' : '';
+
+ const hasDropdown = isHeader && cell.dropdown !== false;
+ const dropdown = hasDropdown ? this.columnmanager.getDropdownHTML() : '';
+
+ let customFormatter = CellManager.getCustomCellFormatter(cell);
+ let contentHTML;
+ if (isHeader || isFilter || !customFormatter) {
+ contentHTML = cell.content;
+ } else {
+ if (!cell.html || refreshHtml) {
+ const row = this.datamanager.getRow(cell.rowIndex);
+ const data = this.datamanager.getData(cell.rowIndex);
+ contentHTML = customFormatter(cell.content, row, cell.column, data);
+ } else {
+ contentHTML = cell.html;
+ }
+ }
+
+ cell.html = contentHTML;
+
+ if (this.options.treeView && !(isHeader || isFilter) && cell.indent !== undefined) {
+ const nextRow = this.datamanager.getRow(cell.rowIndex + 1);
+ const addToggle = nextRow && nextRow.meta.indent > cell.indent;
+ const leftPadding = 20;
+ const unit = 'px';
+
+ // Add toggle and indent in the first column
+ const firstColumnIndex = this.datamanager.getColumnIndexById('_rowIndex') + 1;
+ if (firstColumnIndex === cell.colIndex) {
+ const padding = ((cell.indent || 0)) * leftPadding;
+ const toggleHTML = addToggle ?
+ `
+ ${icons.chevronDown}
+ ${icons.chevronRight}
+ ` : '';
+ contentHTML = `
+ ${toggleHTML}
+ ${contentHTML}
+ `;
+ }
+ }
+
+ const className = [
+ 'dt-cell__content',
+ isHeader ? `dt-cell__content--header-${colIndex}` : `dt-cell__content--col-${colIndex}`
+ ].join(' ');
+
+ return `
+
+ ${contentHTML}
+ ${sortIndicator}
+ ${resizeColumn}
+ ${dropdown}
+
+ ${editCellHTML}
+ `;
+ }
+
+ getEditCellHTML(colIndex) {
+ return ``;
+ }
+
+ selector(colIndex, rowIndex) {
+ return `.dt-cell--${colIndex}-${rowIndex}`;
+ }
+
+ static getCustomCellFormatter(cell) {
+ return cell.format || (cell.column && cell.column.format) || null;
+ }
+ }
+
+ class ColumnManager {
+ constructor(instance) {
+ this.instance = instance;
+
+ linkProperties(this, this.instance, [
+ 'options',
+ 'fireEvent',
+ 'header',
+ 'datamanager',
+ 'cellmanager',
+ 'style',
+ 'wrapper',
+ 'rowmanager',
+ 'bodyScrollable',
+ 'bodyRenderer'
+ ]);
+
+ this.bindEvents();
+ }
+
+ renderHeader() {
+ this.header.innerHTML = '';
+ this.refreshHeader();
+ }
+
+ refreshHeader() {
+ const columns = this.datamanager.getColumns();
+
+ // refresh html
+ $('div', this.header).innerHTML = this.getHeaderHTML(columns);
+
+ this.$filterRow = $('.dt-row-filter', this.header);
+ if (this.$filterRow) {
+ $.style(this.$filterRow, { display: 'none' });
+ }
+ // reset columnMap
+ this.$columnMap = [];
+ this.bindMoveColumn();
+ }
+
+ getHeaderHTML(columns) {
+ let html = this.rowmanager.getRowHTML(columns, {
+ isHeader: 1
+ });
+ if (this.options.inlineFilters) {
+ html += this.rowmanager.getRowHTML(columns, {
+ isFilter: 1
+ });
+ }
+ return html;
+ }
+
+ bindEvents() {
+ this.bindDropdown();
+ this.bindResizeColumn();
+ this.bindPerfectColumnWidth();
+ this.bindFilter();
+ }
+
+ bindDropdown() {
+ let toggleClass = '.dt-dropdown__toggle';
+ let dropdownClass = '.dt-dropdown__list';
+
+ // attach the dropdown list to container
+ this.instance.dropdownContainer.innerHTML = this.getDropdownListHTML();
+ this.$dropdownList = this.instance.dropdownContainer.firstElementChild;
+
+ $.on(this.header, 'click', toggleClass, e => {
+ this.openDropdown(e);
+ });
+
+ const deactivateDropdownOnBodyClick = (e) => {
+ const selector = [
+ toggleClass, toggleClass + ' *',
+ dropdownClass, dropdownClass + ' *'
+ ].join(',');
+ if (e.target.matches(selector)) return;
+ deactivateDropdown();
+ };
+ $.on(document.body, 'click', deactivateDropdownOnBodyClick);
+ document.addEventListener('scroll', deactivateDropdown, true);
+
+ this.instance.on('onDestroy', () => {
+ $.off(document.body, 'click', deactivateDropdownOnBodyClick);
+ $.off(document, 'scroll', deactivateDropdown);
+ });
+
+ $.on(this.$dropdownList, 'click', '.dt-dropdown__list-item', (e, $item) => {
+ if (!this._dropdownActiveColIndex) return;
+ const dropdownItems = this.options.headerDropdown;
+ const { index } = $.data($item);
+ const colIndex = this._dropdownActiveColIndex;
+ let callback = dropdownItems[index].action;
+
+ callback && callback.call(this.instance, this.getColumn(colIndex));
+ this.hideDropdown();
+ });
+
+ const _this = this;
+ function deactivateDropdown(e) {
+ _this.hideDropdown();
+ }
+
+ this.hideDropdown();
+ }
+
+ openDropdown(e) {
+ if (!this._dropdownWidth) {
+ $.style(this.$dropdownList, { display: '' });
+ this._dropdownWidth = $.style(this.$dropdownList, 'width');
+ }
+ $.style(this.$dropdownList, {
+ display: '',
+ left: (e.clientX - this._dropdownWidth + 4) + 'px',
+ top: (e.clientY + 4) + 'px'
+ });
+ const $cell = $.closest('.dt-cell', e.target);
+ const { colIndex } = $.data($cell);
+ this._dropdownActiveColIndex = colIndex;
+ }
+
+ hideDropdown() {
+ $.style(this.$dropdownList, {
+ display: 'none'
+ });
+ this._dropdownActiveColIndex = null;
+ }
+
+ bindResizeColumn() {
+ let isDragging = false;
+ let $resizingCell, startWidth, startX;
+
+ $.on(this.header, 'mousedown', '.dt-cell .dt-cell__resize-handle', (e, $handle) => {
+ document.body.classList.add('dt-resize');
+ const $cell = $handle.parentNode.parentNode;
+ $resizingCell = $cell;
+ const {
+ colIndex
+ } = $.data($resizingCell);
+ const col = this.getColumn(colIndex);
+
+ if (col && col.resizable === false) {
+ return;
+ }
+
+ isDragging = true;
+ startWidth = $.style($('.dt-cell__content', $resizingCell), 'width');
+ startX = e.pageX;
+ });
+
+ const onMouseup = (e) => {
+ document.body.classList.remove('dt-resize');
+ if (!$resizingCell) return;
+ isDragging = false;
+
+ const {
+ colIndex
+ } = $.data($resizingCell);
+ this.setColumnWidth(colIndex);
+ this.style.setBodyStyle();
+ $resizingCell = null;
+ };
+ $.on(document.body, 'mouseup', onMouseup);
+ this.instance.on('onDestroy', () => {
+ $.off(document.body, 'mouseup', onMouseup);
+ });
+
+ const onMouseMove = (e) => {
+ if (!isDragging) return;
+ let delta = e.pageX - startX;
+ if (this.options.direction === 'rtl') {
+ delta = -1 * delta;
+ }
+ const finalWidth = startWidth + delta;
+ const {
+ colIndex
+ } = $.data($resizingCell);
+
+ let columnMinWidth = this.options.minimumColumnWidth;
+ if (columnMinWidth > finalWidth) {
+ // don't resize past 30 pixels
+ return;
+ }
+ this.datamanager.updateColumn(colIndex, {
+ width: finalWidth
+ });
+ this.setColumnHeaderWidth(colIndex);
+ };
+ $.on(document.body, 'mousemove', onMouseMove);
+ this.instance.on('onDestroy', () => {
+ $.off(document.body, 'mousemove', onMouseMove);
+ });
+ }
+
+ bindPerfectColumnWidth() {
+ $.on(this.header, 'dblclick', '.dt-cell .dt-cell__resize-handle', (e, $handle) => {
+ const $cell = $handle.parentNode.parentNode;
+ const { colIndex } = $.data($cell);
+
+ let longestCell = this.bodyRenderer.visibleRows
+ .map(d => d[colIndex])
+ .reduce((acc, curr) => acc.content.length > curr.content.length ? acc : curr);
+
+ let $longestCellHTML = this.cellmanager.getCellHTML(longestCell);
+ let $div = document.createElement('div');
+ $div.innerHTML = $longestCellHTML;
+ let cellText = $div.querySelector('.dt-cell__content').textContent;
+
+ let {
+ borderLeftWidth,
+ borderRightWidth,
+ paddingLeft,
+ paddingRight
+ } = $.getStyle(this.bodyScrollable.querySelector('.dt-cell__content'));
+
+ let padding = [borderLeftWidth, borderRightWidth, paddingLeft, paddingRight]
+ .map(parseFloat)
+ .reduce((sum, val) => sum + val);
+
+ let width = $.measureTextWidth(cellText) + padding;
+ this.datamanager.updateColumn(colIndex, { width });
+ this.setColumnHeaderWidth(colIndex);
+ this.setColumnWidth(colIndex);
+ });
+ }
+
+ bindMoveColumn() {
+ if (this.options.disableReorderColumn) return;
+
+ const $parent = $('.dt-row', this.header);
+
+ this.sortable = Sortable.create($parent, {
+ onEnd: (e) => {
+ const {
+ oldIndex,
+ newIndex
+ } = e;
+ const $draggedCell = e.item;
+ const {
+ colIndex
+ } = $.data($draggedCell);
+ if (+colIndex === newIndex) return;
+
+ this.switchColumn(oldIndex, newIndex);
+ },
+ preventOnFilter: false,
+ filter: '.dt-cell__resize-handle, .dt-dropdown',
+ chosenClass: 'dt-cell--dragging',
+ animation: 150
+ });
+ }
+
+ sortColumn(colIndex, nextSortOrder) {
+ this.instance.freeze();
+ this.sortRows(colIndex, nextSortOrder)
+ .then(() => {
+ this.refreshHeader();
+ return this.rowmanager.refreshRows();
+ })
+ .then(() => this.instance.unfreeze())
+ .then(() => {
+ this.fireEvent('onSortColumn', this.getColumn(colIndex));
+ });
+ }
+
+ removeColumn(colIndex) {
+ const removedCol = this.getColumn(colIndex);
+ this.instance.freeze();
+ this.datamanager.removeColumn(colIndex)
+ .then(() => {
+ this.refreshHeader();
+ return this.rowmanager.refreshRows();
+ })
+ .then(() => this.instance.unfreeze())
+ .then(() => {
+ this.fireEvent('onRemoveColumn', removedCol);
+ });
+ }
+
+ switchColumn(oldIndex, newIndex) {
+ this.instance.freeze();
+ this.datamanager.switchColumn(oldIndex, newIndex)
+ .then(() => {
+ this.refreshHeader();
+ return this.rowmanager.refreshRows();
+ })
+ .then(() => {
+ this.setColumnWidth(oldIndex);
+ this.setColumnWidth(newIndex);
+ this.instance.unfreeze();
+ })
+ .then(() => {
+ this.fireEvent('onSwitchColumn',
+ this.getColumn(oldIndex), this.getColumn(newIndex)
+ );
+ });
+ }
+
+ toggleFilter(flag) {
+ if (!this.options.inlineFilters) return;
+
+ let showFilter;
+ if (flag === undefined) {
+ showFilter = !this.isFilterShown;
+ } else {
+ showFilter = flag;
+ }
+
+ if (showFilter) {
+ $.style(this.$filterRow, { display: '' });
+ } else {
+ $.style(this.$filterRow, { display: 'none' });
+ }
+
+ this.isFilterShown = showFilter;
+ this.style.setBodyStyle();
+ }
+
+ focusFilter(colIndex) {
+ if (!this.isFilterShown) return;
+
+ const $filterInput = $(`.dt-cell--col-${colIndex} .dt-filter`, this.$filterRow);
+ $filterInput.focus();
+ }
+
+ bindFilter() {
+ if (!this.options.inlineFilters) return;
+ const handler = e => {
+ this.applyFilter(this.getAppliedFilters());
+ };
+ $.on(this.header, 'keydown', '.dt-filter', debounce$3(handler, 300));
+ }
+
+ applyFilter(filters) {
+ this.datamanager.filterRows(filters)
+ .then(({
+ rowsToShow
+ }) => {
+ this.rowmanager.showRows(rowsToShow);
+ });
+ }
+
+ getAppliedFilters() {
+ const filters = {};
+ $.each('.dt-filter', this.header).map((input) => {
+ const value = input.value;
+ if (value) {
+ filters[input.dataset.colIndex] = value;
+ }
+ });
+ return filters;
+ }
+
+ applyDefaultSortOrder() {
+ // sort rows if any 1 column has a default sortOrder set
+ const columnsToSort = this.getColumns().filter(col => col.sortOrder !== 'none');
+
+ if (columnsToSort.length === 1) {
+ const column = columnsToSort[0];
+ this.sortColumn(column.colIndex, column.sortOrder);
+ }
+ }
+
+ sortRows(colIndex, sortOrder) {
+ return this.datamanager.sortRows(colIndex, sortOrder);
+ }
+
+ getColumn(colIndex) {
+ return this.datamanager.getColumn(colIndex);
+ }
+
+ getColumns() {
+ return this.datamanager.getColumns();
+ }
+
+ setColumnWidth(colIndex, width) {
+ colIndex = +colIndex;
+
+ let columnWidth = width || this.getColumn(colIndex).width;
+
+ const selector = [
+ `.dt-cell__content--col-${colIndex}`,
+ `.dt-cell__edit--col-${colIndex}`
+ ].join(', ');
+
+ const styles = {
+ width: columnWidth + 'px'
+ };
+
+ this.style.setStyle(selector, styles);
+ }
+
+ setColumnHeaderWidth(colIndex) {
+ colIndex = +colIndex;
+ this.$columnMap = this.$columnMap || [];
+ const selector = `.dt-cell__content--header-${colIndex}`;
+ const {
+ width
+ } = this.getColumn(colIndex);
+
+ let $column = this.$columnMap[colIndex];
+ if (!$column) {
+ $column = this.header.querySelector(selector);
+ this.$columnMap[colIndex] = $column;
+ }
+
+ $column.style.width = width + 'px';
+ }
+
+ getColumnMinWidth(colIndex) {
+ colIndex = +colIndex;
+ return this.getColumn(colIndex).minWidth || 24;
+ }
+
+ getFirstColumnIndex() {
+ return this.datamanager.getColumnIndexById('_rowIndex') + 1;
+ }
+
+ getHeaderCell$(colIndex) {
+ return $(`.dt-cell--header-${colIndex}`, this.header);
+ }
+
+ getLastColumnIndex() {
+ return this.datamanager.getColumnCount() - 1;
+ }
+
+ getDropdownHTML() {
+ const { dropdownButton } = this.options;
+
+ return `
+
+ `;
+ }
+
+ getDropdownListHTML() {
+ const { headerDropdown: dropdownItems } = this.options;
+
+ return `
+
+ ${dropdownItems.map((d, i) => `
+
${d.label}
+ `).join('')}
+
+ `;
+ }
+ }
+
+ class RowManager {
+ constructor(instance) {
+ this.instance = instance;
+ linkProperties(this, this.instance, [
+ 'options',
+ 'fireEvent',
+ 'wrapper',
+ 'bodyScrollable',
+ 'bodyRenderer',
+ 'style'
+ ]);
+
+ this.bindEvents();
+ this.refreshRows = nextTick(this.refreshRows, this);
+ }
+
+ get datamanager() {
+ return this.instance.datamanager;
+ }
+
+ get cellmanager() {
+ return this.instance.cellmanager;
+ }
+
+ bindEvents() {
+ this.bindCheckbox();
+ }
+
+ bindCheckbox() {
+ if (!this.options.checkboxColumn) return;
+
+ // map of checked rows
+ this.checkMap = [];
+
+ $.on(this.wrapper, 'click', '.dt-cell--col-0 [type="checkbox"]', (e, $checkbox) => {
+ const $cell = $checkbox.closest('.dt-cell');
+ const {
+ rowIndex,
+ isHeader
+ } = $.data($cell);
+ const checked = $checkbox.checked;
+
+ if (isHeader) {
+ this.checkAll(checked);
+ } else {
+ this.checkRow(rowIndex, checked);
+ }
+ });
+ }
+
+ refreshRows() {
+ this.instance.renderBody();
+ this.instance.setDimensions();
+ }
+
+ refreshRow(row, rowIndex) {
+ const _row = this.datamanager.updateRow(row, rowIndex);
+
+ _row.forEach(cell => {
+ this.cellmanager.refreshCell(cell, true);
+ });
+ }
+
+ getCheckedRows() {
+ if (!this.checkMap) {
+ return [];
+ }
+
+ let out = [];
+ for (let rowIndex in this.checkMap) {
+ const checked = this.checkMap[rowIndex];
+ if (checked === 1) {
+ out.push(rowIndex);
+ }
+ }
+
+ return out;
+ }
+
+ highlightCheckedRows() {
+ this.getCheckedRows()
+ .map(rowIndex => this.checkRow(rowIndex, true));
+ }
+
+ checkRow(rowIndex, toggle) {
+ const value = toggle ? 1 : 0;
+ const selector = rowIndex => `.dt-cell--0-${rowIndex} [type="checkbox"]`;
+ // update internal map
+ this.checkMap[rowIndex] = value;
+ // set checkbox value explicitly
+ $.each(selector(rowIndex), this.bodyScrollable)
+ .map(input => {
+ input.checked = toggle;
+ });
+ // highlight row
+ this.highlightRow(rowIndex, toggle);
+ this.showCheckStatus();
+ this.fireEvent('onCheckRow', this.datamanager.getRow(rowIndex));
+ }
+
+ checkAll(toggle) {
+ const value = toggle ? 1 : 0;
+
+ // update internal map
+ if (toggle) {
+ this.checkMap = Array.from(Array(this.getTotalRows())).map(c => value);
+ } else {
+ this.checkMap = [];
+ }
+ // set checkbox value
+ $.each('.dt-cell--col-0 [type="checkbox"]', this.bodyScrollable)
+ .map(input => {
+ input.checked = toggle;
+ });
+ // highlight all
+ this.highlightAll(toggle);
+ this.showCheckStatus();
+ this.fireEvent('onCheckRow');
+ }
+
+ showCheckStatus() {
+ if (!this.options.checkedRowStatus) return;
+ const checkedRows = this.getCheckedRows();
+ const count = checkedRows.length;
+ if (count > 0) {
+ let message = this.instance.translate('{count} rows selected', {
+ count: count
+ });
+ this.bodyRenderer.showToastMessage(message);
+ } else {
+ this.bodyRenderer.clearToastMessage();
+ }
+ }
+
+ highlightRow(rowIndex, toggle = true) {
+ const $row = this.getRow$(rowIndex);
+ if (!$row) return;
+
+ if (!toggle && this.bodyScrollable.classList.contains('dt-scrollable--highlight-all')) {
+ $row.classList.add('dt-row--unhighlight');
+ return;
+ }
+
+ if (toggle && $row.classList.contains('dt-row--unhighlight')) {
+ $row.classList.remove('dt-row--unhighlight');
+ }
+
+ this._highlightedRows = this._highlightedRows || {};
+
+ if (toggle) {
+ $row.classList.add('dt-row--highlight');
+ this._highlightedRows[rowIndex] = $row;
+ } else {
+ $row.classList.remove('dt-row--highlight');
+ delete this._highlightedRows[rowIndex];
+ }
+ }
+
+ highlightAll(toggle = true) {
+ if (toggle) {
+ this.bodyScrollable.classList.add('dt-scrollable--highlight-all');
+ } else {
+ this.bodyScrollable.classList.remove('dt-scrollable--highlight-all');
+ for (const rowIndex in this._highlightedRows) {
+ const $row = this._highlightedRows[rowIndex];
+ $row.classList.remove('dt-row--highlight');
+ }
+ this._highlightedRows = {};
+ }
+ }
+
+ showRows(rowIndices) {
+ rowIndices = ensureArray(rowIndices);
+ const rows = rowIndices.map(rowIndex => this.datamanager.getRow(rowIndex));
+ this.bodyRenderer.renderRows(rows);
+ }
+
+ showAllRows() {
+ const rowIndices = this.datamanager.getAllRowIndices();
+ this.showRows(rowIndices);
+ }
+
+ getChildrenToShowForNode(rowIndex) {
+ const row = this.datamanager.getRow(rowIndex);
+ row.meta.isTreeNodeClose = false;
+
+ return this.datamanager.getImmediateChildren(rowIndex);
+ }
+
+ openSingleNode(rowIndex) {
+ const childrenToShow = this.getChildrenToShowForNode(rowIndex);
+ const visibleRowIndices = this.bodyRenderer.visibleRowIndices;
+ const rowsToShow = uniq$1([...childrenToShow, ...visibleRowIndices]).sort(numberSortAsc);
+
+ this.showRows(rowsToShow);
+ }
+
+ getChildrenToHideForNode(rowIndex) {
+ const row = this.datamanager.getRow(rowIndex);
+ row.meta.isTreeNodeClose = true;
+
+ const rowsToHide = this.datamanager.getChildren(rowIndex);
+ rowsToHide.forEach(rowIndex => {
+ const row = this.datamanager.getRow(rowIndex);
+ if (!row.meta.isLeaf) {
+ row.meta.isTreeNodeClose = true;
+ }
+ });
+
+ return rowsToHide;
+ }
+
+ closeSingleNode(rowIndex) {
+ const rowsToHide = this.getChildrenToHideForNode(rowIndex);
+ const visibleRows = this.bodyRenderer.visibleRowIndices;
+ const rowsToShow = visibleRows
+ .filter(rowIndex => !rowsToHide.includes(rowIndex))
+ .sort(numberSortAsc);
+
+ this.showRows(rowsToShow);
+ }
+
+ expandAllNodes() {
+ let rows = this.datamanager.getRows();
+ let rootNodes = rows.filter(row => !row.meta.isLeaf);
+
+ const childrenToShow = rootNodes.map(row => this.getChildrenToShowForNode(row.meta.rowIndex)).flat();
+ const visibleRowIndices = this.bodyRenderer.visibleRowIndices;
+ const rowsToShow = uniq$1([...childrenToShow, ...visibleRowIndices]).sort(numberSortAsc);
+
+ this.showRows(rowsToShow);
+ }
+
+ collapseAllNodes() {
+ let rows = this.datamanager.getRows();
+ let rootNodes = rows.filter(row => row.meta.indent === 0);
+
+ const rowsToHide = rootNodes.map(row => this.getChildrenToHideForNode(row.meta.rowIndex)).flat();
+ const visibleRows = this.bodyRenderer.visibleRowIndices;
+ const rowsToShow = visibleRows
+ .filter(rowIndex => !rowsToHide.includes(rowIndex))
+ .sort(numberSortAsc);
+
+ this.showRows(rowsToShow);
+ }
+
+ setTreeDepth(depth) {
+ let rows = this.datamanager.getRows();
+
+ const rowsToOpen = rows.filter(row => row.meta.indent < depth);
+ const rowsToClose = rows.filter(row => row.meta.indent >= depth);
+ const rowsToHide = rowsToClose.filter(row => row.meta.indent > depth);
+
+ rowsToClose.forEach(row => {
+ if (!row.meta.isLeaf) {
+ row.meta.isTreeNodeClose = true;
+ }
+ });
+ rowsToOpen.forEach(row => {
+ if (!row.meta.isLeaf) {
+ row.meta.isTreeNodeClose = false;
+ }
+ });
+
+ const rowsToShow = rows
+ .filter(row => !rowsToHide.includes(row))
+ .map(row => row.meta.rowIndex)
+ .sort(numberSortAsc);
+ this.showRows(rowsToShow);
+ }
+
+ getRow$(rowIndex) {
+ return $(this.selector(rowIndex), this.bodyScrollable);
+ }
+
+ getTotalRows() {
+ return this.datamanager.getRowCount();
+ }
+
+ getFirstRowIndex() {
+ return 0;
+ }
+
+ getLastRowIndex() {
+ return this.datamanager.getRowCount() - 1;
+ }
+
+ scrollToRow(rowIndex) {
+ rowIndex = +rowIndex;
+ this._lastScrollTo = this._lastScrollTo || 0;
+ const $row = this.getRow$(rowIndex);
+ if ($.inViewport($row, this.bodyScrollable)) return;
+
+ const {
+ height
+ } = $row.getBoundingClientRect();
+ const {
+ top,
+ bottom
+ } = this.bodyScrollable.getBoundingClientRect();
+ const rowsInView = Math.floor((bottom - top) / height);
+
+ let offset = 0;
+ if (rowIndex > this._lastScrollTo) {
+ offset = height * ((rowIndex + 1) - rowsInView);
+ } else {
+ offset = height * ((rowIndex + 1) - 1);
+ }
+
+ this._lastScrollTo = rowIndex;
+ $.scrollTop(this.bodyScrollable, offset);
+ }
+
+ getRowHTML(row, props) {
+ const dataAttr = makeDataAttributeString(props);
+ let rowIdentifier = props.rowIndex;
+
+ if (props.isFilter) {
+ row = row.map(cell => (Object.assign({}, cell, {
+ content: this.getFilterInput({
+ colIndex: cell.colIndex,
+ name: cell.name
+ }),
+ isFilter: 1,
+ isHeader: undefined,
+ editable: false
+ })));
+
+ rowIdentifier = 'filter';
+ }
+
+ if (props.isHeader) {
+ rowIdentifier = 'header';
+ }
+
+ return `
+
+ ${row.map(cell => this.cellmanager.getCellHTML(cell)).join('')}
+
+ `;
+ }
+
+ getFilterInput(props) {
+ let title = `title="Filter based on ${props.name || 'Index'}"`;
+ const dataAttr = makeDataAttributeString(props);
+ return ``;
+ }
+
+ selector(rowIndex) {
+ return `.dt-row-${rowIndex}`;
+ }
+ }
+
+ var hyperlist = createCommonjsModule(function (module, exports) {
+ (function(f){{module.exports=f();}})(function(){return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof commonjsRequire&&commonjsRequire;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t);}return n[i].exports}for(var u="function"==typeof commonjsRequire&&commonjsRequire,i=0;i _this._averageHeight) {
+ var rendered = _this._renderChunk();
+
+ _this._lastRepaint = scrollTop;
+
+ if (rendered !== false && typeof config.afterRender === 'function') {
+ config.afterRender();
+ }
+ }
+ };
+
+ render();
+ }
+
+ _createClass(HyperList, [{
+ key: 'destroy',
+ value: function destroy() {
+ window.cancelAnimationFrame(this._renderAnimationFrame);
+ }
+ }, {
+ key: 'refresh',
+ value: function refresh(element, userProvidedConfig) {
+ var _scrollerStyle;
+
+ Object.assign(this._config, defaultConfig, userProvidedConfig);
+
+ if (!element || element.nodeType !== 1) {
+ throw new Error('HyperList requires a valid DOM Node container');
+ }
+
+ this._element = element;
+
+ var config = this._config;
+
+ var scroller = this._scroller || config.scroller || document.createElement(config.scrollerTagName || 'tr');
+
+ // Default configuration option `useFragment` to `true`.
+ if (typeof config.useFragment !== 'boolean') {
+ this._config.useFragment = true;
+ }
+
+ if (!config.generate) {
+ throw new Error('Missing required `generate` function');
+ }
+
+ if (!isNumber(config.total)) {
+ throw new Error('Invalid required `total` value, expected number');
+ }
+
+ if (!Array.isArray(config.itemHeight) && !isNumber(config.itemHeight)) {
+ throw new Error('\n Invalid required `itemHeight` value, expected number or array\n '.trim());
+ } else if (isNumber(config.itemHeight)) {
+ this._itemHeights = Array(config.total).fill(config.itemHeight);
+ } else {
+ this._itemHeights = config.itemHeight;
+ }
+
+ // Width and height should be coerced to string representations. Either in
+ // `%` or `px`.
+ Object.keys(defaultConfig).filter(function (prop) {
+ return prop in config;
+ }).forEach(function (prop) {
+ var value = config[prop];
+ var isValueNumber = isNumber(value);
+
+ if (value && typeof value !== 'string' && typeof value !== 'number') {
+ var msg = 'Invalid optional `' + prop + '`, expected string or number';
+ throw new Error(msg);
+ } else if (isValueNumber) {
+ config[prop] = value + 'px';
+ }
+ });
+
+ var isHoriz = Boolean(config.horizontal);
+ var value = config[isHoriz ? 'width' : 'height'];
+
+ if (value) {
+ var isValueNumber = isNumber(value);
+ var isValuePercent = isValueNumber ? false : value.slice(-1) === '%';
+ // Compute the containerHeight as number
+ var numberValue = isValueNumber ? value : parseInt(value.replace(/px|%/, ''), 10);
+ var innerSize = window[isHoriz ? 'innerWidth' : 'innerHeight'];
+
+ if (isValuePercent) {
+ this._containerSize = innerSize * numberValue / 100;
+ } else {
+ this._containerSize = isNumber(value) ? value : numberValue;
+ }
+ }
+
+ var scrollContainer = config.scrollContainer;
+ var scrollerHeight = config.itemHeight * config.total;
+ var maxElementHeight = this._maxElementHeight;
+
+ if (scrollerHeight > maxElementHeight) {
+ console.warn(['HyperList: The maximum element height', maxElementHeight + 'px has', 'been exceeded; please reduce your item height.'].join(' '));
+ }
+
+ // Decorate the container element with styles that will match
+ // the user supplied configuration.
+ var elementStyle = {
+ width: '' + config.width,
+ height: scrollContainer ? scrollerHeight + 'px' : '' + config.height,
+ overflow: scrollContainer ? 'none' : 'auto',
+ position: 'relative'
+ };
+
+ HyperList.mergeStyle(element, elementStyle);
+
+ if (scrollContainer) {
+ HyperList.mergeStyle(config.scrollContainer, { overflow: 'auto' });
+ }
+
+ var scrollerStyle = (_scrollerStyle = {
+ opacity: '0',
+ position: 'absolute'
+ }, _defineProperty(_scrollerStyle, isHoriz ? 'height' : 'width', '1px'), _defineProperty(_scrollerStyle, isHoriz ? 'width' : 'height', scrollerHeight + 'px'), _scrollerStyle);
+
+ HyperList.mergeStyle(scroller, scrollerStyle);
+
+ // Only append the scroller element once.
+ if (!this._scroller) {
+ element.appendChild(scroller);
+ }
+
+ var padding = this._computeScrollPadding();
+ this._scrollPaddingBottom = padding.bottom;
+ this._scrollPaddingTop = padding.top;
+
+ // Set the scroller instance.
+ this._scroller = scroller;
+ this._scrollHeight = this._computeScrollHeight();
+
+ // Reuse the item positions if refreshed, otherwise set to empty array.
+ this._itemPositions = this._itemPositions || Array(config.total).fill(0);
+
+ // Each index in the array should represent the position in the DOM.
+ this._computePositions(0);
+
+ // Render after refreshing. Force render if we're calling refresh manually.
+ this._renderChunk(this._lastRepaint !== null);
+
+ if (typeof config.afterRender === 'function') {
+ config.afterRender();
+ }
+ }
+ }, {
+ key: '_getRow',
+ value: function _getRow(i) {
+ var config = this._config;
+ var item = config.generate(i);
+ var height = item.height;
+
+ if (height !== undefined && isNumber(height)) {
+ item = item.element;
+
+ // The height isn't the same as predicted, compute positions again
+ if (height !== this._itemHeights[i]) {
+ this._itemHeights[i] = height;
+ this._computePositions(i);
+ this._scrollHeight = this._computeScrollHeight(i);
+ }
+ } else {
+ height = this._itemHeights[i];
+ }
+
+ if (!item || item.nodeType !== 1) {
+ throw new Error('Generator did not return a DOM Node for index: ' + i);
+ }
+
+ addClass(item, config.rowClassName || 'vrow');
+
+ var top = this._itemPositions[i] + this._scrollPaddingTop;
+
+ HyperList.mergeStyle(item, _defineProperty({
+ position: 'absolute'
+ }, config.horizontal ? 'left' : 'top', top + 'px'));
+
+ return item;
+ }
+ }, {
+ key: '_getScrollPosition',
+ value: function _getScrollPosition() {
+ var config = this._config;
+
+ if (typeof config.overrideScrollPosition === 'function') {
+ return config.overrideScrollPosition();
+ }
+
+ return this._element[config.horizontal ? 'scrollLeft' : 'scrollTop'];
+ }
+ }, {
+ key: '_renderChunk',
+ value: function _renderChunk(force) {
+ var config = this._config;
+ var element = this._element;
+ var scrollTop = this._getScrollPosition();
+ var total = config.total;
+
+ var from = config.reverse ? this._getReverseFrom(scrollTop) : this._getFrom(scrollTop) - 1;
+
+ if (from < 0 || from - this._screenItemsLen < 0) {
+ from = 0;
+ }
+
+ if (!force && this._lastFrom === from) {
+ return false;
+ }
+
+ this._lastFrom = from;
+
+ var to = from + this._cachedItemsLen;
+
+ if (to > total || to + this._cachedItemsLen > total) {
+ to = total;
+ }
+
+ // Append all the new rows in a document fragment that we will later append
+ // to the parent node
+ var fragment = config.useFragment ? document.createDocumentFragment() : []
+ // Sometimes you'll pass fake elements to this tool and Fragments require
+ // real elements.
+
+
+ // The element that forces the container to scroll.
+ ;var scroller = this._scroller;
+
+ // Keep the scroller in the list of children.
+ fragment[config.useFragment ? 'appendChild' : 'push'](scroller);
+
+ for (var i = from; i < to; i++) {
+ var row = this._getRow(i);
+
+ fragment[config.useFragment ? 'appendChild' : 'push'](row);
+ }
+
+ if (config.applyPatch) {
+ return config.applyPatch(element, fragment);
+ }
+
+ element.innerHTML = '';
+ element.appendChild(fragment);
+ }
+ }, {
+ key: '_computePositions',
+ value: function _computePositions() {
+ var from = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
+
+ var config = this._config;
+ var total = config.total;
+ var reverse = config.reverse;
+
+ if (from < 1 && !reverse) {
+ from = 1;
+ }
+
+ for (var i = from; i < total; i++) {
+ if (reverse) {
+ if (i === 0) {
+ this._itemPositions[0] = this._scrollHeight - this._itemHeights[0];
+ } else {
+ this._itemPositions[i] = this._itemPositions[i - 1] - this._itemHeights[i];
+ }
+ } else {
+ this._itemPositions[i] = this._itemHeights[i - 1] + this._itemPositions[i - 1];
+ }
+ }
+ }
+ }, {
+ key: '_computeScrollHeight',
+ value: function _computeScrollHeight() {
+ var _HyperList$mergeStyle2,
+ _this2 = this;
+
+ var config = this._config;
+ var isHoriz = Boolean(config.horizontal);
+ var total = config.total;
+ var scrollHeight = this._itemHeights.reduce(function (a, b) {
+ return a + b;
+ }, 0) + this._scrollPaddingBottom + this._scrollPaddingTop;
+
+ HyperList.mergeStyle(this._scroller, (_HyperList$mergeStyle2 = {
+ opacity: 0,
+ position: 'absolute',
+ top: '0px'
+ }, _defineProperty(_HyperList$mergeStyle2, isHoriz ? 'height' : 'width', '1px'), _defineProperty(_HyperList$mergeStyle2, isHoriz ? 'width' : 'height', scrollHeight + 'px'), _HyperList$mergeStyle2));
+
+ // Calculate the height median
+ var sortedItemHeights = this._itemHeights.slice(0).sort(function (a, b) {
+ return a - b;
+ });
+ var middle = Math.floor(total / 2);
+ var averageHeight = total % 2 === 0 ? (sortedItemHeights[middle] + sortedItemHeights[middle - 1]) / 2 : sortedItemHeights[middle];
+
+ var clientProp = isHoriz ? 'clientWidth' : 'clientHeight';
+ var element = config.scrollContainer ? config.scrollContainer : this._element;
+ var containerHeight = element[clientProp] ? element[clientProp] : this._containerSize;
+ this._screenItemsLen = Math.ceil(containerHeight / averageHeight);
+ this._containerSize = containerHeight;
+
+ // Cache 3 times the number of items that fit in the container viewport.
+ this._cachedItemsLen = Math.max(this._cachedItemsLen || 0, this._screenItemsLen * 3);
+ this._averageHeight = averageHeight;
+
+ if (config.reverse) {
+ window.requestAnimationFrame(function () {
+ if (isHoriz) {
+ _this2._element.scrollLeft = scrollHeight;
+ } else {
+ _this2._element.scrollTop = scrollHeight;
+ }
+ });
+ }
+
+ return scrollHeight;
+ }
+ }, {
+ key: '_computeScrollPadding',
+ value: function _computeScrollPadding() {
+ var config = this._config;
+ var isHoriz = Boolean(config.horizontal);
+ var isReverse = config.reverse;
+ var styles = window.getComputedStyle(this._element);
+
+ var padding = function padding(location) {
+ var cssValue = styles.getPropertyValue('padding-' + location);
+ return parseInt(cssValue, 10) || 0;
+ };
+
+ if (isHoriz && isReverse) {
+ return {
+ bottom: padding('left'),
+ top: padding('right')
+ };
+ } else if (isHoriz) {
+ return {
+ bottom: padding('right'),
+ top: padding('left')
+ };
+ } else if (isReverse) {
+ return {
+ bottom: padding('top'),
+ top: padding('bottom')
+ };
+ } else {
+ return {
+ bottom: padding('bottom'),
+ top: padding('top')
+ };
+ }
+ }
+ }, {
+ key: '_getFrom',
+ value: function _getFrom(scrollTop) {
+ var i = 0;
+
+ while (this._itemPositions[i] < scrollTop) {
+ i++;
+ }
+
+ return i;
+ }
+ }, {
+ key: '_getReverseFrom',
+ value: function _getReverseFrom(scrollTop) {
+ var i = this._config.total - 1;
+
+ while (i > 0 && this._itemPositions[i] < scrollTop + this._containerSize) {
+ i--;
+ }
+
+ return i;
+ }
+ }]);
+
+ return HyperList;
+ }();
+
+ exports.default = HyperList;
+ module.exports = exports['default'];
+
+ },{}]},{},[1])(1)
+ });
+ });
+
+ var HyperList = unwrapExports(hyperlist);
+
+ class BodyRenderer {
+ constructor(instance) {
+ this.instance = instance;
+ this.options = instance.options;
+ this.datamanager = instance.datamanager;
+ this.rowmanager = instance.rowmanager;
+ this.cellmanager = instance.cellmanager;
+ this.bodyScrollable = instance.bodyScrollable;
+ this.footer = this.instance.footer;
+ this.log = instance.log;
+ }
+
+ renderRows(rows) {
+ this.visibleRows = rows;
+ this.visibleRowIndices = rows.map(row => row.meta.rowIndex);
+
+ if (rows.length === 0) {
+ this.bodyScrollable.innerHTML = this.getNoDataHTML();
+ return;
+ }
+
+ const rowViewOrder = this.datamanager.rowViewOrder.map(index => {
+ if (this.visibleRowIndices.includes(index)) {
+ return index;
+ }
+ return null;
+ }).filter(index => index !== null);
+
+ const computedStyle = getComputedStyle(this.bodyScrollable);
+
+ let config = {
+ width: computedStyle.width,
+ height: computedStyle.height,
+ itemHeight: this.options.cellHeight,
+ total: rows.length,
+ generate: (index) => {
+ const el = document.createElement('div');
+ const rowIndex = rowViewOrder[index];
+ const row = this.datamanager.getRow(rowIndex);
+ const rowHTML = this.rowmanager.getRowHTML(row, row.meta);
+ el.innerHTML = rowHTML;
+ return el.children[0];
+ },
+ afterRender: () => {
+ this.restoreState();
+ }
+ };
+
+ if (!this.hyperlist) {
+ this.hyperlist = new HyperList(this.bodyScrollable, config);
+ } else {
+ this.hyperlist.refresh(this.bodyScrollable, config);
+ }
+
+ this.renderFooter();
+ }
+
+ render() {
+ const rows = this.datamanager.getRowsForView();
+ this.renderRows(rows);
+ // setDimensions requires atleast 1 row to exist in dom
+ this.instance.setDimensions();
+ }
+
+ renderFooter() {
+ if (!this.options.showTotalRow) return;
+
+ const totalRow = this.getTotalRow();
+ let html = this.rowmanager.getRowHTML(totalRow, { isTotalRow: 1, rowIndex: 'totalRow' });
+
+ this.footer.innerHTML = html;
+ }
+
+ getTotalRow() {
+ const columns = this.datamanager.getColumns();
+ const totalRowTemplate = columns.map(col => {
+ let content = null;
+ if (['_rowIndex', '_checkbox'].includes(col.id)) {
+ content = '';
+ }
+ return {
+ content,
+ isTotalRow: 1,
+ colIndex: col.colIndex,
+ column: col
+ };
+ });
+
+ const totalRow = totalRowTemplate.map((cell, i) => {
+ if (cell.content === '') return cell;
+
+ if (this.options.hooks.columnTotal) {
+ const columnValues = this.visibleRows.map(row => row[i].content);
+ const result = this.options.hooks.columnTotal.call(this.instance, columnValues, cell);
+ if (result != null) {
+ cell.content = result;
+ return cell;
+ }
+ }
+
+ cell.content = this.visibleRows.reduce((acc, prevRow) => {
+ const prevCell = prevRow[i];
+ if (typeof prevCell.content === 'number') {
+ if (acc == null) acc = 0;
+ return acc + prevCell.content;
+ }
+ return acc;
+ }, cell.content);
+
+ return cell;
+ });
+
+ return totalRow;
+ }
+
+ restoreState() {
+ this.rowmanager.highlightCheckedRows();
+ this.cellmanager.selectAreaOnClusterChanged();
+ this.cellmanager.focusCellOnClusterChanged();
+ }
+
+ showToastMessage(message, hideAfter) {
+ this.instance.toastMessage.innerHTML = this.getToastMessageHTML(message);
+
+ if (hideAfter) {
+ setTimeout(() => {
+ this.clearToastMessage();
+ }, hideAfter * 1000);
+ }
+ }
+
+ clearToastMessage() {
+ this.instance.toastMessage.innerHTML = '';
+ }
+
+ getNoDataHTML() {
+ return `${this.options.noDataMessage}
`;
+ }
+
+ getToastMessageHTML(message) {
+ return `${message}`;
+ }
+ }
+
+ class Style {
+ constructor(instance) {
+ this.instance = instance;
+
+ linkProperties(this, this.instance, [
+ 'options', 'datamanager', 'columnmanager',
+ 'header', 'footer', 'bodyScrollable', 'datatableWrapper',
+ 'getColumn', 'bodyRenderer'
+ ]);
+
+ this.scopeClass = 'dt-instance-' + instance.constructor.instances;
+ instance.datatableWrapper.classList.add(this.scopeClass);
+
+ const styleEl = document.createElement('style');
+ instance.wrapper.insertBefore(styleEl, instance.datatableWrapper);
+ this.styleEl = styleEl;
+
+ this.bindResizeWindow();
+ this.bindScrollHeader();
+ }
+
+ get stylesheet() {
+ return this.styleEl.sheet;
+ }
+
+ bindResizeWindow() {
+ this.onWindowResize = this.onWindowResize.bind(this);
+ this.onWindowResize = throttle$1(this.onWindowResize, 300);
+
+ if (this.options.layout === 'fluid') {
+ $.on(window, 'resize', this.onWindowResize);
+ }
+ }
+
+ bindScrollHeader() {
+ this._settingHeaderPosition = false;
+
+ $.on(this.bodyScrollable, 'scroll', (e) => {
+ if (this._settingHeaderPosition) return;
+
+ this._settingHeaderPosition = true;
+
+ requestAnimationFrame(() => {
+ const left = -e.target.scrollLeft;
+
+ $.style(this.header, {
+ transform: `translateX(${left}px)`
+ });
+ $.style(this.footer, {
+ transform: `translateX(${left}px)`
+ });
+ this._settingHeaderPosition = false;
+ });
+ });
+ }
+
+ onWindowResize() {
+ this.distributeRemainingWidth();
+ this.refreshColumnWidth();
+ this.setBodyStyle();
+ }
+
+ destroy() {
+ this.styleEl.remove();
+ $.off(window, 'resize', this.onWindowResize);
+ }
+
+ setStyle(selector, styleObject) {
+ if (selector.includes(',')) {
+ selector.split(',')
+ .map(s => s.trim())
+ .forEach(selector => {
+ this.setStyle(selector, styleObject);
+ });
+ return;
+ }
+
+ selector = selector.trim();
+ if (!selector) return;
+
+ this._styleRulesMap = this._styleRulesMap || {};
+ const prefixedSelector = this._getPrefixedSelector(selector);
+
+ if (this._styleRulesMap[prefixedSelector]) {
+ this.removeStyle(selector);
+
+ // merge with old styleobject
+ styleObject = Object.assign({}, this._styleRulesMap[prefixedSelector], styleObject);
+ }
+
+ const styleString = this._getRuleString(styleObject);
+ const ruleString = `${prefixedSelector} { ${styleString} }`;
+
+ this._styleRulesMap[prefixedSelector] = styleObject;
+ this.stylesheet.insertRule(ruleString);
+ }
+
+ removeStyle(selector) {
+ if (selector.includes(',')) {
+ selector.split(',')
+ .map(s => s.trim())
+ .forEach(selector => {
+ this.removeStyle(selector);
+ });
+ return;
+ }
+
+ selector = selector.trim();
+ if (!selector) return;
+
+ // find and remove
+ const prefixedSelector = this._getPrefixedSelector(selector);
+ const index = Array.from(this.stylesheet.cssRules)
+ .findIndex(rule => rule.selectorText === prefixedSelector);
+
+ if (index === -1) return;
+ this.stylesheet.deleteRule(index);
+ }
+
+ _getPrefixedSelector(selector) {
+ return `.${this.scopeClass} ${selector}`;
+ }
+
+ _getRuleString(styleObject) {
+ return Object.keys(styleObject)
+ .map(prop => {
+ let dashed = prop;
+ if (!prop.includes('-')) {
+ dashed = camelCaseToDash(prop);
+ }
+ return `${dashed}:${styleObject[prop]};`;
+ })
+ .join('');
+ }
+
+ setDimensions() {
+ this.setCellHeight();
+ this.setupMinWidth();
+ this.setupNaturalColumnWidth();
+ this.setupColumnWidth();
+ this.distributeRemainingWidth();
+ this.setColumnStyle();
+ this.setBodyStyle();
+ }
+
+ setCellHeight() {
+ this.setStyle('.dt-cell', {
+ height: this.options.cellHeight + 'px'
+ });
+ }
+
+ setupMinWidth() {
+ $.each('.dt-cell--header', this.header).map(col => {
+ const { colIndex } = $.data(col);
+ const column = this.getColumn(colIndex);
+
+ if (!column.minWidth) {
+ const width = $.style($('.dt-cell__content', col), 'width');
+ // only set this once
+ column.minWidth = width;
+ }
+ });
+ }
+
+ setupNaturalColumnWidth() {
+ if (!$('.dt-row')) return;
+
+ $.each('.dt-row-header .dt-cell', this.header).map($headerCell => {
+ const { colIndex } = $.data($headerCell);
+ const column = this.datamanager.getColumn(colIndex);
+ let width = $.style($('.dt-cell__content', $headerCell), 'width');
+ if (typeof width === 'number' && width >= this.options.minimumColumnWidth) {
+ column.naturalWidth = width;
+ } else {
+ column.naturalWidth = this.options.minimumColumnWidth;
+ }
+ });
+
+ // set initial width as naturally calculated by table's first row
+ $.each('.dt-row-0 .dt-cell', this.bodyScrollable).map($cell => {
+ const {
+ colIndex
+ } = $.data($cell);
+ const column = this.datamanager.getColumn(colIndex);
+
+ let naturalWidth = $.style($('.dt-cell__content', $cell), 'width');
+
+ if (typeof naturalWidth === 'number' && naturalWidth >= column.naturalWidth) {
+ column.naturalWidth = naturalWidth;
+ } else {
+ column.naturalWidth = column.naturalWidth;
+ }
+ });
+ }
+
+ setupColumnWidth() {
+ if (this.options.layout === 'ratio') {
+ let totalWidth = $.style(this.datatableWrapper, 'width');
+
+ if (this.options.serialNoColumn) {
+ const rowIndexColumn = this.datamanager.getColumnById('_rowIndex');
+ totalWidth = totalWidth - rowIndexColumn.width - 1;
+ }
+
+ if (this.options.checkboxColumn) {
+ const rowIndexColumn = this.datamanager.getColumnById('_checkbox');
+ totalWidth = totalWidth - rowIndexColumn.width - 1;
+ }
+
+ const totalParts = this.datamanager.getColumns()
+ .map(column => {
+ if (column.id === '_rowIndex' || column.id === '_checkbox') {
+ return 0;
+ }
+ if (!column.width) {
+ column.width = 1;
+ }
+ column.ratioWidth = parseInt(column.width, 10);
+ return column.ratioWidth;
+ })
+ .reduce((a, c) => a + c);
+
+ const onePart = totalWidth / totalParts;
+
+ this.datamanager.getColumns()
+ .map(column => {
+ if (column.id === '_rowIndex' || column.id === '_checkbox') return;
+ column.width = Math.floor(onePart * column.ratioWidth) - 1;
+ });
+ } else {
+ this.datamanager.getColumns()
+ .map(column => {
+ if (!column.width) {
+ column.width = column.naturalWidth;
+ }
+ if (column.id === '_rowIndex') {
+ column.width = this.getRowIndexColumnWidth();
+ }
+ if (column.width < this.options.minimumColumnWidth) {
+ column.width = this.options.minimumColumnWidth;
+ }
+ });
+ }
+ }
+
+ distributeRemainingWidth() {
+ if (this.options.layout !== 'fluid') return;
+
+ const wrapperWidth = $.style(this.instance.datatableWrapper, 'width');
+ let firstRow = $('.dt-row', this.bodyScrollable);
+ let firstRowWidth = wrapperWidth;
+ if (!firstRow) {
+ let headerRow = $('.dt-row', this.instance.header);
+ let cellWidths = Array.from(headerRow.children)
+ .map(cell => cell.offsetWidth);
+ firstRowWidth = cellWidths.reduce((sum, a) => sum + a, 0);
+ } else {
+ firstRowWidth = $.style(firstRow, 'width');
+ }
+ const resizableColumns = this.datamanager.getColumns().filter(col => col.resizable);
+ const deltaWidth = (wrapperWidth - firstRowWidth) / resizableColumns.length;
+
+ resizableColumns.map(col => {
+ const width = $.style(this.getColumnHeaderElement(col.colIndex), 'width');
+ let finalWidth = Math.floor(width + deltaWidth) - 2;
+
+ this.datamanager.updateColumn(col.colIndex, {
+ width: finalWidth
+ });
+ });
+ }
+
+ setColumnStyle() {
+ // align columns
+ this.datamanager.getColumns()
+ .map(column => {
+ // alignment
+ if (!column.align) {
+ column.align = 'left';
+ }
+ if (!['left', 'center', 'right'].includes(column.align)) {
+ column.align = 'left';
+ }
+ this.setStyle(`.dt-cell--col-${column.colIndex}`, {
+ 'text-align': column.align
+ });
+
+ // width
+ this.columnmanager.setColumnHeaderWidth(column.colIndex);
+ this.columnmanager.setColumnWidth(column.colIndex);
+ });
+ }
+
+ refreshColumnWidth() {
+ this.datamanager.getColumns()
+ .map(column => {
+ this.columnmanager.setColumnHeaderWidth(column.colIndex);
+ this.columnmanager.setColumnWidth(column.colIndex);
+ });
+ }
+
+ setBodyStyle() {
+ const bodyWidth = $.style(this.datatableWrapper, 'width');
+ const firstRow = $('.dt-row', this.bodyScrollable);
+ if (!firstRow) return;
+ const rowWidth = $.style(firstRow, 'width');
+
+ let width = bodyWidth > rowWidth ? rowWidth : bodyWidth;
+ $.style(this.bodyScrollable, {
+ width: width + 'px'
+ });
+
+ // remove the body height, so that it resets to it's original
+ $.removeStyle(this.bodyScrollable, 'height');
+
+ // when there are less rows than the container
+ // adapt the container height
+ let bodyHeight = $.getStyle(this.bodyScrollable, 'height');
+ const scrollHeight = (this.bodyRenderer.hyperlist || {})._scrollHeight || Infinity;
+ const hasHorizontalOverflow = $.hasHorizontalOverflow(this.bodyScrollable);
+
+ let height;
+
+ if (scrollHeight < bodyHeight) {
+ height = scrollHeight;
+
+ // account for scrollbar size when
+ // there is horizontal overflow
+ if (hasHorizontalOverflow) {
+ height += $.scrollbarSize();
+ }
+
+ $.style(this.bodyScrollable, {
+ height: height + 'px'
+ });
+ }
+
+ const verticalOverflow = this.bodyScrollable.scrollHeight - this.bodyScrollable.offsetHeight;
+ if (verticalOverflow < $.scrollbarSize()) {
+ // if verticalOverflow is less than scrollbar size
+ // then most likely scrollbar is causing the scroll
+ // which is not needed
+ $.style(this.bodyScrollable, {
+ overflowY: 'hidden'
+ });
+ }
+
+ if (this.options.layout === 'fluid') {
+ $.style(this.bodyScrollable, {
+ overflowX: 'hidden'
+ });
+ }
+ }
+
+ getColumnHeaderElement(colIndex) {
+ colIndex = +colIndex;
+ if (colIndex < 0) return null;
+ return $(`.dt-cell--col-${colIndex}`, this.header);
+ }
+
+ getRowIndexColumnWidth() {
+ const rowCount = this.datamanager.getRowCount();
+ const padding = 22;
+ return $.measureTextWidth(rowCount + '') + padding;
+ }
+ }
+
+ const KEYCODES = {
+ 13: 'enter',
+ 91: 'meta',
+ 16: 'shift',
+ 17: 'ctrl',
+ 18: 'alt',
+ 37: 'left',
+ 38: 'up',
+ 39: 'right',
+ 40: 'down',
+ 9: 'tab',
+ 27: 'esc',
+ 67: 'c',
+ 70: 'f',
+ 86: 'v'
+ };
+
+ class Keyboard {
+ constructor(element) {
+ this.listeners = {};
+ $.on(element, 'keydown', this.handler.bind(this));
+ }
+
+ handler(e) {
+ let key = KEYCODES[e.keyCode];
+
+ if (e.shiftKey && key !== 'shift') {
+ key = 'shift+' + key;
+ }
+
+ if ((e.ctrlKey && key !== 'ctrl') || (e.metaKey && key !== 'meta')) {
+ key = 'ctrl+' + key;
+ }
+
+ const listeners = this.listeners[key];
+
+ if (listeners && listeners.length > 0) {
+ for (let listener of listeners) {
+ const preventBubbling = listener(e);
+ if (preventBubbling === undefined || preventBubbling === true) {
+ e.preventDefault();
+ }
+ }
+ }
+ }
+
+ on(key, listener) {
+ const keys = key.split(',').map(k => k.trim());
+
+ keys.map(key => {
+ this.listeners[key] = this.listeners[key] || [];
+ this.listeners[key].push(listener);
+ });
+ }
+ }
+
+ var en = {
+ "Sort Ascending": "Sort Ascending",
+ "Sort Descending": "Sort Descending",
+ "Reset sorting": "Reset sorting",
+ "Remove column": "Remove column",
+ "No Data": "No Data",
+ "{count} cells copied": {"1":"{count} cell copied","default":"{count} cells copied"},
+ "{count} rows selected": {"1":"{count} row selected","default":"{count} rows selected"}
+ };
+
+ var de = {
+ "Sort Ascending": "Aufsteigend sortieren",
+ "Sort Descending": "Absteigend sortieren",
+ "Reset sorting": "Sortierung zurücksetzen",
+ "Remove column": "Spalte entfernen",
+ "No Data": "Keine Daten",
+ "{count} cells copied": {"1":"{count} Zelle kopiert","default":"{count} Zellen kopiert"},
+ "{count} rows selected": {"1":"{count} Zeile ausgewählt","default":"{count} Zeilen ausgewählt"}
+ };
+
+ var fr = {
+ "Sort Ascending": "Trier par ordre croissant",
+ "Sort Descending": "Trier par ordre décroissant",
+ "Reset sorting": "Réinitialiser le tri",
+ "Remove column": "Supprimer colonne",
+ "No Data": "Pas de données",
+ "{count} cells copied": {"1":"{count} cellule copiée","default":"{count} cellules copiées"},
+ "{count} rows selected": {"1":"{count} ligne sélectionnée","default":"{count} lignes sélectionnées"}
+ };
+
+ var it = {
+ "Sort Ascending": "Ordinamento ascendente",
+ "Sort Descending": "Ordinamento decrescente",
+ "Reset sorting": "Azzeramento ordinamento",
+ "Remove column": "Rimuovi colonna",
+ "No Data": "Nessun dato",
+ "{count} cells copied": {"1":"Copiato {count} cella","default":"{count} celle copiate"},
+ "{count} rows selected": {"1":"{count} linea selezionata","default":"{count} linee selezionate"}
+ };
+
+ function getTranslations() {
+ return {
+ en,
+ de,
+ fr,
+ it,
+ };
+ }
+
+ class TranslationManager {
+ constructor(language) {
+ this.language = language;
+ this.translations = getTranslations();
+ }
+
+ addTranslations(translations) {
+ this.translations = Object.assign(this.translations, translations);
+ }
+
+ translate(sourceText, args) {
+ let translation = (this.translations[this.language] &&
+ this.translations[this.language][sourceText]) || sourceText;
+
+ if (typeof translation === 'object') {
+ translation = args && args.count ?
+ this.getPluralizedTranslation(translation, args.count) :
+ sourceText;
+ }
+
+ return format(translation, args || {});
+ }
+
+ getPluralizedTranslation(translations, count) {
+ return translations[count] || translations['default'];
+ }
+ }
+
+ function filterRows(rows, filters) {
+ let filteredRowIndices = [];
+
+ if (Object.keys(filters).length === 0) {
+ return rows.map(row => row.meta.rowIndex);
+ }
+
+ for (let colIndex in filters) {
+ const keyword = filters[colIndex];
+
+ const filteredRows = filteredRowIndices.length ?
+ filteredRowIndices.map(i => rows[i]) :
+ rows;
+
+ const cells = filteredRows.map(row => row[colIndex]);
+
+ let filter = guessFilter(keyword);
+ let filterMethod = getFilterMethod(rows, filter);
+
+ if (filterMethod) {
+ filteredRowIndices = filterMethod(filter.text, cells);
+ } else {
+ filteredRowIndices = cells.map(cell => cell.rowIndex);
+ }
+ }
+
+ return filteredRowIndices;
+ }
+ function getFilterMethod(rows, filter) {
+ const getFormattedValue = cell => {
+ let formatter = CellManager.getCustomCellFormatter(cell);
+ if (formatter && cell.content) {
+ cell.html = formatter(cell.content, rows[cell.rowIndex], cell.column, rows[cell.rowIndex]);
+ return stripHTML(cell.html);
+ }
+ return cell.content || '';
+ };
+
+ const stringCompareValue = cell =>
+ String(stripHTML(cell.html || '') || getFormattedValue(cell)).toLowerCase();
+
+ const numberCompareValue = cell => parseFloat(cell.content);
+
+ const getCompareValues = (cell, keyword) => {
+ if (cell.column.compareValue) {
+ const compareValues = cell.column.compareValue(cell, keyword);
+ if (compareValues && Array.isArray(compareValues)) return compareValues;
+ }
+
+ // check if it can be converted to number
+ const float = numberCompareValue(cell);
+ if (!isNaN(float)) {
+ return [float, keyword];
+ }
+
+ return [stringCompareValue(cell), keyword];
+ };
+
+ let filterMethodMap = {
+ contains(keyword, cells) {
+ return cells
+ .filter(cell => {
+ const hay = stringCompareValue(cell);
+ const needle = (keyword || '').toLowerCase();
+ return !needle || hay.includes(needle);
+ })
+ .map(cell => cell.rowIndex);
+ },
+
+ greaterThan(keyword, cells) {
+ return cells
+ .filter(cell => {
+ const [compareValue, keywordValue] = getCompareValues(cell, keyword);
+ return compareValue > keywordValue;
+ })
+ .map(cell => cell.rowIndex);
+ },
+
+ lessThan(keyword, cells) {
+ return cells
+ .filter(cell => {
+ const [compareValue, keywordValue] = getCompareValues(cell, keyword);
+ return compareValue < keywordValue;
+ })
+ .map(cell => cell.rowIndex);
+ },
+
+ equals(keyword, cells) {
+ return cells
+ .filter(cell => {
+ const value = parseFloat(cell.content);
+ return value === keyword;
+ })
+ .map(cell => cell.rowIndex);
+ },
+
+ notEquals(keyword, cells) {
+ return cells
+ .filter(cell => {
+ const value = parseFloat(cell.content);
+ return value !== keyword;
+ })
+ .map(cell => cell.rowIndex);
+ },
+
+ range(rangeValues, cells) {
+ return cells
+ .filter(cell => {
+ const values1 = getCompareValues(cell, rangeValues[0]);
+ const values2 = getCompareValues(cell, rangeValues[1]);
+ const value = values1[0];
+ return value >= values1[1] && value <= values2[1];
+ })
+ .map(cell => cell.rowIndex);
+ },
+
+ containsNumber(keyword, cells) {
+ return cells
+ .filter(cell => {
+ let number = parseFloat(keyword, 10);
+ let string = keyword;
+ let hayNumber = numberCompareValue(cell);
+ let hayString = stringCompareValue(cell);
+
+ return number === hayNumber || hayString.includes(string);
+ })
+ .map(cell => cell.rowIndex);
+ }
+ };
+
+ return filterMethodMap[filter.type];
+ }
+
+ function guessFilter(keyword = '') {
+ if (keyword.length === 0) return {};
+
+ let compareString = keyword;
+
+ if (['>', '<', '='].includes(compareString[0])) {
+ compareString = keyword.slice(1);
+ } else if (compareString.startsWith('!=')) {
+ compareString = keyword.slice(2);
+ }
+
+ if (keyword.startsWith('>')) {
+ if (compareString) {
+ return {
+ type: 'greaterThan',
+ text: compareString.trim()
+ };
+ }
+ }
+
+ if (keyword.startsWith('<')) {
+ if (compareString) {
+ return {
+ type: 'lessThan',
+ text: compareString.trim()
+ };
+ }
+ }
+
+ if (keyword.startsWith('=')) {
+ if (isNumber(compareString)) {
+ return {
+ type: 'equals',
+ text: Number(keyword.slice(1).trim())
+ };
+ }
+ }
+
+ if (isNumber(compareString)) {
+ return {
+ type: 'containsNumber',
+ text: compareString
+ };
+ }
+
+ if (keyword.startsWith('!=')) {
+ if (isNumber(compareString)) {
+ return {
+ type: 'notEquals',
+ text: Number(keyword.slice(2).trim())
+ };
+ }
+ }
+
+ if (keyword.split(':').length === 2) {
+ compareString = keyword.split(':');
+ return {
+ type: 'range',
+ text: compareString.map(v => v.trim())
+ };
+ }
+
+ return {
+ type: 'contains',
+ text: compareString.toLowerCase()
+ };
+ }
+
+ function getDefaultOptions(instance) {
+ return {
+ columns: [],
+ data: [],
+ dropdownButton: icons.chevronDown,
+ headerDropdown: [
+ {
+ label: instance.translate('Sort Ascending'),
+ action: function (column) {
+ this.sortColumn(column.colIndex, 'asc');
+ }
+ },
+ {
+ label: instance.translate('Sort Descending'),
+ action: function (column) {
+ this.sortColumn(column.colIndex, 'desc');
+ }
+ },
+ {
+ label: instance.translate('Reset sorting'),
+ action: function (column) {
+ this.sortColumn(column.colIndex, 'none');
+ }
+ },
+ {
+ label: instance.translate('Remove column'),
+ action: function (column) {
+ this.removeColumn(column.colIndex);
+ }
+ }
+ ],
+ events: {
+ onRemoveColumn(column) {},
+ onSwitchColumn(column1, column2) {},
+ onSortColumn(column) {},
+ onCheckRow(row) {},
+ onDestroy() {}
+ },
+ hooks: {
+ columnTotal: null
+ },
+ sortIndicator: {
+ asc: '↑',
+ desc: '↓',
+ none: ''
+ },
+ overrideComponents: {
+ // ColumnManager: CustomColumnManager
+ },
+ filterRows: filterRows,
+ freezeMessage: '',
+ getEditor: null,
+ serialNoColumn: true,
+ checkboxColumn: false,
+ clusterize: true,
+ logs: false,
+ layout: 'fixed', // fixed, fluid, ratio
+ noDataMessage: instance.translate('No Data'),
+ cellHeight: 40,
+ minimumColumnWidth: 30,
+ inlineFilters: false,
+ treeView: false,
+ checkedRowStatus: true,
+ dynamicRowHeight: false,
+ pasteFromClipboard: false,
+ showTotalRow: false,
+ direction: 'ltr',
+ disableReorderColumn: false
+ };
+ }
+
+ let defaultComponents = {
+ DataManager,
+ CellManager,
+ ColumnManager,
+ RowManager,
+ BodyRenderer,
+ Style,
+ Keyboard
+ };
+
+ class DataTable {
+ constructor(wrapper, options) {
+ DataTable.instances++;
+
+ if (typeof wrapper === 'string') {
+ // css selector
+ wrapper = document.querySelector(wrapper);
+ }
+ this.wrapper = wrapper;
+ if (!(this.wrapper instanceof HTMLElement)) {
+ throw new Error('Invalid argument given for `wrapper`');
+ }
+
+ this.initializeTranslations(options);
+ this.setDefaultOptions();
+ this.buildOptions(options);
+ this.prepare();
+ this.initializeComponents();
+
+ if (this.options.data) {
+ this.refresh();
+ this.columnmanager.applyDefaultSortOrder();
+ }
+ }
+
+ initializeTranslations(options) {
+ this.language = options.language || 'en';
+ this.translationManager = new TranslationManager(this.language);
+
+ if (options.translations) {
+ this.translationManager.addTranslations(options.translations);
+ }
+ }
+
+ setDefaultOptions() {
+ this.DEFAULT_OPTIONS = getDefaultOptions(this);
+ }
+
+ buildOptions(options) {
+ this.options = this.options || {};
+
+ this.options = Object.assign(
+ {}, this.DEFAULT_OPTIONS,
+ this.options || {}, options
+ );
+
+ options.headerDropdown = options.headerDropdown || [];
+ this.options.headerDropdown = [
+ ...this.DEFAULT_OPTIONS.headerDropdown,
+ ...options.headerDropdown
+ ];
+
+ // custom user events
+ this.events = Object.assign(
+ {}, this.DEFAULT_OPTIONS.events,
+ this.options.events || {},
+ options.events || {}
+ );
+ this.fireEvent = this.fireEvent.bind(this);
+ }
+
+ prepare() {
+ this.prepareDom();
+ this.unfreeze();
+ }
+
+ initializeComponents() {
+ let components = Object.assign({}, defaultComponents, this.options.overrideComponents);
+ let {
+ Style: Style$$1,
+ Keyboard: Keyboard$$1,
+ DataManager: DataManager$$1,
+ RowManager: RowManager$$1,
+ ColumnManager: ColumnManager$$1,
+ CellManager: CellManager$$1,
+ BodyRenderer: BodyRenderer$$1
+ } = components;
+
+ this.style = new Style$$1(this);
+ this.keyboard = new Keyboard$$1(this.wrapper);
+ this.datamanager = new DataManager$$1(this.options);
+ this.rowmanager = new RowManager$$1(this);
+ this.columnmanager = new ColumnManager$$1(this);
+ this.cellmanager = new CellManager$$1(this);
+ this.bodyRenderer = new BodyRenderer$$1(this);
+ }
+
+ prepareDom() {
+ this.wrapper.innerHTML = `
+
+
+
+
+
+
+ ${this.options.freezeMessage}
+
+
+
+
+
+
+ `;
+
+ this.datatableWrapper = $('.datatable', this.wrapper);
+ this.header = $('.dt-header', this.wrapper);
+ this.footer = $('.dt-footer', this.wrapper);
+ this.bodyScrollable = $('.dt-scrollable', this.wrapper);
+ this.freezeContainer = $('.dt-freeze', this.wrapper);
+ this.toastMessage = $('.dt-toast', this.wrapper);
+ this.pasteTarget = $('.dt-paste-target', this.wrapper);
+ this.dropdownContainer = $('.dt-dropdown-container', this.wrapper);
+ }
+
+ refresh(data, columns) {
+ this.datamanager.init(data, columns);
+ this.render();
+ this.setDimensions();
+ }
+
+ destroy() {
+ this.wrapper.innerHTML = '';
+ this.style.destroy();
+ this.fireEvent('onDestroy');
+ }
+
+ appendRows(rows) {
+ this.datamanager.appendRows(rows);
+ this.rowmanager.refreshRows();
+ }
+
+ refreshRow(row, rowIndex) {
+ this.rowmanager.refreshRow(row, rowIndex);
+ }
+
+ render() {
+ this.renderHeader();
+ this.renderBody();
+ }
+
+ renderHeader() {
+ this.columnmanager.renderHeader();
+ }
+
+ renderBody() {
+ this.bodyRenderer.render();
+ }
+
+ setDimensions() {
+ this.style.setDimensions();
+ }
+
+ showToastMessage(message, hideAfter) {
+ this.bodyRenderer.showToastMessage(message, hideAfter);
+ }
+
+ clearToastMessage() {
+ this.bodyRenderer.clearToastMessage();
+ }
+
+ getColumn(colIndex) {
+ return this.datamanager.getColumn(colIndex);
+ }
+
+ getColumns() {
+ return this.datamanager.getColumns();
+ }
+
+ getRows() {
+ return this.datamanager.getRows();
+ }
+
+ getCell(colIndex, rowIndex) {
+ return this.datamanager.getCell(colIndex, rowIndex);
+ }
+
+ getColumnHeaderElement(colIndex) {
+ return this.columnmanager.getColumnHeaderElement(colIndex);
+ }
+
+ getViewportHeight() {
+ if (!this.viewportHeight) {
+ this.viewportHeight = $.style(this.bodyScrollable, 'height');
+ }
+
+ return this.viewportHeight;
+ }
+
+ sortColumn(colIndex, sortOrder) {
+ this.columnmanager.sortColumn(colIndex, sortOrder);
+ }
+
+ removeColumn(colIndex) {
+ this.columnmanager.removeColumn(colIndex);
+ }
+
+ scrollToLastColumn() {
+ this.datatableWrapper.scrollLeft = 9999;
+ }
+
+ freeze() {
+ $.style(this.freezeContainer, {
+ display: ''
+ });
+ }
+
+ unfreeze() {
+ $.style(this.freezeContainer, {
+ display: 'none'
+ });
+ }
+
+ updateOptions(options) {
+ this.buildOptions(options);
+ }
+
+ fireEvent(eventName, ...args) {
+ // fire internalEventHandlers if any
+ // and then user events
+ const handlers = [
+ ...(this._internalEventHandlers[eventName] || []),
+ this.events[eventName]
+ ].filter(Boolean);
+
+ for (let handler of handlers) {
+ handler.apply(this, args);
+ }
+ }
+
+ on(event, handler) {
+ this._internalEventHandlers = this._internalEventHandlers || {};
+ this._internalEventHandlers[event] = this._internalEventHandlers[event] || [];
+ this._internalEventHandlers[event].push(handler);
+ }
+
+ log() {
+ if (this.options.logs) {
+ console.log.apply(console, arguments);
+ }
+ }
+
+ translate(str, args) {
+ return this.translationManager.translate(str, args);
+ }
+ }
+
+ DataTable.instances = 0;
+
+ var name = "influxframework-datatable";
+ var version = "0.0.0-development";
+ var description = "A modern datatable library for the web";
+ var main = "dist/influxframework-datatable.cjs.js";
+ var unpkg = "dist/influxframework-datatable.min.js";
+ var jsdelivr = "dist/influxframework-datatable.min.js";
+ var scripts = {"start":"yarn run dev","build":"rollup -c && NODE_ENV=production rollup -c","dev":"rollup -c -w","cy:server":"http-server -p 8989","cy:open":"cypress open","cy:run":"cypress run","test":"start-server-and-test cy:server http://localhost:8989 cy:run","test-local":"start-server-and-test cy:server http://localhost:8989 cy:open","travis-deploy-once":"travis-deploy-once","semantic-release":"semantic-release","lint":"eslint src","lint-and-build":"yarn lint && yarn build","commit":"npx git-cz"};
+ var files = ["dist","src"];
+ var devDependencies = {"autoprefixer":"^9.0.0","chai":"3.5.0","cypress":"^9.2.0","cz-conventional-changelog":"^2.1.0","deepmerge":"^2.0.1","eslint":"^5.0.1","eslint-config-airbnb":"^16.1.0","eslint-config-airbnb-base":"^12.1.0","eslint-plugin-import":"^2.11.0","http-server":"^0.11.1","mocha":"3.3.0","postcss-custom-properties":"^7.0.0","postcss-nested":"^3.0.0","rollup":"^0.59.4","rollup-plugin-commonjs":"^8.3.0","rollup-plugin-eslint":"^4.0.0","rollup-plugin-json":"^2.3.0","rollup-plugin-node-resolve":"^3.0.3","rollup-plugin-postcss":"^1.2.8","rollup-plugin-uglify-es":"^0.0.1","semantic-release":"^17.1.1","start-server-and-test":"^1.4.1","travis-deploy-once":"^5.0.1"};
+ var repository = {"type":"git","url":"https://github.com/influxframework/datatable.git"};
+ var keywords = ["datatable","data","grid","table"];
+ var author = "Faris Ansari";
+ var license = "MIT";
+ var bugs = {"url":"https://github.com/influxframework/datatable/issues"};
+ var homepage = "https://influxframework.com/datatable";
+ var dependencies = {"hyperlist":"^1.0.0-beta","lodash":"^4.17.5","sortablejs":"^1.7.0"};
+ var config = {"commitizen":{"path":"cz-conventional-changelog"}};
+ var packageJson = {
+ name: name,
+ version: version,
+ description: description,
+ main: main,
+ unpkg: unpkg,
+ jsdelivr: jsdelivr,
+ scripts: scripts,
+ files: files,
+ devDependencies: devDependencies,
+ repository: repository,
+ keywords: keywords,
+ author: author,
+ license: license,
+ bugs: bugs,
+ homepage: homepage,
+ dependencies: dependencies,
+ config: config
+ };
+
+ DataTable.__version__ = packageJson.version;
+
+ return DataTable;
+
+}(Sortable));