iobroker.javascript 10.0.0 → 10.1.1

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 (180) hide show
  1. package/README.md +48 -12
  2. package/admin/assets/AiChatPanel-C_WbKMGX.js +242 -0
  3. package/admin/assets/AiDiffView-BtXbDWZ8.js +1 -0
  4. package/admin/assets/Debugger-DAdJnEhw.js +15 -0
  5. package/admin/assets/Error-DQ1pzKcf.js +1 -0
  6. package/admin/assets/Import-BjMtH6Gk.js +1 -0
  7. package/admin/assets/RulesEditor-dDRPHw4X.js +1 -0
  8. package/admin/assets/RulesEditor-k-DMHotH.css +1 -0
  9. package/admin/assets/{ScriptEditor-BGIuoNei.js → ScriptEditor-CZSc7rwE.js} +2 -2
  10. package/admin/assets/ScriptEditorVanillaMonaco-DEKg14_A.js +3 -0
  11. package/admin/assets/_virtual_mf-localSharedImportMap___mfe_internal__iobroker_javascript__mf_owner__1-DPmwd_G2.js +1 -0
  12. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-C8inUeKt.js +1243 -0
  13. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-VcQjt7_9.js +9 -0
  14. package/admin/assets/aiPromptBuilder-D5FDo74i.js +1 -0
  15. package/admin/assets/blockly-DBw-ytY1.js +1047 -0
  16. package/admin/assets/blocks_action-BqfWEwpf.js +1 -0
  17. package/admin/assets/blocks_convert-CX0i-JfS.js +3 -0
  18. package/admin/assets/blocks_logic-D-QSzw95.js +1 -0
  19. package/admin/assets/blocks_number-D7JuNYsw.js +1 -0
  20. package/admin/assets/blocks_object-C9DzAlc7.js +1 -0
  21. package/admin/assets/blocks_procedures-BJMz6HWW.js +3 -0
  22. package/admin/assets/blocks_sendto-DRh7lk9o.js +5 -0
  23. package/admin/assets/blocks_switch-BfmboMex.js +2 -0
  24. package/admin/assets/blocks_system-B-78JfAc.js +1 -0
  25. package/admin/assets/blocks_text-CGbR0Ozw.js +1 -0
  26. package/admin/assets/blocks_time-5vS2geJg.js +1 -0
  27. package/admin/assets/blocks_timeout-BdaTywfN.js +1 -0
  28. package/admin/assets/blocks_trigger-D8kgHKbg.js +2 -0
  29. package/admin/assets/blocks_words-BhHIf_1o.js +1 -0
  30. package/admin/assets/{cronHoverProvider-pKJkSevo.js → cronHoverProvider-DpgESCox.js} +1 -1
  31. package/admin/assets/dist-CaOsN0XW.js +4 -0
  32. package/admin/assets/field_cron-B40zwSfJ.js +1 -0
  33. package/admin/assets/field_oid-KfoBnEM9.js +1 -0
  34. package/admin/assets/field_script-yXhe1iCp.js +1 -0
  35. package/admin/assets/helpers-B0ByElPJ.js +1 -0
  36. package/admin/assets/hostInit-9REPDUe7.js +1 -0
  37. package/admin/assets/index-07o_WKsX.js +726 -0
  38. package/admin/assets/index-Z8Hkv58g.css +1 -0
  39. package/admin/assets/{inlineChatWidget-ZaisTOLh.js → inlineChatWidget-B9y_gQcH.js} +2 -2
  40. package/admin/assets/{inlineDiffController-hTwHHqcc.js → inlineDiffController-C07CdIFo.js} +2 -2
  41. package/admin/assets/{mf-entry-bootstrap-0-d6052b31.js → mf-entry-bootstrap-0-efcb49d3.js} +7 -2
  42. package/admin/assets/stateHoverProvider-CqeoOjXs.js +9 -0
  43. package/admin/assets/{virtualExposes-Ed0lGq7k.js → virtualExposes-DgGB-18P.js} +1 -1
  44. package/admin/assets/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__iobroker_javascript__remoteEntry_js-BwSvIKEV.js +1 -0
  45. package/admin/assets/virtual_mf-exposes___mfe_internal__iobroker_javascript__remoteEntry_js-ChiJXsfH.js +1 -0
  46. package/admin/custom/assets/Components-BCQTjBgm.js +1 -0
  47. package/admin/custom/assets/_virtual_mf-localSharedImportMap___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1-BuAc8OyX.js +1 -0
  48. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_emotion_mf_1_react__loadShare__.js-BGJv4zZP.js +1 -0
  49. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_emotion_mf_1_styled__loadShare__.js-CGb5Cofy.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_emotion_mf_1_styled__loadShare__.js-B4E6MFJ4.js} +1 -1
  50. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-BVCIAgbo.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-aRlxyEhq.js} +78 -78
  51. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_json_mf_2_config__loadShare__.js-DQwzYx75.js +1494 -0
  52. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare__leaflet__loadShare__.js-h-rCw3iz.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare__leaflet__loadShare__.js-n2zIPi0L.js} +1 -1
  53. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-xxszBNje.js +9 -0
  54. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__prebuild__date_mf_2_fns__prebuild__-Cb5TR7FB.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__prebuild__date_mf_2_fns__prebuild__-Tq7tesSS.js} +1 -1
  55. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__prebuild__leaflet__prebuild__-DARUoA03.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__prebuild__leaflet__prebuild__-BeF0jEWM.js} +1 -1
  56. package/admin/custom/assets/hostInit-CHwrowP4.js +1 -0
  57. package/admin/custom/assets/{index-uEKAoVT7.js → index-Cm6xl9K4.js} +1 -1
  58. package/admin/custom/assets/{lib-Yj8cfN0A.js → lib-bhrFL6jR.js} +1 -1
  59. package/admin/custom/assets/{mf-entry-bootstrap-0-77793feb.js → mf-entry-bootstrap-0-f08941e2.js} +7 -2
  60. package/admin/custom/assets/{setYear-DJ9FbdkO.js → setYear-DzTZ2h9A.js} +1 -1
  61. package/admin/custom/assets/virtualExposes-B5LJmnfO.js +1 -0
  62. package/admin/custom/assets/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__ConfigCustomJavascriptSet__customComponents_js-ML-chjs6.js +1 -0
  63. package/admin/custom/assets/virtual_mf-exposes___mfe_internal__ConfigCustomJavascriptSet__customComponents_js-C3ImybiM.js +2 -0
  64. package/admin/custom/customComponents.js +12 -1
  65. package/admin/custom/i18n/de.json +11 -1
  66. package/admin/custom/i18n/en.json +11 -1
  67. package/admin/custom/i18n/es.json +11 -1
  68. package/admin/custom/i18n/fr.json +11 -1
  69. package/admin/custom/i18n/it.json +11 -1
  70. package/admin/custom/i18n/nl.json +11 -1
  71. package/admin/custom/i18n/pl.json +11 -1
  72. package/admin/custom/i18n/pt.json +11 -1
  73. package/admin/custom/i18n/ru.json +11 -1
  74. package/admin/custom/i18n/uk.json +11 -1
  75. package/admin/custom/i18n/zh-cn.json +11 -1
  76. package/admin/google-blockly/VERSION +1 -0
  77. package/admin/google-blockly/media/drop.mp3 +0 -0
  78. package/admin/google-blockly/msg/js/de.js +332 -56
  79. package/admin/google-blockly/msg/js/en.js +319 -43
  80. package/admin/google-blockly/msg/js/es.js +319 -42
  81. package/admin/google-blockly/msg/js/fr.js +315 -39
  82. package/admin/google-blockly/msg/js/it.js +327 -51
  83. package/admin/google-blockly/msg/js/nl.js +376 -100
  84. package/admin/google-blockly/msg/js/pl.js +318 -42
  85. package/admin/google-blockly/msg/js/pt.js +318 -42
  86. package/admin/google-blockly/msg/js/ru.js +320 -44
  87. package/admin/google-blockly/msg/js/uk.js +326 -50
  88. package/admin/google-blockly/msg/js/zh-cn.js +322 -46
  89. package/admin/i18n/de.json +51 -26
  90. package/admin/i18n/en.json +50 -25
  91. package/admin/i18n/es.json +51 -18
  92. package/admin/i18n/fr.json +50 -17
  93. package/admin/i18n/it.json +50 -17
  94. package/admin/i18n/nl.json +50 -17
  95. package/admin/i18n/pl.json +51 -18
  96. package/admin/i18n/pt.json +50 -17
  97. package/admin/i18n/ru.json +50 -17
  98. package/admin/i18n/uk.json +50 -17
  99. package/admin/i18n/zh-cn.json +50 -17
  100. package/admin/jsonConfig.json +398 -0
  101. package/admin/mf-manifest.json +1 -1
  102. package/admin/mf-stats.json +1 -1
  103. package/admin/remoteEntry.js +12 -1
  104. package/admin/tab.html +6 -98
  105. package/build/lib/javascript.d.ts +48 -0
  106. package/build/lib/mirror.js +9 -0
  107. package/build/lib/mirror.js.map +1 -1
  108. package/build/lib/sandbox.js +37 -0
  109. package/build/lib/sandbox.js.map +1 -1
  110. package/build/lib/secrets.js +220 -0
  111. package/build/lib/secrets.js.map +1 -0
  112. package/build/lib/typescriptSettings.js +112 -2
  113. package/build/lib/typescriptSettings.js.map +1 -1
  114. package/build/lib/typescriptTools.js +168 -43
  115. package/build/lib/typescriptTools.js.map +1 -1
  116. package/build/main.js +61 -5
  117. package/build/main.js.map +1 -1
  118. package/build/types.d.ts +48 -0
  119. package/docs/en/blockly.md +29 -0
  120. package/docs/en/javascript.md +72 -0
  121. package/docs/en/upgrade-guide.md +3 -0
  122. package/io-package.json +59 -28
  123. package/package.json +8 -3
  124. package/admin/assets/AiChatPanel-p0KAg6Cs.js +0 -237
  125. package/admin/assets/AiDiffView-BPTLnNx1.js +0 -1
  126. package/admin/assets/AiScriptAnalyzer--BHML2rN.js +0 -9
  127. package/admin/assets/BlocklyEditor-CAjVMhYT.js +0 -81
  128. package/admin/assets/ContextWrapper-BVc8wTK_.css +0 -1
  129. package/admin/assets/ContextWrapper-D9CrnNKs.js +0 -127
  130. package/admin/assets/Debugger-SjROokFI.js +0 -15
  131. package/admin/assets/Error-DDfrxAt2.js +0 -1
  132. package/admin/assets/Import-DpTg1Jow.js +0 -1
  133. package/admin/assets/RulesEditor-BSRFWpEm.css +0 -1
  134. package/admin/assets/RulesEditor-D0hgrcBc.js +0 -1
  135. package/admin/assets/ScriptEditorVanillaMonaco-DyMQtoTQ.js +0 -3
  136. package/admin/assets/_virtual_mf-localSharedImportMap___mfe_internal__iobroker_javascript__mf_owner__1-DWJotR1B.js +0 -1
  137. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-CxICLr5Z.js +0 -1243
  138. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-BBAXJaqI.js +0 -9
  139. package/admin/assets/aiCodeLensProvider-hyAmut0e.js +0 -4
  140. package/admin/assets/aiPromptBuilder-Bs27qOHT.js +0 -7
  141. package/admin/assets/blocklyDarkTheme-DEiAX5DT.js +0 -1
  142. package/admin/assets/dist-uw5orDgd.js +0 -4
  143. package/admin/assets/hostInit-BFg5u9hY.js +0 -1
  144. package/admin/assets/index-B1gICjdq.js +0 -192
  145. package/admin/assets/index-BHhhebRG.css +0 -1
  146. package/admin/assets/mjs-DWqivHEk.js +0 -90
  147. package/admin/assets/stateHoverProvider-C7DawIs-.js +0 -9
  148. package/admin/assets/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__iobroker_javascript__remoteEntry_js-BYs71IQF.js +0 -1
  149. package/admin/assets/virtual_mf-exposes___mfe_internal__iobroker_javascript__remoteEntry_js-DnnXQRkG.js +0 -1
  150. package/admin/custom/assets/Components-BrDyDHXh.js +0 -1
  151. package/admin/custom/assets/_virtual_mf-localSharedImportMap___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1-CyYJM2-v.js +0 -1
  152. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_emotion_mf_1_react__loadShare__.js-BdzCssLn.js +0 -1
  153. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_json_mf_2_config__loadShare__.js-BkB0anQX.js +0 -1494
  154. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-BotqDuw-.js +0 -9
  155. package/admin/custom/assets/hostInit-DJx2uXXy.js +0 -1
  156. package/admin/custom/assets/virtualExposes-CxDjLoYu.js +0 -1
  157. package/admin/custom/assets/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__ConfigCustomJavascriptSet__customComponents_js-BU-ugy_7.js +0 -1
  158. package/admin/custom/assets/virtual_mf-exposes-ssr___mfe_internal__ConfigCustomJavascriptSet__customComponents_js-BEfZbrf7.js +0 -2
  159. package/admin/custom/assets/virtual_mf-exposes___mfe_internal__ConfigCustomJavascriptSet__customComponents_js-CTeZ0Jpm.js +0 -2
  160. package/admin/google-blockly/blockly_compressed.js +0 -1679
  161. package/admin/google-blockly/blocks_compressed.js +0 -193
  162. package/admin/google-blockly/javascript_compressed.js +0 -264
  163. package/admin/google-blockly/msg/messages.js +0 -1630
  164. package/admin/google-blockly/own/blocks_action.js +0 -765
  165. package/admin/google-blockly/own/blocks_convert.js +0 -480
  166. package/admin/google-blockly/own/blocks_logic.js +0 -459
  167. package/admin/google-blockly/own/blocks_number.js +0 -30
  168. package/admin/google-blockly/own/blocks_object.js +0 -402
  169. package/admin/google-blockly/own/blocks_procedures.js +0 -688
  170. package/admin/google-blockly/own/blocks_sendto.js +0 -702
  171. package/admin/google-blockly/own/blocks_switch.js +0 -257
  172. package/admin/google-blockly/own/blocks_system.js +0 -1409
  173. package/admin/google-blockly/own/blocks_text.js +0 -104
  174. package/admin/google-blockly/own/blocks_time.js +0 -639
  175. package/admin/google-blockly/own/blocks_timeout.js +0 -551
  176. package/admin/google-blockly/own/blocks_trigger.js +0 -1791
  177. package/admin/google-blockly/own/blocks_words.js +0 -708
  178. package/admin/google-blockly/own/field_cron.js +0 -158
  179. package/admin/google-blockly/own/field_oid.js +0 -642
  180. package/admin/google-blockly/own/field_script.js +0 -159
@@ -1,642 +0,0 @@
1
- 'use strict';
2
-
3
- if (typeof goog !== 'undefined') {
4
- goog.provide('Blockly.FieldOID');
5
- goog.require('Blockly.Field');
6
- goog.require('Blockly.Msg');
7
- goog.require('goog.asserts');
8
- goog.require('goog.dom');
9
- goog.require('goog.userAgent');
10
- }
11
-
12
- class FieldOID extends Blockly.Field {
13
- constructor(value, type) {
14
- super(value);
15
-
16
- this._type = type;
17
-
18
- this.FONTSIZE = 11;
19
- this.CURSOR = 'pointer';
20
- this.SERIALIZABLE = true;
21
- this.spellcheck_ = false;
22
- this.maxDisplayLength = 200;
23
-
24
- // Live value display (only meaningful for state IDs)
25
- this._showValue = false; // whether the value is shown inline next to the ID
26
- this._stateValueText = null; // last formatted value, used by tooltip + inline display
27
- this._valueBadge = null; // clickable "=" badge element
28
- this._hoverHandler = null; // mouseenter prefetch handler
29
- this._disposed = false; // set on dispose() so late async callbacks are dropped
30
- }
31
-
32
- dispose() {
33
- this._disposed = true;
34
- Blockly.WidgetDiv.hideIfOwner(this);
35
- if (this.fieldGroup_ && this._hoverHandler) {
36
- this.fieldGroup_.removeEventListener('mouseenter', this._hoverHandler);
37
- this._hoverHandler = null;
38
- }
39
- if (this._valueBadge) {
40
- this._valueBadge.remove();
41
- this._valueBadge = null;
42
- }
43
- super.dispose();
44
- }
45
-
46
- initView() {
47
- super.initView();
48
- const id = this.getValue();
49
- if (id) {
50
- this.updateIcon_(id);
51
- }
52
- // Prefetch the value when the pointer enters the field, so the tooltip
53
- // (shown by Blockly after a short delay) already contains a fresh value.
54
- if (this.fieldGroup_ && !this._hoverHandler) {
55
- this._hoverHandler = () => this._fetchStateValue();
56
- this.fieldGroup_.addEventListener('mouseenter', this._hoverHandler);
57
- }
58
- this._ensureValueBadge();
59
- }
60
-
61
- static _resolveObjectName(id) {
62
- const objects = window.main.objects;
63
- if (!objects || !objects[id] || !objects[id].common || !objects[id].common.name) {
64
- return null;
65
- }
66
- let name = objects[id].common.name;
67
- if (typeof name === 'object') {
68
- name = name[systemLang] || name.en;
69
- }
70
- return name || null;
71
- }
72
-
73
- static computeDisplayText(id, maxLen) {
74
- const mode = FieldOID.displayMode;
75
- const objects = window.main.objects;
76
- let text;
77
-
78
- switch (mode) {
79
- case 1: { // Path: all parent names joined with "."
80
- const parts = id.split('.');
81
- const names = [];
82
- for (let i = 0; i < parts.length; i++) {
83
- const ancestorId = parts.slice(0, i + 1).join('.');
84
- const name = FieldOID._resolveObjectName(ancestorId);
85
- names.push(name || parts[i]);
86
- }
87
- text = names.join('.');
88
- break;
89
- }
90
- case 2: // State ID: last segment only
91
- text = id.split('.').pop() || id;
92
- break;
93
- case 3: // Full ID
94
- text = id;
95
- break;
96
- default: { // Mode 0: Name (current behavior)
97
- text = FieldOID._resolveObjectName(id) || id;
98
- break;
99
- }
100
- }
101
-
102
- // Append type suffix for non-state objects
103
- if (objects && objects[id]?.type && !['state', 'meta', 'script'].includes(objects[id].type)) {
104
- text += ` (${objects[id].type})`;
105
- }
106
-
107
- if (maxLen && text.length > maxLen) {
108
- text = text.substring(0, maxLen - 2) + '\u2026';
109
- }
110
-
111
- // Replace whitespace with non-breaking spaces so the text doesn't collapse.
112
- text = text.replace(/\s/g, Blockly.Field.NBSP);
113
-
114
- return text || Blockly.Field.NBSP;
115
- }
116
-
117
- refreshDisplay() {
118
- const id = this.getValue();
119
- if (id) {
120
- this._idName = FieldOID.computeDisplayText(id, this.maxDisplayLength);
121
- this.updateIcon_(id);
122
- this.forceRerender();
123
- // For path mode, async-fetch parent objects to resolve names
124
- if (FieldOID.displayMode === 1) {
125
- this._resolvePathNamesAsync(id);
126
- }
127
- }
128
- }
129
-
130
- _resolvePathNamesAsync(id) {
131
- if (typeof window.main.getObject !== 'function') return;
132
- const objects = window.main.objects;
133
- const parts = id.split('.');
134
- let pending = 0;
135
- let updated = false;
136
- for (let i = 0; i < parts.length; i++) {
137
- const ancestorId = parts.slice(0, i + 1).join('.');
138
- if (objects[ancestorId]) continue; // already cached
139
- pending++;
140
- window.main.getObject(ancestorId, (err, obj) => {
141
- if (obj) {
142
- objects[obj._id] = objects[obj._id] || obj;
143
- updated = true;
144
- }
145
- pending--;
146
- if (pending === 0 && updated) {
147
- this._idName = FieldOID.computeDisplayText(id, this.maxDisplayLength);
148
- this.forceRerender();
149
- }
150
- });
151
- }
152
- }
153
-
154
- _getIconOffset() {
155
- return (FieldOID.showIcon && this._iconElement) ? 20 : 0;
156
- }
157
-
158
- // Override Blockly's size + position to account for icon
159
- updateSize_(margin) {
160
- const constants = this.getConstants();
161
- const xPad = margin !== undefined ? margin :
162
- (this.isFullBlockField() ? 0 : constants.FIELD_BORDER_RECT_X_PADDING);
163
- const iconOffset = this._getIconOffset();
164
- const badgeOffset = this._getBadgeOffset();
165
-
166
- let textWidth = 0;
167
- if (this.textElement_) {
168
- textWidth = this.textElement_.getComputedTextLength();
169
- }
170
-
171
- let totalWidth = 2 * xPad + textWidth + iconOffset + badgeOffset;
172
- let totalHeight = constants.FIELD_TEXT_HEIGHT;
173
- if (!this.isFullBlockField()) {
174
- totalHeight = Math.max(totalHeight, constants.FIELD_BORDER_RECT_HEIGHT);
175
- }
176
-
177
- this.size_.height = totalHeight;
178
- this.size_.width = totalWidth;
179
-
180
- this.positionTextElement_(xPad + iconOffset, textWidth);
181
- this.positionBorderRect_();
182
- this._positionValueBadge(totalWidth, xPad, totalHeight);
183
- }
184
-
185
- updateIcon_(id) {
186
- if (!this.fieldGroup_) {
187
- return;
188
- }
189
- if (!FieldOID.showIcon) {
190
- if (this._iconElement) {
191
- this._iconElement.remove();
192
- this._iconElement = null;
193
- this._iconUrl = null;
194
- }
195
- return;
196
- }
197
- // First try synchronous resolve with cached objects
198
- const iconUrl = FieldOID.resolveIcon(id);
199
- if (iconUrl) {
200
- this._applyIcon(iconUrl);
201
- }
202
- // Then async-fetch parent objects to find a closer (more specific) icon
203
- this._resolveIconAsync(id);
204
- }
205
-
206
- _applyIcon(iconUrl) {
207
- if (!this.fieldGroup_) return;
208
- if (this._iconUrl === iconUrl && this._iconElement) {
209
- return;
210
- }
211
- this._iconUrl = iconUrl;
212
- const iconSize = 16;
213
- if (!this._iconElement) {
214
- this._iconElement = document.createElementNS('http://www.w3.org/2000/svg', 'image');
215
- this._iconElement.setAttribute('width', String(iconSize));
216
- this._iconElement.setAttribute('height', String(iconSize));
217
- if (this.textElement_) {
218
- this.fieldGroup_.insertBefore(this._iconElement, this.textElement_);
219
- } else {
220
- this.fieldGroup_.appendChild(this._iconElement);
221
- }
222
- }
223
- this._iconElement.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', iconUrl);
224
- this._positionIcon();
225
- }
226
-
227
- _positionIcon() {
228
- if (!this._iconElement || !this.textElement_) return;
229
- const iconSize = 16;
230
- try {
231
- // Position icon at the start of the field content area
232
- const constants = this.getConstants();
233
- const xPad = this.borderRect_ ? constants.FIELD_BORDER_RECT_X_PADDING : 0;
234
- this._iconElement.setAttribute('x', String(xPad + 2));
235
-
236
- // Vertically center icon with text
237
- const textBBox = this.textElement_.getBBox();
238
- if (textBBox.height > 0) {
239
- const textMidY = textBBox.y + textBBox.height / 2;
240
- this._iconElement.setAttribute('y', String(textMidY - iconSize / 2));
241
- }
242
- } catch (e) {
243
- // getBBox can fail if element is not in DOM yet
244
- }
245
- }
246
-
247
- _resolveIconAsync(id) {
248
- if (!FieldOID.showIcon || typeof window.main.getObject !== 'function') {
249
- return;
250
- }
251
- const objects = window.main.objects;
252
- const parts = id.split('.');
253
- const adapterName = parts[0];
254
- // Fetch ancestors from state level up to adapter instance level
255
- // Start from the state itself, go up - first icon found wins
256
- let remaining = parts.length;
257
- const tryNext = (idx) => {
258
- if (idx < 1) return;
259
- const ancestorId = parts.slice(0, idx).join('.');
260
- // Already in cache?
261
- if (objects[ancestorId]) {
262
- if (objects[ancestorId].common && objects[ancestorId].common.icon) {
263
- this._applyIcon(FieldOID._buildIconUrl(objects[ancestorId].common.icon, adapterName));
264
- return; // Found closest icon, stop
265
- }
266
- tryNext(idx - 1); // No icon here, try parent
267
- return;
268
- }
269
- // Fetch this ancestor
270
- window.main.getObject(ancestorId, (err, obj) => {
271
- if (obj) {
272
- objects[obj._id] = objects[obj._id] || obj;
273
- if (obj.common && obj.common.icon) {
274
- this._applyIcon(FieldOID._buildIconUrl(obj.common.icon, adapterName));
275
- return; // Found closest icon, stop
276
- }
277
- }
278
- tryNext(idx - 1); // Try parent
279
- });
280
- };
281
- tryNext(remaining);
282
- }
283
-
284
- // ---- Live state value: tooltip (always) + clickable inline badge ----
285
-
286
- /** Format a state value for display: value + unit, truncated, with a "not acked" marker. */
287
- static _formatStateValue(id, state) {
288
- if (!state || state.val === undefined || state.val === null) {
289
- return 'null';
290
- }
291
- let v = state.val;
292
- if (typeof v === 'object') {
293
- try {
294
- v = JSON.stringify(v);
295
- } catch (e) {
296
- v = String(v);
297
- }
298
- } else {
299
- v = String(v);
300
- }
301
- if (v.length > 60) {
302
- v = v.substring(0, 60) + '…';
303
- }
304
- const objects = window.main && window.main.objects;
305
- const common = objects && objects[id] && objects[id].common;
306
- if (common && common.unit) {
307
- v += ' ' + common.unit;
308
- }
309
- if (state.ack === false) {
310
- v += ' (?)'; // value not acknowledged
311
- }
312
- return v;
313
- }
314
-
315
- /** Fetch the current value from the backend and remember it for tooltip/inline display. */
316
- _fetchStateValue(cb) {
317
- const id = this.getValue();
318
- if (!id || !window.main || typeof window.main.getState !== 'function') {
319
- cb && cb();
320
- return;
321
- }
322
- window.main.getState(id, (err, state) => {
323
- if (this._disposed) {
324
- return;
325
- }
326
- this._stateValueText = state ? FieldOID._formatStateValue(id, state) : null;
327
- cb && cb();
328
- });
329
- }
330
-
331
- /** Tooltip text: object name + ID + (for states) the current value. */
332
- _composeTooltip() {
333
- const id = this.getValue();
334
- if (!id) {
335
- return '';
336
- }
337
- const name = FieldOID._resolveObjectName(id);
338
- let txt = name ? name + '\n' + id : id;
339
- if (this._stateValueText) {
340
- txt += '\n= ' + this._stateValueText;
341
- }
342
- return txt;
343
- }
344
-
345
- _getBadgeOffset() {
346
- return this._valueBadge ? 18 : 0;
347
- }
348
-
349
- /** Create the clickable "=" badge for state fields (once, lazily). */
350
- _ensureValueBadge() {
351
- if (this._type !== 'state' || this._valueBadge || !this.fieldGroup_ || !this.getValue()) {
352
- return;
353
- }
354
- const badge = document.createElementNS('http://www.w3.org/2000/svg', 'text');
355
- badge.setAttribute('class', 'blocklyText');
356
- badge.setAttribute('text-anchor', 'middle');
357
- badge.style.fill = '#2196f3';
358
- badge.style.fontWeight = 'bold';
359
- badge.style.cursor = 'pointer';
360
- badge.textContent = '=';
361
- const title = document.createElementNS('http://www.w3.org/2000/svg', 'title');
362
- title.textContent = 'Show/hide current value';
363
- badge.appendChild(title);
364
- // Don't let a click on the badge open the ID-selector dialog or start a drag.
365
- const stop = e => e.stopPropagation();
366
- badge.addEventListener('pointerdown', stop);
367
- badge.addEventListener('mousedown', stop);
368
- badge.addEventListener('click', e => {
369
- e.stopPropagation();
370
- this._toggleValue();
371
- });
372
- this._valueBadge = badge;
373
- this.fieldGroup_.appendChild(badge);
374
- if (this.sourceBlock_ && this.sourceBlock_.rendered) {
375
- this.forceRerender();
376
- }
377
- }
378
-
379
- /** Position the badge at the right edge of the field. */
380
- _positionValueBadge(totalWidth, xPad, totalHeight) {
381
- if (!this._valueBadge) {
382
- return;
383
- }
384
- this._valueBadge.setAttribute('x', String(totalWidth - xPad - 9));
385
- this._valueBadge.setAttribute('y', String(totalHeight / 2 + 4));
386
- }
387
-
388
- /** Toggle inline value display (the "button press" behaviour). */
389
- _toggleValue() {
390
- this._showValue = !this._showValue;
391
- if (this._valueBadge) {
392
- this._valueBadge.textContent = this._showValue ? '×' : '=';
393
- }
394
- if (this._showValue) {
395
- this._stateValueText = this._stateValueText || '…';
396
- this.forceRerender();
397
- this._fetchStateValue(() => this.forceRerender());
398
- } else {
399
- this.forceRerender();
400
- }
401
- }
402
-
403
- setValue(id) {
404
- if (id === null) {
405
- return; // No change if null.
406
- }
407
-
408
- const objects = window.main.objects;
409
-
410
- if (objects && !objects[id] && typeof window.main.getObject === 'function') {
411
- this._idName = id || Blockly.Field.NBSP;
412
- window.main.getObject(id, (err, obj) => {
413
- if (obj) {
414
- objects[obj._id] = objects[obj._id] || obj;
415
- const text = FieldOID.computeDisplayText(id, this.maxDisplayLength);
416
- if (text) {
417
- this._idName = text;
418
- this.forceRerender();
419
- }
420
- // Resolve parent names for path mode
421
- if (FieldOID.displayMode === 1) {
422
- this._resolvePathNamesAsync(id);
423
- }
424
- }
425
- });
426
- } else {
427
- this._idName = FieldOID.computeDisplayText(id, this.maxDisplayLength);
428
- // Resolve parent names for path mode
429
- if (FieldOID.displayMode === 1) {
430
- this._resolvePathNamesAsync(id);
431
- }
432
- }
433
-
434
- this.updateIcon_(id);
435
- super.setValue(id);
436
- // Tooltip is a function so it always reflects the latest fetched value.
437
- super.setTooltip(() => this._composeTooltip());
438
- this._ensureValueBadge();
439
- }
440
-
441
- getDisplayText_() {
442
- let text = this._idName || this.text_;
443
- if (this._showValue && this._stateValueText) {
444
- text = (text || '') + ' = ' + this._stateValueText;
445
- }
446
- if (!text) {
447
- // Prevent the field from disappearing if empty.
448
- return Blockly.Field.NBSP;
449
- }
450
- if (text.length > this.maxDisplayLength) {
451
- // Truncate displayed string and add an ellipsis ('...').
452
- text = text.substring(0, this.maxDisplayLength - 2) + '\u2026';
453
- }
454
- // Replace whitespace with non-breaking spaces so the text doesn't collapse.
455
- text = text.replace(/\s/g, Blockly.Field.NBSP);
456
- if (this.sourceBlock_.RTL) {
457
- // The SVG is LTR, force text to be RTL.
458
- text += '\u200F';
459
- }
460
- return text;
461
- }
462
-
463
- showEditor_(opt_quietInput) {
464
- this.workspace_ = this.sourceBlock_.workspace;
465
- window.main && window.main.selectIdDialog && window.main.selectIdDialog(this.getValue(), this._type, (newId) => { newId !== null && this.setValue(newId); });
466
- }
467
-
468
- /**
469
- * Handle a change to the editor.
470
- * @param {!Event} e Keyboard event.
471
- * @private
472
- */
473
- onHtmlInputChange_(e) {
474
- const htmlInput = Blockly.FieldOID.htmlInput_;
475
- // Update source block.
476
- const text = htmlInput.value;
477
- if (text !== htmlInput.oldValue_) {
478
- htmlInput.oldValue_ = text;
479
- this.setValue(text);
480
- this.validate_();
481
- } else if (goog.userAgent.WEBKIT) {
482
- // Cursor key. Render the source block to show the caret moving.
483
- // Chrome only (version 26, OS X).
484
- this.sourceBlock_.render();
485
- }
486
-
487
- this.resizeEditor_();
488
- Blockly.svgResize(this.sourceBlock_.workspace);
489
- }
490
-
491
- /*
492
- * Check to see if the contents of the editor validates.
493
- * Style the editor accordingly.
494
- * @private
495
- */
496
- validate_() {
497
- goog.asserts.assertObject(Blockly.FieldOID.htmlInput_);
498
-
499
- const htmlInput = Blockly.FieldOID.htmlInput_;
500
-
501
- if (htmlInput.value) {
502
- Blockly.addClass_(htmlInput, 'blocklyInvalidInput');
503
- } else {
504
- Blockly.removeClass_(htmlInput, 'blocklyInvalidInput');
505
- }
506
- }
507
-
508
- /**
509
- * Resize the editor and the underlying block to fit the text.
510
- * @private
511
- */
512
- resizeEditor_() {
513
- const div = Blockly.WidgetDiv.getDiv();
514
- const bBox = this.fieldGroup_.getBBox();
515
- div.style.width = bBox.width * this.workspace_.scale + 'px';
516
- div.style.height = bBox.height * this.workspace_.scale + 'px';
517
- const xy = this.getAbsoluteXY_();
518
- // In RTL mode block fields and LTR input fields the left edge moves,
519
- // whereas the right edge is fixed. Reposition the editor.
520
- if (this.sourceBlock_.RTL) {
521
- const borderBBox = this.getScaledBBox_();
522
- xy.x += borderBBox.width;
523
- xy.x -= div.offsetWidth;
524
- }
525
- // Shift by a few pixels to line up exactly.
526
- xy.y += 1;
527
- if (goog.userAgent.GECKO && Blockly.WidgetDiv.getDiv().style.top) {
528
- // Firefox mis-reports the location of the border by a pixel
529
- // once the WidgetDiv is moved into position.
530
- xy.x -= 1;
531
- xy.y -= 1;
532
- }
533
- if (goog.userAgent.WEBKIT) {
534
- xy.y -= 3;
535
- }
536
- div.style.left = xy.x + 'px';
537
- div.style.top = xy.y + 'px';
538
- }
539
-
540
- /**
541
- * Close the editor, save the results, and dispose of the editable
542
- * text field's elements.
543
- * @return {!Function} Closure to call on destruction of the WidgetDiv.
544
- * @private
545
- */
546
- widgetDispose_() {
547
- return () => {
548
- const htmlInput = this.htmlInput_;
549
- // Save the edit (if it validates).
550
- const text = htmlInput.value;
551
- this.setValue(text);
552
- this.sourceBlock_.rendered && this.sourceBlock_.render();
553
-
554
- Blockly.unbindEvent_(htmlInput.onKeyDownWrapper_);
555
- Blockly.unbindEvent_(htmlInput.onKeyUpWrapper_);
556
- Blockly.unbindEvent_(htmlInput.onKeyPressWrapper_);
557
-
558
- this.workspace_.removeChangeListener(htmlInput.onWorkspaceChangeWrapper_);
559
-
560
- this.htmlInput_ = null;
561
-
562
- // Delete style properties.
563
- const style = Blockly.WidgetDiv.getDiv().style;
564
- style.width = 'auto';
565
- style.height = 'auto';
566
- style.fontSize = '';
567
- };
568
- }
569
- }
570
-
571
- FieldOID.DISPLAY_MODE_KEYS = ['oid_display_name', 'oid_display_path', 'oid_display_id', 'oid_display_full_id'];
572
- FieldOID.displayMode = parseInt(localStorage.getItem('Blockly.FieldOID.displayMode') || '0', 10) || 0;
573
- FieldOID.showIcon = localStorage.getItem('Blockly.FieldOID.showIcon') === 'true';
574
-
575
- FieldOID._buildIconUrl = function (icon, adapterName) {
576
- if (!icon) return null;
577
- // base64 data URIs and absolute URLs are used as-is
578
- if (icon.startsWith('data:') || icon.startsWith('http://') || icon.startsWith('https://') || icon.startsWith('/')) {
579
- return icon;
580
- }
581
- return '/adapter/' + adapterName + '/' + icon;
582
- };
583
-
584
- FieldOID.resolveIcon = function (id) {
585
- const objects = window.main.objects;
586
- if (!objects) return null;
587
- const parts = id.split('.');
588
- const adapterName = parts[0];
589
- // Walk up the object hierarchy looking for common.icon
590
- for (let i = parts.length; i >= 1; i--) {
591
- const ancestorId = parts.slice(0, i).join('.');
592
- const obj = objects[ancestorId];
593
- if (obj && obj.common && obj.common.icon) {
594
- return FieldOID._buildIconUrl(obj.common.icon, adapterName);
595
- }
596
- }
597
- // Fallback: check system.adapter.{adapterName}.{instance} (e.g. system.adapter.zigbee2mqtt.0)
598
- if (parts.length >= 2) {
599
- const instanceId = 'system.adapter.' + parts[0] + '.' + parts[1];
600
- const sysInstance = objects[instanceId];
601
- if (sysInstance && sysInstance.common && sysInstance.common.icon) {
602
- return FieldOID._buildIconUrl(sysInstance.common.icon, adapterName);
603
- }
604
- }
605
- return null;
606
- };
607
-
608
- FieldOID.setDisplayMode = function (mode, workspace) {
609
- FieldOID.displayMode = mode;
610
- localStorage.setItem('Blockly.FieldOID.displayMode', String(mode));
611
-
612
- const blocks = workspace.getAllBlocks(false);
613
- for (const block of blocks) {
614
- for (const input of block.inputList) {
615
- for (const field of input.fieldRow) {
616
- if (field instanceof FieldOID) {
617
- field.refreshDisplay();
618
- }
619
- }
620
- }
621
- }
622
- };
623
-
624
- FieldOID.setShowIcon = function (show, workspace) {
625
- FieldOID.showIcon = show;
626
- localStorage.setItem('Blockly.FieldOID.showIcon', String(show));
627
-
628
- const blocks = workspace.getAllBlocks(false);
629
- for (const block of blocks) {
630
- for (const input of block.inputList) {
631
- for (const field of input.fieldRow) {
632
- if (field instanceof FieldOID) {
633
- field.refreshDisplay();
634
- }
635
- }
636
- }
637
- }
638
- };
639
-
640
- Blockly.FieldOID = FieldOID;
641
-
642
- //Blockly.Field.register('field_oid', FieldOID);