@frangoteam/fuxa-min 1.2.4 → 1.2.5

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.
Files changed (136) hide show
  1. package/LICENSE +1 -1
  2. package/api/command/index.js +2 -2
  3. package/api/index.js +5 -0
  4. package/api/jwt-helper.js +24 -13
  5. package/api/projects/index.js +14 -9
  6. package/api/resources/index.js +46 -1
  7. package/api/scripts/index.js +3 -3
  8. package/dist/assets/i18n/de.json +12 -12
  9. package/dist/assets/i18n/en.json +49 -8
  10. package/dist/assets/i18n/es.json +63 -64
  11. package/dist/assets/i18n/fr.json +10 -7
  12. package/dist/assets/i18n/ko.json +8 -8
  13. package/dist/assets/i18n/pt.json +5 -5
  14. package/dist/assets/i18n/ru.json +14 -14
  15. package/dist/assets/i18n/sv.json +1691 -0
  16. package/dist/assets/i18n/tr.json +52 -52
  17. package/dist/assets/i18n/ua.json +4 -4
  18. package/dist/assets/i18n/zh-cn.json +19 -11
  19. package/dist/assets/lib/svgeditor/fuxa-editor.min.js +7 -7
  20. package/dist/index.html +1 -1
  21. package/dist/{main.629fe2fe8530302f.js → main.ba5fa312f4ed2f21.js} +66 -66
  22. package/main.js +13 -2
  23. package/package.json +3 -3
  24. package/runtime/devices/device-utils.js +1 -1
  25. package/runtime/devices/device.js +12 -2
  26. package/runtime/devices/gpio/index.js +296 -0
  27. package/runtime/devices/index.js +1 -1
  28. package/runtime/devices/modbus/index.js +1 -1
  29. package/runtime/devices/opcua/index.js +1 -0
  30. package/runtime/index.js +26 -14
  31. package/runtime/plugins/index.js +3 -3
  32. package/runtime/project/index.js +31 -11
  33. package/runtime/scripts/index.js +15 -5
  34. package/runtime/scripts/msm.js +4 -0
  35. package/runtime/utils.js +30 -1
  36. package/settings.default.js +9 -3
  37. package/_widgets/controls/draggingIndicatorExample.svg +0 -243
  38. package/_widgets/controls/timePickerHHmmSS.svg +0 -396
  39. package/_widgets/controls/timePickerHHmmSSsss.svg +0 -428
  40. package/_widgets/dynamicSVG/blower3.svg +0 -282
  41. package/_widgets/dynamicSVG/blower3_flipped.svg +0 -282
  42. package/_widgets/dynamicSVG/blower4.svg +0 -304
  43. package/_widgets/dynamicSVG/blower4_flipped.svg +0 -304
  44. package/_widgets/dynamicSVG/checkvalve_closed.svg +0 -333
  45. package/_widgets/dynamicSVG/checkvalve_closed_flipped.svg +0 -333
  46. package/_widgets/dynamicSVG/checkvalve_open.svg +0 -316
  47. package/_widgets/dynamicSVG/checkvalve_open_flipped.svg +0 -316
  48. package/_widgets/dynamicSVG/compressor3.svg +0 -361
  49. package/_widgets/dynamicSVG/compressor6.svg +0 -299
  50. package/_widgets/dynamicSVG/condensor_3d_h1.svg +0 -300
  51. package/_widgets/dynamicSVG/conveyor.svg +0 -316
  52. package/_widgets/dynamicSVG/conveyor4.svg +0 -314
  53. package/_widgets/dynamicSVG/conveyor_20degdecline.svg +0 -341
  54. package/_widgets/dynamicSVG/conveyor_20degincline.svg +0 -340
  55. package/_widgets/dynamicSVG/cyclonicseparator.svg +0 -317
  56. package/_widgets/dynamicSVG/fanblades.svg +0 -280
  57. package/_widgets/dynamicSVG/flange_3d_1.svg +0 -286
  58. package/_widgets/dynamicSVG/flowmeter.svg +0 -305
  59. package/_widgets/dynamicSVG/flowmeter1.svg +0 -281
  60. package/_widgets/dynamicSVG/gaugedial_3d_h1.svg +0 -288
  61. package/_widgets/dynamicSVG/generator_1.svg +0 -534
  62. package/_widgets/dynamicSVG/heatexchanger_pipetopipe.svg +0 -329
  63. package/_widgets/dynamicSVG/heatexchanger_shelltube.svg +0 -329
  64. package/_widgets/dynamicSVG/heatexchanger_singlepipetopipe.svg +0 -299
  65. package/_widgets/dynamicSVG/meter_3d_h1.svg +0 -299
  66. package/_widgets/dynamicSVG/meter_3d_h2.svg +0 -293
  67. package/_widgets/dynamicSVG/mixer1.svg +0 -369
  68. package/_widgets/dynamicSVG/motor.svg +0 -285
  69. package/_widgets/dynamicSVG/motor1.svg +0 -300
  70. package/_widgets/dynamicSVG/motor1_flipped.svg +0 -300
  71. package/_widgets/dynamicSVG/motor_wshaft.svg +0 -325
  72. package/_widgets/dynamicSVG/pipe_3d_elbow.svg +0 -287
  73. package/_widgets/dynamicSVG/pipe_3d_elbow2.svg +0 -285
  74. package/_widgets/dynamicSVG/pipe_3d_h1.svg +0 -286
  75. package/_widgets/dynamicSVG/pipe_3d_h2.svg +0 -287
  76. package/_widgets/dynamicSVG/pipe_3d_h3.svg +0 -286
  77. package/_widgets/dynamicSVG/pipe_3d_heatexchange1.svg +0 -298
  78. package/_widgets/dynamicSVG/pipe_3d_heatexchange2.svg +0 -293
  79. package/_widgets/dynamicSVG/pipe_3d_heatexchange3.svg +0 -289
  80. package/_widgets/dynamicSVG/pipe_3d_tjunction.svg +0 -286
  81. package/_widgets/dynamicSVG/piperedirect1.svg +0 -294
  82. package/_widgets/dynamicSVG/piperedirect1_flipped.svg +0 -294
  83. package/_widgets/dynamicSVG/pressure_regulator.svg +0 -374
  84. package/_widgets/dynamicSVG/pulsationdampener.svg +0 -300
  85. package/_widgets/dynamicSVG/pump1.svg +0 -328
  86. package/_widgets/dynamicSVG/pump2.svg +0 -310
  87. package/_widgets/dynamicSVG/pump2_flipped.svg +0 -310
  88. package/_widgets/dynamicSVG/pump_3d_180.svg +0 -323
  89. package/_widgets/dynamicSVG/pump_3d_180_flipped.svg +0 -323
  90. package/_widgets/dynamicSVG/pump_3d_90_1.svg +0 -318
  91. package/_widgets/dynamicSVG/pump_3d_90_1_flipped.svg +0 -318
  92. package/_widgets/dynamicSVG/pump_3d_example.svg +0 -292
  93. package/_widgets/dynamicSVG/pump_3d_straight1.svg +0 -321
  94. package/_widgets/dynamicSVG/pump_3d_straight1_flipped.svg +0 -321
  95. package/_widgets/dynamicSVG/pumphead_profile_3d.svg +0 -303
  96. package/_widgets/dynamicSVG/reducer_3d_1.svg +0 -290
  97. package/_widgets/dynamicSVG/reducer_3d_2.svg +0 -287
  98. package/_widgets/dynamicSVG/sensor_rtd.svg +0 -391
  99. package/_widgets/dynamicSVG/sensor_rtd_silhouette.svg +0 -286
  100. package/_widgets/dynamicSVG/strainer_basket.svg +0 -376
  101. package/_widgets/dynamicSVG/strainer_basket_r.svg +0 -377
  102. package/_widgets/dynamicSVG/strainer_wye.svg +0 -401
  103. package/_widgets/dynamicSVG/strainer_wye_r.svg +0 -403
  104. package/_widgets/dynamicSVG/tank_3d_medium_rtop1.svg +0 -286
  105. package/_widgets/dynamicSVG/tank_3d_v1.svg +0 -291
  106. package/_widgets/dynamicSVG/tank_3d_v2.svg +0 -295
  107. package/_widgets/dynamicSVG/tank_3d_v4.svg +0 -290
  108. package/_widgets/dynamicSVG/tank_3d_v5.svg +0 -295
  109. package/_widgets/dynamicSVG/tank_3d_v6.svg +0 -290
  110. package/_widgets/dynamicSVG/tank_3d_v7.svg +0 -292
  111. package/_widgets/dynamicSVG/trucking.svg +0 -297
  112. package/_widgets/dynamicSVG/turbine1.svg +0 -284
  113. package/_widgets/dynamicSVG/turbine2.svg +0 -329
  114. package/_widgets/dynamicSVG/turbinerounded.svg +0 -291
  115. package/_widgets/dynamicSVG/valve_1path_down.svg +0 -291
  116. package/_widgets/dynamicSVG/valve_3d_3way1.svg +0 -329
  117. package/_widgets/dynamicSVG/valve_3d_3way1_nopipe.svg +0 -310
  118. package/_widgets/dynamicSVG/valve_3d_4way1.svg +0 -358
  119. package/_widgets/dynamicSVG/valve_3d_4way1_nopipe.svg +0 -328
  120. package/_widgets/dynamicSVG/valve_3d_angled1.svg +0 -325
  121. package/_widgets/dynamicSVG/valve_3d_angled1_nopipe.svg +0 -305
  122. package/_widgets/dynamicSVG/valve_3d_angled2.svg +0 -323
  123. package/_widgets/dynamicSVG/valve_3d_angled2_nopipe.svg +0 -301
  124. package/_widgets/dynamicSVG/valve_3d_common1.svg +0 -321
  125. package/_widgets/dynamicSVG/valve_3d_common2.svg +0 -327
  126. package/_widgets/dynamicSVG/valve_3d_common2_nopipe.svg +0 -298
  127. package/_widgets/dynamicSVG/valve_3d_common3.svg +0 -327
  128. package/_widgets/dynamicSVG/valve_3d_common4.svg +0 -329
  129. package/_widgets/dynamicSVG/valve_3d_common4_nopipe.svg +0 -299
  130. package/_widgets/dynamicSVG/valve_3d_h1.svg +0 -286
  131. package/_widgets/dynamicSVG/valve_3d_h2_closed.svg +0 -299
  132. package/_widgets/dynamicSVG/valve_3d_h2_open.svg +0 -291
  133. package/_widgets/dynamicSVG/valve_circle.svg +0 -295
  134. package/_widgets/indicators/analogIndicatorExample.svg +0 -272
  135. package/_widgets/indicators/timeDisplayHHmmSS.svg +0 -160
  136. package/_widgets/indicators/timeDisplayHHmmSSsss.svg +0 -164
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2019-2021 frangoteam 4frango@gmail.com
3
+ Copyright (c) 2019-2025 frangoteam info@frangoteam.org
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -71,8 +71,8 @@ module.exports = {
71
71
  const permission = checkGroupsFnc(req);
72
72
  if (res.statusCode === 403) {
73
73
  runtime.logger.error("api get getTagValue: Tocken Expired");
74
- } else if (!authJwt.haveAdminPermission(permission)) {
75
- res.status(401).json({error:"unauthorized_error", message: "Unauthorized!"});
74
+ } else if (!authJwt.haveAdminPermission(permission) && !runtime.scriptsMgr.isAuthorisedByScriptName(req.query.sourceScriptName, permission)) {
75
+ res.status(400).json({error:"unauthorized_error", message: "Unauthorized!"});
76
76
  runtime.logger.error("api get getTagValue: Unauthorized");
77
77
  } else {
78
78
  try {
package/api/index.js CHANGED
@@ -134,6 +134,11 @@ function init(_server, _runtime) {
134
134
  } else {
135
135
  res.end();
136
136
  }
137
+ } else if (req.userId === 'guest') {
138
+ res.status(200).json({
139
+ message: 'guest',
140
+ token: authJwt.getGuestToken()
141
+ });
137
142
  } else {
138
143
  res.end();
139
144
  }
package/api/jwt-helper.js CHANGED
@@ -20,7 +20,7 @@ function init(_secureEnabled, _secretCode, _tokenExpires) {
20
20
 
21
21
  /**
22
22
  * Verify token
23
- * @param {*} token
23
+ * @param {*} token
24
24
  */
25
25
  function verify (token) {
26
26
  return new Promise ((resolve, reject) => {
@@ -31,29 +31,28 @@ function verify (token) {
31
31
  } else {
32
32
  resolve(true);
33
33
  }
34
- });
34
+ });
35
35
  });
36
36
  }
37
37
 
38
38
  /**
39
39
  * Verify WebAPI token (take from header)
40
- * @param {*} req
40
+ * @param {*} req
41
41
  * @param {*} res
42
42
  * @param {*} next
43
43
  */
44
44
  function verifyToken (req, res, next) {
45
45
  let token = req.headers['x-access-token'];
46
46
 
47
+ if (!token) {
48
+ token = getGuestToken();
49
+ }
50
+
47
51
  if (token) {
48
52
  jwt.verify(token, secretCode, (err, decoded) => {
49
53
  if (err) {
50
- req.userId = null;
51
- req.userGroups = null;
52
- if (err.name === 'TokenExpiredError' || err.name === 'JsonWebTokenError') {
53
- req.tokenExpired = true;
54
- res.status(403).json({error:"unauthorized_error", message: "Token Expired!"});
55
- }
56
- next();
54
+ req.userId = "guest";
55
+ req.userGroups = ["guest"];
57
56
  } else {
58
57
  req.userId = decoded.id;
59
58
  req.userGroups = decoded.groups;
@@ -63,8 +62,8 @@ function verifyToken (req, res, next) {
63
62
  res.status(403).json({ error: "unauthorized_error", message: "User Profile Corrupted!" });
64
63
  }
65
64
  }
66
- next();
67
65
  }
66
+ next();
68
67
  });
69
68
  } else {
70
69
  // notice that no token was provided...}
@@ -84,13 +83,24 @@ function getNewToken(headers) {
84
83
  id: authUser.user,
85
84
  groups: authUser.groups
86
85
  },
87
- secretCode, {
88
- expiresIn: tokenExpiresIn
86
+ secretCode, {
87
+ expiresIn: tokenExpiresIn
89
88
  });
90
89
  }
91
90
  return null;
92
91
  }
93
92
 
93
+ function getGuestToken() {
94
+ const token = jwt.sign({
95
+ id: "guest",
96
+ groups: ["guest"]
97
+ },
98
+ secretCode, {
99
+ expiresIn: tokenExpiresIn
100
+ });
101
+ return token;
102
+ }
103
+
94
104
  function haveAdminPermission(permission) {
95
105
  if (permission === null || permission === undefined) {
96
106
  return false;
@@ -110,6 +120,7 @@ module.exports = {
110
120
  verify: verify,
111
121
  verifyToken: verifyToken,
112
122
  getNewToken: getNewToken,
123
+ getGuestToken: getGuestToken,
113
124
  get secretCode() { return secretCode },
114
125
  get tokenExpiresIn() { return tokenExpiresIn },
115
126
  haveAdminPermission: haveAdminPermission,
@@ -29,7 +29,7 @@ module.exports = {
29
29
 
30
30
  /**
31
31
  * GET Project data
32
- * Take from project storage and reply
32
+ * Take from project storage and reply
33
33
  */
34
34
  prjApp.get("/api/project", secureFnc, function(req, res) {
35
35
  const permission = checkGroupsFnc(req);
@@ -113,7 +113,7 @@ module.exports = {
113
113
 
114
114
  /**
115
115
  * GET Project demo data
116
- * Take the project demo file from server folder
116
+ * Take the project demo file from server folder
117
117
  */
118
118
  prjApp.get("/api/projectdemo", secureFnc, function (req, res) {
119
119
  const data = runtime.project.getProjectDemo();
@@ -129,7 +129,7 @@ module.exports = {
129
129
 
130
130
  /**
131
131
  * GET Device property like security
132
- * Take from project storage and reply
132
+ * Take from project storage and reply
133
133
  */
134
134
  prjApp.get("/api/device", secureFnc, function(req, res) {
135
135
  const permission = checkGroupsFnc(req);
@@ -181,7 +181,7 @@ module.exports = {
181
181
  res.status(400).json({error:"unexpected_error", message: err});
182
182
  runtime.logger.error("api post device: " + err);
183
183
  }
184
- });
184
+ });
185
185
  }
186
186
  });
187
187
 
@@ -198,20 +198,25 @@ module.exports = {
198
198
  // let basedata = file.data.replace(/^data:.*,/, '');
199
199
  // let basedata = file.data.replace(/^data:image\/png;base64,/, "");
200
200
  let fileName = file.name.replace(new RegExp('../', 'g'), '');
201
+ let fullPath = file.fullPath || file.name;
202
+ fullPath = fullPath.replace(/(\.\.[/\\])/g, '');
203
+ fullPath = path.normalize(fullPath).replace(/^(\.\.[/\\])+/, '');
204
+
201
205
  if (file.type !== 'svg') {
202
206
  basedata = file.data.replace(/^data:.*,/, '');
203
207
  encoding = {encoding: 'base64'};
204
208
  }
205
- var filePath = path.join(runtime.settings.uploadFileDir, fileName);
209
+ var filePath = path.join(runtime.settings.uploadFileDir, fullPath || fileName);
206
210
  if (destination) {
207
211
  const destinationDir = path.resolve(runtime.settings.appDir, `_${destination}`);
208
- if (!fs.existsSync(destinationDir)) {
209
- fs.mkdirSync(destinationDir);
212
+ filePath = path.join(destinationDir, fullPath || fileName);
213
+ const dir = path.dirname(filePath);
214
+ if (!fs.existsSync(dir)) {
215
+ fs.mkdirSync(dir, { recursive: true });
210
216
  }
211
- filePath = path.join(destinationDir, fileName);
212
217
  }
213
218
  fs.writeFileSync(filePath, basedata, encoding);
214
- let result = {'location': '/' + runtime.settings.httpUploadFileStatic + '/' + fileName };
219
+ let result = {'location': '/' + runtime.settings.httpUploadFileStatic + '/' + fullPath || fileName };
215
220
  res.json(result);
216
221
  } catch (err) {
217
222
  if (err && err.code) {
@@ -120,7 +120,7 @@ module.exports = {
120
120
  var wwwSubDir = path.join('_widgets', resourcesDirs[i]);
121
121
  var files = getFiles(dirPath, ['.svg']);
122
122
  for (var x = 0; x < files.length; x++) {
123
- var filename = files[x].replace(/\.[^\/.]+$/, '');
123
+ var filename = files[x];
124
124
  group.items.push({ path: path.join(wwwSubDir, files[x]).split(path.sep).join(path.posix.sep), name: filename });
125
125
  }
126
126
  result.groups.push(group);
@@ -137,6 +137,51 @@ module.exports = {
137
137
  }
138
138
  });
139
139
 
140
+ /**
141
+ * POST Remove Server widget item
142
+ */
143
+ resourcesApp.post('/api/resources/removeWidget', secureFnc, function (req, res) {
144
+ const permission = checkGroupsFnc(req);
145
+ if (res.statusCode === 403) {
146
+ runtime.logger.error("api resources/removeWidget: Tocken Expired");
147
+ } else if (!authJwt.haveAdminPermission(permission)) {
148
+ runtime.logger.error("api resources/removeWidget: Unauthorized!");
149
+ return res.status(401).json({ error: "unauthorized_error", message: "Unauthorized!" });
150
+ }
151
+ try {
152
+ const relPath = req.body?.path;
153
+ if (!relPath || typeof relPath !== 'string') {
154
+ return res.status(400).json({ error: "invalid_path", message: "Missing or invalid widget path." });
155
+ }
156
+ const basePath = path.resolve(runtime.settings.appDir);
157
+ const fullPath = path.resolve(basePath, relPath);
158
+
159
+ if (!fullPath.startsWith(basePath)) {
160
+ runtime.logger.error("api resources/widgets: security_violation " + fullPath);
161
+ return res.status(403).json({ error: 'security_violation', message: 'Invalid path' });
162
+ }
163
+
164
+ if (!fs.existsSync(fullPath)) {
165
+ return res.status(404).json({ error: "not_found", message: "Widget file not found." });
166
+ }
167
+
168
+ try {
169
+ fs.unlinkSync(fullPath);
170
+ res.json({ success: true, path: relPath });
171
+ } catch (err) {
172
+ runtime.logger.error("api removeWidget: " + err.message);
173
+ res.status(500).json({ error: "delete_failed", message: err.message });
174
+ }
175
+ } catch (err) {
176
+ if (err.code) {
177
+ res.status(400).json({ error: err.code, message: err.message });
178
+ } else {
179
+ res.status(400).json({ error: "unexpected_error", message: err.toString() });
180
+ }
181
+ runtime.logger.error("api resources/removeWidget: " + err.message);
182
+ }
183
+ });
184
+
140
185
  return resourcesApp;
141
186
  }
142
187
  }
@@ -36,11 +36,11 @@ module.exports = {
36
36
  runtime.logger.error("api post runscript: Tocken Expired");
37
37
  //runtime.settings.secureEnabled
38
38
  } else if (!runtime.scriptsMgr.isAuthorised(req.body.params.script, permission)) {
39
- res.status(401).json({ error: "unauthorized_error", message: "Unauthorized!" });
39
+ res.status(400).json({ error: "unauthorized_error", message: "Unauthorized!" });
40
40
  runtime.logger.error("api post runscript: Unauthorized");
41
41
  } else {
42
42
  //req.body.params.script.parameters.permission = groups;
43
- runtime.scriptsMgr.runScript(req.body.params.script).then(function (result) {
43
+ runtime.scriptsMgr.runScript(req.body.params.script, req.body.params.toLogEvent).then(function (result) {
44
44
  res.json(result);
45
45
  }).catch(function (err) {
46
46
  if (err.code) {
@@ -62,7 +62,7 @@ module.exports = {
62
62
  if (res.statusCode === 403) {
63
63
  runtime.logger.error("api post runSysFunction: Tocken Expired");
64
64
  } else if (authJwt.adminGroups.indexOf(groups) === -1 ) {
65
- res.status(401).json({error:"unauthorized_error", message: "Unauthorized!"});
65
+ res.status(400).json({error:"unauthorized_error", message: "Unauthorized!"});
66
66
  runtime.logger.error("api post runSysFunction: Unauthorized");
67
67
  } else {
68
68
  try {
@@ -326,7 +326,7 @@
326
326
  "graph.property-datalabels-show": "Anzeigen",
327
327
  "graph.property-datalabels-fontsize": "Schriftgröße",
328
328
  "graph.property-datalabels-align": "Ausrichten",
329
-
329
+
330
330
  "graph.rangetype-last1h": "Letzte Stunde",
331
331
  "graph.rangetype-last1d": "Letzter Tag",
332
332
  "graph.rangetype-last3d": "Letzte 3 Tage",
@@ -353,7 +353,7 @@
353
353
  "table.property-row-height": "Höhe",
354
354
  "table.property-row-background": "Hintergrund",
355
355
  "table.property-row-color": "Farbe",
356
- "table.property-row-fontSize": "Schriftgröße",
356
+ "table.property-row-fontSize": "Schriftgröße",
357
357
  "table.property-layout": "Layout",
358
358
  "table.property-filter": "Filter",
359
359
  "table.property-paginator": "Paginator",
@@ -806,7 +806,7 @@
806
806
  "device.topic-name-exist": "Der Topic-Name '{{value}}' existiert bereits",
807
807
  "device.topic-subs-address-exist": "Das zu abonnierende Topic mit der Adresse '{{value}}' existiert bereits",
808
808
  "device.topic-pubs-address-exist": "Das zu veröffentlichende Thema mit der Adresse '{{value}}' existiert bereits",
809
-
809
+
810
810
  "device.property-mqtt-address": "Adresse (mqtt://[Server]:[Port])",
811
811
  "device.tag-property-title": "Tag-Eigenschaft",
812
812
  "device.browsetag-property-title": "Tags im Server durchsuchen",
@@ -823,8 +823,8 @@
823
823
  "device.tag-property-obj-name": "Name",
824
824
  "device.tag-property-path": "Pfad",
825
825
  "device.tag-property-unit": "Einheit",
826
- "device.tag-property-digits": "Dezimal",
827
- "device.tag-property-initvalue": "Initialwert",
826
+ "device.tag-property-digits": "Dezimal",
827
+ "device.tag-property-initvalue": "Initialwert",
828
828
  "device.tag-array-id": "Array-ID:",
829
829
  "device.tag-array-value": "Wert:",
830
830
  "device-tag-dialog-title": "Tag-Auswahl",
@@ -843,7 +843,7 @@
843
843
  "device.tag-raw-high": "Raw High",
844
844
  "device.tag-scaled-low": "Skaliert niedrig",
845
845
  "device.tag-scaled-high": "Skaliert hoch",
846
-
846
+
847
847
  "device.connect-ok": "Ok",
848
848
  "device.connect-error": "Fehler",
849
849
  "device.connect-failed": "Fehlgeschlagen",
@@ -1120,7 +1120,7 @@
1120
1120
  "report.item-interval-min10": "10 Minuten",
1121
1121
  "report.item-interval-min30": "30 Minuten",
1122
1122
  "report.item-interval-hour": "1 Stunde",
1123
- "report.item-interval-day": "1 Tag",
1123
+ "report.item-interval-day": "1 Tag",
1124
1124
  "report.item-function-type": "Funktionstyp",
1125
1125
  "report.item-function-min": "Min",
1126
1126
  "report.item-function-max": "Max",
@@ -1162,6 +1162,11 @@
1162
1162
  "texts.list-group": "Gruppe",
1163
1163
  "texts.list-value": "Text",
1164
1164
 
1165
+ "text.settings-title": "Text",
1166
+ "text.settings-id": "ID",
1167
+ "text.settings-group": "Group",
1168
+ "text.settings-value": "Text",
1169
+
1165
1170
  "gui.range-number-min": "Min",
1166
1171
  "gui.range-number-max": "Max",
1167
1172
  "gui.range-number-boolean": "Maskenwert",
@@ -1171,11 +1176,6 @@
1171
1176
 
1172
1177
  "dlg.bitmask-title": "Bitmaske",
1173
1178
 
1174
- "dlg.textproperty-title": "Text",
1175
- "dlg.textproperty-id": "ID",
1176
- "dlg.textproperty-group": "Gruppe",
1177
- "dlg.textproperty-value": "Text",
1178
-
1179
1179
  "dlg.plugins-title": "Plugins",
1180
1180
  "dlg.plugins-name": "Name",
1181
1181
  "dlg.plugins-version": "Version",
@@ -66,6 +66,7 @@
66
66
  "dlg.layout-lbl-margin-right": "Margin right",
67
67
  "dlg.layout-lbl-login-info": "Login info",
68
68
  "dlg.layout-lbl-custom-styles": "Custom styles",
69
+ "dlg.layout-lbl-show-language": "Show language",
69
70
 
70
71
  "dlg.menuitem-title": "Menu Item",
71
72
  "dlg.menuitem-image": "[My Image...]",
@@ -137,6 +138,7 @@
137
138
  "dlg.userproperty-groups": "Authorization Groups",
138
139
  "dlg.userproperty-roles": "Authorization Roles",
139
140
  "dlg.useraccess-groups": "Authorization (void = full for everyone)",
141
+ "dlg.userproperty-language": "Language",
140
142
  "dlg.userproperty-start": "Start View",
141
143
 
142
144
  "dlg.gauge-permission-title": "Authorization",
@@ -217,6 +219,11 @@
217
219
  "item.logininfo-type-fullname": "Full name",
218
220
  "item.logininfo-type-both": "Username & Full name",
219
221
 
222
+ "item.language-show-mode-nothing": "Hide",
223
+ "item.language-show-mode-simple": "Show",
224
+ "item.language-show-mode-key": "Show with Key",
225
+ "item.language-show-mode-fullname": "Show with Full name",
226
+
220
227
  "item.zoommode-disabled": "Disabled",
221
228
  "item.zoommode-enabled": "Manually",
222
229
  "item.zoommode-autoresize": "Auto-resize",
@@ -283,6 +290,7 @@
283
290
  "chart.property-date-last-range": "Date range",
284
291
  "chart.property-refresh-interval": "Refresh interval (min.)",
285
292
  "chart.property-hide-toolbar": "Hide toolbar",
293
+ "chart.property-thouch-zoom": "Thouch Zoom",
286
294
  "chart.property-load-old-values": "Load history",
287
295
  "chart.property-date.format": "Date format",
288
296
  "chart.property-time.format": "Time format",
@@ -634,6 +642,8 @@
634
642
  "editor.transform-change-image-title": "NOTE: This image cannot be embedded. It will depend on this path to be displayed",
635
643
  "editor.transform-angle": "angle",
636
644
  "editor.transform-angle-title": "Change rotation angle",
645
+ "editor.transform-hide": "Hide",
646
+ "editor.transform-lock": "Lock",
637
647
 
638
648
  "editor.align": "Align",
639
649
  "editor.align-left-title": "Align Left",
@@ -845,6 +855,7 @@
845
855
  "device.list-remove-all": "Remove all Tags",
846
856
  "device.list-options": "Tag options",
847
857
  "device.list-clipboard": "Copy Tag object to clipboard",
858
+ "device.list-direction": "Direction",
848
859
 
849
860
  "devices.export": "Export Devices",
850
861
  "devices.export-json": "JSON",
@@ -970,6 +981,11 @@
970
981
  "device.tag-property-digits": "Decimal",
971
982
  "device.tag-property-initvalue": "Init value",
972
983
  "device.tag-property-description": "Description",
984
+ "device.tag-property-direction": "Direction",
985
+ "device.tag-property-edge": "Edge",
986
+ "device.tag-property-gpio": "GPIO Number",
987
+
988
+
973
989
  "device.tag-array-id": "Array ID:",
974
990
  "device.tag-array-value": "Value:",
975
991
  "device-tag-dialog-title": "Tag Selection",
@@ -1126,6 +1142,7 @@
1126
1142
  "gauges.property-tag-label": "Tag",
1127
1143
  "gauges.property-tag-internal-title": "Set placeholder of Destination (or Tag of internal device)",
1128
1144
  "gauges.property-input-range": "Allowed input range",
1145
+ "gauges.property-language-text": "Text or @TextID",
1129
1146
 
1130
1147
  "bag.property-ticks": "Ticks",
1131
1148
  "bag.property-divisions": "Divisions",
@@ -1348,6 +1365,11 @@
1348
1365
  "script.template-chart-data-tooltip": "Code template of customized chart data to return",
1349
1366
  "script.template-invoke-chart-update-options-text": "Update Chart options",
1350
1367
  "script.template-invoke-chart-update-options-tooltip": "Code template to update Chart options (Y-axis scale range). CLIENT mode only!",
1368
+ "script.template-getHistoricalTagsoptions-text": "Get Historical Tags values",
1369
+ "script.template-getHistoricalTagsoptions-tooltip": "Code template to get Historical Tags values.",
1370
+
1371
+ "client.script-access-title": "Client Scripts access",
1372
+ "client.script-access-info": "Configure access to system functions from the frontend (e.g. widgets) via window.fuxaScriptAPI.",
1351
1373
 
1352
1374
  "reports.list-title": "Reports settings",
1353
1375
  "reports.list-name": "Name",
@@ -1467,12 +1489,29 @@
1467
1489
  "resource.list-title-fonts": "Fonts list",
1468
1490
  "resources.lib-widgets": "Widgets",
1469
1491
 
1470
- "texts.list-title": "Texts settings",
1492
+ "widgets.kiosk-title": "Widgets Kiosk",
1493
+ "widget.remove": "Remove Widget",
1494
+
1495
+ "texts.list-title": "Language settings",
1471
1496
  "texts.list-filter": "Filter",
1472
1497
  "texts.list-filter-group": "Group",
1473
1498
  "texts.list-id": "ID",
1474
1499
  "texts.list-group": "Group",
1475
- "texts.list-value": "Text",
1500
+ "texts.list-value": "Text (default)",
1501
+ "texts.list-add-text": "Add Text",
1502
+ "texts.list-edit-language": "Edit Language",
1503
+ "language.settings-title": "Language settings",
1504
+ "language.settings-add-tooltip": "Add Language",
1505
+ "language.settings-id": "Key",
1506
+ "language.settings-default-id": "Key (default)",
1507
+ "language.settings-id-placeholder": "ex: EN",
1508
+ "language.settings-name": "Name",
1509
+ "language.settings-default-name": "Name (default)",
1510
+ "language.settings-name-placeholder": "ex: English",
1511
+ "text.settings-title": "Text",
1512
+ "text.settings-id": "Name (Allowed letters and numbers, no spaces)",
1513
+ "text.settings-group": "Group",
1514
+ "text.settings-value": "Text (default)",
1476
1515
 
1477
1516
  "gui.range-number-min": "Min",
1478
1517
  "gui.range-number-max": "Max",
@@ -1483,11 +1522,6 @@
1483
1522
 
1484
1523
  "dlg.bitmask-title": "Bitmask",
1485
1524
 
1486
- "dlg.textproperty-title": "Text",
1487
- "dlg.textproperty-id": "ID",
1488
- "dlg.textproperty-group": "Group",
1489
- "dlg.textproperty-value": "Text",
1490
-
1491
1525
  "dlg.plugins-title": "Server Plugins",
1492
1526
  "dlg.plugins-info": "Not installed plugins can be installed in the server manually: 'npm install [package name]@[version]'",
1493
1527
  "dlg.plugins-type": "Type",
@@ -1519,11 +1553,13 @@
1519
1553
  "dlg.setup-scripts": "Scripts",
1520
1554
  "dlg.setup-reports": "Reports",
1521
1555
  "dlg.setup-settings": "Settings",
1556
+ "dlg.setup-client-access": "Frontend Scripts",
1522
1557
  "dlg.setup-logs": "Logs",
1523
1558
  "dlg.setup-notifications": "Notifications",
1524
1559
  "dlg.setup-events": "Events",
1525
1560
  "dlg.setup-resources": "Resources",
1526
1561
  "dlg.setup-fonts": "Fonts",
1562
+ "dlg.setup-language": "Language",
1527
1563
  "dlg.app-settings-title": "Settings",
1528
1564
  "dlg.app-settings-system": "System",
1529
1565
  "dlg.app-settings-smtp": "SMTP",
@@ -1541,6 +1577,7 @@
1541
1577
  "dlg.app-language-ua": "Ukrainian",
1542
1578
  "dlg.app-language-zh-cn": "Chinese",
1543
1579
  "dlg.app-language-pt": "Portuguese",
1580
+ "dlg.app-language-sv": "Swedish",
1544
1581
  "dlg.app-language-tr": "Turkish",
1545
1582
  "dlg.app-language-ko": "Korean",
1546
1583
  "dlg.app-language-es": "Spanish",
@@ -1645,6 +1682,7 @@
1645
1682
  "msg.device-tags-request-result": "Load {{value}} of {{current}}",
1646
1683
  "msg.chart-with-script": "The defined script receives the list of chart lines as a parameter and then returns the completed list with the data. Code sample in script 'Templates'",
1647
1684
  "msg.script-name-exist": "Script name exist!",
1685
+ "msg.invalid-script-name": "Invalid script name!",
1648
1686
  "msg.templates-exist-ask-overwrite": "The template with name '{{value}}' already exist. Do you want to overwrite it?",
1649
1687
  "msg.templates-save-success": "Template save successful!",
1650
1688
  "msg.view-name-exist": "View name exist!",
@@ -1652,5 +1690,8 @@
1652
1690
  "msg.file-remove": "Would you like to remove '{{value}}'?",
1653
1691
  "msg.notification-remove": "Would you like to remove Notification '{{value}}'?",
1654
1692
  "msg.maps-location-remove": "Would you like to remove Maps Location '{{value}}'?",
1655
- "msg.maps-location-name-exist": "The Maps Location name already exist!"
1693
+ "msg.maps-location-name-exist": "The Maps Location name already exist!",
1694
+ "msg.text-name-exist": "Text name already exist!",
1695
+ "msg.texts-text-remove": "Would you like to remove Text '{{value}}'?",
1696
+ "msg.operation-unauthorized": "Operation Unauthorized!"
1656
1697
  }