@openremote/core 1.8.0-snapshot.20250725074716 → 1.8.0-snapshot.20250725120000

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/util.js CHANGED
@@ -1 +1,1000 @@
1
- import e from"i18next";import t from"qs";import{AssetModelUtil as r}from"@openremote/model";import n from"moment";import{transform as o}from"lodash";export class Deferred{get resolve(){return this._resolve}get reject(){return this._reject}get promise(){return this._promise}constructor(){this._promise=new Promise((e,t)=>{this._resolve=e,this._reject=t}),Object.freeze(this)}}export function getBrowserLanguage(){return navigator.language.split("-")[0]||"en"}export function getQueryParameters(e){return t.parse(e,{ignoreQueryPrefix:!0})}export function getQueryParameter(e){let t;if(location.search&&""!==location.search&&(t=getQueryParameters(location.search)),location.hash){let e=location.hash.indexOf("?");e>-1&&(t=getQueryParameters(location.hash.substring(e+1)))}return t?t[e]:void 0}export function getGeoNotificationsFromRulesSet(e){let t=[];return e.rules.forEach(e=>{if(e.when&&e.then&&e.then.length>0){let r=new Map;for(let n of(addGeofencePredicatesFromRuleCondition(e.when,0,r),r.size>0&&e.then.forEach(e=>addPushNotificationsFromRuleAction(e,r)),r.values()))n.forEach(e=>{e.notification&&t.push(e)})}}),t}function addGeofencePredicatesFromRuleCondition(e,t,r){if(!e)return;let n=[];e.groups&&e.groups.forEach(e=>{e.items&&e.items.forEach(e=>{n.push(e)})}),e.items&&e.items.forEach(e=>{n.push(e)}),n&&n.forEach(e=>{if(e.assets&&e.assets.attributes){let n=[];if(addGeoNotificationsFromAttributePredicateCondition(e.assets.attributes,n),n.length>0){let o=e.tag||t.toString();r.set(o,n)}}})}function addGeoNotificationsFromAttributePredicateCondition(e,t){e&&(e.items.forEach(e=>{e.value&&("radial"===e.value.predicateType||"rect"===e.value.predicateType)&&t.push({predicate:e.value})}),e.groups&&e.groups.forEach(e=>addGeoNotificationsFromAttributePredicateCondition(e,t)))}function addPushNotificationsFromRuleAction(e,t){if(e&&"notification"===e.action&&e.notification&&e.notification.message&&"push"===e.notification.message.type){let r=e.target;if(r&&r.conditionAssets){let n=t.get(r.conditionAssets);n&&n.forEach(t=>{t.notification=e.notification.message})}else for(let r of t.values())r.forEach(t=>{t.notification=e.notification.message})}}let TIME_DURATION_REGEXP=/([+-])?((\d+)[Dd])?\s*((\d+)[Hh])?\s*((\d+)[Mm]$)?\s*((\d+)[Ss])?\s*((\d+)([Mm][Ss]$))?\s*((\d+)[Ww])?\s*((\d+)[Mm][Nn])?\s*((\d+)[Yy])?/;export function isTimeDuration(e){return!!e&&(e=e.trim()).length>0&&(TIME_DURATION_REGEXP.test(e)||isTimeDurationPositiveInfinity(e)||isTimeDurationNegativeInfinity(e))}export function isTimeDurationPositiveInfinity(e){return"*"===(e=null!=e?e.trim():void 0)||"+*"===e}export function isTimeDurationNegativeInfinity(e){return"-*"===(e=null!=e?e.trim():void 0)}export function isObject(e){return!!e&&"object"==typeof e}export function isFunction(e){return!!(e&&e.constructor&&e.call&&e.apply)}export function objectsEqual(e,t,r=!0){if(null==e||null==t)return e===t;if(e.constructor!==t.constructor)return!1;if(e instanceof Function||e instanceof RegExp)return e===t;if(e===t||e.valueOf()===t.valueOf())return!0;if(Array.isArray(e)&&e.length!==t.length||e instanceof Date||!(e instanceof Object)||!(t instanceof Object))return!1;if(r){let r=Object.keys(e);return Object.keys(t).every(e=>-1!==r.indexOf(e))&&r.every(r=>objectsEqual(e[r],t[r]))}return!1}export function difference(e,t){let r=(e,t)=>o(e,function(e,n,o){objectsEqual(n,null==t?void 0:t[o])||(e[o]=isObject(n)&&isObject(null==t?void 0:t[o])?r(n,null==t?void 0:t[o]):n)});return r(e,t)}export function arrayRemove(e,t){if(0===e.length)return;let r=e.indexOf(t);r>=0&&e.splice(r,1)}export function sentenceCaseToCamelCase(e){return null==e?"":e.split(" ").map((e,t)=>0===t?e[0].toLowerCase()+e.substring(1):e[0].toUpperCase()+e.substring(1)).join("")}export function camelCaseToSentenceCase(e){if(null==e)return"";let t=!1;return e.split(/([A-Z]|\d)/).map((e,r,n)=>{if(!e)return e;if(!t)return t=!0,e.charAt(0).toUpperCase()+e.slice(1);if("_"===e)return" ";if(1===e.length&&e===e.toUpperCase()){let t=!n[r-1]||"_"===n[r-1],o=r+1<n.length&&n[r+1]&&"_"!==n[r+1],a=r+3>n.length||!n[r+1]&&!n[r+3];(!t||o)&&(e=" "+e),!o&&(t||a)||(e=e.toLowerCase())}return e}).join("").trim()}export function stringMatch(e,t){if(t===e)return!0;let r=e.endsWith("*"),n=!r&&e.startsWith("*"),o=!r&&!n&&e.startsWith("^")&&e.endsWith("$");if(r&&t.startsWith(e.substr(0,e.length-1))||n&&t.endsWith(e.substr(1)))return!0;if(o)try{return new RegExp(e).test(t)}catch(e){console.error("Failed to compile needle as a RegExp: "+e)}return!1}export function capitaliseFirstLetter(e){if(e)return 1===e.length?e.toUpperCase():e.charAt(0).toUpperCase()+e.slice(1)}export function enumContains(e,t){return e&&Object.values(e).includes(t)}export function getEnumKeyAsString(e,t){return Object.keys(e).find(r=>e[r]===t)}export function getWeekNumber(e){(e=new Date(Date.UTC(e.getFullYear(),e.getMonth(),e.getDate()))).setUTCDate(e.getUTCDate()+4-(e.getUTCDay()||7));let t=new Date(Date.UTC(e.getUTCFullYear(),0,1));return Math.ceil(((e.getTime()-t.getTime())/864e5+1)/7)}let monthNames=["JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC"];export function formatCronString(e,t,r,n,o,a,i){let s="";if(a?Array.isArray(a)?s+=a.toString().replace(" ",""):s+=a.toString():s+="0",s+=" ",o?Array.isArray(o)?s+=o.toString().replace(" ",""):s+=o.toString():s+="0",s+=" ",n?Array.isArray(n)?s+=n.toString().replace(" ",""):s+=n.toString():s+="0",s+=" ",r?Array.isArray(r)?s+=r.toString().replace(" ",""):s+=r.toString():s+="*",s+=" ",t)if(Array.isArray(t))if("number"==typeof t[0]){let e=[];t.forEach(t=>{e.push(monthNames[t].toString())}),s+=e.toString().replace(" ","")}else s+=t.toString().replace(" ","");else s+=t.toString();else s+="*";return s+=" ",i?s+=i.toString():s+="?",s+=" ",e?Array.isArray(e)?s+=e.toString().replace(" ",""):s+=e:s+="*",s}export function dateToCronString(e){return formatCronString(e.getFullYear(),monthNames[e.getMonth()],[e.getDate()],[e.getHours()],[e.getMinutes()])}export function cronStringToISOString(e,t){let r=e.split(" ");if(!Number.isNaN(Number(r[0]))&&!Number.isNaN(Number(r[1]))&&!Number.isNaN(Number(r[2]))&&(!Number.isNaN(Number(r[3]))||"*"==r[3])){let e=(!Number.isNaN(Number(r[6]))?r[6]:new Date().getFullYear()).toString(),o="";o=1==(o="*"!=r[4]?monthNames.indexOf(r[4]).toString():new Date().getMonth().toString()).length?"0"+o:o;let a=1==r[3].length&&"*"!=r[3]?"0"+r[3]:r[3].replace("*","01"),i=1==r[2].length&&"*"!=r[2]?"0"+r[2]:r[2].replace("*","00"),s=1==r[1].length&&"*"!=r[1]?"0"+r[1]:r[1].replace("*","00"),u=1==r[0].length&&"*"!=r[0]?"0"+r[0]:r[0].replace("*","00");if(e.length>0&&o.length>0)if(t)return n.utc({year:Number(e),month:Number(o),date:Number(a),hour:Number(i),minute:Number(s),second:Number(u)}).toISOString();else return n({year:Number(e),month:Number(o),date:Number(a),hour:Number(i),minute:Number(s),second:Number(u)}).toISOString()}}export function getMetaValue(e,t,r){let n="string"==typeof e?e:e.name;return t&&t.meta&&t.meta.hasOwnProperty(n)?t.meta[n]:r&&r.meta?r.meta[n]:void 0}export function hasMetaItem(e,t,r){let n="string"==typeof e?e:e.name;return!!(t&&t.meta&&t.meta.hasOwnProperty(n)||r&&r.meta&&r.meta.hasOwnProperty(n))}export function getAssetTypeLabel(t){return("string"==typeof t&&(t=r.getAssetDescriptor(t)),t)?e.t("label.asset."+t.name,{defaultValue:camelCaseToSentenceCase(t.name)}):""}export function getValueDescriptorLabel(t){if(!t)return e.t("label.value.unknown",{defaultValue:"Unknown"});let r="string"==typeof t?t:t.name;return e.t("label.value."+r,{defaultValue:camelCaseToSentenceCase(r||"")})}export function getAllowedValueLabel(t,r){if(t)return e.t("label.allowedValue."+t,{defaultValue:r||camelCaseToSentenceCase(t||""),nsSeparator:!1})}export function getMetaItemNameValueHolder(e,t){let n="string"==typeof e?r.getMetaItemDescriptor(e):e;return n?{name:n.name,type:n.type,value:t}:{name:typeof e,type:r.resolveValueTypeFromValue(t),value:t}}export function getAttributeLabel(e,t,r,n,o){return getValueHolderLabel(e,t,r,n,!0,o)}export function getMetaLabel(e,t,r,n,o){return getValueHolderLabel(e||getMetaItemNameValueHolder(t,null),t,r,n,!1,o)}function getValueHolderLabel(e,t,r,n,o,a){let i=doStandardTranslationLookup("label",e,t,r,o),s="";if(i||(i=getMetaValue("label",e,t&&t.meta?t:void 0)),n&&(s=resolveUnits(getValueFormatConstraintOrUnits("units",e,t,r,o))),!i){let r=e?e.name:t?"string"==typeof t?t:t.name:void 0;i=a||(r?camelCaseToSentenceCase(r):void 0)}return i?i+(s?" ("+s+")":""):s}export function getAttributeValueAsString(e,t,r,n,o){return getValueHolderValueAsString(e,t,r,n,!0,o)}export function getMetaValueAsString(e,t,r,n,o){return getValueHolderValueAsString(e||getMetaItemNameValueHolder(t,null),t,r,n,!1,o)}function getValueHolderValueAsString(e,t,r,n,o,a){let i=getValueAsString(e?e.value:void 0,()=>getValueFormatConstraintOrUnits("format",e,t,r,o),void 0,a),s=getAttributeUnits(e,t,r);return n&&s&&s.length&&(i=resolveUnits(getValueFormatConstraintOrUnits("units",e,t,r,o),i)),i}export function getValueAsString(t,r,o,a){let i="";if(null==t)i=a||"";else if("string"==typeof t)i=t;else if("number"==typeof t||"boolean"==typeof t||t instanceof Date){let a=r&&r();if(a&&0!==Object.keys(a).length)switch("number"==typeof t?a.asBoolean?t=!!t:a.asDate&&(t=new Date(t)):"boolean"==typeof t&&a.asNumber&&(t=+!!t),typeof t){case"number":i=new Intl.NumberFormat(o||e.language,a).format(t);break;case"boolean":a.asOnOff&&(i=t?e.t("on"):e.t("off")),a.asOpenClosed&&(i=t?e.t("open"):e.t("closed")),a.asPressedReleased&&(i=t?e.t("pressed"):e.t("released"));break;case"object":if(a.momentJsFormat)i=n(t).format(a.momentJsFormat);else if(a.iso8601)i=t.toISOString();else if(a.week){let e=getWeekNumber(t);i="2-digit"===a.week?String(e).padStart(2,"0"):Number(e).toString(10)}else i=new Intl.DateTimeFormat(o||e.language,a).format(t)}else i=Object(t).toString()}return i}export function resolveUnits(t,r){if(!t)return"";r||(r="");let n=t.map((t,n)=>{if(3!==t.length||t.toUpperCase()!==t)return e.t(["units."+t,"or:units."+t]);{let o=new Intl.NumberFormat(e.language,{currency:t,style:"currency"}).formatToParts();if(0!==n||"currency"!==o[0].type)return"currency"===o[0].type?o[0].value:o[o.length-1].value;if(!r)return o[0].value;r=o[0].value+r}}).join("");return r.length>0?r+" "+n:n}export function getAttributeValueConstraints(e,t,r){return getValueFormatConstraintOrUnits("constraints",e,t,r,!0)}export function getMetaValueConstraints(e,t,r){return getValueFormatConstraintOrUnits("constraints",e||getMetaItemNameValueHolder(t,null),t,r,!1)}export function getAttributeUnits(e,t,r){return getValueFormatConstraintOrUnits("units",e,t,r,!0)}export function getMetaUnits(e,t,r){return getValueFormatConstraintOrUnits("units",e||getMetaItemNameValueHolder(t,null),t,r,!1)}export function getAttributeValueFormat(e,t,r){return getValueFormatConstraintOrUnits("format",e,t,r,!0)}export function getMetaValueFormat(e,t,r){return getValueFormatConstraintOrUnits("format",e||getMetaItemNameValueHolder(t,null),t,r,!1)}export function mergeObjects(e,t,r){if(e&&!t)return Object.assign({},e);if(t&&!e)return Object.assign({},t);let n=Object.assign({},e),o=[];return Object.entries(t).forEach(([e,t])=>{mergeObjectKey(n,o,e,t,r)}),n}function mergeObjectKey(e,t,r,n,o){let a=e;t.forEach(e=>{a.hasOwnProperty(e)||(a[e]={}),a=a[e]}),a&&(a.hasOwnProperty(r)?null==n?delete a[r]:Array.isArray(a[r])?o?a[r]=[...a[r],...n]:a[r]=[...n]:"object"==typeof n?a[r]=mergeObjects(Object.assign({},a[r]),n,o):a[r]=n:null==n?delete a[r]:Array.isArray(n)?a[r]=[...n]:"object"==typeof n?a[r]=Object.assign({},n):a[r]=n)}function getValueFormatConstraintOrUnits(e,t,n,o,a){let i,s=[],u=doStandardTranslationLookup(e,t&&"string"==typeof t?t:t?t.name:n?"string"==typeof n?n:n.name:void 0,n,o,a);if(u&&(i=JSON.parse(u)))if("format"!==e)return i;else s.push(i);if(t&&t.meta&&(i=getMetaValue(e,t,n)))if("format"!==e)return i;else s.push(i);if(n&&"string"!=typeof n&&n.hasOwnProperty(e)){if(i=n[e],"format"!==e)return i;s.push(i)}if(n&&n.type){if(i=r.getValueDescriptor(n.type)[e],"format"!==e)return i;s.push(i)}if("format"!==e||0===s.length)return i;let l={};return s.reverse().forEach(e=>{l=Object.assign(Object.assign({},l),e)}),l}function doStandardTranslationLookup(t,r,n,o,a,i){let s;if(r?s="string"==typeof r?r:r.name:n&&(s="string"==typeof n?n:n.name),!s)return;let u=[],l=t+"."+(a?"attribute":"meta")+".";if(o&&u.push(l+o+"."+s),n&&"string"!=typeof n&&(u.push(l+n.type+"."+s),u.push(l+n.type)),u.push(l+s),u.length>0)return e.t(u,{defaultValue:i||""})}export function updateAsset(e,t){let r=t.ref.name;if(e.attributes)if(t.deleted)delete e.attributes[r];else{let n=e.attributes[r];n&&(n.value=t.value,n.timestamp=t.timestamp)}return Object.assign({},e)}export function loadJs(e){return new Promise((t,r)=>{let n=document.createElement("script");n.type="text/javascript",n.src=e,n.addEventListener("load",e=>t(e),!1),n.addEventListener("error",e=>r(e),!1),document.body.appendChild(n)})}export function sortByNumber(e){return(t,r)=>{let n=e(t),o=e(r);return n||o?n&&!o?1:!n&&o?-1:n-o:0}}export function sortByString(e){return(t,r)=>{let n=e(t),o=e(r);return n||o?n&&!o?1:!n&&o?-1:n.localeCompare(o):0}}export function dispatchCancellableEvent(e,t){let r=new Deferred;return e.dispatchEvent(t),window.setTimeout(()=>{r.resolve(t.detail)}),r.promise}let keys={37:1,38:1,39:1,40:1};function preventDefault(e){e.preventDefault()}function preventDefaultForScrollKeys(e){if(keys[e.keyCode])return preventDefault(e),!1}let supportsPassive=!1;try{window.addEventListener("test",null,Object.defineProperty({},"passive",{get:()=>{supportsPassive=!0}}))}catch(e){}let wheelOpt=!!supportsPassive&&{passive:!1},wheelEvent="onwheel"in document.createElement("div")?"wheel":"mousewheel";export function disableScroll(){window.addEventListener("DOMMouseScroll",preventDefault,!1),window.addEventListener(wheelEvent,preventDefault,wheelOpt),window.addEventListener("touchmove",preventDefault,wheelOpt),window.addEventListener("keydown",preventDefaultForScrollKeys,!1)}export function enableScroll(){window.removeEventListener("DOMMouseScroll",preventDefault,!1),window.removeEventListener(wheelEvent,preventDefault,wheelOpt),window.removeEventListener("touchmove",preventDefault,wheelOpt),window.removeEventListener("keydown",preventDefaultForScrollKeys,!1)}export function blobToBase64(e){return new Promise((t,r)=>{let n=new FileReader;n.readAsDataURL(e),n.onload=()=>{t(n.result)},n.onerror=e=>{r(e)}})}
1
+ import i18next from "i18next";
2
+ import Qs from "qs";
3
+ import { AssetModelUtil } from "@openremote/model";
4
+ import moment from "moment";
5
+ import { transform } from "lodash";
6
+ export class Deferred {
7
+ get resolve() {
8
+ return this._resolve;
9
+ }
10
+ get reject() {
11
+ return this._reject;
12
+ }
13
+ get promise() {
14
+ return this._promise;
15
+ }
16
+ constructor() {
17
+ this._promise = new Promise((resolve1, reject1) => {
18
+ this._resolve = resolve1;
19
+ this._reject = reject1;
20
+ });
21
+ Object.freeze(this);
22
+ }
23
+ }
24
+ export function getBrowserLanguage() {
25
+ return navigator.language.split("-")[0] || "en";
26
+ }
27
+ export function getQueryParameters(queryStr) {
28
+ return Qs.parse(queryStr, { ignoreQueryPrefix: true });
29
+ }
30
+ export function getQueryParameter(parameter) {
31
+ let parsed;
32
+ if (location.search && location.search !== "") {
33
+ parsed = getQueryParameters(location.search);
34
+ }
35
+ if (location.hash) {
36
+ const index = location.hash.indexOf("?");
37
+ if (index > -1) {
38
+ parsed = getQueryParameters(location.hash.substring(index + 1));
39
+ }
40
+ }
41
+ return parsed ? parsed[parameter] : undefined;
42
+ }
43
+ export function getGeoNotificationsFromRulesSet(rulesetDefinition) {
44
+ const geoNotifications = [];
45
+ rulesetDefinition.rules.forEach((rule) => {
46
+ if (rule.when && rule.then && rule.then.length > 0) {
47
+ const geoNotificationMap = new Map();
48
+ addGeofencePredicatesFromRuleCondition(rule.when, 0, geoNotificationMap);
49
+ if (geoNotificationMap.size > 0) {
50
+ rule.then.forEach((ruleAction) => addPushNotificationsFromRuleAction(ruleAction, geoNotificationMap));
51
+ }
52
+ for (const geoNotificationsArr of geoNotificationMap.values()) {
53
+ geoNotificationsArr.forEach((geoNotification) => {
54
+ if (geoNotification.notification) {
55
+ geoNotifications.push(geoNotification);
56
+ }
57
+ });
58
+ }
59
+ }
60
+ });
61
+ return geoNotifications;
62
+ }
63
+ function addGeofencePredicatesFromRuleCondition(ruleCondition, index, geoNotificationMap) {
64
+ if (!ruleCondition) {
65
+ return;
66
+ }
67
+ const items = [];
68
+ if (ruleCondition.groups) {
69
+ ruleCondition.groups.forEach((ruleGroup) => {
70
+ if (ruleGroup.items) {
71
+ ruleGroup.items.forEach((ruleTrigger) => {
72
+ items.push(ruleTrigger);
73
+ });
74
+ }
75
+ });
76
+ }
77
+ if (ruleCondition.items) {
78
+ ruleCondition.items.forEach((ruleTrigger) => {
79
+ items.push(ruleTrigger);
80
+ });
81
+ }
82
+ if (items) {
83
+ items.forEach((ruleTrigger) => {
84
+ if (ruleTrigger.assets && ruleTrigger.assets.attributes) {
85
+ const geoNotifications = [];
86
+ addGeoNotificationsFromAttributePredicateCondition(ruleTrigger.assets.attributes, geoNotifications);
87
+ if (geoNotifications.length > 0) {
88
+ const tagName = ruleTrigger.tag || index.toString();
89
+ geoNotificationMap.set(tagName, geoNotifications);
90
+ }
91
+ }
92
+ });
93
+ }
94
+ }
95
+ function addGeoNotificationsFromAttributePredicateCondition(attributeCondition, geoNotifications) {
96
+ if (!attributeCondition) {
97
+ return;
98
+ }
99
+ attributeCondition.items.forEach((predicate) => {
100
+ if (predicate.value && (predicate.value.predicateType === "radial" || predicate.value.predicateType === "rect")) {
101
+ geoNotifications.push({
102
+ predicate: predicate.value
103
+ });
104
+ }
105
+ });
106
+ if (attributeCondition.groups) {
107
+ attributeCondition.groups.forEach((condition) => addGeoNotificationsFromAttributePredicateCondition(condition, geoNotifications));
108
+ }
109
+ }
110
+ function addPushNotificationsFromRuleAction(ruleAction, geoPredicateMap) {
111
+ if (ruleAction && ruleAction.action === "notification") {
112
+ if (ruleAction.notification && ruleAction.notification.message && ruleAction.notification.message.type === "push") {
113
+ // Find applicable targets
114
+ const target = ruleAction.target;
115
+ if (target && target.conditionAssets) {
116
+ const geoNotifications = geoPredicateMap.get(target.conditionAssets);
117
+ if (geoNotifications) {
118
+ geoNotifications.forEach((geoNotification) => {
119
+ geoNotification.notification = ruleAction.notification.message;
120
+ });
121
+ }
122
+ }
123
+ else {
124
+ // Applies to all LHS rule triggers
125
+ for (const geoNotifications of geoPredicateMap.values()) {
126
+ geoNotifications.forEach((geoNotification) => {
127
+ geoNotification.notification = ruleAction.notification.message;
128
+ });
129
+ }
130
+ }
131
+ }
132
+ }
133
+ }
134
+ const TIME_DURATION_REGEXP = /([+-])?((\d+)[Dd])?\s*((\d+)[Hh])?\s*((\d+)[Mm]$)?\s*((\d+)[Ss])?\s*((\d+)([Mm][Ss]$))?\s*((\d+)[Ww])?\s*((\d+)[Mm][Nn])?\s*((\d+)[Yy])?/;
135
+ export function isTimeDuration(time) {
136
+ if (!time) {
137
+ return false;
138
+ }
139
+ time = time.trim();
140
+ return time.length > 0
141
+ && (TIME_DURATION_REGEXP.test(time)
142
+ || isTimeDurationPositiveInfinity(time)
143
+ || isTimeDurationNegativeInfinity(time));
144
+ }
145
+ export function isTimeDurationPositiveInfinity(time) {
146
+ time = time != null ? time.trim() : undefined;
147
+ return "*" === time || "+*" === time;
148
+ }
149
+ export function isTimeDurationNegativeInfinity(time) {
150
+ time = time != null ? time.trim() : undefined;
151
+ return "-*" === time;
152
+ }
153
+ export function isObject(object) {
154
+ if (!!object) {
155
+ return typeof object === "object";
156
+ }
157
+ return false;
158
+ }
159
+ export function isFunction(object) {
160
+ return !!(object && object.constructor && object.call && object.apply);
161
+ }
162
+ export function objectsEqual(obj1, obj2, deep = true) {
163
+ if (obj1 === null || obj1 === undefined || obj2 === null || obj2 === undefined) {
164
+ return obj1 === obj2;
165
+ }
166
+ // after this just checking type of one would be enough
167
+ if (obj1.constructor !== obj2.constructor) {
168
+ return false;
169
+ }
170
+ // if they are functions, they should exactly refer to same one (because of closures)
171
+ if (obj1 instanceof Function) {
172
+ return obj1 === obj2;
173
+ }
174
+ // if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES)
175
+ if (obj1 instanceof RegExp) {
176
+ return obj1 === obj2;
177
+ }
178
+ if (obj1 === obj2 || obj1.valueOf() === obj2.valueOf()) {
179
+ return true;
180
+ }
181
+ if (Array.isArray(obj1) && obj1.length !== obj2.length) {
182
+ return false;
183
+ }
184
+ // if they are dates, they must had equal valueOf
185
+ if (obj1 instanceof Date) {
186
+ return false;
187
+ }
188
+ // if they are strictly equal, they both need to be object at least
189
+ if (!(obj1 instanceof Object)) {
190
+ return false;
191
+ }
192
+ if (!(obj2 instanceof Object)) {
193
+ return false;
194
+ }
195
+ if (deep) {
196
+ // recursive object equality check
197
+ const p = Object.keys(obj1);
198
+ return Object.keys(obj2).every((i) => {
199
+ return p.indexOf(i) !== -1;
200
+ }) &&
201
+ p.every((i) => {
202
+ return objectsEqual(obj1[i], obj2[i]);
203
+ });
204
+ }
205
+ return false;
206
+ }
207
+ /**
208
+ * Deep diff between two object, using lodash
209
+ * @param {Object} object Object compared
210
+ * @param {Object} base Object to compare with
211
+ * @return {Object} Return a new object who represent the diff
212
+ */
213
+ export function difference(object, base) {
214
+ const changes = (object, base) => transform(object, function (result, value, key) {
215
+ if (!objectsEqual(value, base === null || base === void 0 ? void 0 : base[key])) {
216
+ result[key] = (isObject(value) && isObject(base === null || base === void 0 ? void 0 : base[key])) ? changes(value, base === null || base === void 0 ? void 0 : base[key]) : value;
217
+ }
218
+ });
219
+ return changes(object, base);
220
+ }
221
+ export function arrayRemove(arr, item) {
222
+ if (arr.length === 0) {
223
+ return;
224
+ }
225
+ const index = arr.indexOf(item);
226
+ if (index >= 0) {
227
+ arr.splice(index, 1);
228
+ }
229
+ }
230
+ export function sentenceCaseToCamelCase(str) {
231
+ if (str === undefined || str === null) {
232
+ return "";
233
+ }
234
+ return str.split(' ').map((value, index) => {
235
+ if (index === 0) {
236
+ return value[0].toLowerCase() + value.substring(1);
237
+ }
238
+ return value[0].toUpperCase() + value.substring(1);
239
+ }).join('');
240
+ }
241
+ export function camelCaseToSentenceCase(str) {
242
+ if (str === undefined || str === null) {
243
+ return "";
244
+ }
245
+ let startDone = false;
246
+ return str.split(/([A-Z]|\d)/).map((v, i, arr) => {
247
+ // Skip empty blocks
248
+ if (!v)
249
+ return v;
250
+ // If first block then capitalise 1st letter regardless
251
+ if (!startDone) {
252
+ startDone = true;
253
+ return v.charAt(0).toUpperCase() + v.slice(1);
254
+ }
255
+ // Underscore substitution
256
+ if (v === '_')
257
+ return " ";
258
+ // We have a capital or number
259
+ if (v.length === 1 && v === v.toUpperCase()) {
260
+ const previousCapital = !arr[i - 1] || arr[i - 1] === '_';
261
+ const nextWord = i + 1 < arr.length && arr[i + 1] && arr[i + 1] !== '_';
262
+ const nextTwoCapitalsOrEndOfString = i + 3 > arr.length || !arr[i + 1] && !arr[i + 3];
263
+ // Insert space
264
+ if (!previousCapital || nextWord)
265
+ v = " " + v;
266
+ // Start of word or single letter word
267
+ if (nextWord || (!previousCapital && !nextTwoCapitalsOrEndOfString))
268
+ v = v.toLowerCase();
269
+ }
270
+ return v;
271
+ }).join("").trim();
272
+ }
273
+ export function stringMatch(needle, haystack) {
274
+ if (haystack === needle) {
275
+ return true;
276
+ }
277
+ const startsWith = needle.endsWith("*");
278
+ const endsWith = !startsWith && needle.startsWith("*");
279
+ const regExp = !startsWith && !endsWith && needle.startsWith("^") && needle.endsWith("$");
280
+ if (startsWith && haystack.startsWith(needle.substr(0, needle.length - 1))) {
281
+ return true;
282
+ }
283
+ if (endsWith && haystack.endsWith(needle.substr(1))) {
284
+ return true;
285
+ }
286
+ if (regExp) {
287
+ try {
288
+ const regexp = new RegExp(needle);
289
+ return regexp.test(haystack);
290
+ }
291
+ catch (e) {
292
+ console.error("Failed to compile needle as a RegExp: " + e);
293
+ }
294
+ }
295
+ return false;
296
+ }
297
+ export function capitaliseFirstLetter(str) {
298
+ if (!str) {
299
+ return;
300
+ }
301
+ if (str.length === 1) {
302
+ return str.toUpperCase();
303
+ }
304
+ return str.charAt(0).toUpperCase() + str.slice(1);
305
+ }
306
+ export function enumContains(enm, val) {
307
+ return enm && Object.values(enm).includes(val);
308
+ }
309
+ export function getEnumKeyAsString(enm, val) {
310
+ // @ts-ignore
311
+ const key = Object.keys(enm).find((k) => enm[k] === val);
312
+ return key;
313
+ }
314
+ /* For a given date, get the ISO week number
315
+ *
316
+ * Based on information at:
317
+ *
318
+ * http://www.merlyn.demon.co.uk/weekcalc.htm#WNR
319
+ *
320
+ * Algorithm is to find nearest thursday, it's year
321
+ * is the year of the week number. Then get weeks
322
+ * between that date and the first day of that year.
323
+ *
324
+ * Note that dates in one year can be weeks of previous
325
+ * or next year, overlap is up to 3 days.
326
+ *
327
+ * e.g. 2014/12/29 is Monday in week 1 of 2015
328
+ * 2012/1/1 is Sunday in week 52 of 2011
329
+ */
330
+ export function getWeekNumber(date) {
331
+ // Copy date so don't modify original
332
+ date = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
333
+ // Set to nearest Thursday: current date + 4 - current day number
334
+ // Make Sunday's day number 7
335
+ date.setUTCDate(date.getUTCDate() + 4 - (date.getUTCDay() || 7));
336
+ // Get first day of year
337
+ const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1));
338
+ // Calculate full weeks to nearest Thursday
339
+ const weekNo = Math.ceil(((((date.getTime() - yearStart.getTime()) / 86400000) + 1) / 7));
340
+ // Return array of year and week number
341
+ return weekNo;
342
+ }
343
+ const monthNames = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'];
344
+ /* Creates a cron string based on the input parameters.
345
+ * You can for example trigger an expression once a month on friday, or on a specific date ass March 1st.
346
+ *
347
+ * Useful URLs for understanding cron:
348
+ * - https://en.wikipedia.org/wiki/Cron
349
+ * - https://www.freeformatter.com/cron-expression-generator-quartz.html
350
+ *
351
+ * @param years What years it should trigger. For example ['2022','2023'] or a string to inject.
352
+ * @param months What months of the year it should trigger. For example ['JAN','FEB'] or [0,1,2,3,4,5] or a string to inject.
353
+ * @param days What days of the month it should trigger. For example [7,14,21,28] or a string to inject.
354
+ * @param hours What hours of the day it should trigger. For example [3,6,9,12,15,18,21] or a string to inject.
355
+ * @param minutes What minutes of the hour it should trigger. For example [0,15,30,45] or a string to inject.
356
+ * @param seconds What seconds of the minute it should trigger. For example [0,15,30,45] or a string to inject.
357
+ * @param daysOfTheWeek String to inject for specifying specific days such as '1st monday of the month'.
358
+ * */
359
+ export function formatCronString(years, months, days, hours, minutes, seconds, daysOfTheWeek) {
360
+ let cron = "";
361
+ if (seconds) {
362
+ if (Array.isArray(seconds)) {
363
+ cron += (seconds.toString().replace(" ", ""));
364
+ }
365
+ else {
366
+ cron += seconds.toString();
367
+ }
368
+ }
369
+ else {
370
+ cron += "0";
371
+ }
372
+ cron += " ";
373
+ if (minutes) {
374
+ if (Array.isArray(minutes)) {
375
+ cron += (minutes.toString().replace(" ", ""));
376
+ }
377
+ else {
378
+ cron += minutes.toString();
379
+ }
380
+ }
381
+ else {
382
+ cron += "0";
383
+ }
384
+ cron += " ";
385
+ if (hours) {
386
+ if (Array.isArray(hours)) {
387
+ cron += (hours.toString().replace(" ", ""));
388
+ }
389
+ else {
390
+ cron += hours.toString();
391
+ }
392
+ }
393
+ else {
394
+ cron += "0";
395
+ }
396
+ cron += " ";
397
+ if (days) {
398
+ if (Array.isArray(days)) {
399
+ cron += (days.toString().replace(" ", ""));
400
+ }
401
+ else {
402
+ cron += days.toString();
403
+ }
404
+ }
405
+ else {
406
+ cron += "*";
407
+ }
408
+ cron += " ";
409
+ if (months) {
410
+ if (Array.isArray(months)) {
411
+ if (typeof months[0] == 'number') {
412
+ const monthStrings = [];
413
+ months.forEach(month => { monthStrings.push(monthNames[month].toString()); });
414
+ cron += (monthStrings.toString().replace(" ", ""));
415
+ }
416
+ else {
417
+ cron += (months.toString().replace(" ", ""));
418
+ }
419
+ }
420
+ else {
421
+ cron += months.toString();
422
+ }
423
+ }
424
+ else {
425
+ cron += "*";
426
+ }
427
+ cron += " ";
428
+ if (daysOfTheWeek) {
429
+ cron += daysOfTheWeek.toString();
430
+ }
431
+ else {
432
+ cron += "?";
433
+ }
434
+ cron += " ";
435
+ if (years) {
436
+ if (Array.isArray(years)) {
437
+ cron += (years.toString().replace(" ", ""));
438
+ }
439
+ else {
440
+ cron += years;
441
+ }
442
+ }
443
+ else {
444
+ cron += "*";
445
+ }
446
+ return cron;
447
+ }
448
+ /* Transforms an JS date to a cron string, to trigger ONCE A YEAR on that specific date */
449
+ export function dateToCronString(date) {
450
+ return formatCronString(date.getFullYear(), monthNames[date.getMonth()], [date.getDate()], [date.getHours()], [date.getMinutes()]);
451
+ }
452
+ /*
453
+ * Transforms a cron expression string into an ISO String.
454
+ * Input for example would be `0 00 11 * * ? *` for every day at 11am.
455
+ * If the input is '*', it will be replaced by 1. (month parameter of '*' becomes January)
456
+ */
457
+ export function cronStringToISOString(cronString, isUTC) {
458
+ const splStr = cronString.split(" ");
459
+ if (!Number.isNaN(Number(splStr[0])) && !Number.isNaN(Number(splStr[1])) && !Number.isNaN(Number(splStr[2])) && (!Number.isNaN(Number(splStr[3])) || splStr[3] == '*')) {
460
+ const year = (!Number.isNaN(Number(splStr[6])) ? splStr[6] : new Date().getFullYear()).toString();
461
+ let month = "";
462
+ if (splStr[4] != '*') {
463
+ month = monthNames.indexOf(splStr[4]).toString();
464
+ }
465
+ else {
466
+ month = new Date().getMonth().toString();
467
+ }
468
+ month = (month.length == 1 ? ("0" + month) : month);
469
+ const date = ((splStr[3].length == 1 && splStr[3] != '*') ? ("0" + splStr[3]) : splStr[3].replace('*', '01'));
470
+ const hour = ((splStr[2].length == 1 && splStr[2] != '*') ? ("0" + splStr[2]) : splStr[2].replace('*', '00'));
471
+ const minute = ((splStr[1].length == 1 && splStr[1] != '*') ? ("0" + splStr[1]) : splStr[1].replace('*', '00'));
472
+ const second = ((splStr[0].length == 1 && splStr[0] != '*') ? ("0" + splStr[0]) : splStr[0].replace('*', '00'));
473
+ if (year.length > 0 && month.length > 0) {
474
+ if (isUTC) {
475
+ return moment.utc({ year: Number(year), month: Number(month), date: Number(date), hour: Number(hour), minute: Number(minute), second: Number(second) }).toISOString();
476
+ }
477
+ else {
478
+ return moment({ year: Number(year), month: Number(month), date: Number(date), hour: Number(hour), minute: Number(minute), second: Number(second) }).toISOString();
479
+ }
480
+ }
481
+ }
482
+ return undefined;
483
+ }
484
+ export function getMetaValue(name, attribute, descriptor) {
485
+ const metaName = typeof name === "string" ? name : name.name;
486
+ if (attribute && attribute.meta && attribute.meta.hasOwnProperty(metaName)) {
487
+ return attribute.meta[metaName];
488
+ }
489
+ if (descriptor && descriptor.meta) {
490
+ return descriptor.meta[metaName];
491
+ }
492
+ }
493
+ export function hasMetaItem(name, attribute, descriptor) {
494
+ const metaName = typeof name === "string" ? name : name.name;
495
+ if (attribute && attribute.meta && attribute.meta.hasOwnProperty(metaName)) {
496
+ return true;
497
+ }
498
+ if (descriptor && descriptor.meta && descriptor.meta.hasOwnProperty(metaName)) {
499
+ return true;
500
+ }
501
+ return false;
502
+ }
503
+ export function getAssetTypeLabel(type) {
504
+ if (typeof type === "string") {
505
+ type = AssetModelUtil.getAssetDescriptor(type);
506
+ }
507
+ if (!type) {
508
+ return "";
509
+ }
510
+ return i18next.t("label.asset." + type.name, { defaultValue: camelCaseToSentenceCase(type.name) });
511
+ }
512
+ export function getValueDescriptorLabel(descriptor) {
513
+ if (!descriptor) {
514
+ return i18next.t("label.value.unknown", { defaultValue: "Unknown" });
515
+ }
516
+ const name = (typeof (descriptor) === "string" ? descriptor : descriptor.name);
517
+ return i18next.t("label.value." + name, { defaultValue: camelCaseToSentenceCase(name || "") });
518
+ }
519
+ export function getAllowedValueLabel(allowedValue, fallback) {
520
+ if (!allowedValue) {
521
+ return;
522
+ }
523
+ return i18next.t("label.allowedValue." + allowedValue, { defaultValue: fallback || camelCaseToSentenceCase(allowedValue || ""), nsSeparator: false });
524
+ }
525
+ export function getMetaItemNameValueHolder(metaNameOrDescriptor, value) {
526
+ const descriptor = typeof metaNameOrDescriptor === "string" ? AssetModelUtil.getMetaItemDescriptor(metaNameOrDescriptor) : metaNameOrDescriptor;
527
+ if (descriptor) {
528
+ return {
529
+ name: descriptor.name,
530
+ type: descriptor.type,
531
+ value: value
532
+ };
533
+ }
534
+ return {
535
+ name: typeof metaNameOrDescriptor,
536
+ type: AssetModelUtil.resolveValueTypeFromValue(value),
537
+ value: value
538
+ };
539
+ }
540
+ export function getAttributeLabel(attribute, descriptor, assetType, showUnits, fallback) {
541
+ return getValueHolderLabel(attribute, descriptor, assetType, showUnits, true, fallback);
542
+ }
543
+ export function getMetaLabel(metaItem, descriptor, assetType, showUnits, fallback) {
544
+ const metaValueHolder = metaItem || getMetaItemNameValueHolder(descriptor, null);
545
+ return getValueHolderLabel(metaValueHolder, descriptor, assetType, showUnits, false, fallback);
546
+ }
547
+ function getValueHolderLabel(nameValueHolder, descriptor, assetType, showUnits, isAttribute, fallback) {
548
+ let label = doStandardTranslationLookup("label" /* WellknownMetaItems.LABEL */, nameValueHolder, descriptor, assetType, isAttribute);
549
+ let unitsStr = "";
550
+ if (!label) {
551
+ // Look in meta if it exists
552
+ label = getMetaValue("label" /* WellknownMetaItems.LABEL */, nameValueHolder, descriptor && descriptor.meta ? descriptor : undefined);
553
+ }
554
+ if (showUnits) {
555
+ const units = getValueFormatConstraintOrUnits("units" /* WellknownMetaItems.UNITS */, nameValueHolder, descriptor, assetType, isAttribute);
556
+ unitsStr = resolveUnits(units);
557
+ }
558
+ if (!label) {
559
+ const name = nameValueHolder ? nameValueHolder.name : descriptor ? typeof descriptor === "string" ? descriptor : descriptor.name : undefined;
560
+ label = fallback || (name ? camelCaseToSentenceCase(name) : undefined);
561
+ }
562
+ return label ? label + (unitsStr ? " (" + unitsStr + ")" : "") : unitsStr;
563
+ }
564
+ export function getAttributeValueAsString(attribute, descriptor, assetType, showUnits, fallback) {
565
+ return getValueHolderValueAsString(attribute, descriptor, assetType, showUnits, true, fallback);
566
+ }
567
+ export function getMetaValueAsString(metaItem, descriptor, assetType, showUnits, fallback) {
568
+ const metaValueHolder = metaItem || getMetaItemNameValueHolder(descriptor, null);
569
+ return getValueHolderValueAsString(metaValueHolder, descriptor, assetType, showUnits, false, fallback);
570
+ }
571
+ function getValueHolderValueAsString(nameValueHolder, descriptor, assetType, showUnits, isAttribute, fallback) {
572
+ let valueStr = getValueAsString(nameValueHolder ? nameValueHolder.value : undefined, () => getValueFormatConstraintOrUnits("format" /* WellknownMetaItems.FORMAT */, nameValueHolder, descriptor, assetType, isAttribute), undefined, fallback);
573
+ const attrUnits = getAttributeUnits(nameValueHolder, descriptor, assetType);
574
+ if (showUnits && attrUnits && !!attrUnits.length) {
575
+ const units = getValueFormatConstraintOrUnits("units" /* WellknownMetaItems.UNITS */, nameValueHolder, descriptor, assetType, isAttribute);
576
+ valueStr = resolveUnits(units, valueStr);
577
+ }
578
+ return valueStr;
579
+ }
580
+ export function getValueAsString(value, formatProvider, language, fallback) {
581
+ let valueStr = "";
582
+ if (value === null || typeof (value) === "undefined") {
583
+ valueStr = fallback || "";
584
+ }
585
+ else {
586
+ if (typeof (value) === "string") {
587
+ valueStr = value;
588
+ }
589
+ else if (typeof (value) === "number" || typeof (value) === "boolean" || value instanceof Date) {
590
+ const format = formatProvider && formatProvider();
591
+ if (format && Object.keys(format).length !== 0) {
592
+ if (typeof (value) === "number") {
593
+ if (format.asBoolean) {
594
+ value = !!value;
595
+ }
596
+ else if (format.asDate) {
597
+ // Assume UNIX timestamp in ms
598
+ value = new Date(value);
599
+ }
600
+ }
601
+ else if (typeof (value) === "boolean" && format.asNumber) {
602
+ value = value ? 1 : 0;
603
+ }
604
+ switch (typeof (value)) {
605
+ case "number":
606
+ valueStr = new Intl.NumberFormat(language || i18next.language, format).format(value);
607
+ break;
608
+ case "boolean":
609
+ if (format.asOnOff) {
610
+ valueStr = value ? i18next.t("on") : i18next.t("off");
611
+ }
612
+ if (format.asOpenClosed) {
613
+ valueStr = value ? i18next.t("open") : i18next.t("closed");
614
+ }
615
+ if (format.asPressedReleased) {
616
+ valueStr = value ? i18next.t("pressed") : i18next.t("released");
617
+ }
618
+ break;
619
+ case "object": // Date instance
620
+ // Special handling for some format options
621
+ if (format.momentJsFormat) {
622
+ valueStr = moment(value).format(format.momentJsFormat);
623
+ }
624
+ else if (format.iso8601) {
625
+ valueStr = value.toISOString();
626
+ }
627
+ else if (format.week) {
628
+ const weekNo = getWeekNumber(value);
629
+ valueStr = format.week === "2-digit" /* ValueFormatStyleRepresentation.DIGIT_2 */ ? String(weekNo).padStart(2, "0") : Number(weekNo).toString(10);
630
+ }
631
+ else {
632
+ valueStr = new Intl.DateTimeFormat(language || i18next.language, format).format(value);
633
+ }
634
+ break;
635
+ }
636
+ }
637
+ else {
638
+ valueStr = Object(value).toString();
639
+ }
640
+ }
641
+ }
642
+ return valueStr;
643
+ }
644
+ /**
645
+ * Resolve supplied units using current translation locale; and optionally apply the units to the supplied value (this
646
+ * is useful for units containing currency which in some locales is prefixed to the value e.g. £0.00 kW/hr rather than
647
+ * 0.00 £kW/hr)
648
+ */
649
+ export function resolveUnits(units, valueStr) {
650
+ if (!units) {
651
+ return "";
652
+ }
653
+ if (!valueStr) {
654
+ valueStr = "";
655
+ }
656
+ const unitsStr = units.map((unit, index) => {
657
+ if (unit.length === 3 && unit.toUpperCase() === unit) {
658
+ // This is a currency code - use Intl API to find the symbol
659
+ const parts = new Intl.NumberFormat(i18next.language, { currency: unit, style: "currency" }).formatToParts();
660
+ // Check whether it goes before or after the value
661
+ if (index === 0 && parts[0].type === "currency") {
662
+ if (valueStr) {
663
+ valueStr = parts[0].value + valueStr;
664
+ }
665
+ else {
666
+ return parts[0].value;
667
+ }
668
+ }
669
+ else {
670
+ return (parts[0].type === "currency" ? parts[0].value : parts[parts.length - 1].value);
671
+ }
672
+ }
673
+ else {
674
+ return i18next.t(["units." + unit, "or:units." + unit]);
675
+ }
676
+ }).join("");
677
+ return valueStr.length > 0 ? (valueStr + " " + unitsStr) : unitsStr;
678
+ }
679
+ /**
680
+ * Looks for {@link ValueConstraint[]} for the specified {@link Attribute} (see {@link getValueFormatConstraintOrUnits})
681
+ */
682
+ export function getAttributeValueConstraints(attribute, descriptor, assetType) {
683
+ return getValueFormatConstraintOrUnits("constraints" /* WellknownMetaItems.CONSTRAINTS */, attribute, descriptor, assetType, true);
684
+ }
685
+ /**
686
+ * Looks for {@link ValueConstraint[]} for the specified {@link NameValueHolder} (see {@link getValueFormatConstraintOrUnits})
687
+ */
688
+ export function getMetaValueConstraints(metaItem, descriptor, assetType) {
689
+ const metaValueHolder = metaItem || getMetaItemNameValueHolder(descriptor, null);
690
+ return getValueFormatConstraintOrUnits("constraints" /* WellknownMetaItems.CONSTRAINTS */, metaValueHolder, descriptor, assetType, false);
691
+ }
692
+ /**
693
+ * Looks for units string[] for the specified {@link Attribute} (see {@link getValueFormatConstraintOrUnits})
694
+ */
695
+ export function getAttributeUnits(attribute, descriptor, assetType) {
696
+ return getValueFormatConstraintOrUnits("units" /* WellknownMetaItems.UNITS */, attribute, descriptor, assetType, true);
697
+ }
698
+ /**
699
+ * Looks for units string[] for the specified {@link MetaItem} (see {@link getValueFormatConstraintOrUnits})
700
+ */
701
+ export function getMetaUnits(metaItem, descriptor, assetType) {
702
+ const metaValueHolder = metaItem || getMetaItemNameValueHolder(descriptor, null);
703
+ return getValueFormatConstraintOrUnits("units" /* WellknownMetaItems.UNITS */, metaValueHolder, descriptor, assetType, false);
704
+ }
705
+ /**
706
+ * Looks for a {@link ValueFormat} for the specified {@link Attribute} (see {@link getValueFormatConstraintOrUnits})
707
+ */
708
+ export function getAttributeValueFormat(attribute, descriptor, assetType) {
709
+ return getValueFormatConstraintOrUnits("format" /* WellknownMetaItems.FORMAT */, attribute, descriptor, assetType, true);
710
+ }
711
+ /**
712
+ * Looks for a {@see ValueFormat} for the specified {@link MetaItem} (see {@link getValueFormatConstraintOrUnits})
713
+ */
714
+ export function getMetaValueFormat(metaItem, descriptor, assetType) {
715
+ const metaValueHolder = metaItem || getMetaItemNameValueHolder(descriptor, null);
716
+ return getValueFormatConstraintOrUnits("format" /* WellknownMetaItems.FORMAT */, metaValueHolder, descriptor, assetType, false);
717
+ }
718
+ export function mergeObjects(a, b, mergeArrays) {
719
+ if (a && !b) {
720
+ return Object.assign({}, a);
721
+ }
722
+ if (b && !a) {
723
+ return Object.assign({}, b);
724
+ }
725
+ const merged = Object.assign({}, a);
726
+ const path = [];
727
+ Object.entries(b).forEach(([k, v]) => {
728
+ mergeObjectKey(merged, path, k, v, mergeArrays);
729
+ });
730
+ return merged;
731
+ }
732
+ function mergeObjectKey(destination, path, key, value, mergeArrays) {
733
+ let dest = destination;
734
+ path.forEach((p) => {
735
+ if (!dest.hasOwnProperty(p)) {
736
+ dest[p] = {};
737
+ }
738
+ dest = dest[p];
739
+ });
740
+ if (!dest) {
741
+ return;
742
+ }
743
+ if (!dest.hasOwnProperty(key)) {
744
+ if (value === null || value === undefined) {
745
+ delete dest[key];
746
+ }
747
+ else if (Array.isArray(value)) {
748
+ dest[key] = [...value];
749
+ }
750
+ else if (typeof (value) === "object") {
751
+ dest[key] = Object.assign({}, value);
752
+ }
753
+ else {
754
+ dest[key] = value;
755
+ }
756
+ }
757
+ else {
758
+ if (value === undefined || value === null) {
759
+ delete dest[key];
760
+ }
761
+ else if (Array.isArray(dest[key])) {
762
+ if (mergeArrays) {
763
+ dest[key] = [...dest[key], ...value];
764
+ }
765
+ else {
766
+ dest[key] = [...value];
767
+ }
768
+ }
769
+ else if (typeof (value) === "object") {
770
+ dest[key] = mergeObjects(Object.assign({}, dest[key]), value, mergeArrays);
771
+ }
772
+ else {
773
+ dest[key] = value;
774
+ }
775
+ }
776
+ }
777
+ /**
778
+ * Looks for the requested {@link ValueFormat}, {@link ValueConstraint[]} or units string[] defined in the translation
779
+ * file in several locations (see {@link doStandardTranslationLookup}).
780
+ * <p>
781
+ * If no value is found in translation files then the standard resolution is used by looking at the nameValueHolder and/or
782
+ * the provided descriptor(s); the resolution order is:
783
+ * {@link NameValueHolder}, {@link ValueDescriptorHolder}, {@link ValueDescriptor}, the first value encountered will be
784
+ * returned; with the exception of {@link ValueFormat} which are merged in reverse priority order.
785
+ */
786
+ function getValueFormatConstraintOrUnits(lookup, nameValueHolder, descriptor, assetType, isAttribute) {
787
+ let matched;
788
+ const formats = [];
789
+ const name = nameValueHolder && typeof nameValueHolder === "string" ? nameValueHolder : nameValueHolder ? nameValueHolder.name : descriptor ? typeof (descriptor) === "string" ? descriptor : descriptor.name : undefined;
790
+ const str = doStandardTranslationLookup(lookup, name, descriptor, assetType, isAttribute);
791
+ if (str) {
792
+ matched = JSON.parse(str);
793
+ if (matched) {
794
+ if (lookup === "format" /* WellknownMetaItems.FORMAT */) {
795
+ formats.push(matched);
796
+ }
797
+ else {
798
+ return matched;
799
+ }
800
+ }
801
+ }
802
+ // Look in meta
803
+ if (nameValueHolder && nameValueHolder.meta) {
804
+ matched = getMetaValue(lookup, nameValueHolder, descriptor);
805
+ if (matched) {
806
+ if (lookup === "format" /* WellknownMetaItems.FORMAT */) {
807
+ formats.push(matched);
808
+ }
809
+ else {
810
+ return matched;
811
+ }
812
+ }
813
+ }
814
+ if (descriptor && typeof (descriptor) !== "string" && descriptor.hasOwnProperty(lookup)) {
815
+ matched = descriptor[lookup];
816
+ if (lookup === "format" /* WellknownMetaItems.FORMAT */) {
817
+ formats.push(matched);
818
+ }
819
+ else {
820
+ return matched;
821
+ }
822
+ }
823
+ if (descriptor && descriptor.type) {
824
+ const valueDescriptor = AssetModelUtil.getValueDescriptor(descriptor.type);
825
+ matched = valueDescriptor[lookup];
826
+ if (lookup === "format" /* WellknownMetaItems.FORMAT */) {
827
+ formats.push(matched);
828
+ }
829
+ else {
830
+ return matched;
831
+ }
832
+ }
833
+ if (lookup !== "format" /* WellknownMetaItems.FORMAT */ || formats.length === 0) {
834
+ return matched;
835
+ }
836
+ let mergedFormat = {};
837
+ formats.reverse().forEach((format) => {
838
+ mergedFormat = Object.assign(Object.assign({}, mergedFormat), format);
839
+ });
840
+ return mergedFormat;
841
+ }
842
+ /**
843
+ * Looks up the requested lookup in several keys, for example lookup=label, name=custom, isAttribute=true, assetType=ThingAsset,
844
+ * descriptorType=number:
845
+ * <ol>
846
+ * <li>label.attribute.ThingAsset.custom</li>
847
+ * <li>label.attribute.number.custom</li>
848
+ * <li>label.attribute.number</li>
849
+ */
850
+ function doStandardTranslationLookup(lookup, nameValueHolder, valueHolderDescriptor, assetType, isAttribute, fallback) {
851
+ // Look in translation files for an override in multiple keys e.g.
852
+ // format.ThingAsset.[attribute|meta].custom (look for valueFormat where custom is meta item or attribute name)
853
+ // format.Duration (where Duration is the ValueDescriptor type)
854
+ // units.BuildingAsset.attribute.temperature
855
+ let name;
856
+ if (nameValueHolder) {
857
+ name = typeof nameValueHolder === "string" ? nameValueHolder : nameValueHolder.name;
858
+ }
859
+ else if (valueHolderDescriptor) {
860
+ name = typeof (valueHolderDescriptor) === "string" ? valueHolderDescriptor : valueHolderDescriptor.name;
861
+ }
862
+ if (!name) {
863
+ return;
864
+ }
865
+ const lookups = [];
866
+ const prefix = lookup + "." + (isAttribute ? "attribute" : "meta") + ".";
867
+ if (assetType) {
868
+ lookups.push(prefix + assetType + "." + name);
869
+ }
870
+ if (valueHolderDescriptor && typeof (valueHolderDescriptor) !== "string") {
871
+ lookups.push(prefix + valueHolderDescriptor.type + "." + name);
872
+ lookups.push(prefix + valueHolderDescriptor.type);
873
+ }
874
+ lookups.push(prefix + name);
875
+ if (lookups.length > 0) {
876
+ return i18next.t(lookups, { defaultValue: fallback || "" });
877
+ }
878
+ }
879
+ /**
880
+ * Immutable update of an asset using the supplied attribute event
881
+ */
882
+ export function updateAsset(asset, event) {
883
+ const attributeName = event.ref.name;
884
+ if (asset.attributes) {
885
+ if (event.deleted) {
886
+ delete asset.attributes[attributeName];
887
+ }
888
+ else {
889
+ const attribute = asset.attributes[attributeName];
890
+ if (attribute) {
891
+ attribute.value = event.value;
892
+ attribute.timestamp = event.timestamp;
893
+ }
894
+ }
895
+ }
896
+ return Object.assign({}, asset);
897
+ }
898
+ export function loadJs(url) {
899
+ return new Promise((resolve, reject) => {
900
+ const script = document.createElement('script');
901
+ script.type = 'text/javascript';
902
+ script.src = url;
903
+ script.addEventListener('load', (e) => resolve(e), false);
904
+ script.addEventListener('error', (e) => reject(e), false);
905
+ document.body.appendChild(script);
906
+ });
907
+ }
908
+ ;
909
+ export function sortByNumber(valueExtractor) {
910
+ return (a, b) => {
911
+ const v1 = valueExtractor(a);
912
+ const v2 = valueExtractor(b);
913
+ if (!v1 && !v2) {
914
+ return 0;
915
+ }
916
+ if (v1 && !v2) {
917
+ return 1;
918
+ }
919
+ if (!v1 && v2) {
920
+ return -1;
921
+ }
922
+ return v1 - v2;
923
+ };
924
+ }
925
+ export function sortByString(valueExtractor) {
926
+ return (a, b) => {
927
+ const v1 = valueExtractor(a);
928
+ const v2 = valueExtractor(b);
929
+ if (!v1 && !v2) {
930
+ return 0;
931
+ }
932
+ if (v1 && !v2) {
933
+ return 1;
934
+ }
935
+ if (!v1 && v2) {
936
+ return -1;
937
+ }
938
+ return v1.localeCompare(v2);
939
+ };
940
+ }
941
+ export function dispatchCancellableEvent(target, event) {
942
+ const deferred = new Deferred();
943
+ target.dispatchEvent(event);
944
+ window.setTimeout(() => {
945
+ deferred.resolve(event.detail);
946
+ });
947
+ return deferred.promise;
948
+ }
949
+ // left: 37, up: 38, right: 39, down: 40,
950
+ // spacebar: 32, pageup: 33, pagedown: 34, end: 35, home: 36
951
+ const keys = { 37: 1, 38: 1, 39: 1, 40: 1 };
952
+ function preventDefault(e) {
953
+ e.preventDefault();
954
+ }
955
+ function preventDefaultForScrollKeys(e) {
956
+ if (keys[e.keyCode]) {
957
+ preventDefault(e);
958
+ return false;
959
+ }
960
+ }
961
+ // modern Chrome requires { passive: false } when adding event
962
+ let supportsPassive = false;
963
+ try {
964
+ // @ts-ignore
965
+ window.addEventListener("test", null, Object.defineProperty({}, 'passive', {
966
+ get: () => { supportsPassive = true; }
967
+ }));
968
+ }
969
+ catch (e) { }
970
+ const wheelOpt = supportsPassive ? { passive: false } : false;
971
+ const wheelEvent = "onwheel" in document.createElement("div") ? "wheel" : "mousewheel";
972
+ // call this to Disable
973
+ export function disableScroll() {
974
+ window.addEventListener('DOMMouseScroll', preventDefault, false); // older FF
975
+ window.addEventListener(wheelEvent, preventDefault, wheelOpt); // modern desktop
976
+ window.addEventListener('touchmove', preventDefault, wheelOpt); // mobile
977
+ window.addEventListener('keydown', preventDefaultForScrollKeys, false);
978
+ }
979
+ // call this to Enable
980
+ export function enableScroll() {
981
+ window.removeEventListener('DOMMouseScroll', preventDefault, false);
982
+ // @ts-ignore
983
+ window.removeEventListener(wheelEvent, preventDefault, wheelOpt);
984
+ // @ts-ignore
985
+ window.removeEventListener('touchmove', preventDefault, wheelOpt);
986
+ window.removeEventListener('keydown', preventDefaultForScrollKeys, false);
987
+ }
988
+ export function blobToBase64(blob) {
989
+ return new Promise((resolve, reject) => {
990
+ const fileReader = new FileReader();
991
+ fileReader.readAsDataURL(blob);
992
+ fileReader.onload = () => {
993
+ resolve(fileReader.result);
994
+ };
995
+ fileReader.onerror = (error) => {
996
+ reject(error);
997
+ };
998
+ });
999
+ }
1000
+ //# sourceMappingURL=util.js.map