web/frontend/libs/@deltix/hd.components-utils/index.esm.js (417 lines of code) (raw):

import { Subscriber } from 'rxjs'; import { filter, map } from 'rxjs/operators'; import * as R from 'ramda'; import { toDecimal, roundDecimalBy } from '@deltix/decimal-utils'; import Big from 'big.js'; const reverseString$1 = str => Array.from(str).reverse().join(); const trimEnd$1 = (str, delimiter) => { if (!delimiter) { return reverseString$1(reverseString$1(str).replace(/^\s+/gm, "")); } let index = str.length; while (str.endsWith(delimiter) && index >= 0) { str = str.substring(0, str.length - 1); --index; } return str; }; const uniqueArray = (...arr) => { const d = {}; const result = []; for (const a of arr) { for (const item of a) { if (d[item] === void 0) { result.push(item); d[item] = 0; } } } return result; }; const boundary = (min, max) => value => Math.min(min, Math.max(max, value)); // todo VolumePlotter const boundaryFor = (min, max, value) => Math.min(min, Math.max(max, value)); const isFunction = v => !!(v && v.constructor && v.call && v.apply); const isUndefined = v => typeof v === "undefined"; const isDefined = v => !isUndefined(v); const isObject = v => isPlainObject(v); function isObjectObject(o) { return (o != null && typeof o === "object" && Array.isArray(o) === false) === true && Object.prototype.toString.call(o) === "[object Object]"; } function isPlainObject(o) { if (isObjectObject(o) === false) { return false; } // If has modified constructor const ctor = o.constructor; if (typeof ctor !== "function") { return false; } // If has modified prototype const prot = ctor.prototype; if (isObjectObject(prot) === false) { return false; } // If constructor does not have an Object-specific method if (prot.hasOwnProperty("isPrototypeOf") === false) { return false; } // Most likely a plain Object return true; } const createThunk = v => isFunction(v) ? v : () => v; const delay = duration => new Promise(resolve => setTimeout(resolve, duration)); // see test for exploration const stringDifferentPart = (first, second) => { if (first === second) { return 0; } let differentIndex = -1; for (let i = first.length - 1; i >= 0; i--) { if ("." === first[i] || first[i] === second[i]) { continue; } differentIndex = i; } return -1 === differentIndex ? 0 : first.length - differentIndex; }; var EResourceType; (function (EResourceType) { EResourceType["image"] = "image"; EResourceType["font"] = "font"; EResourceType["bitmap"] = "bitmap"; })(EResourceType || (EResourceType = {})); function trimRightChar(s, c) { while (s.charAt(s.length - 1) === c) { s = s.substring(0, s.length - 1); } return s; } const getRealPrecision = (price, precision, symbolWidth, maxWidth) => { const fixed = price.toFixed(precision); const overflow = fixed.length * symbolWidth - maxWidth; if (overflow <= 0) { return precision; } return Math.max(0, precision - Math.max(0, Math.ceil(overflow / symbolWidth))); }; const splitPriceWithMaxWidth = (price, precision, symbolWidth, maxWidth) => { return splitPrice(price, getRealPrecision(price, precision, symbolWidth, maxWidth)); }; const splitPrice = (price, precision) => { const fixed = price.toFixed(precision); // eslint-disable-next-line prefer-const let [ceil, decimal] = fixed.split('.'); decimal = decimal || ''; const reversed = decimal.split('').reverse(); let zeroPartLength = 0; for (; zeroPartLength < decimal.length; zeroPartLength++) { if (reversed[zeroPartLength] !== '0') { break; } } return { ceil, decimal: trimRightChar(decimal, '0'), zero: '0'.repeat(zeroPartLength) }; }; class TakeWhileInclusiveSubscriber extends Subscriber { constructor(destination, predicate) { super(destination); this.destination = destination; this.predicate = predicate; this.index = 0; } _next(value) { const destination = this.destination; let result; try { result = this.predicate(value, this.index++); } catch (err) { destination.error(err); return; } destination.next(value); if (!result) { destination.complete(); } } } /* tslint:disable-next-line*/ class TakeWhileInclusiveOperator { constructor(predicate) { this.predicate = predicate; } call(subscriber, source) { return source.subscribe(new TakeWhileInclusiveSubscriber(subscriber, this.predicate)); } } /** * Emits values emitted by the source Observable so long as each value satisfies * the given `predicate`, and then completes after the last emitted value * satisfies the `predicate`. * * `takeWhileInclusive` subscribes and begins mirroring the source Observable. * Each value emitted on the source is emitted then given to the `predicate` * function which returns a boolean, representing a condition to be satisfied by * the source values. The output Observable emits the source values until such * time as the `predicate` returns false, at which point `takeWhileInclusive` * stops mirroring the source Observable and completes the output Observable. * * @param {function(value: T, index: number): boolean} predicate A function that * evaluates a value emitted by the source Observable and returns a boolean. * Also takes the (zero-based) index as the second argument. * @return {Observable<T>} An Observable that emits the values from the source * Observable and completes after emitting a value that satisfies the condition * defined by the `predicate`. * @method takeWhileInclusive * @owner Observable */ function takeWhileInclusive(predicate) { return source => source.lift(new TakeWhileInclusiveOperator(predicate)); } var EGradientDirection; (function (EGradientDirection) { EGradientDirection["vertical"] = "vertical"; EGradientDirection["horizontal"] = "horizontal"; })(EGradientDirection || (EGradientDirection = {})); const getType = actionCreator => actionCreator().type; function isCreator(...creators) { const creatorTypes = {}; for (const c of creators) { creatorTypes[c().type] = void 0; } return source => source.pipe(filter(v => { if (typeof v !== 'object') { return false; } if (!('type' in v)) { return false; } return v.type in creatorTypes; })); } const select = selector => state$ => map(() => selector(state$.value)); const createRootReducer = (reducerMap, initialState = undefined) => { const map = reducerMap.reduce((reducerMap, [reducer, ...actions]) => actions.reduce((map, action) => { const type = getType(action); if (!map.hasOwnProperty(type)) { map[type] = []; } map[type].push(reducer); return map; }, reducerMap), {}); return (state = initialState, action) => map.hasOwnProperty(action.type) ? map[action.type].reduce((nextState, reducer) => reducer(nextState, action), state) : state; }; const rootSymbol = '@root'; const enhancePath = (path, reducer) => (state, action) => { const current = R.path(path, state); const next = reducer(current, action); if (next === current) { return state; } return R.assocPath(path, next, state); }; const combineMaps = (...maps) => { const reducers = []; maps.forEach(map => { Object.entries(map).forEach(([path, reducer]) => { if (path === rootSymbol) { reducers.push(reducer); return; } reducers.push(enhancePath(path.split('.'), reducer)); }); }); return mergeReducer(...reducers); }; const mergeReducer = (...reducers) => (state, action) => reducers.reduce((nextState, reducer) => reducer(nextState, action), state); const enhanceReducer = (part, reducer) => (state, action) => { const statePart = state[part]; const nextStatePart = reducer(statePart, action); if (nextStatePart !== statePart) { return R.assoc(part, nextStatePart, state); } return state; }; /** * Return tuple (number, bool). * * IF price founded second value will be true first will be index of element. * ELSE first value will index of nearest element and second value will be false. */ const binarySearch = comparator => direction => (haystack, needle) => { let low = 0; let high = haystack.length - 1; while (low <= high) { /* Note that "(low + high) >>> 1" may overflow, and results in a typecast * to double (which gives the wrong results). */ const mid = low + (high - low >> 1); const cmp = +comparator(haystack[mid], needle) * (direction === "ASC" ? -1 : 1); /* Too low. */ if (cmp < 0.0) { low = mid + 1; /* Too high. */ } else if (cmp > 0.0) { high = mid - 1; /* Key found. */ } else { return [mid, true]; } } /* Key not found. */ return [low, false]; }; /** * @param min * @param max * @param ticks suggest count ticks */ const createTicks = (min, max, ticks) => { // This routine creates the Y axis values for a graph. // // Calculate Min amd Max graphical labels and graph // increments. The number of ticks defaults to // 5 which is the SUGGESTED value. Any tick value // entered is used as a suggested value which is // adjusted to be a 'pretty' value. // // Output will be an array of the Y axis values that // encompass the Y values. const result = []; // If min and max are identical, then // adjust the min and max values to actually // make a graph. Also avoids division by zero errors. if (min === max) { min = min - 10; // some small value max = max + 10; // some small value } // Determine Range const range = max - min; // Adjust ticks if needed if (ticks < 2) { ticks = 2; } else if (ticks > 2) { ticks = Math.max(2, ticks - 2); } // calculate an initial guess at step size const tempStep = range / ticks; // Calculate pretty step value const magnitude = Math.floor(Math.log(tempStep) / Math.LN10); const magnitudePow = Math.pow(10, magnitude); // calculate most significant digit of the new step size const magnitudeMsd = tempStep / magnitudePow + 0.5 | 0; const stepSize = magnitudeMsd * magnitudePow; // Lower and upper bounds calculations const loverBoundary = stepSize * Math.floor(min / stepSize); const upperBoundary = stepSize * Math.ceil(max / stepSize); // Build array let val = loverBoundary; if (isFinite(ticks)) { while (result.length <= ticks) { result.push(val); val += stepSize; if (val >= upperBoundary) { break; } } } return result; }; /** * @param min * @param max * @param ticks suggest count ticks */ const createDecimalTicks = (min, max, ticks) => { // This routine creates the Y axis values for a graph. // // Calculate Min amd Max graphical labels and graph // increments. The number of ticks defaults to // 5 which is the SUGGESTED value. Any tick value // entered is used as a suggested value which is // adjusted to be a 'pretty' value. // // Output will be an array of the Y axis values that // encompass the Y values. const result = []; // If min and max are identical, then // adjust the min and max values to actually // make a graph. Also avoids division by zero errors. if (min.eq(max)) { min = min.minus(10); // some small value max = max.add(10); // some small value } // Determine Range const range = max.minus(min); // Adjust ticks if needed if (ticks < 2) { ticks = 2; } else if (ticks > 2) { ticks = Math.max(2, ticks - 2); } // calculate an initial guess at step size const tempStep = range.div(ticks); // Calculate pretty step value const magnitude = Math.floor(Math.log(tempStep.toNumber()) / Math.LN10); const magnitudePow = Math.pow(10, magnitude); // calculate most significant digit of the new step size const magnitudeMsd = tempStep.toNumber() / magnitudePow + 0.5 | 0; const stepSize = magnitudeMsd * magnitudePow; // Lower and upper bounds calculations const loverBoundary = toDecimal(stepSize * Math.floor(min.toNumber() / stepSize)); const upperBoundary = toDecimal(stepSize * Math.ceil(max.toNumber() / stepSize)); // Build array let val = loverBoundary; if (isFinite(ticks)) { while (result.length <= ticks) { result.push(val); val = val.add(stepSize); if (val.gte(upperBoundary)) { break; } } } return result; }; const bigHelper = { max: (a, b) => a.gt(b) ? a : b, min: (a, b) => a.lt(b) ? a : b }; const reverseString = str => Array.from(str).reverse().join(); const trimEnd = (str, delimiter) => { if (!delimiter) { return reverseString(reverseString(str).replace(/^\s+/gm, '')); } let index = str.length; while (str.endsWith(delimiter) && index >= 0) { str = str.substring(0, str.length - 1); --index; } return str; }; const formatMoney = (sum, precision) => { const [ceil, decimal] = sum.toFixed(precision).split('.'); // const ceilFormatted = ceil.replace(/(\d)(?=(\d{3})+$)/g, "$1"); const formattedDecimal = undefined === decimal ? '' : trimEnd(decimal, '0'); return precision === 0 || formattedDecimal.length === 0 ? ceil : `${ceil}.${formattedDecimal}`; }; const fullFieldTime = unit => unit < 10 ? "0" + unit : unit.toString(); var Format; (function (Format) { Format[Format["MM-DD-YY HH-mm-SS"] = 0] = "MM-DD-YY HH-mm-SS"; Format[Format["HH-mm-SS"] = 1] = "HH-mm-SS"; })(Format || (Format = {})); const formatTime = (time, format) => { const d = new Date(time); const f = fullFieldTime; if (format === Format["MM-DD-YY HH-mm-SS"]) { return `${f(d.getMonth() + 1)}-${f(d.getDate())}-${d.getFullYear()} ${f(d.getHours())}:${f(d.getMinutes())}:${f(d.getSeconds())}`; } if (format === Format["HH-mm-SS"]) { return `${f(d.getHours())}:${f(d.getMinutes())}:${f(d.getSeconds())}`; } return `${time}`; }; // In Math: // Floor: Go to the next integer left of where you are. // Ceiling: Go to the next integer right of where you are. // // -.8 -.8 // ---|-----|--|-----|--|-------------> // -1 0 1 // // Math.floor(-.8) === -1 <- go to first left // Math.floor(.8) === 0 <- go to first left // Math.ceil(-.8) === 0 <- go to first right // Math.ceil(.8) === 1 <- go to first right const roundOffset = n => n === 0 ? 0 : n > 0 ? Math.floor(Math.abs(n)) : -Math.ceil(Math.abs(n)); const formatPriceByPrecision = (precision, price) => { if (price == null) { return null; } const value = roundDecimalBy(new Big(price), precision); return value instanceof Big ? value.toFixed() : value; }; const month = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; const shortMonth = ["Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec"]; const decimalPartWithoutZero = (labels, maxLength = 6) => { const countZero = []; const allDecimalPartIsZero = []; labels.forEach(label => { const [_, decimal] = label.toFixed(maxLength).split("."); if (!decimal) { return; } let countZeroInString = 0; for (const char of decimal.split("").reverse()) { if (char !== "0") { break; } countZeroInString++; } countZero.push(countZeroInString); allDecimalPartIsZero.push(decimal.length === countZeroInString); }); if (0 === countZero.length) { return 0; } return Math.min.apply(Math, countZero) + +allDecimalPartIsZero.every(v => v); }; const prettyAxisLabels = (labels, maxLength = 6) => { const slice = decimalPartWithoutZero(labels, maxLength) || -Infinity; return labels.map(label => label.toFixed(maxLength).slice(0, -slice)); }; const bigNumberToString = bigNum => { var _a; return (_a = bigNum === null || bigNum === void 0 ? void 0 : bigNum.toFixed()) !== null && _a !== void 0 ? _a : ''; }; export { EGradientDirection, EResourceType, Format, bigHelper, bigNumberToString, binarySearch, boundary, boundaryFor, combineMaps, createDecimalTicks, createRootReducer, createThunk, createTicks, delay, enhanceReducer, formatMoney, formatPriceByPrecision, formatTime, fullFieldTime, getRealPrecision, getType, isCreator, isDefined, isFunction, isObject, isUndefined, mergeReducer, month, prettyAxisLabels, reverseString$1 as reverseString, rootSymbol, roundOffset, select, shortMonth, splitPrice, splitPriceWithMaxWidth, stringDifferentPart, takeWhileInclusive, trimEnd$1 as trimEnd, uniqueArray };