UNPKG

@senx/discovery-widgets

Version:

Discovery Widgets Elements

379 lines (378 loc) 17.2 kB
/* * Copyright 2022-2025 SenX S.A.S. * * 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. */ import { GTSLib } from "./gts.lib"; import { Param } from "../model/param"; import { cloneDeep, merge } from "lodash"; import { LangUtils } from "./lang-utils"; export class Utils { static clone(inObject) { return cloneDeep(inObject); } static throttle(func, delay, ctx) { let isRunning; return (...args) => { const context = ctx || this; // store the context of the object that owns this function if (!isRunning) { isRunning = true; func.apply(context, args); // execute the function with the context of the object that owns it setTimeout(() => isRunning = false, delay); } }; } static httpPost(theUrl, payload, headers) { return new Promise((resolve, reject) => { const xmlHttp = new XMLHttpRequest(); const resHeaders = {}; xmlHttp.onreadystatechange = () => { xmlHttp.getAllResponseHeaders().split('\n').forEach(header => { if (header.trim() !== '') { const h = header.split(':'); resHeaders[h[0].trim().toLowerCase()] = h[1].trim().replace('\r', ''); } }); if (xmlHttp.readyState === 4 && xmlHttp.status < 400 && xmlHttp.status !== 0) { resolve({ data: xmlHttp.responseText, headers: resHeaders, status: { ops: parseInt(resHeaders['x-warp10-ops'], 10), elapsed: parseInt(resHeaders['x-warp10-elapsed'], 10), fetched: parseInt(resHeaders['x-warp10-fetched'], 10), }, }); } else if (xmlHttp.readyState === 4 && (xmlHttp.status === 403 || xmlHttp.status === 401)) { reject({ statusText: xmlHttp.statusText, status: xmlHttp.status, url: theUrl, headers: resHeaders, message: 'Not Authorized', }); } else if (xmlHttp.readyState === 4 && xmlHttp.status >= 400) { if (resHeaders['x-warp10-error-line'] && resHeaders['x-warp10-error-message']) { reject({ statusText: xmlHttp.statusText, status: xmlHttp.status, url: theUrl, headers: resHeaders, message: `line #${resHeaders['x-warp10-error-line']}: ${resHeaders['x-warp10-error-message']}`, detail: { mess: resHeaders['x-warp10-error-message'], line: resHeaders['x-warp10-error-line'], }, }); } else { reject({ statusText: xmlHttp.statusText, status: xmlHttp.status, url: theUrl, headers: resHeaders, message: 'WarpScript Error without message', }); } } else if (xmlHttp.readyState === 4 && xmlHttp.status === 0) { reject({ statusText: theUrl + ' is unreachable', status: 404, url: theUrl, headers: resHeaders, message: theUrl + ' is unreachable', detail: { mess: theUrl + ' is unreachable', line: -1, }, }); } }; xmlHttp.open('POST', theUrl, true); // true for asynchronous xmlHttp.setRequestHeader('Content-Type', 'text/plain; charset=utf-8'); Object.keys(headers || {}) .filter(h => h.toLowerCase() !== 'accept' && h.toLowerCase() !== 'content-type') .forEach(h => xmlHttp.setRequestHeader(h, headers[h])); xmlHttp.send(payload); }); } static merge(options, options2) { if (typeof options === 'string') { options = JSON.parse(options); } return Utils.clone(Object.assign(Object.assign(Object.assign({}, new Param()), options), options2)); } static sanitize(data) { if (typeof data === 'string') return '["' + data + '"]'; else return data; } static mergeDeep(base, ext) { return merge(base, ext); } static getLabelColor(el) { return Utils.getCSSColor(el, '--warp-view-chart-label-color', '#8e8e8e').trim(); } static getGridColor(el) { return Utils.getCSSColor(el, '--warp-view-chart-grid-color', '#8e8e8e').trim(); } static getCSSColor(el, property, defColor) { try { const color = getComputedStyle(el).getPropertyValue(property).trim(); return color === '' ? defColor : color; // eslint-disable-next-line no-unused-vars } catch (_e) { return defColor; } } static getContentBounds(el) { return el ? { h: el.clientHeight - parseInt(getComputedStyle(el, null).getPropertyValue('padding-top'), 10) - parseInt(getComputedStyle(el, null).getPropertyValue('padding-bottom'), 10), w: el.clientWidth - parseInt(getComputedStyle(el, null).getPropertyValue('padding-left'), 10) - parseInt(getComputedStyle(el, null).getPropertyValue('padding-right'), 10), } : { h: 100, w: 100 }; } static unsescape(str) { return str.replace(/&gt;/g, '>') .replace(/&lt;/g, '<') .replace(/&quot;/g, '"') .replace(/&apos;/g, '\'') .replace(/&amp;/g, '&'); } static parseEventData(evt, eventHandler, id) { var _a, _b; const parsed = { style: undefined, data: undefined, xpath: undefined, popup: undefined, vars: undefined, audio: undefined, zoom: undefined, focus: undefined, margin: undefined, bounds: undefined, title: undefined, description: undefined, selected: undefined, link: undefined, hasEvent: false, poi: [], }; if (eventHandler && evt.source !== id) { let tag = '.*'; let type = '.*'; for (const eh of eventHandler.split(',')) { if (eh.startsWith('tag')) { tag = eh.split('=')[1]; } if (eh.startsWith('type')) { type = eh.split('=')[1]; } } const tagRex = new RegExp(`^${tag}$`); if (evt.tags && typeof evt.tags === 'string') { evt.tags = [evt.tags]; } if (((_a = evt.tags) !== null && _a !== void 0 ? _a : []).some(t => tagRex.test(t)) && new RegExp(type).test((_b = evt.type) !== null && _b !== void 0 ? _b : '')) { switch (evt.type) { case 'data': parsed.data = GTSLib.getData(evt.value); parsed.hasEvent = true; break; case 'style': // map css selector -> content parsed.style = evt.value; parsed.hasEvent = true; break; case 'xpath': parsed.xpath = { selector: evt.selector, value: evt.value }; parsed.hasEvent = true; break; case 'popup': parsed.popup = evt.value; parsed.hasEvent = true; break; case 'variable': parsed.vars = evt.value; parsed.hasEvent = true; break; case 'audio': parsed.audio = evt.value; parsed.hasEvent = true; break; case 'zoom': parsed.zoom = evt.value; if (evt.selector) { const v = {}; v[evt.selector] = evt.value; parsed.vars = v; } parsed.hasEvent = true; break; case 'focus': parsed.focus = evt.value; if (evt.selector) { const v = {}; v[evt.selector] = evt.value; parsed.vars = v; } parsed.hasEvent = true; break; case 'margin': parsed.margin = evt.value; parsed.hasEvent = true; break; case 'bounds': parsed.bounds = evt.value; if (evt.selector) { const v = {}; v[evt.selector] = evt.value; parsed.vars = v; } parsed.hasEvent = true; break; case 'title': parsed.title = evt.value; parsed.hasEvent = true; break; case 'description': parsed.description = evt.value; parsed.hasEvent = true; break; case 'link': parsed.link = typeof evt.value === 'string' ? { link: evt.value, target: 'self' } : Object.assign({}, evt.value); parsed.hasEvent = true; break; case 'selected': parsed.selected = parsed.selected || {}; parsed.selected[evt.selector] = evt.value; parsed.hasEvent = true; break; case 'poi': parsed.poi = evt.value; if (evt.selector) { const v = {}; v[evt.selector] = evt.value; parsed.vars = v; } parsed.hasEvent = true; break; default: // nothing } } } return parsed; } static parseXML(xmlString, contentType) { const parser = new DOMParser(); // Parse a simple Invalid XML source to get namespace of <parsererror>: const docError = parser.parseFromString('INVALID', contentType); const parserErrorNS = docError.getElementsByTagName('parsererror')[0].namespaceURI; // Parse xmlString: // (XMLDocument object) const doc = parser.parseFromString(xmlString, contentType); if (doc.getElementsByTagNameNS(parserErrorNS, 'parsererror').length > 0) { throw new Error('Error parsing XML'); } return doc; } /** * Compute the backend url if it is a relative one * * @param url */ static getUrl(url) { if (!(url !== null && url !== void 0 ? url : '').toLowerCase().startsWith('http') && !(url !== null && url !== void 0 ? url : '').toLowerCase().startsWith('ws')) { const { host, pathname, port, protocol, search } = window.location; let urlComputed = protocol + '//' + host + (port !== '' ? ':' + port : ''); urlComputed += (url !== null && url !== void 0 ? url : '').startsWith('/') ? (url !== null && url !== void 0 ? url : '') : pathname + (pathname.endsWith('/') ? '' : '/') + (url !== null && url !== void 0 ? url : ''); return urlComputed + search; } else { return (url !== null && url !== void 0 ? url : ''); } } static deepEqual(object1, object2) { /* if (object1 == null && object2 != null || object1 != null && object2 == null) { return false; }*/ const keys1 = Object.keys(object1 !== null && object1 !== void 0 ? object1 : {}); const keys2 = Object.keys(object2 !== null && object2 !== void 0 ? object2 : {}); if (keys1.length !== keys2.length) { return false; } for (const key of keys1) { const val1 = object1[key]; const val2 = object2[key]; const areObjects = Utils.isObject(val1) && Utils.isObject(val2); if (areObjects && !Utils.deepEqual(val1, val2) || !areObjects && val1 !== val2) { return false; } } return true; } static isObject(object) { return object != null && typeof object === 'object'; } static execAction(macro, widget) { var _a, _b, _c; const ws = LangUtils.prepare(`${macro} EVAL`, (_a = widget.innerVars) !== null && _a !== void 0 ? _a : {}, (_c = (_b = widget.innerOptions) === null || _b === void 0 ? void 0 : _b.skippedVars) !== null && _c !== void 0 ? _c : [], widget.type, widget.language); Utils.httpPost(widget.url, ws, widget.innerOptions.httpHeaders) .then(res => { var _a, _b, _c, _d; (_a = widget.LOG) === null || _a === void 0 ? void 0 : _a.debug(['execAction', 'res.data'], res.data); const result = GTSLib.getData(res.data); (_b = widget.LOG) === null || _b === void 0 ? void 0 : _b.debug(['execAction', 'getData'], result); if (result) { for (const e of ((_c = result.events) !== null && _c !== void 0 ? _c : [])) { (_d = widget.LOG) === null || _d === void 0 ? void 0 : _d.debug(['execAction', 'emit'], { discoveryEvent: e }); if (typeof e.value !== 'object' && GTSLib.isArray(e.value)) { e.value = [e.value]; } widget.discoveryEvent.emit(Object.assign(Object.assign({}, e), { source: widget.el.id })); } } }) .catch(e => { var _a; widget.execError.emit(e); (_a = widget.LOG) === null || _a === void 0 ? void 0 : _a.error(['exec'], e); }); } } Utils.DEFICON = 'image://data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'16\' height=\'16\' fill=\'currentColor\' class=\'bi bi-question-square\' viewBox=\'0 0 16 16\'%3E%3Cpath d=\'M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z\'/%3E%3Cpath d=\'M5.255 5.786a.237.237 0 0 0 .241.247h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286m1.557 5.763c0 .533.425.927 1.01.927.609 0 1.028-.394 1.028-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94\'/%3E%3C/svg%3E'; Utils.getNavigatorLanguage = () => { var _a, _b, _c; let lang; if (navigator.languages && navigator.languages.length) { lang = navigator.languages[0]; } else { lang = (_c = (_b = (_a = navigator['userLanguage']) !== null && _a !== void 0 ? _a : navigator.language) !== null && _b !== void 0 ? _b : navigator['browserLanguage']) !== null && _c !== void 0 ? _c : 'en'; } return lang.split('-')[0].toLowerCase(); }; //# sourceMappingURL=utils.js.map