UNPKG

stepler

Version:

Define iteration rules for certain data structure and compute next step value from input data.

243 lines (197 loc) 8.14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; function resolve(obj, prop) { var val = obj[prop]; for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { args[_key - 2] = arguments[_key]; } return typeof val !== "function" ? val : val.apply(undefined, args); } function resolveRequired(obj, prop) { var val = resolve.apply(undefined, arguments); if (val == null) { throw new Error("[stepler] Required option '" + prop + "' is missed"); } return val; } function resolveOptional(defaultVal) { for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { args[_key2 - 1] = arguments[_key2]; } var val = resolve.apply(undefined, args); return val != null ? val : defaultVal; } var negate = function negate(fn) { return function () { return fn.apply(undefined, arguments) * -1; }; }; var has = function has(obj, key) { return obj.hasOwnProperty(key); }; // ----------- function findIndex(val, list, criteria) { if (typeof criteria !== "function") { return list.indexOf(val); } for (var i = 0; i < list.length; i++) { if (criteria(val, list[i])) { return i; } } return -1; } // ----------- var getStep = function getStep(options) { for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { args[_key3 - 1] = arguments[_key3]; } return resolveOptional.apply(undefined, [1, options, "step"].concat(args)); }; var getList = function getList(options) { for (var _len4 = arguments.length, args = Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { args[_key4 - 1] = arguments[_key4]; } return resolveRequired.apply(undefined, [options, "list"].concat(args)); }; var getVal = function getVal(options) { for (var _len5 = arguments.length, args = Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) { args[_key5 - 1] = arguments[_key5]; } return resolveRequired.apply(undefined, [options, "val"].concat(args)); }; var formatResult = function formatResult(val, options) { for (var _len6 = arguments.length, args = Array(_len6 > 2 ? _len6 - 2 : 0), _key6 = 2; _key6 < _len6; _key6++) { args[_key6 - 2] = arguments[_key6]; } return resolveOptional.apply(undefined, [val, options, "format", val].concat(args)); }; // ----------- var OVERFLOW_STOP = "stop"; var OVERFLOW_LOOP = "loop"; var OVERFLOW_SNAP = "snap"; var handleOverflow = function handleOverflow(opt, next, val, forward, min, max, data) { if (typeof opt === "function") { return opt(next, data, { forward: forward, max: max, min: min, val: val }); } switch (opt) { case OVERFLOW_STOP: return val; case OVERFLOW_LOOP: return forward ? min : max; case OVERFLOW_SNAP: return forward ? max : min; default: throw new Error("[stepler] Unknown value for 'overflow' option: '" + opt + "'"); } }; // ----------- var iterator = function iterator(options) { options = _extends({}, options); // clone var _options = options; var _options$overflow = _options.overflow; var overflow = _options$overflow === undefined ? OVERFLOW_STOP : _options$overflow; return function (data) { var step = getStep(options, data); var val = getVal(options, data); var max = resolveRequired(options, "max", data); var min = resolveOptional(0, options, "min", data); var forward = step > 0; var next = val + step; var isOverflow = forward ? next > max : next < min; // Consider calling overflow function as a kind of exception - don't apply formatting. if (isOverflow && typeof overflow === "function") { return overflow(next, data, { forward: forward, max: max, min: min, val: val }); } return formatResult(!isOverflow ? next : handleOverflow(overflow, next, val, forward, min, max, data), options, data, { forward: forward }); }; }; iterator.list = function (options) { var overflow = options.overflow; var hasOverflow = false; var overflowValue = undefined; var isForward = false; var next = iterator(_extends({}, options, { min: 0, max: function max(data) { return getList(options, data).length - 1; }, val: function val(data) { return findIndex(getVal(options, data), getList(options, data), options.match); }, format: function format(val, data, _ref) { var forward = _ref.forward; isForward = forward; // don't format intermediate value (i.e. index) return val; }, overflow: typeof overflow !== "function" ? overflow : function () { hasOverflow = true; overflowValue = overflow.apply(undefined, arguments); return overflowValue; }, step: function step() { for (var _len7 = arguments.length, args = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { args[_key7] = arguments[_key7]; } var step = getStep.apply(undefined, [options].concat(args)); if (Math.round(step) !== step) { throw new Error("[stepler] Fractional step size is not allowed for list iterator (got " + step + ")"); } return step; } })); return function (data) { hasOverflow = false; overflowValue = undefined; isForward = false; var nextIdx = next(data); var list = getList(options, data); var nextItem = list[nextIdx]; return hasOverflow ? overflowValue : formatResult(nextItem, options, data, { forward: isForward }); }; }; // ----------- var paired = function paired(factory) { factory.pair = function (options) { var step = function step() { for (var _len8 = arguments.length, args = Array(_len8), _key8 = 0; _key8 < _len8; _key8++) { args[_key8] = arguments[_key8]; } var step = getStep.apply(undefined, [options].concat(args)); if (step < 0) { throw new Error("[stepler] Negative step size is not allowed for paired iterator (got " + step + ")"); } return Math.abs(step); }; if (has(options, "overflow") && (has(options, "overflowForward") || has(options, "overflowBackward"))) { throw new Error("[stepler] It's not allowed to use at the same time options 'overflow' and 'overflowBackward' / 'overflowForward'"); } if (has(options, "format") && (has(options, "formatForward") || has(options, "formatBackward"))) { throw new Error("[stepler] It's not allowed to use at the same time options 'format' and 'formatBackward' / 'formatForward'"); } return { prev: factory(_extends({}, options, { step: negate(step), overflow: options.overflowBackward || options.overflow, format: options.formatBackward || options.format })), next: factory(_extends({}, options, { step: step, overflow: options.overflowForward || options.overflow, format: options.formatForward || options.format })) }; }; }; paired(iterator); paired(iterator.list); // ----------- iterator.OVERFLOW_STOP = OVERFLOW_STOP; iterator.OVERFLOW_LOOP = OVERFLOW_LOOP; iterator.OVERFLOW_SNAP = OVERFLOW_SNAP; exports["default"] = iterator; module.exports = exports["default"];