2019-08-09 19:13:18 +03:00
|
|
|
///
|
2020-02-20 10:26:43 +02:00
|
|
|
/// Copyright © 2016-2020 The Thingsboard Authors
|
2019-08-09 19:13:18 +03:00
|
|
|
///
|
|
|
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
/// you may not use this file except in compliance with the License.
|
|
|
|
|
/// You may obtain a copy of the License at
|
|
|
|
|
///
|
|
|
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
///
|
|
|
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
/// See the License for the specific language governing permissions and
|
|
|
|
|
/// limitations under the License.
|
|
|
|
|
///
|
|
|
|
|
|
2020-02-26 18:15:04 +02:00
|
|
|
import _ from 'lodash';
|
2020-04-24 11:22:26 +03:00
|
|
|
import { Observable, Subject, fromEvent, of } from 'rxjs';
|
2020-03-02 12:15:14 +02:00
|
|
|
import { finalize, share, map } from 'rxjs/operators';
|
2019-09-05 21:15:40 +03:00
|
|
|
import base64js from 'base64-js';
|
2019-08-09 19:13:18 +03:00
|
|
|
|
|
|
|
|
export function onParentScrollOrWindowResize(el: Node): Observable<Event> {
|
|
|
|
|
const scrollSubject = new Subject<Event>();
|
|
|
|
|
const scrollParentNodes = scrollParents(el);
|
|
|
|
|
const eventListenerObject: EventListenerObject = {
|
|
|
|
|
handleEvent(evt: Event) {
|
|
|
|
|
scrollSubject.next(evt);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
scrollParentNodes.forEach((scrollParentNode) => {
|
|
|
|
|
scrollParentNode.addEventListener('scroll', eventListenerObject);
|
|
|
|
|
});
|
|
|
|
|
window.addEventListener('resize', eventListenerObject);
|
|
|
|
|
const shared = scrollSubject.pipe(
|
|
|
|
|
finalize(() => {
|
|
|
|
|
scrollParentNodes.forEach((scrollParentNode) => {
|
|
|
|
|
scrollParentNode.removeEventListener('scroll', eventListenerObject);
|
|
|
|
|
});
|
|
|
|
|
window.removeEventListener('resize', eventListenerObject);
|
|
|
|
|
}),
|
|
|
|
|
share()
|
|
|
|
|
);
|
|
|
|
|
return shared;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-20 20:42:48 +03:00
|
|
|
export function isLocalUrl(url: string): boolean {
|
|
|
|
|
const parser = document.createElement('a');
|
|
|
|
|
parser.href = url;
|
|
|
|
|
const host = parser.hostname;
|
|
|
|
|
if (host === 'localhost' || host === '127.0.0.1') {
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-03 19:31:16 +03:00
|
|
|
export function animatedScroll(element: HTMLElement, scrollTop: number, delay?: number) {
|
|
|
|
|
let currentTime = 0;
|
|
|
|
|
const increment = 20;
|
|
|
|
|
const start = element.scrollTop;
|
|
|
|
|
const to = scrollTop;
|
|
|
|
|
const duration = delay ? delay : 0;
|
|
|
|
|
const remaining = to - start;
|
|
|
|
|
const animateScroll = () => {
|
2019-09-25 19:37:29 +03:00
|
|
|
if (duration === 0) {
|
|
|
|
|
element.scrollTop = to;
|
|
|
|
|
} else {
|
|
|
|
|
currentTime += increment;
|
|
|
|
|
const val = easeInOut(currentTime, start, remaining, duration);
|
|
|
|
|
element.scrollTop = val;
|
|
|
|
|
if (currentTime < duration) {
|
|
|
|
|
setTimeout(animateScroll, increment);
|
|
|
|
|
}
|
2019-09-03 19:31:16 +03:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
animateScroll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isUndefined(value: any): boolean {
|
|
|
|
|
return typeof value === 'undefined';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isDefined(value: any): boolean {
|
|
|
|
|
return typeof value !== 'undefined';
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-12 19:58:42 +03:00
|
|
|
export function isDefinedAndNotNull(value: any): boolean {
|
|
|
|
|
return typeof value !== 'undefined' && value !== null;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-05 21:15:40 +03:00
|
|
|
export function isFunction(value: any): boolean {
|
|
|
|
|
return typeof value === 'function';
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-12 19:58:42 +03:00
|
|
|
export function isObject(value: any): boolean {
|
|
|
|
|
return value !== null && typeof value === 'object';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isNumber(value: any): boolean {
|
|
|
|
|
return typeof value === 'number';
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-04 15:47:36 +02:00
|
|
|
export function isNumeric(value: any): boolean {
|
2020-03-03 19:24:58 +02:00
|
|
|
return (value - parseFloat(value) + 1) >= 0;
|
2019-11-04 15:47:36 +02:00
|
|
|
}
|
|
|
|
|
|
2019-09-19 20:10:52 +03:00
|
|
|
export function isString(value: any): boolean {
|
|
|
|
|
return typeof value === 'string';
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-04 15:47:36 +02:00
|
|
|
export function formatValue(value: any, dec?: number, units?: string, showZeroDecimals?: boolean): string | undefined {
|
|
|
|
|
if (isDefined(value) &&
|
2020-02-05 17:52:18 +02:00
|
|
|
value !== null && isNumeric(value)) {
|
2019-11-04 15:47:36 +02:00
|
|
|
let formatted: string | number = Number(value);
|
|
|
|
|
if (isDefined(dec)) {
|
|
|
|
|
formatted = formatted.toFixed(dec);
|
|
|
|
|
}
|
|
|
|
|
if (!showZeroDecimals) {
|
|
|
|
|
formatted = (Number(formatted) * 1);
|
|
|
|
|
}
|
|
|
|
|
formatted = formatted.toString();
|
|
|
|
|
if (isDefined(units) && units.length > 0) {
|
|
|
|
|
formatted += ' ' + units;
|
|
|
|
|
}
|
|
|
|
|
return formatted;
|
|
|
|
|
} else {
|
2020-02-05 17:52:18 +02:00
|
|
|
return value !== null ? value : '';
|
2019-11-04 15:47:36 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-05 17:52:18 +02:00
|
|
|
export function objectValues(obj: any): any[] {
|
|
|
|
|
return Object.keys(obj).map(e => obj[e]);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-10 13:00:29 +03:00
|
|
|
export function deleteNullProperties(obj: any) {
|
|
|
|
|
if (isUndefined(obj) || obj == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Object.keys(obj).forEach((propName) => {
|
|
|
|
|
if (obj[propName] === null || isUndefined(obj[propName])) {
|
|
|
|
|
delete obj[propName];
|
|
|
|
|
} else if (isObject(obj[propName])) {
|
|
|
|
|
deleteNullProperties(obj[propName]);
|
|
|
|
|
} else if (obj[propName] instanceof Array) {
|
|
|
|
|
(obj[propName] as any[]).forEach((elem) => {
|
|
|
|
|
deleteNullProperties(elem);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-05 21:15:40 +03:00
|
|
|
export function objToBase64(obj: any): string {
|
|
|
|
|
const json = JSON.stringify(obj);
|
|
|
|
|
const encoded = utf8Encode(json);
|
|
|
|
|
const b64Encoded: string = base64js.fromByteArray(encoded);
|
|
|
|
|
return b64Encoded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function base64toObj(b64Encoded: string): any {
|
|
|
|
|
const encoded: Uint8Array | number[] = base64js.toByteArray(b64Encoded);
|
|
|
|
|
const json = utf8Decode(encoded);
|
|
|
|
|
const obj = JSON.parse(json);
|
|
|
|
|
return obj;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function utf8Encode(str: string): Uint8Array | number[] {
|
|
|
|
|
let result: Uint8Array | number[];
|
|
|
|
|
if (isUndefined(Uint8Array)) {
|
|
|
|
|
result = utf8ToBytes(str);
|
|
|
|
|
} else {
|
|
|
|
|
result = new Uint8Array(utf8ToBytes(str));
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function utf8Decode(bytes: Uint8Array | number[]): string {
|
|
|
|
|
return utf8Slice(bytes, 0, bytes.length);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-09 19:13:18 +03:00
|
|
|
const scrollRegex = /(auto|scroll)/;
|
|
|
|
|
|
|
|
|
|
function parentNodes(node: Node, nodes: Node[]): Node[] {
|
|
|
|
|
if (node.parentNode === null) {
|
|
|
|
|
return nodes;
|
|
|
|
|
}
|
|
|
|
|
return parentNodes(node.parentNode, nodes.concat([node]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function style(el: Element, prop: string): string {
|
|
|
|
|
return getComputedStyle(el, null).getPropertyValue(prop);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function overflow(el: Element): string {
|
|
|
|
|
return style(el, 'overflow') + style(el, 'overflow-y') + style(el, 'overflow-x');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isScrollNode(node: Node): boolean {
|
|
|
|
|
if (node instanceof Element) {
|
|
|
|
|
return scrollRegex.test(overflow(node));
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function scrollParents(node: Node): Node[] {
|
|
|
|
|
if (!(node instanceof HTMLElement || node instanceof SVGElement)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
const scrollParentNodes = [];
|
|
|
|
|
const nodeParents = parentNodes(node, []);
|
|
|
|
|
nodeParents.forEach((nodeParent) => {
|
|
|
|
|
if (isScrollNode(nodeParent)) {
|
|
|
|
|
scrollParentNodes.push(nodeParent);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if (document.scrollingElement) {
|
|
|
|
|
scrollParentNodes.push(document.scrollingElement);
|
|
|
|
|
} else if (document.documentElement) {
|
|
|
|
|
scrollParentNodes.push(document.documentElement);
|
|
|
|
|
}
|
|
|
|
|
return scrollParentNodes;
|
|
|
|
|
}
|
2019-09-03 19:31:16 +03:00
|
|
|
|
2020-03-02 12:15:14 +02:00
|
|
|
function hashCode(str) {
|
2020-03-18 13:21:46 +02:00
|
|
|
let hash = 0;
|
|
|
|
|
let i, char;
|
2020-03-02 12:15:14 +02:00
|
|
|
if (str.length == 0) return hash;
|
|
|
|
|
for (i = 0; i < str.length; i++) {
|
2020-03-03 19:24:58 +02:00
|
|
|
char = str.charCodeAt(i);
|
|
|
|
|
hash = ((hash << 5) - hash) + char;
|
|
|
|
|
hash = hash & hash; // Convert to 32bit integer
|
2020-03-02 12:15:14 +02:00
|
|
|
}
|
|
|
|
|
return hash;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-03 19:31:16 +03:00
|
|
|
function easeInOut(
|
|
|
|
|
currentTime: number,
|
|
|
|
|
startTime: number,
|
|
|
|
|
remainingTime: number,
|
|
|
|
|
duration: number) {
|
|
|
|
|
currentTime /= duration / 2;
|
|
|
|
|
|
|
|
|
|
if (currentTime < 1) {
|
|
|
|
|
return (remainingTime / 2) * currentTime * currentTime + startTime;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentTime--;
|
|
|
|
|
return (
|
|
|
|
|
(-remainingTime / 2) * (currentTime * (currentTime - 2) - 1) + startTime
|
|
|
|
|
);
|
|
|
|
|
}
|
2019-09-05 21:15:40 +03:00
|
|
|
|
|
|
|
|
function utf8Slice(buf: Uint8Array | number[], start: number, end: number): string {
|
|
|
|
|
let res = '';
|
|
|
|
|
let tmp = '';
|
|
|
|
|
end = Math.min(buf.length, end || Infinity);
|
|
|
|
|
start = start || 0;
|
|
|
|
|
|
|
|
|
|
for (let i = start; i < end; i++) {
|
|
|
|
|
if (buf[i] <= 0x7F) {
|
|
|
|
|
res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]);
|
|
|
|
|
tmp = '';
|
|
|
|
|
} else {
|
|
|
|
|
tmp += '%' + buf[i].toString(16);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return res + decodeUtf8Char(tmp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function decodeUtf8Char(str: string): string {
|
|
|
|
|
try {
|
|
|
|
|
return decodeURIComponent(str);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
return String.fromCharCode(0xFFFD); // UTF 8 invalid char
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function utf8ToBytes(input: string, units?: number): number[] {
|
|
|
|
|
units = units || Infinity;
|
|
|
|
|
let codePoint: number;
|
|
|
|
|
const length = input.length;
|
|
|
|
|
let leadSurrogate: number = null;
|
|
|
|
|
const bytes: number[] = [];
|
|
|
|
|
let i = 0;
|
|
|
|
|
|
|
|
|
|
for (; i < length; i++) {
|
|
|
|
|
codePoint = input.charCodeAt(i);
|
|
|
|
|
|
|
|
|
|
// is surrogate component
|
|
|
|
|
if (codePoint > 0xD7FF && codePoint < 0xE000) {
|
|
|
|
|
// last char was a lead
|
|
|
|
|
if (leadSurrogate) {
|
|
|
|
|
// 2 leads in a row
|
|
|
|
|
if (codePoint < 0xDC00) {
|
|
|
|
|
units -= 3;
|
|
|
|
|
if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }
|
|
|
|
|
leadSurrogate = codePoint;
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
// valid surrogate pair
|
|
|
|
|
// tslint:disable-next-line:no-bitwise
|
|
|
|
|
codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000;
|
|
|
|
|
leadSurrogate = null;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// no lead yet
|
|
|
|
|
|
|
|
|
|
if (codePoint > 0xDBFF) {
|
|
|
|
|
// unexpected trail
|
|
|
|
|
units -= 3;
|
|
|
|
|
if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }
|
|
|
|
|
continue;
|
|
|
|
|
} else if (i + 1 === length) {
|
|
|
|
|
// unpaired lead
|
|
|
|
|
units -= 3;
|
|
|
|
|
if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
// valid lead
|
|
|
|
|
leadSurrogate = codePoint;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (leadSurrogate) {
|
|
|
|
|
// valid bmp char, but last char was a lead
|
|
|
|
|
units -= 3;
|
|
|
|
|
if (units > -1) { bytes.push(0xEF, 0xBF, 0xBD); }
|
|
|
|
|
leadSurrogate = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// encode utf8
|
|
|
|
|
if (codePoint < 0x80) {
|
|
|
|
|
units -= 1;
|
|
|
|
|
if (units < 0) { break; }
|
|
|
|
|
bytes.push(codePoint);
|
|
|
|
|
} else if (codePoint < 0x800) {
|
|
|
|
|
units -= 2;
|
|
|
|
|
if (units < 0) { break; }
|
|
|
|
|
bytes.push(
|
|
|
|
|
// tslint:disable-next-line:no-bitwise
|
|
|
|
|
codePoint >> 0x6 | 0xC0,
|
|
|
|
|
// tslint:disable-next-line:no-bitwise
|
|
|
|
|
codePoint & 0x3F | 0x80
|
|
|
|
|
);
|
|
|
|
|
} else if (codePoint < 0x10000) {
|
|
|
|
|
units -= 3;
|
|
|
|
|
if (units < 0) { break; }
|
|
|
|
|
bytes.push(
|
|
|
|
|
// tslint:disable-next-line:no-bitwise
|
|
|
|
|
codePoint >> 0xC | 0xE0,
|
|
|
|
|
// tslint:disable-next-line:no-bitwise
|
|
|
|
|
codePoint >> 0x6 & 0x3F | 0x80,
|
|
|
|
|
// tslint:disable-next-line:no-bitwise
|
|
|
|
|
codePoint & 0x3F | 0x80
|
|
|
|
|
);
|
|
|
|
|
} else if (codePoint < 0x200000) {
|
|
|
|
|
units -= 4;
|
|
|
|
|
if (units < 0) { break; }
|
|
|
|
|
bytes.push(
|
|
|
|
|
// tslint:disable-next-line:no-bitwise
|
|
|
|
|
codePoint >> 0x12 | 0xF0,
|
|
|
|
|
// tslint:disable-next-line:no-bitwise
|
|
|
|
|
codePoint >> 0xC & 0x3F | 0x80,
|
|
|
|
|
// tslint:disable-next-line:no-bitwise
|
|
|
|
|
codePoint >> 0x6 & 0x3F | 0x80,
|
|
|
|
|
// tslint:disable-next-line:no-bitwise
|
|
|
|
|
codePoint & 0x3F | 0x80
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error('Invalid code point');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return bytes;
|
|
|
|
|
}
|
2019-09-10 15:12:10 +03:00
|
|
|
|
2020-01-16 12:50:40 +02:00
|
|
|
export function deepClone<T>(target: T, ignoreFields?: string[]): T {
|
2019-09-12 19:58:42 +03:00
|
|
|
if (target === null) {
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
if (target instanceof Date) {
|
|
|
|
|
return new Date(target.getTime()) as any;
|
|
|
|
|
}
|
|
|
|
|
if (target instanceof Array) {
|
|
|
|
|
const cp = [] as any[];
|
|
|
|
|
(target as any[]).forEach((v) => { cp.push(v); });
|
|
|
|
|
return cp.map((n: any) => deepClone<any>(n)) as any;
|
|
|
|
|
}
|
|
|
|
|
if (typeof target === 'object' && target !== {}) {
|
|
|
|
|
const cp = { ...(target as { [key: string]: any }) } as { [key: string]: any };
|
|
|
|
|
Object.keys(cp).forEach(k => {
|
2020-01-16 12:50:40 +02:00
|
|
|
if (!ignoreFields || ignoreFields.indexOf(k) === -1) {
|
|
|
|
|
cp[k] = deepClone<any>(cp[k]);
|
|
|
|
|
}
|
2019-09-12 19:58:42 +03:00
|
|
|
});
|
|
|
|
|
return cp as T;
|
2019-09-10 15:12:10 +03:00
|
|
|
}
|
2019-09-12 19:58:42 +03:00
|
|
|
return target;
|
2019-09-10 15:12:10 +03:00
|
|
|
}
|
2019-10-31 10:06:57 +02:00
|
|
|
|
2020-02-26 18:15:04 +02:00
|
|
|
export function isEqual(a: any, b: any): boolean {
|
|
|
|
|
return _.isEqual(a, b);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-23 16:05:28 +02:00
|
|
|
export function mergeDeep<T>(target: T, ...sources: T[]): T {
|
|
|
|
|
return _.merge(target, ...sources);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-31 10:06:57 +02:00
|
|
|
export function guid(): string {
|
|
|
|
|
function s4(): string {
|
|
|
|
|
return Math.floor((1 + Math.random()) * 0x10000)
|
|
|
|
|
.toString(16)
|
|
|
|
|
.substring(1);
|
|
|
|
|
}
|
|
|
|
|
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
|
|
|
|
|
s4() + '-' + s4() + s4() + s4();
|
|
|
|
|
}
|
2019-12-23 14:36:44 +02:00
|
|
|
|
|
|
|
|
const SNAKE_CASE_REGEXP = /[A-Z]/g;
|
|
|
|
|
|
|
|
|
|
export function snakeCase(name: string, separator: string): string {
|
|
|
|
|
separator = separator || '_';
|
|
|
|
|
return name.replace(SNAKE_CASE_REGEXP, (letter, pos) => {
|
|
|
|
|
return (pos ? separator : '') + letter.toLowerCase();
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-01-24 19:05:41 +02:00
|
|
|
|
|
|
|
|
export function getDescendantProp(obj: any, path: string): any {
|
2020-02-04 15:14:17 +02:00
|
|
|
return path.split('.').reduce((acc, part) => acc && acc[part], obj);
|
2020-01-24 19:05:41 +02:00
|
|
|
}
|
2020-03-02 12:15:14 +02:00
|
|
|
|
2020-03-03 19:24:58 +02:00
|
|
|
export function imageLoader(imageUrl: string): Observable<HTMLImageElement> {
|
2020-03-02 12:15:14 +02:00
|
|
|
const image = new Image();
|
2020-04-24 11:22:26 +03:00
|
|
|
const imageLoad$ = fromEvent(image, 'load').pipe(map(() => image));
|
2020-03-02 12:15:14 +02:00
|
|
|
image.src = imageUrl;
|
|
|
|
|
return imageLoad$;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const imageAspectMap = {};
|
|
|
|
|
|
2020-03-03 19:24:58 +02:00
|
|
|
export function aspectCache(imageUrl: string): Observable<number> {
|
|
|
|
|
if (imageUrl?.length) {
|
2020-03-02 12:15:14 +02:00
|
|
|
const hash = hashCode(imageUrl);
|
|
|
|
|
let aspect = imageAspectMap[hash];
|
2020-03-03 19:24:58 +02:00
|
|
|
if (aspect) {
|
2020-03-02 12:15:14 +02:00
|
|
|
return of(aspect);
|
|
|
|
|
}
|
2020-03-03 19:24:58 +02:00
|
|
|
else return imageLoader(imageUrl).pipe(map(image => {
|
|
|
|
|
aspect = image.width / image.height;
|
2020-03-02 12:15:14 +02:00
|
|
|
imageAspectMap[hash] = aspect;
|
|
|
|
|
return aspect;
|
|
|
|
|
}))
|
|
|
|
|
}
|
2020-03-06 19:22:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function parseArray(input: any[]): any[] {
|
2020-03-23 16:54:48 +02:00
|
|
|
return _(input).groupBy(el => el?.datasource?.entityName)
|
|
|
|
|
.values().value().map((entityArray, dsIndex) =>
|
|
|
|
|
entityArray[0].data.map((el, i) => {
|
|
|
|
|
const obj = {
|
|
|
|
|
entityName: entityArray[0]?.datasource?.entityName,
|
|
|
|
|
$datasource: entityArray[0]?.datasource,
|
|
|
|
|
dsIndex,
|
|
|
|
|
time: el[0],
|
|
|
|
|
deviceType: null
|
|
|
|
|
};
|
|
|
|
|
entityArray.forEach(entity => {
|
|
|
|
|
obj[entity?.dataKey?.label] = entity?.data[i][1];
|
|
|
|
|
obj[entity?.dataKey?.label + '|ts'] = entity?.data[0][0];
|
|
|
|
|
if (entity?.dataKey?.label === 'type') {
|
|
|
|
|
obj.deviceType = entity?.data[0][1];
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return obj;
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function parseData(input: any[]): any[] {
|
|
|
|
|
return _(input).groupBy(el => el?.datasource?.entityName)
|
|
|
|
|
.values().value().map((entityArray, i) => {
|
2020-03-06 19:22:47 +02:00
|
|
|
const obj = {
|
2020-03-23 16:54:48 +02:00
|
|
|
entityName: entityArray[0]?.datasource?.entityName,
|
|
|
|
|
$datasource: entityArray[0]?.datasource,
|
|
|
|
|
dsIndex: i,
|
2020-03-18 13:21:46 +02:00
|
|
|
deviceType: null
|
2020-03-06 19:22:47 +02:00
|
|
|
};
|
2020-03-23 16:54:48 +02:00
|
|
|
entityArray.forEach(el => {
|
|
|
|
|
obj[el?.dataKey?.label] = el?.data[0][1];
|
2020-03-11 19:22:41 +02:00
|
|
|
obj[el?.dataKey?.label + '|ts'] = el?.data[0][0];
|
2020-03-20 14:25:18 +02:00
|
|
|
if (el?.dataKey?.label === 'type') {
|
2020-03-18 13:21:46 +02:00
|
|
|
obj.deviceType = el?.data[0][1];
|
2020-03-11 19:22:41 +02:00
|
|
|
}
|
2020-03-06 19:22:47 +02:00
|
|
|
});
|
|
|
|
|
return obj;
|
2020-03-11 19:22:41 +02:00
|
|
|
});
|
2020-03-06 19:22:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function safeExecute(func: Function, params = []) {
|
|
|
|
|
let res = null;
|
2020-03-20 14:25:18 +02:00
|
|
|
if (func && typeof (func) === 'function') {
|
2020-03-11 19:22:41 +02:00
|
|
|
try {
|
|
|
|
|
res = func(...params);
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
2020-03-17 16:56:18 +02:00
|
|
|
console.log('error in external function:', err);
|
2020-03-11 19:22:41 +02:00
|
|
|
res = null;
|
|
|
|
|
}
|
2020-03-06 19:22:47 +02:00
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-20 14:25:18 +02:00
|
|
|
export function parseFunction(source: any, params: string[] = []): Function {
|
2020-03-06 19:22:47 +02:00
|
|
|
let res = null;
|
|
|
|
|
if (source?.length) {
|
2020-03-11 19:22:41 +02:00
|
|
|
try {
|
|
|
|
|
res = new Function(...params, source);
|
|
|
|
|
}
|
|
|
|
|
catch (err) {
|
|
|
|
|
console.error(err);
|
|
|
|
|
res = null;
|
|
|
|
|
}
|
2020-03-06 19:22:47 +02:00
|
|
|
}
|
|
|
|
|
return res;
|
2020-03-13 14:24:56 +02:00
|
|
|
}
|
|
|
|
|
|
2020-04-24 11:22:26 +03:00
|
|
|
export function parseTemplate(template: string, data: object, translateFn?: (key: string) => string) {
|
2020-03-13 14:24:56 +02:00
|
|
|
let res = '';
|
2020-04-24 11:22:26 +03:00
|
|
|
let variables = '';
|
2020-03-13 14:24:56 +02:00
|
|
|
try {
|
2020-04-24 11:22:26 +03:00
|
|
|
if (template.match(/<link-act/g)) {
|
|
|
|
|
template = template.replace(/<link-act/g, '<a').replace(/link-act>/g, 'a>').replace(/name=(\'|")(.*?)(\'|")/g, `class='tb-custom-action' id='$2'`);
|
|
|
|
|
}
|
|
|
|
|
if (template.includes('i18n')) {
|
|
|
|
|
const translateRegexp = /\{i18n:(.*?)\}/;
|
|
|
|
|
template.match(new RegExp(translateRegexp.source, translateRegexp.flags + 'g')).forEach(match => {
|
|
|
|
|
template = template.replace(match, translateFn(match.match(translateRegexp)[1]));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
const expressions = template.match(/\{(.*?)\}/g);
|
|
|
|
|
if (expressions) {
|
2020-04-26 19:44:59 +03:00
|
|
|
const clearMatches = template.match(/(.)/g);
|
2020-04-24 11:22:26 +03:00
|
|
|
for (const key in data) {
|
|
|
|
|
if (!key.includes('|'))
|
|
|
|
|
variables += `let ${key} = '${clearMatches[key] ? padValue(data[key], +clearMatches[key]) : data[key]}';`;
|
|
|
|
|
}
|
|
|
|
|
template = template.replace(/\:\d+\}/g, '}');
|
|
|
|
|
res = safeExecute(parseFunction(variables + ' return' + '`' + template + '`'));
|
2020-03-13 14:24:56 +02:00
|
|
|
}
|
2020-04-24 11:22:26 +03:00
|
|
|
else res = template;
|
2020-03-13 14:24:56 +02:00
|
|
|
}
|
|
|
|
|
catch (ex) {
|
2020-04-24 11:22:26 +03:00
|
|
|
console.log(ex, variables, template)
|
2020-03-13 14:24:56 +02:00
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-24 11:22:26 +03:00
|
|
|
export let parseWithTranslation = {
|
|
|
|
|
translate(): string {
|
|
|
|
|
throw console.error('Translate not assigned');
|
|
|
|
|
},
|
|
|
|
|
parseTemplate(template: string, data: object, forceTranslate = false): string {
|
|
|
|
|
return parseTemplate(forceTranslate ? this.translate(template) : template, data, this?.translate);
|
|
|
|
|
},
|
|
|
|
|
setTranslate(translateFn: (key: string, defaultTranslation?: string) => string) {
|
|
|
|
|
this.translate = translateFn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-13 14:24:56 +02:00
|
|
|
export function padValue(val: any, dec: number): string {
|
|
|
|
|
let strVal;
|
|
|
|
|
let n;
|
|
|
|
|
|
|
|
|
|
val = parseFloat(val);
|
|
|
|
|
n = (val < 0);
|
|
|
|
|
val = Math.abs(val);
|
|
|
|
|
|
|
|
|
|
if (dec > 0) {
|
|
|
|
|
strVal = val.toFixed(dec).toString()
|
|
|
|
|
} else {
|
|
|
|
|
strVal = Math.round(val).toString();
|
|
|
|
|
}
|
|
|
|
|
strVal = (n ? '-' : '') + strVal;
|
|
|
|
|
return strVal;
|
2020-03-23 16:05:28 +02:00
|
|
|
}
|