iobroker.eos-admin 7.9.35 → 7.9.37
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/admin/i18n/de.json +7 -3
- package/admin/i18n/en.json +5 -3
- package/admin/i18n/es.json +5 -3
- package/admin/i18n/fr.json +5 -3
- package/admin/i18n/it.json +5 -3
- package/admin/i18n/nl.json +5 -3
- package/admin/i18n/pl.json +5 -3
- package/admin/i18n/pt.json +5 -3
- package/admin/i18n/ru.json +5 -3
- package/admin/i18n/uk.json +5 -3
- package/admin/i18n/zh-cn.json +5 -3
- package/admin/jsonConfig.json5 +8 -7
- package/adminWww/assets/bootstrap-COulQZax.js +1 -1
- package/adminWww/css/eos-branding.css +150 -0
- package/adminWww/index.html +4 -5
- package/adminWww/js/eos-branding.js +216 -18
- package/adminWww/js/eos-hard-logout.js +15 -162
- package/adminWww/js/eos-runtime-fixes.js +144 -0
- package/adminWww/js/eos-security-ui.js +40 -7
- package/build/i18n/de.json +2 -2
- package/build/i18n/en.json +2 -2
- package/build/lib/web.js +48 -64
- package/build/main.js +13 -3
- package/docs/NEXOWATT_EOS_UI_V37_STABILITY_DE.md +13 -0
- package/io-package.json +13 -12
- package/package.json +2 -2
- package/public/404.html +12 -0
- package/tools/nexowatt-validate-package.cjs +9 -3
|
@@ -3319,3 +3319,153 @@ html.eos-app #eos-assist-root {
|
|
|
3319
3319
|
html.eos-app .eos-assist-root,
|
|
3320
3320
|
html.eos-app #eos-assist-root { right: 96px !important; }
|
|
3321
3321
|
}
|
|
3322
|
+
|
|
3323
|
+
|
|
3324
|
+
/* === NexoWatt EOS v37: native adapter configuration safe mode =============
|
|
3325
|
+
Custom adapter configuration pages (React/HTML/jsonConfig) must be fully
|
|
3326
|
+
controlled by the adapter itself. EOS shell decoration is disabled inside the
|
|
3327
|
+
content area so buttons such as "Gerät hinzufügen" and "Gerät bearbeiten" stay
|
|
3328
|
+
clickable and dialogs/popovers are not hidden behind overlays. */
|
|
3329
|
+
html.eos-app.eos-adapter-config-surface #app-paper,
|
|
3330
|
+
html.eos-app.eos-adapter-config-surface #app-paper * {
|
|
3331
|
+
pointer-events: auto !important;
|
|
3332
|
+
}
|
|
3333
|
+
html.eos-app.eos-adapter-config-surface #app-paper button,
|
|
3334
|
+
html.eos-app.eos-adapter-config-surface #app-paper [role="button"],
|
|
3335
|
+
html.eos-app.eos-adapter-config-surface #app-paper a,
|
|
3336
|
+
html.eos-app.eos-adapter-config-surface #app-paper input,
|
|
3337
|
+
html.eos-app.eos-adapter-config-surface #app-paper select,
|
|
3338
|
+
html.eos-app.eos-adapter-config-surface #app-paper textarea,
|
|
3339
|
+
html.eos-app.eos-adapter-config-surface #app-paper .MuiButton-root,
|
|
3340
|
+
html.eos-app.eos-adapter-config-surface #app-paper .MuiIconButton-root,
|
|
3341
|
+
html.eos-app.eos-adapter-config-surface #app-paper .MuiMenuItem-root {
|
|
3342
|
+
pointer-events: auto !important;
|
|
3343
|
+
user-select: auto !important;
|
|
3344
|
+
}
|
|
3345
|
+
html.eos-app.eos-adapter-config-surface .eos-assist-root,
|
|
3346
|
+
html.eos-app.eos-adapter-config-surface #eos-assist-root,
|
|
3347
|
+
html.eos-app.eos-adapter-config-surface .eos-assist-config-hidden {
|
|
3348
|
+
display: none !important;
|
|
3349
|
+
visibility: hidden !important;
|
|
3350
|
+
pointer-events: none !important;
|
|
3351
|
+
}
|
|
3352
|
+
html.eos-app.eos-adapter-config-surface .MuiDialog-root,
|
|
3353
|
+
html.eos-app.eos-adapter-config-surface .MuiModal-root,
|
|
3354
|
+
html.eos-app.eos-adapter-config-surface .MuiPopover-root,
|
|
3355
|
+
html.eos-app.eos-adapter-config-surface .MuiMenu-root,
|
|
3356
|
+
html.eos-app.eos-adapter-config-surface .MuiDialog-container {
|
|
3357
|
+
pointer-events: auto !important;
|
|
3358
|
+
z-index: 4200 !important;
|
|
3359
|
+
}
|
|
3360
|
+
html.eos-app.eos-adapter-config-surface .MuiDialog-paper,
|
|
3361
|
+
html.eos-app.eos-adapter-config-surface .MuiPopover-paper,
|
|
3362
|
+
html.eos-app.eos-adapter-config-surface .MuiMenu-paper {
|
|
3363
|
+
pointer-events: auto !important;
|
|
3364
|
+
}
|
|
3365
|
+
html.eos-app.eos-adapter-config-surface #app-paper .eos-protected-delete-control,
|
|
3366
|
+
html.eos-app.eos-adapter-config-surface #app-paper .eos-security-hidden-delete,
|
|
3367
|
+
html.eos-app.eos-adapter-config-surface #app-paper .eos-protected-adapter-row {
|
|
3368
|
+
display: revert !important;
|
|
3369
|
+
visibility: visible !important;
|
|
3370
|
+
opacity: 1 !important;
|
|
3371
|
+
filter: none !important;
|
|
3372
|
+
pointer-events: auto !important;
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3375
|
+
/* === NexoWatt EOS v37: notification close compatibility ==================
|
|
3376
|
+
Toasts/snackbars/alerts must stay above EOS decoration and their close
|
|
3377
|
+
actions must remain clickable on all routes. */
|
|
3378
|
+
html.eos-app .MuiSnackbar-root,
|
|
3379
|
+
html.eos-app .SnackbarItem-root,
|
|
3380
|
+
html.eos-app .SnackbarItem-wrappedRoot,
|
|
3381
|
+
html.eos-app .notistack-Snackbar,
|
|
3382
|
+
html.eos-app .Toastify__toast-container,
|
|
3383
|
+
html.eos-app .Toastify__toast,
|
|
3384
|
+
html.eos-app [role="alert"],
|
|
3385
|
+
html.eos-app .MuiAlert-root {
|
|
3386
|
+
pointer-events: auto !important;
|
|
3387
|
+
z-index: 5200 !important;
|
|
3388
|
+
}
|
|
3389
|
+
html.eos-app .MuiSnackbar-root button,
|
|
3390
|
+
html.eos-app .SnackbarItem-root button,
|
|
3391
|
+
html.eos-app .notistack-Snackbar button,
|
|
3392
|
+
html.eos-app .Toastify__toast button,
|
|
3393
|
+
html.eos-app .MuiAlert-root button,
|
|
3394
|
+
html.eos-app [role="alert"] button,
|
|
3395
|
+
html.eos-app button[aria-label="close"],
|
|
3396
|
+
html.eos-app button[aria-label="Close"],
|
|
3397
|
+
html.eos-app button[title="Schließen"],
|
|
3398
|
+
html.eos-app button[title="Close"] {
|
|
3399
|
+
pointer-events: auto !important;
|
|
3400
|
+
opacity: 1 !important;
|
|
3401
|
+
visibility: visible !important;
|
|
3402
|
+
z-index: 5201 !important;
|
|
3403
|
+
}
|
|
3404
|
+
html.eos-app .MuiSnackbar-root svg,
|
|
3405
|
+
html.eos-app .SnackbarItem-root svg,
|
|
3406
|
+
html.eos-app .MuiAlert-root svg,
|
|
3407
|
+
html.eos-app [role="alert"] svg {
|
|
3408
|
+
pointer-events: none !important;
|
|
3409
|
+
}
|
|
3410
|
+
|
|
3411
|
+
/* v37: BackItUp-safe mode. EOS keeps delete/stop controls protected in the UI,
|
|
3412
|
+
but does not force adapter-object ACLs for runtime adapters by default. */
|
|
3413
|
+
html.eos-app .eos-backitup-safe-note { color: rgba(226,245,255,.78); }
|
|
3414
|
+
|
|
3415
|
+
/* v37: native Admin notifications must always be closable. */
|
|
3416
|
+
html.eos-app .MuiSnackbar-root,
|
|
3417
|
+
html.eos-app .MuiAlert-root,
|
|
3418
|
+
html.eos-app .MuiSnackbarContent-root,
|
|
3419
|
+
html.eos-app [role="alert"],
|
|
3420
|
+
html.eos-app .Toastify__toast,
|
|
3421
|
+
html.eos-app .notistack-Snackbar,
|
|
3422
|
+
html.eos-app .eos-notification-safe {
|
|
3423
|
+
pointer-events: auto !important;
|
|
3424
|
+
z-index: 5200 !important;
|
|
3425
|
+
}
|
|
3426
|
+
html.eos-app .MuiSnackbar-root button,
|
|
3427
|
+
html.eos-app .MuiAlert-root button,
|
|
3428
|
+
html.eos-app .MuiSnackbarContent-root button,
|
|
3429
|
+
html.eos-app [role="alert"] button,
|
|
3430
|
+
html.eos-app .Toastify__toast button,
|
|
3431
|
+
html.eos-app .notistack-Snackbar button,
|
|
3432
|
+
html.eos-app .eos-notification-safe button,
|
|
3433
|
+
html.eos-app .eos-notification-safe [role="button"],
|
|
3434
|
+
html.eos-app .eos-notification-safe a,
|
|
3435
|
+
html.eos-app .eos-notification-safe .MuiIconButton-root {
|
|
3436
|
+
pointer-events: auto !important;
|
|
3437
|
+
visibility: visible !important;
|
|
3438
|
+
opacity: 1 !important;
|
|
3439
|
+
}
|
|
3440
|
+
html.eos-app .eos-notification-safe .eos-protected-delete-control,
|
|
3441
|
+
html.eos-app .eos-notification-safe .eos-security-hidden-delete {
|
|
3442
|
+
display: inline-flex !important;
|
|
3443
|
+
pointer-events: auto !important;
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3446
|
+
|
|
3447
|
+
/* v37 notification dialog safety: notification dialogs must stay fully native/clickable. */
|
|
3448
|
+
html.eos-app .eos-notification-dialog-root,
|
|
3449
|
+
html.eos-app .MuiModal-root:has(#notifications-dialog-close),
|
|
3450
|
+
html.eos-app .MuiDialog-root:has(#notifications-dialog-close) {
|
|
3451
|
+
pointer-events: auto !important;
|
|
3452
|
+
z-index: 4300 !important;
|
|
3453
|
+
}
|
|
3454
|
+
html.eos-app .eos-notification-dialog,
|
|
3455
|
+
html.eos-app .MuiDialog-paper:has(#notifications-dialog-close) {
|
|
3456
|
+
pointer-events: auto !important;
|
|
3457
|
+
z-index: 4301 !important;
|
|
3458
|
+
}
|
|
3459
|
+
html.eos-app .eos-notification-dialog button,
|
|
3460
|
+
html.eos-app .eos-notification-dialog [role="button"],
|
|
3461
|
+
html.eos-app .MuiDialog-paper:has(#notifications-dialog-close) button,
|
|
3462
|
+
html.eos-app .MuiDialog-paper:has(#notifications-dialog-close) [role="button"] {
|
|
3463
|
+
pointer-events: auto !important;
|
|
3464
|
+
visibility: visible !important;
|
|
3465
|
+
}
|
|
3466
|
+
|
|
3467
|
+
/* v37: security text must never show mojibake-like fragments after runtime repair. */
|
|
3468
|
+
html.eos-app .eos-security-admin-only-field,
|
|
3469
|
+
html.eos-app .eos-settings-dialog {
|
|
3470
|
+
unicode-bidi: plaintext;
|
|
3471
|
+
}
|
package/adminWww/index.html
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
rel="stylesheet"
|
|
32
32
|
href="css/leaflet.css"
|
|
33
33
|
/>
|
|
34
|
-
<link rel="stylesheet" href="./css/eos-branding.css?v=
|
|
34
|
+
<link rel="stylesheet" href="./css/eos-branding.css?v=37" />
|
|
35
35
|
<link
|
|
36
36
|
rel="manifest"
|
|
37
37
|
href="manifest.json"
|
|
@@ -154,10 +154,9 @@
|
|
|
154
154
|
<script type="module" crossorigin src="./assets/index-CQZugZ1z.js"></script>
|
|
155
155
|
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BDBacUwf.js">
|
|
156
156
|
<link rel="modulepreload" crossorigin href="./assets/iobroker_admin__mf_v__runtimeInit__mf_v__-g2X2zhAf.js">
|
|
157
|
-
<script defer src="./js/eos-branding.js?v=
|
|
158
|
-
<script defer src="./js/eos-security-ui.js?v=
|
|
159
|
-
<script defer src="./js/eos-
|
|
160
|
-
<script defer src="./js/eos-assistant.js?v=35"></script>
|
|
157
|
+
<script defer src="./js/eos-branding.js?v=37"></script>
|
|
158
|
+
<script defer src="./js/eos-security-ui.js?v=37"></script>
|
|
159
|
+
<script defer src="./js/eos-assistant.js?v=37"></script>
|
|
161
160
|
</head>
|
|
162
161
|
<body>
|
|
163
162
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(() => {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
window.NEXOWATT_EOS_UI_VERSION = '
|
|
4
|
+
window.NEXOWATT_EOS_UI_VERSION = 'v37-notification-backitup-security-text-fix';
|
|
5
5
|
|
|
6
6
|
const BRAND = 'NexoWatt EOS';
|
|
7
7
|
const EOS_MEANING = 'Energy Operation System';
|
|
@@ -48,6 +48,46 @@
|
|
|
48
48
|
'ß': 'ß', 'Ä': 'Ä', 'Ö': 'Ö', 'Ü': 'Ü', 'ä': 'ä', 'ö': 'ö', 'ü': 'ü'
|
|
49
49
|
}));
|
|
50
50
|
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
const decodeMojibakeChunk = chunk => {
|
|
54
|
+
try {
|
|
55
|
+
const bytes = Uint8Array.from(Array.from(chunk, char => char.charCodeAt(0) & 0xff));
|
|
56
|
+
const decoded = new TextDecoder('utf-8', { fatal: false }).decode(bytes);
|
|
57
|
+
return decoded && decoded !== chunk ? decoded : chunk;
|
|
58
|
+
} catch (_) {
|
|
59
|
+
return chunk;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const repairMojibake = value => {
|
|
64
|
+
let text = String(value || '');
|
|
65
|
+
// Repair common UTF-8-as-Latin1 fragments, including double-encoded strings
|
|
66
|
+
// such as dürfen, Löschen, geschützt.
|
|
67
|
+
for (let i = 0; i < 3 && /(?:Ã.|Â.|â.|�)/.test(text); i += 1) {
|
|
68
|
+
const repaired = text.replace(/[\u00C2-\u00F4][\u0080-\u00BF\u00A0-\u00BF]+/g, decodeMojibakeChunk);
|
|
69
|
+
if (repaired === text) break;
|
|
70
|
+
text = repaired;
|
|
71
|
+
}
|
|
72
|
+
const hardMap = new Map(Object.entries({
|
|
73
|
+
'dürfen': 'dürfen', 'dürfen': 'dürfen', 'Dürfen': 'Dürfen', 'Dürfen': 'Dürfen',
|
|
74
|
+
'für': 'für', 'für': 'für', 'Für': 'Für', 'Für': 'Für',
|
|
75
|
+
'können': 'können', 'können': 'können', 'Können': 'Können', 'Können': 'Können',
|
|
76
|
+
'möglich': 'möglich', 'möglich': 'möglich', 'Möglich': 'Möglich', 'Möglich': 'Möglich',
|
|
77
|
+
'Löschen': 'Löschen', 'Löschen': 'Löschen', 'löschen': 'löschen', 'löschen': 'löschen',
|
|
78
|
+
'schützen': 'schützen', 'schützen': 'schützen', 'Schützen': 'Schützen', 'Schützen': 'Schützen',
|
|
79
|
+
'Geschützte': 'Geschützte', 'Geschützte': 'Geschützte', 'geschützte': 'geschützte', 'geschützte': 'geschützte',
|
|
80
|
+
'ausgewählte': 'ausgewählte', 'ausgewählte': 'ausgewählte', 'Ausgewählte': 'Ausgewählte', 'Ausgewählte': 'Ausgewählte',
|
|
81
|
+
'ändern': 'ändern', 'ändern': 'ändern', 'Über': 'Über', 'Über': 'Über', 'über': 'über', 'über': 'über',
|
|
82
|
+
'Gerät': 'Gerät', 'Gerät': 'Gerät', 'Geräte': 'Geräte', 'Geräte': 'Geräte',
|
|
83
|
+
'schließen': 'schließen', 'schließen': 'schließen', 'ß': 'ß', 'ß': 'ß',
|
|
84
|
+
'ä': 'ä', 'ä': 'ä', 'ö': 'ö', 'ö': 'ö', 'ü': 'ü', 'ü': 'ü',
|
|
85
|
+
'Ä': 'Ä', 'Ä': 'Ä', 'Ö': 'Ö', 'Ö': 'Ö', 'Ü': 'Ü', 'Ü': 'Ü', ' ': ' ', 'Â': ''
|
|
86
|
+
}));
|
|
87
|
+
for (const [from, to] of hardMap) if (text.includes(from)) text = text.split(from).join(to);
|
|
88
|
+
return text;
|
|
89
|
+
};
|
|
90
|
+
|
|
51
91
|
const EXACT_LABELS = new Map(Object.entries({
|
|
52
92
|
'Admin': BRAND,
|
|
53
93
|
'NEXOWATT': 'NEXOWATT EOS',
|
|
@@ -104,11 +144,13 @@
|
|
|
104
144
|
|
|
105
145
|
const replaceBrand = value => {
|
|
106
146
|
if (!value || typeof value !== 'string') return value;
|
|
107
|
-
let next = value;
|
|
147
|
+
let next = repairMojibake(value);
|
|
108
148
|
for (const [from, to] of MOJIBAKE_REPLACEMENTS) {
|
|
109
149
|
if (next.includes(from)) next = next.split(from).join(to);
|
|
110
150
|
}
|
|
151
|
+
next = repairMojibake(next);
|
|
111
152
|
for (const [pattern, replacement] of TEXT_REPLACEMENTS) next = next.replace(pattern, replacement);
|
|
153
|
+
next = repairMojibake(next);
|
|
112
154
|
const compact = next.trim();
|
|
113
155
|
if (EXACT_LABELS.has(compact)) next = next.replace(compact, EXACT_LABELS.get(compact));
|
|
114
156
|
return next;
|
|
@@ -145,6 +187,32 @@
|
|
|
145
187
|
while ((node = walker.nextNode())) patchTextNode(node);
|
|
146
188
|
});
|
|
147
189
|
|
|
190
|
+
|
|
191
|
+
const patchMojibakeTextNode = node => {
|
|
192
|
+
if (!node || node.nodeType !== Node.TEXT_NODE || !node.nodeValue) return;
|
|
193
|
+
if (skipElement(node.parentElement)) return;
|
|
194
|
+
const before = node.nodeValue;
|
|
195
|
+
const after = repairMojibake(before);
|
|
196
|
+
if (after !== before) node.nodeValue = after;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const patchMojibakeTextNodes = root => safe(() => {
|
|
200
|
+
if (!root) return;
|
|
201
|
+
if (root.nodeType === Node.TEXT_NODE) {
|
|
202
|
+
patchMojibakeTextNode(root);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (root.nodeType !== Node.ELEMENT_NODE && root.nodeType !== Node.DOCUMENT_NODE) return;
|
|
206
|
+
if (skipElement(root)) return;
|
|
207
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
|
|
208
|
+
acceptNode(node) {
|
|
209
|
+
return skipElement(node.parentElement) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
let node;
|
|
213
|
+
while ((node = walker.nextNode())) patchMojibakeTextNode(node);
|
|
214
|
+
});
|
|
215
|
+
|
|
148
216
|
const patchImage = img => {
|
|
149
217
|
const src = img.getAttribute('src') || '';
|
|
150
218
|
const alt = img.getAttribute('alt') || '';
|
|
@@ -335,6 +403,26 @@
|
|
|
335
403
|
});
|
|
336
404
|
};
|
|
337
405
|
|
|
406
|
+
|
|
407
|
+
const releaseNotificationControls = () => safe(() => {
|
|
408
|
+
// v37: notification/snackbar close buttons belong to the native Admin UI.
|
|
409
|
+
// They must never be disabled or covered by EOS security/layout layers.
|
|
410
|
+
document.querySelectorAll('.MuiSnackbar-root, .MuiAlert-root, .MuiSnackbarContent-root, [role="alert"], .Toastify__toast, .notistack-Snackbar').forEach(box => {
|
|
411
|
+
box.classList.add('eos-notification-safe');
|
|
412
|
+
box.style.pointerEvents = 'auto';
|
|
413
|
+
box.querySelectorAll('button, [role="button"], a, .MuiIconButton-root, svg').forEach(control => {
|
|
414
|
+
control.classList.remove('eos-protected-delete-control', 'eos-security-hidden-delete');
|
|
415
|
+
control.removeAttribute('disabled');
|
|
416
|
+
control.removeAttribute('aria-disabled');
|
|
417
|
+
if ('disabled' in control) control.disabled = false;
|
|
418
|
+
control.style.pointerEvents = 'auto';
|
|
419
|
+
control.style.display = '';
|
|
420
|
+
control.style.visibility = '';
|
|
421
|
+
control.style.opacity = '';
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
|
|
338
426
|
const protectDeleteDialogs = () => {
|
|
339
427
|
if (isAdminUser() || state.securityPolicy.restrictProtectedAdapterControls === false) return;
|
|
340
428
|
const protectedAdapters = state.securityPolicy.protectedAdapters || [];
|
|
@@ -398,6 +486,10 @@
|
|
|
398
486
|
const applySecurityUiGuard = () => safe(() => {
|
|
399
487
|
const policy = state.securityPolicy;
|
|
400
488
|
applySecurityClasses();
|
|
489
|
+
releaseNotificationControls();
|
|
490
|
+
// Do not apply EOS security decoration inside native adapter configuration pages.
|
|
491
|
+
// Adapter UIs must remain 100% functional; backend/role checks still protect EOS actions.
|
|
492
|
+
if (isAdapterConfigSurface()) return;
|
|
401
493
|
if (!policy.loaded) return;
|
|
402
494
|
if (isAdminUser()) {
|
|
403
495
|
document.querySelectorAll('.eos-hidden-legacy-admin, .eos-protected-adapter-row').forEach(el => {
|
|
@@ -470,16 +562,33 @@
|
|
|
470
562
|
};
|
|
471
563
|
|
|
472
564
|
|
|
565
|
+
const isAdapterConfigSurface = () => document.documentElement.classList.contains('eos-adapter-config-surface');
|
|
566
|
+
|
|
473
567
|
const markAdapterConfigSurface = () => safe(() => {
|
|
474
|
-
const
|
|
475
|
-
const
|
|
476
|
-
|
|
568
|
+
const app = document.querySelector('#app-paper') || document.body;
|
|
569
|
+
const text = textOfElement(app).slice(0, 3500);
|
|
570
|
+
const isConfig = /Instanzeinstellungen:|Instance settings:|Gerät hinzufügen|Gerät bearbeiten|Geräteliste|Adapterkonfiguration|json exportieren|json importieren|Speichern und schließen|Save and close/i.test(text);
|
|
477
571
|
document.documentElement.classList.toggle('eos-adapter-config-surface', !!isConfig);
|
|
478
572
|
if (!isConfig) return;
|
|
479
|
-
|
|
480
|
-
|
|
573
|
+
|
|
574
|
+
// Native adapter configuration UIs are owned by the adapter. EOS must never
|
|
575
|
+
// block, rewrite or intercept their controls. This is critical for custom
|
|
576
|
+
// React/HTML configuration pages such as nexowatt-devices.
|
|
577
|
+
document.querySelectorAll('#app-paper button, #app-paper [role="button"], #app-paper a, #app-paper input, #app-paper select, #app-paper textarea, #app-paper [tabindex]').forEach(control => {
|
|
481
578
|
if (control.closest('.eos-assist-root, #eos-assist-root, .eos-standalone-nav-toggle')) return;
|
|
482
579
|
control.style.pointerEvents = 'auto';
|
|
580
|
+
control.removeAttribute('aria-disabled');
|
|
581
|
+
});
|
|
582
|
+
document.querySelectorAll('#app-paper .eos-protected-delete-control, #app-paper .eos-security-hidden-delete, #app-paper .eos-protected-adapter-row').forEach(el => {
|
|
583
|
+
el.classList.remove('eos-protected-delete-control', 'eos-security-hidden-delete', 'eos-protected-adapter-row');
|
|
584
|
+
el.removeAttribute('aria-disabled');
|
|
585
|
+
if (el.style) {
|
|
586
|
+
el.style.pointerEvents = '';
|
|
587
|
+
el.style.display = '';
|
|
588
|
+
el.style.visibility = '';
|
|
589
|
+
el.style.opacity = '';
|
|
590
|
+
}
|
|
591
|
+
if ('disabled' in el && !el.dataset.eosOriginalDisabled) el.disabled = false;
|
|
483
592
|
});
|
|
484
593
|
});
|
|
485
594
|
|
|
@@ -592,13 +701,10 @@
|
|
|
592
701
|
}
|
|
593
702
|
|
|
594
703
|
const installHardLogoutWatchdog = () => {
|
|
595
|
-
|
|
704
|
+
// v36: disabled. Upstream ioBroker Admin session handling is used again so
|
|
705
|
+
// the configured admin TTL is respected and native adapter config pages are
|
|
706
|
+
// not interrupted by duplicate EOS timers.
|
|
596
707
|
hardLogoutInstalled = true;
|
|
597
|
-
window.addEventListener('focus', () => checkHardLogoutSession(), { passive: true });
|
|
598
|
-
document.addEventListener('visibilitychange', () => {
|
|
599
|
-
if (!document.hidden) checkHardLogoutSession();
|
|
600
|
-
}, { passive: true });
|
|
601
|
-
scheduleHardLogoutCheck(2500);
|
|
602
708
|
};
|
|
603
709
|
|
|
604
710
|
const ensureBrandBadge = toolbar => {
|
|
@@ -730,6 +836,7 @@
|
|
|
730
836
|
}
|
|
731
837
|
patchDrawerHeader(document.querySelector('.MuiDrawer-paper'));
|
|
732
838
|
hideNativeLogoutNav();
|
|
839
|
+
patchNotifications();
|
|
733
840
|
removeLogoutButton();
|
|
734
841
|
});
|
|
735
842
|
|
|
@@ -850,6 +957,26 @@
|
|
|
850
957
|
});
|
|
851
958
|
|
|
852
959
|
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
const ensureNotificationDialogClasses = () => safe(() => {
|
|
963
|
+
document.querySelectorAll('.MuiDialog-root, .MuiModal-root, [role="presentation"]').forEach(root => {
|
|
964
|
+
const paper = root.querySelector?.('.MuiDialog-paper, [role="dialog"]');
|
|
965
|
+
if (!paper) return;
|
|
966
|
+
const txt = normalize(paper.textContent || '');
|
|
967
|
+
if (!/(benachrichtigungen|notifications|acknowledge|bestätigen|schließen|close)/i.test(txt) && !paper.querySelector('#notifications-dialog-close')) return;
|
|
968
|
+
root.classList.add('eos-notification-dialog-root');
|
|
969
|
+
paper.classList.add('eos-notification-dialog');
|
|
970
|
+
paper.querySelectorAll('button, [role="button"], a, .MuiButtonBase-root, .MuiIconButton-root').forEach(control => {
|
|
971
|
+
control.style.pointerEvents = 'auto';
|
|
972
|
+
control.style.userSelect = 'auto';
|
|
973
|
+
if (control.getAttribute('aria-disabled') === 'true' && /schließen|close/i.test(control.textContent || control.getAttribute('aria-label') || control.getAttribute('title') || '')) {
|
|
974
|
+
control.removeAttribute('aria-disabled');
|
|
975
|
+
}
|
|
976
|
+
});
|
|
977
|
+
});
|
|
978
|
+
});
|
|
979
|
+
|
|
853
980
|
const ensureSettingsDialogClasses = () => safe(() => {
|
|
854
981
|
const dialogs = Array.from(document.querySelectorAll('.MuiDialog-paper, [role="dialog"]'));
|
|
855
982
|
dialogs.forEach(dialog => {
|
|
@@ -903,6 +1030,28 @@
|
|
|
903
1030
|
});
|
|
904
1031
|
|
|
905
1032
|
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
const patchNotifications = () => safe(() => {
|
|
1036
|
+
const selectors = [
|
|
1037
|
+
'.MuiSnackbar-root', '.SnackbarItem-root', '.SnackbarItem-wrappedRoot', '.notistack-Snackbar',
|
|
1038
|
+
'.Toastify__toast-container', '.Toastify__toast', '.MuiAlert-root', '[role="alert"]'
|
|
1039
|
+
];
|
|
1040
|
+
document.querySelectorAll(selectors.join(',')).forEach(node => {
|
|
1041
|
+
node.classList.add('eos-notification-surface');
|
|
1042
|
+
if (node.style) {
|
|
1043
|
+
node.style.pointerEvents = 'auto';
|
|
1044
|
+
if (!node.closest('.MuiDialog-root')) node.style.zIndex = '5200';
|
|
1045
|
+
}
|
|
1046
|
+
node.querySelectorAll('button,[role="button"],a').forEach(control => {
|
|
1047
|
+
control.classList.add('eos-notification-action');
|
|
1048
|
+
control.style.pointerEvents = 'auto';
|
|
1049
|
+
control.style.visibility = 'visible';
|
|
1050
|
+
control.style.opacity = '1';
|
|
1051
|
+
});
|
|
1052
|
+
});
|
|
1053
|
+
});
|
|
1054
|
+
|
|
906
1055
|
const applyNavCompactPreference = () => safe(() => {
|
|
907
1056
|
const compact = localStorage.getItem('nexowatt:eosNavCompact') === '1';
|
|
908
1057
|
document.documentElement.classList.toggle('eos-nav-compact', compact);
|
|
@@ -1009,6 +1158,11 @@
|
|
|
1009
1158
|
document.querySelectorAll('.eos-assist-launcher,.eos-assist-panel:not(#eos-assist-root .eos-assist-panel)').forEach(el => el.remove());
|
|
1010
1159
|
return;
|
|
1011
1160
|
}
|
|
1161
|
+
if (isAdapterConfigSurface()) {
|
|
1162
|
+
const existing = document.getElementById('eos-assist-root');
|
|
1163
|
+
if (existing) existing.classList.add('eos-assist-config-hidden');
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1012
1166
|
|
|
1013
1167
|
let root = document.getElementById('eos-assist-root');
|
|
1014
1168
|
if (!root) {
|
|
@@ -1042,6 +1196,7 @@
|
|
|
1042
1196
|
document.body.appendChild(root);
|
|
1043
1197
|
}
|
|
1044
1198
|
|
|
1199
|
+
root.classList.remove('eos-assist-config-hidden');
|
|
1045
1200
|
const ctx = assistContext();
|
|
1046
1201
|
const button = root.querySelector('.eos-assist-button');
|
|
1047
1202
|
const input = root.querySelector('.eos-assist-input');
|
|
@@ -1200,11 +1355,27 @@
|
|
|
1200
1355
|
ensureRightsHelper();
|
|
1201
1356
|
ensurePermissionPresets();
|
|
1202
1357
|
ensureSettingsDialogClasses();
|
|
1358
|
+
ensureNotificationDialogClasses();
|
|
1203
1359
|
hideNativeLogoutNav();
|
|
1204
1360
|
hideOfficialNexoWattRepoWarning();
|
|
1361
|
+
patchNotifications();
|
|
1205
1362
|
applySecurityUiGuard();
|
|
1206
|
-
|
|
1207
|
-
|
|
1363
|
+
if (isAdapterConfigSurface()) {
|
|
1364
|
+
// Adapter-owned configuration pages must not be rebranded or structurally patched.
|
|
1365
|
+
// We still repair broken UTF-8/mojibake text because jsonConfig labels can be
|
|
1366
|
+
// rendered through different legacy paths. This is text-only and does not touch
|
|
1367
|
+
// adapter controls, React state, events or attributes.
|
|
1368
|
+
patchMojibakeTextNodes(document.getElementById('app-paper'));
|
|
1369
|
+
['.MuiAppBar-root', '.MuiDrawer-paper', 'nav', '.eos-brand-badge', '.eos-top-toolbar'].forEach(selector => {
|
|
1370
|
+
document.querySelectorAll(selector).forEach(scope => {
|
|
1371
|
+
patchTextNodes(scope);
|
|
1372
|
+
patchAttributes(scope);
|
|
1373
|
+
});
|
|
1374
|
+
});
|
|
1375
|
+
} else {
|
|
1376
|
+
patchTextNodes(document.body || document.documentElement);
|
|
1377
|
+
patchAttributes(document.body || document.documentElement);
|
|
1378
|
+
}
|
|
1208
1379
|
};
|
|
1209
1380
|
|
|
1210
1381
|
const scopePatch = () => {
|
|
@@ -1223,11 +1394,17 @@
|
|
|
1223
1394
|
ensureRightsHelper();
|
|
1224
1395
|
ensurePermissionPresets();
|
|
1225
1396
|
ensureSettingsDialogClasses();
|
|
1397
|
+
ensureNotificationDialogClasses();
|
|
1226
1398
|
hideNativeLogoutNav();
|
|
1227
1399
|
hideOfficialNexoWattRepoWarning();
|
|
1400
|
+
patchNotifications();
|
|
1228
1401
|
applySecurityUiGuard();
|
|
1229
1402
|
for (const scope of scopes.slice(0, 80)) {
|
|
1230
1403
|
if (!scope || !scope.isConnected) continue;
|
|
1404
|
+
if (isAdapterConfigSurface() && (scope.id === 'app-paper' || scope.closest?.('#app-paper'))) {
|
|
1405
|
+
patchMojibakeTextNodes(scope);
|
|
1406
|
+
continue;
|
|
1407
|
+
}
|
|
1231
1408
|
patchTextNodes(scope);
|
|
1232
1409
|
patchAttributes(scope);
|
|
1233
1410
|
}
|
|
@@ -1258,14 +1435,17 @@
|
|
|
1258
1435
|
const observer = new MutationObserver(mutations => {
|
|
1259
1436
|
for (const mutation of mutations) {
|
|
1260
1437
|
if (mutation.type === 'characterData') {
|
|
1261
|
-
|
|
1438
|
+
if (isAdapterConfigSurface() && mutation.target?.parentElement?.closest?.('#app-paper')) patchMojibakeTextNode(mutation.target);
|
|
1439
|
+
else patchTextNode(mutation.target);
|
|
1262
1440
|
continue;
|
|
1263
1441
|
}
|
|
1264
1442
|
if (mutation.type !== 'childList') continue;
|
|
1265
1443
|
mutation.addedNodes.forEach(node => {
|
|
1266
1444
|
if (!node) return;
|
|
1267
|
-
if (node.nodeType === Node.TEXT_NODE)
|
|
1268
|
-
|
|
1445
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
1446
|
+
if (isAdapterConfigSurface() && node.parentElement?.closest?.('#app-paper')) patchMojibakeTextNode(node);
|
|
1447
|
+
else patchTextNode(node);
|
|
1448
|
+
} else if (node.nodeType === Node.ELEMENT_NODE) state.pendingScopes.add(node);
|
|
1269
1449
|
});
|
|
1270
1450
|
}
|
|
1271
1451
|
if (state.pendingScopes.size) scheduleScopePatch();
|
|
@@ -1294,3 +1474,21 @@
|
|
|
1294
1474
|
window.addEventListener('load', () => scheduleFullPatch(0), { once: true });
|
|
1295
1475
|
window.addEventListener('hashchange', () => scheduleFullPatch(0));
|
|
1296
1476
|
})();
|
|
1477
|
+
|
|
1478
|
+
|
|
1479
|
+
// v37 eos notification close compatibility: never let EOS overlays block native notification dialogs.
|
|
1480
|
+
(() => {
|
|
1481
|
+
const normalize = value => String(value || '').replace(/\s+/g, ' ').trim();
|
|
1482
|
+
document.addEventListener('click', event => {
|
|
1483
|
+
const target = event.target?.closest?.('button, [role="button"], a, .MuiButtonBase-root, .MuiIconButton-root');
|
|
1484
|
+
if (!target) return;
|
|
1485
|
+
const dialog = target.closest?.('.eos-notification-dialog, .MuiDialog-paper, [role="dialog"]');
|
|
1486
|
+
if (!dialog || !/benachrichtigungen|notifications|acknowledge|bestätigen|schließen|close/i.test(dialog.textContent || '')) return;
|
|
1487
|
+
const label = normalize(`${target.textContent || ''} ${target.getAttribute?.('aria-label') || ''} ${target.getAttribute?.('title') || ''}`);
|
|
1488
|
+
if (/schließen|close|bestätigen|acknowledge/i.test(label)) {
|
|
1489
|
+
target.style.pointerEvents = 'auto';
|
|
1490
|
+
// Do not prevent React handlers; only stop EOS-specific bubbling side effects.
|
|
1491
|
+
event.stopPropagation();
|
|
1492
|
+
}
|
|
1493
|
+
}, true);
|
|
1494
|
+
})();
|