iobroker.mywebui 1.42.47 → 1.42.99

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 (305) hide show
  1. package/README.md +66 -1
  2. package/admin/jsonConfig.json +29 -1
  3. package/default-controls/3dcontrols/test.3dcontrol +0 -0
  4. package/default-controls/controls/Language/LanguageSelector.control +1 -0
  5. package/default-controls/controls/Language/LanguageSelector2.control +1 -0
  6. package/default-controls/controls/ReportSelector.control +1 -0
  7. package/default-controls/controls/Tank.control +1 -0
  8. package/default-controls/controls/Valve.control +1 -0
  9. package/default-controls/controls/WebuiChartButton.control +1 -0
  10. package/default-controls/controls/WebuiConnectionMonitor1.control +1 -0
  11. package/default-controls/controls/WebuiEditor3D.control +1 -0
  12. package/default-controls/controls/WebuiHabpanelImageButton.control +1 -0
  13. package/default-controls/controls/WebuiHabpanelValueDisplay.control +1 -0
  14. package/default-controls/controls/WebuiSimpleClock.control +1 -0
  15. package/default-controls/controls/Webui_Valve.control +1 -0
  16. package/default-controls/controls/Webui_led_light1.control +1 -0
  17. package/default-controls/controls/XX_123.control +1 -0
  18. package/default-controls/controls/analog_points/scada_point_panel.control +1 -0
  19. package/default-controls/controls/check_write_permission.control +1 -0
  20. package/default-controls/controls/densitymeter.control +1 -0
  21. package/default-controls/controls/input_controls/input_with_button.control +1 -0
  22. package/default-controls/controls/led_light1.control +1 -0
  23. package/default-controls/controls/levelbars/level.control +1 -0
  24. package/default-controls/controls/levelbars/level_bar.control +1 -0
  25. package/default-controls/controls/logout.control +1 -0
  26. package/default-controls/controls/pdraumhzgchart.control +1 -0
  27. package/default-controls/controls/pipes/Short_Horizontal_Pipe.control +1 -0
  28. package/default-controls/controls/pipes/Short_Vertical_Pipe.control +1 -0
  29. package/default-controls/controls/pipes/pipe_90deg_curve1.control +1 -0
  30. package/default-controls/controls/pipes/pipe_90deg_curve2.control +1 -0
  31. package/default-controls/controls/pipes/pipe_90deg_curve3.control +1 -0
  32. package/default-controls/controls/pipes/pipe_90deg_curve4.control +1 -0
  33. package/default-controls/controls/pipes/pipe_tee.control +1 -0
  34. package/default-controls/controls/print/PrintButton.control +1 -0
  35. package/default-controls/controls/prover.control +1 -0
  36. package/default-controls/controls/pumps/pump.control +1 -0
  37. package/default-controls/controls/reactor/reactor1/Process_column_2.control +1 -0
  38. package/default-controls/controls/reactor/reactor1/reactor_A.control +1 -0
  39. package/default-controls/controls/shoelace.control +1 -0
  40. package/default-controls/controls/sidebar/hamburger-menu.control +1 -0
  41. package/default-controls/controls/sidebar/menuitems.control +1 -0
  42. package/default-controls/controls/sidebar/modern-sidebar.control +1 -0
  43. package/default-controls/controls/sidebar/sidebarcontrol.control +1 -0
  44. package/default-controls/controls/smart_components.control +1 -0
  45. package/default-controls/controls/svetofor.control +1 -0
  46. package/default-controls/controls/switches/switch1.control +1 -0
  47. package/default-controls/controls/switches/switch2.control +1 -0
  48. package/default-controls/controls/test/mybutton.control +1 -0
  49. package/default-controls/controls/test.control +1 -0
  50. package/default-controls/controls/txtviewer/ReportSelector.control +1 -0
  51. package/default-controls/controls/txtviewer/TextViewer.control +1 -0
  52. package/default-controls/controls/username.control +1 -0
  53. package/default-controls/controls/valve_faceplate.control +1 -0
  54. package/dist/backend/3d-editor-integration.js +1 -484
  55. package/dist/backend/ImportmapCreator.js +1 -262
  56. package/dist/backend/LicenseValidator.js +1 -1
  57. package/dist/backend/UploadHelper.js +1 -182
  58. package/dist/backend/main.js +1 -1
  59. package/io-package.json +27 -4
  60. package/package.json +24 -4
  61. package/scripts/build-backend-dev.mjs +40 -0
  62. package/scripts/build-runtime-bundle.mjs +47 -0
  63. package/scripts/build-spectrum-editors.mjs +21 -0
  64. package/scripts/build-spectrum-pilot.mjs +21 -0
  65. package/scripts/convert-default-controls.mjs +75 -0
  66. package/scripts/obfuscate-frontend.mjs +96 -0
  67. package/www/3d-editor/js/Animation.js +1 -618
  68. package/www/3d-editor/js/AnimationResizer.js +1 -73
  69. package/www/3d-editor/js/Command.js +1 -40
  70. package/www/3d-editor/js/Config.js +1 -86
  71. package/www/3d-editor/js/Editor.js +1 -885
  72. package/www/3d-editor/js/EditorControls.js +1 -489
  73. package/www/3d-editor/js/GLTFImportDialog.js +1 -112
  74. package/www/3d-editor/js/History.js +1 -321
  75. package/www/3d-editor/js/Loader.js +1 -1151
  76. package/www/3d-editor/js/LoaderUtils.js +1 -90
  77. package/www/3d-editor/js/Menubar.Add.js +1 -559
  78. package/www/3d-editor/js/Menubar.Edit.js +1 -155
  79. package/www/3d-editor/js/Menubar.File.js +1 -586
  80. package/www/3d-editor/js/Menubar.Help.js +1 -73
  81. package/www/3d-editor/js/Menubar.Render.js +1 -858
  82. package/www/3d-editor/js/Menubar.Status.js +1 -51
  83. package/www/3d-editor/js/Menubar.View.js +1 -193
  84. package/www/3d-editor/js/Menubar.js +1 -29
  85. package/www/3d-editor/js/Player.js +1 -53
  86. package/www/3d-editor/js/Resizer.js +1 -65
  87. package/www/3d-editor/js/Script.js +1 -541
  88. package/www/3d-editor/js/Selector.js +1 -124
  89. package/www/3d-editor/js/Sidebar.Geometry.BoxGeometry.js +1 -121
  90. package/www/3d-editor/js/Sidebar.Geometry.BufferGeometry.js +1 -124
  91. package/www/3d-editor/js/Sidebar.Geometry.CapsuleGeometry.js +1 -109
  92. package/www/3d-editor/js/Sidebar.Geometry.CircleGeometry.js +1 -97
  93. package/www/3d-editor/js/Sidebar.Geometry.CylinderGeometry.js +1 -121
  94. package/www/3d-editor/js/Sidebar.Geometry.DodecahedronGeometry.js +1 -73
  95. package/www/3d-editor/js/Sidebar.Geometry.ExtrudeGeometry.js +1 -196
  96. package/www/3d-editor/js/Sidebar.Geometry.IcosahedronGeometry.js +1 -73
  97. package/www/3d-editor/js/Sidebar.Geometry.LatheGeometry.js +1 -98
  98. package/www/3d-editor/js/Sidebar.Geometry.Modifiers.js +1 -73
  99. package/www/3d-editor/js/Sidebar.Geometry.OctahedronGeometry.js +1 -74
  100. package/www/3d-editor/js/Sidebar.Geometry.PlaneGeometry.js +1 -97
  101. package/www/3d-editor/js/Sidebar.Geometry.RingGeometry.js +1 -121
  102. package/www/3d-editor/js/Sidebar.Geometry.ShapeGeometry.js +1 -76
  103. package/www/3d-editor/js/Sidebar.Geometry.SphereGeometry.js +1 -133
  104. package/www/3d-editor/js/Sidebar.Geometry.TetrahedronGeometry.js +1 -74
  105. package/www/3d-editor/js/Sidebar.Geometry.TextGeometry.js +1 -136
  106. package/www/3d-editor/js/Sidebar.Geometry.TorusGeometry.js +1 -109
  107. package/www/3d-editor/js/Sidebar.Geometry.TorusKnotGeometry.js +1 -121
  108. package/www/3d-editor/js/Sidebar.Geometry.TubeGeometry.js +1 -135
  109. package/www/3d-editor/js/Sidebar.Geometry.js +1 -411
  110. package/www/3d-editor/js/Sidebar.Material.BooleanProperty.js +1 -60
  111. package/www/3d-editor/js/Sidebar.Material.ColorProperty.js +1 -87
  112. package/www/3d-editor/js/Sidebar.Material.ConstantProperty.js +1 -62
  113. package/www/3d-editor/js/Sidebar.Material.MapProperty.js +1 -277
  114. package/www/3d-editor/js/Sidebar.Material.NumberProperty.js +1 -60
  115. package/www/3d-editor/js/Sidebar.Material.Program.js +1 -73
  116. package/www/3d-editor/js/Sidebar.Material.RangeValueProperty.js +1 -63
  117. package/www/3d-editor/js/Sidebar.Material.js +1 -751
  118. package/www/3d-editor/js/Sidebar.Object.js +1 -892
  119. package/www/3d-editor/js/Sidebar.Project.App.js +1 -218
  120. package/www/3d-editor/js/Sidebar.Project.Materials.js +1 -82
  121. package/www/3d-editor/js/Sidebar.Project.Renderer.js +1 -205
  122. package/www/3d-editor/js/Sidebar.Project.Resources.js +1 -242
  123. package/www/3d-editor/js/Sidebar.Project.js +1 -21
  124. package/www/3d-editor/js/Sidebar.Properties.js +1 -73
  125. package/www/3d-editor/js/Sidebar.Scene.js +1 -594
  126. package/www/3d-editor/js/Sidebar.Script.js +1 -129
  127. package/www/3d-editor/js/Sidebar.Settings.History.js +1 -146
  128. package/www/3d-editor/js/Sidebar.Settings.Shortcuts.js +1 -198
  129. package/www/3d-editor/js/Sidebar.Settings.js +1 -58
  130. package/www/3d-editor/js/Sidebar.js +1 -41
  131. package/www/3d-editor/js/Storage.js +1 -98
  132. package/www/3d-editor/js/Strings.js +1 -2718
  133. package/www/3d-editor/js/TextureParametersDialog.js +1 -293
  134. package/www/3d-editor/js/Toolbar.js +1 -77
  135. package/www/3d-editor/js/Viewport.Controls.js +1 -98
  136. package/www/3d-editor/js/Viewport.Info.js +1 -144
  137. package/www/3d-editor/js/Viewport.Pathtracer.js +1 -91
  138. package/www/3d-editor/js/Viewport.ViewHelper.js +1 -41
  139. package/www/3d-editor/js/Viewport.XR.js +1 -233
  140. package/www/3d-editor/js/Viewport.js +1 -983
  141. package/www/3d-editor/js/commands/AddObjectCommand.js +1 -68
  142. package/www/3d-editor/js/commands/AddScriptCommand.js +1 -75
  143. package/www/3d-editor/js/commands/Commands.js +1 -24
  144. package/www/3d-editor/js/commands/MoveObjectCommand.js +1 -117
  145. package/www/3d-editor/js/commands/MultiCmdsCommand.js +1 -85
  146. package/www/3d-editor/js/commands/RemoveObjectCommand.js +1 -88
  147. package/www/3d-editor/js/commands/RemoveScriptCommand.js +1 -81
  148. package/www/3d-editor/js/commands/SetColorCommand.js +1 -73
  149. package/www/3d-editor/js/commands/SetGeometryCommand.js +1 -86
  150. package/www/3d-editor/js/commands/SetGeometryValueCommand.js +1 -70
  151. package/www/3d-editor/js/commands/SetMaterialColorCommand.js +1 -87
  152. package/www/3d-editor/js/commands/SetMaterialCommand.js +1 -80
  153. package/www/3d-editor/js/commands/SetMaterialMapCommand.js +1 -144
  154. package/www/3d-editor/js/commands/SetMaterialRangeCommand.js +1 -92
  155. package/www/3d-editor/js/commands/SetMaterialValueCommand.js +1 -91
  156. package/www/3d-editor/js/commands/SetMaterialVectorCommand.js +1 -88
  157. package/www/3d-editor/js/commands/SetPositionCommand.js +1 -84
  158. package/www/3d-editor/js/commands/SetRotationCommand.js +1 -84
  159. package/www/3d-editor/js/commands/SetScaleCommand.js +1 -84
  160. package/www/3d-editor/js/commands/SetSceneCommand.js +1 -104
  161. package/www/3d-editor/js/commands/SetScriptValueCommand.js +1 -80
  162. package/www/3d-editor/js/commands/SetShadowValueCommand.js +1 -73
  163. package/www/3d-editor/js/commands/SetTextureParametersCommand.js +1 -143
  164. package/www/3d-editor/js/commands/SetUuidCommand.js +1 -70
  165. package/www/3d-editor/js/commands/SetValueCommand.js +1 -75
  166. package/www/3d-lib/jsm/exporters/DRACOExporter.js +311 -0
  167. package/www/3d-lib/jsm/exporters/EXRExporter.js +618 -0
  168. package/www/3d-lib/jsm/exporters/GLTFExporter.js +3738 -0
  169. package/www/3d-lib/jsm/exporters/KTX2Exporter.js +347 -0
  170. package/www/3d-lib/jsm/exporters/OBJExporter.js +308 -0
  171. package/www/3d-lib/jsm/exporters/PLYExporter.js +562 -0
  172. package/www/3d-lib/jsm/exporters/STLExporter.js +221 -0
  173. package/www/3d-lib/jsm/exporters/USDZExporter.js +1254 -0
  174. package/www/3d-lib/jsm/renderers/CSS3DRenderer.js +454 -0
  175. package/www/assets/dockspawn.css +104 -4
  176. package/www/assets/favicon.png +0 -0
  177. package/www/assets/icons/neon/cube3d-control.svg +1 -0
  178. package/www/assets/icons/neon/cube3d.svg +1 -0
  179. package/www/assets/icons/neon/file-screen.svg +1 -0
  180. package/www/assets/icons/neon/folder-3dcontrols.svg +1 -0
  181. package/www/assets/icons/neon/folder-3dscreens.svg +1 -0
  182. package/www/assets/icons/neon/folder-packages.svg +1 -0
  183. package/www/assets/icons/neon/folder-screens.svg +1 -0
  184. package/www/dist/frontend/bundle/chunk-3C6XC7RW.js +1 -0
  185. package/www/dist/frontend/bundle/chunk-5UBRCTRX.js +21 -0
  186. package/www/dist/frontend/bundle/chunk-AWKVUUKN.js +21 -0
  187. package/www/dist/frontend/bundle/chunk-BHSHNRXW.js +1 -0
  188. package/www/dist/frontend/bundle/chunk-MECWXZRN.js +1 -0
  189. package/www/dist/frontend/bundle/chunk-PJYK24KZ.js +1 -0
  190. package/www/dist/frontend/bundle/chunk-R6IQDPAP.js +21 -0
  191. package/www/dist/frontend/bundle/chunk-SE6N4ZEN.js +1 -0
  192. package/www/dist/frontend/bundle/chunk-V4C5FGJL.js +21 -0
  193. package/www/dist/frontend/bundle/chunk-WED7CGWA.js +21 -0
  194. package/www/dist/frontend/bundle/chunk-WQ5H5LTG.js +21 -0
  195. package/www/dist/frontend/bundle/common/IobrokerHandler.js +1 -0
  196. package/www/dist/frontend/bundle/runtime/CustomControls.js +1 -0
  197. package/www/dist/frontend/bundle/runtime/ScreenViewer.js +1 -0
  198. package/www/dist/frontend/bundle/runtime/controls.js +75 -0
  199. package/www/dist/frontend/bundle/runtime/init.js +74 -0
  200. package/www/dist/frontend/common/Common.globals.js +1 -7
  201. package/www/dist/frontend/common/IobrokerHandler.js +1 -1029
  202. package/www/dist/frontend/common/Runtime.js +1 -37
  203. package/www/dist/frontend/config/CommandHandling.js +1 -141
  204. package/www/dist/frontend/config/ConfigureWebcomponentDesigner.js +1 -125
  205. package/www/dist/frontend/config/DockHelper.js +1 -19
  206. package/www/dist/frontend/config/IobrokerWebui3DDriveEngine.js +1 -565
  207. package/www/dist/frontend/config/IobrokerWebui3DScreenEditor.js +17 -365
  208. package/www/dist/frontend/config/IobrokerWebui3DScreenPropertiesPanel.js +73 -504
  209. package/www/dist/frontend/config/IobrokerWebui3DScreenViewer.js +5 -338
  210. package/www/dist/frontend/config/IobrokerWebuiAppShell.js +30 -2250
  211. package/www/dist/frontend/config/IobrokerWebuiBindingsEditor.js +1 -25
  212. package/www/dist/frontend/config/IobrokerWebuiConfirmationWrapper.js +10 -27
  213. package/www/dist/frontend/config/IobrokerWebuiControlPropertiesEditor.js +3 -233
  214. package/www/dist/frontend/config/IobrokerWebuiDynamicPropsEditor.js +1 -159
  215. package/www/dist/frontend/config/IobrokerWebuiEventAssignment.js +1 -53
  216. package/www/dist/frontend/config/IobrokerWebuiIconsView.js +4 -74
  217. package/www/dist/frontend/config/IobrokerWebuiMonacoEditor.js +3 -183
  218. package/www/dist/frontend/config/IobrokerWebuiPropertyGrid.js +1 -363
  219. package/www/dist/frontend/config/IobrokerWebuiScreenEditor.js +1 -881
  220. package/www/dist/frontend/config/IobrokerWebuiScreensView.js +4 -70
  221. package/www/dist/frontend/config/IobrokerWebuiSignalPropertyEditor.js +1 -18
  222. package/www/dist/frontend/config/IobrokerWebuiSolutionExplorer.js +85 -1274
  223. package/www/dist/frontend/config/IobrokerWebuiTranslationEditor.js +1 -443
  224. package/www/dist/frontend/config/IobrokerWebuiWidgetGallery.js +116 -0
  225. package/www/dist/frontend/elements-layout.json +44 -0
  226. package/www/dist/frontend/helper/DialogHelper.js +3 -112
  227. package/www/dist/frontend/helper/EsprimaHelper.js +1 -18
  228. package/www/dist/frontend/helper/Helper.js +1 -68
  229. package/www/dist/frontend/helper/XmlHelper.js +1 -100
  230. package/www/dist/frontend/helper/ZipHelper.js +1 -0
  231. package/www/dist/frontend/interfaces/IControl.js +1 -1
  232. package/www/dist/frontend/interfaces/ICustomControlScript.js +1 -1
  233. package/www/dist/frontend/interfaces/IGlobalConfig.js +1 -1
  234. package/www/dist/frontend/interfaces/IGlobalScript.js +1 -1
  235. package/www/dist/frontend/interfaces/IIobrokerWebuiBinding.js +1 -4
  236. package/www/dist/frontend/interfaces/IScreen.js +1 -1
  237. package/www/dist/frontend/interfaces/IWebUiConfig.js +1 -1
  238. package/www/dist/frontend/runtime/AnimationService.js +1 -918
  239. package/www/dist/frontend/runtime/CustomControls.js +1 -230
  240. package/www/dist/frontend/runtime/DynamicElementProperties.js +1 -97
  241. package/www/dist/frontend/runtime/HabPanelLikeMenu.js +3 -41
  242. package/www/dist/frontend/runtime/ScreenViewer.js +3 -463
  243. package/www/dist/frontend/runtime/TranslateableText.js +2 -67
  244. package/www/dist/frontend/runtime/VisibilityService.js +1 -75
  245. package/www/dist/frontend/runtime/controls.js +1 -31
  246. package/www/dist/frontend/runtime/init.js +1 -16
  247. package/www/dist/frontend/scripting/IobrokerWebuiScriptCommands.js +1 -1
  248. package/www/dist/frontend/scripting/IobrokerWebuiScriptSystem.js +1 -88
  249. package/www/dist/frontend/scripting/blockly/OpenScreen.js +1 -31
  250. package/www/dist/frontend/scripting/blockly/webuiBlocklyToolbox.js +1 -19
  251. package/www/dist/frontend/services/DynamicPropertiesHelper.js +1 -75
  252. package/www/dist/frontend/services/IobrokerWebuiBindableLocalObjectsService.js +1 -46
  253. package/www/dist/frontend/services/IobrokerWebuiBindableObjectDragDropService.js +1 -29
  254. package/www/dist/frontend/services/IobrokerWebuiBindableObjectsForPropertiesService.js +1 -30
  255. package/www/dist/frontend/services/IobrokerWebuiBindableObjectsService.js +1 -51
  256. package/www/dist/frontend/services/IobrokerWebuiConfigButtonProvider.js +1 -40
  257. package/www/dist/frontend/services/IobrokerWebuiCopyPasteService.js +1 -28
  258. package/www/dist/frontend/services/IobrokerWebuiCustomControlEventsService.js +1 -26
  259. package/www/dist/frontend/services/IobrokerWebuiCustomElementContextMenu.js +1 -19
  260. package/www/dist/frontend/services/IobrokerWebuiDemoProviderService.js +1 -22
  261. package/www/dist/frontend/services/IobrokerWebuiDynamicPropertiesService.js +1 -36
  262. package/www/dist/frontend/services/IobrokerWebuiEventsService.js +1 -28
  263. package/www/dist/frontend/services/IobrokerWebuiExternalDragDropService.js +1 -26
  264. package/www/dist/frontend/services/IobrokerWebuiLitPropertiesService.js +1 -52
  265. package/www/dist/frontend/services/IobrokerWebuiPropertiesService.js +1 -87
  266. package/www/dist/frontend/services/IobrokerWebuiPropertyGroupsService.js +1 -13
  267. package/www/dist/frontend/services/IobrokerWebuiRefactorService.js +1 -51
  268. package/www/dist/frontend/services/IobrokerWebuiScreenContextMenu.js +1 -19
  269. package/www/dist/frontend/services/IobrokerWebuiSpecialPropertiesService.js +1 -19
  270. package/www/dist/frontend/services/IobrokerWebuiSpectrumEditorService.js +1 -0
  271. package/www/dist/frontend/services/IobrokerWebuiVisibilityPropertiesService.js +1 -324
  272. package/www/dist/frontend/services/LayoutComponentsPropertiesService.js +1 -0
  273. package/www/dist/frontend/widgets/configWidgets.js +1 -3
  274. package/www/dist/frontend/widgets/customElementsObserver.js +1 -41
  275. package/www/dist/frontend/widgets/designerAddons.js +1 -3
  276. package/www/dist/frontend/widgets/importWidgetFiles.js +1 -4
  277. package/www/dist/frontend/widgets/importWidgetFilesRuntime.js +1 -1
  278. package/www/dist/frontend/widgets/layout-components.js +1 -0
  279. package/www/dist/frontend/widgets/layout-placement-services.js +1 -0
  280. package/www/dist/frontend/widgets/testElement.js +1 -3
  281. package/www/index.html +86 -17
  282. package/www/libs/@gokturk413/assets/images/expander.svg +1 -0
  283. package/www/libs/@gokturk413/assets/images/expanderClose.svg +1 -0
  284. package/www/libs/@gokturk413/assets/images/file.svg +1 -0
  285. package/www/libs/@gokturk413/assets/images/folder.svg +1 -0
  286. package/www/libs/@gokturk413/propertygrid.webcomponent/dist/PropertyGrid.js +7 -2
  287. package/www/libs/@gokturk413/web-component-designer/dist/elements/widgets/propertyGrid/PropertyGridPropertyList.js +2 -2
  288. package/www/libs/@gokturk413/web-component-designer/dist/elements/widgets/propertyGrid/PropertyGridWithHeader.js +2 -2
  289. package/www/libs/@gokturk413/web-component-designer/dist/elements/widgets/refactorView/refactor-view.js +5 -2
  290. package/www/libs/@gokturk413/web-component-designer/dist/elements/widgets/treeView/treeView.js +1 -1
  291. package/www/libs/@gokturk413/web-component-designer/dist/index-min.js +6 -6
  292. package/www/libs/@gokturk413/web-component-designer/dist/index-min.js.bak +16 -0
  293. package/www/libs/@gokturk413/web-component-designer-codeview-monaco/dist/widgets/codeView/code-view-monaco.js +21 -1
  294. package/www/libs/@gokturk413/web-component-designer-visualization-addons/dist/components/EventAssignment.js +8 -0
  295. package/www/libs/@gokturk413/web-component-designer-visualization-addons/dist/components/SimpleScriptEditor.js +5 -1
  296. package/www/libs/@gokturk413/web-component-designer-widgets-wunderbaum/dist/widgets/bindableObjectsBrowser/bindable-objects-browser.js +13 -1
  297. package/www/libs/spectrum/editors-entry.js +14 -0
  298. package/www/libs/spectrum/spectrum-editors.bundle.js +1109 -0
  299. package/www/node_modules/@iobroker/socket-client/dist/esm/Connection.js +1 -1
  300. package/www/runtime.html +40 -10
  301. package/www/spectrum/pilot.bundle.js +1160 -0
  302. package/www/spectrum/pilot.html +74 -0
  303. package/www/spectrum/pilot.js +139 -0
  304. package/www/spectrum/theme-system.js +113 -0
  305. package/www/libs/@node-projects/css-parser/dist/index-min.js.map +0 -7
package/README.md CHANGED
@@ -98,7 +98,72 @@ For detailed usage instructions, see [SCADA_SETUP.md](./SCADA_SETUP.md)
98
98
  - Time formatting and deadband/hysteresis functions
99
99
  - Global access in all Custom Controls and Formulas
100
100
 
101
-
101
+ ---
102
+
103
+ ## Custom Edition Features (gokturk413)
104
+
105
+ This custom edition extends the original mywebui with a SCADA‑oriented feature set. The
106
+ items below are the major additions over upstream.
107
+
108
+ ### 🧩 Multi‑project support
109
+ - Several independent projects in one adapter, each fully self‑contained under
110
+ `mywebui.0.projects/<name>/{data,widgets}` (screens, 3D screens, custom controls,
111
+ global style/script, installed npm widgets).
112
+ - Project selector in the designer toolbar with **create / rename / delete / export /
113
+ import** per project. The runtime opens a project via `runtime.html?project=<name>`
114
+ (parameter‑less `runtime.html` always shows `default`, kiosk‑safe).
115
+
116
+ ### 🟦 3D Editor (Three.js)
117
+ - A full 3D screen editor and 3D custom controls alongside the classic 2D designer:
118
+ `3D Screens` and `3D Custom Controls` (nested under `Controls`).
119
+ - Scene editing with assets, lights, camera, grid, signal bindings and drives; rendered
120
+ in the runtime via the embedded 3D viewer.
121
+
122
+ ### 📦 Global (platform‑default) custom controls
123
+ - A library of project‑independent default controls (2D + a sample 3D control) that ship
124
+ with the platform and are seeded on install into `mywebui.0.data/global/`.
125
+ - Editable, usable in every project's screens, browsable under the `Global` tree node
126
+ (`CustomControls`, `3D Custom Controls`). Project controls with the same name take
127
+ precedence over a global one.
128
+
129
+ ### 🔐 Group‑based access control (Screen Accessibility)
130
+ Server‑enforced (the adapter resolves ioBroker group membership from the DB — the
131
+ frontend is not trusted), runtime‑gated visualization access:
132
+ - **Runtime (read):** *blacklist* model — ticking a group **blocks** it from a screen at
133
+ runtime; unticked groups are allowed; nothing ticked = open to all. Per‑screen
134
+ (overrides global) **and** a project‑wide global default. When blocked, the screen shows
135
+ an **“Access Denied” message** or **redirects** to another screen (configurable).
136
+ - **Editor (write):** the designer is **administrator‑group only**; the built‑in `admin`
137
+ user always has editor access and can additionally forbid specific administrator users
138
+ from the editor. The built‑in `admin` user can be blocked at *runtime* but never from
139
+ the editor.
140
+ - Two toolbar panels: **🔐 Permissions** (project‑level groups + editor‑user control) and
141
+ **🗂️ Screen Access** (per‑screen). Both mirror the Settings‑tab **Screen Accessibility
142
+ Control** and sync on save.
143
+ - ioBroker's own file/object ACLs are left at their defaults — access control is purely
144
+ app‑level so it does not conflict with the platform's permission system.
145
+
146
+ ### 🎨 Theming & branding
147
+ - Light/dark/other theme adaptation via `color-scheme` + `light-dark()` + `--ui-*`
148
+ design tokens across the whole designer.
149
+ - Neon‑glassmorphic solution explorer, branded tab title (**MYWEBUI DESIGNER** /
150
+ **MYWEBUI**) and custom favicon.
151
+
152
+ ### 🔒 License protection & code obfuscation
153
+ - Hardware‑bound license validation (Ed25519‑signed, validated by the obfuscated backend;
154
+ the frontend shows a license overlay if invalid).
155
+ - **Hybrid build model:** you develop with **readable source** (git, local); the published
156
+ npm package ships **obfuscated** backend + own frontend (vendored libraries untouched).
157
+ Obfuscation runs in the CI publish workflow on a disposable runner — your source is never
158
+ obfuscated in place. License parts are obfuscated locally (their secret keys never enter
159
+ CI). Source maps and readable license source are excluded from the npm package.
160
+
161
+ ### Notable fixes
162
+ - **OAuth token‑refresh under a subpath:** the socket client refreshed the auth token via a
163
+ relative `./oauth/token`, which resolved to `/mywebui/oauth/token` (404) when served under
164
+ `/mywebui/` and forced periodic full‑page reloads. It now uses the absolute `/oauth/token`
165
+ endpoint, so the runtime/designer no longer auto‑refresh on token expiry.
166
+
102
167
  ## Concepts
103
168
 
104
169
  ### Description
@@ -42,9 +42,37 @@
42
42
  "borderRadius": "4px"
43
43
  }
44
44
  },
45
+ "_hwidHeader": {
46
+ "type": "header",
47
+ "text": "Your Hardware ID",
48
+ "size": 4,
49
+ "marginTop": 10
50
+ },
51
+ "hardwareId": {
52
+ "type": "text",
53
+ "label": "Hardware ID",
54
+ "readOnly": true,
55
+ "copyToClipboard": true,
56
+ "sm": 12,
57
+ "md": 10,
58
+ "lg": 8,
59
+ "help": "Send this ID to gokturk413 to receive a license key for this computer."
60
+ },
61
+ "_getHwidBtn": {
62
+ "type": "sendTo",
63
+ "label": "Show / refresh my Hardware ID",
64
+ "command": "getHardwareId",
65
+ "jsonData": "{}",
66
+ "variant": "contained",
67
+ "icon": "info",
68
+ "useNative": true,
69
+ "onLoaded": true,
70
+ "sm": 12,
71
+ "md": 4
72
+ },
45
73
  "_hardwareIdInfo": {
46
74
  "type": "staticText",
47
- "text": "ℹ️ Your Hardware ID will be shown in the adapter logs on first start. You cannot manually add hardware IDs - they are encrypted in the license key.",
75
+ "text": "ℹ️ The Hardware ID above identifies this computer. Send it to gokturk413 to get a license key. You cannot manually add hardware IDs they are cryptographically bound inside the license key.",
48
76
  "style": {
49
77
  "fontSize": 12,
50
78
  "color": "#2196F3",
@@ -0,0 +1 @@
1
+ {"html":"<div id=\"lang-selector\" style=\"display:flex;flex-wrap:wrap;gap:6px;padding:6px;width:100%;height:100%;box-sizing:border-box;align-items:center;justify-content:center;\"></div>\n","style":":host {\n display: block;\n width: 100%;\n height: 100%;\n background: transparent;\n}\n* { box-sizing: border-box; }\n.lang-btn {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n border-radius: 8px;\n border: 2px solid transparent;\n background: transparent;\n padding: 4px 8px;\n transition: all 0.2s ease;\n user-select: none;\n gap: 2px;\n}\n.lang-btn:hover {\n background: rgba(255,255,255,0.12);\n transform: scale(1.08);\n}\n.lang-btn.active {\n border-color: #2196f3;\n background: rgba(33,150,243,0.18);\n box-shadow: 0 0 8px rgba(33,150,243,0.4);\n}\n.lang-flag { line-height: 1; }\n.lang-code {\n font-size: 10px;\n font-weight: bold;\n color: #ccc;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n}\n.lang-btn.active .lang-code { color: #2196f3; }\n","script":"\nlet iobrokerHandler;\nlet _langChangedHandler;\nlet _translChangedHandler;\n\nconst FLAGS = {\n az: '🇦🇿',\n ru: '🇷🇺',\n en: '🇬🇧',\n de: '🇩🇪',\n fr: '🇫🇷',\n tr: '🇹🇷',\n uk: '🇺🇦',\n pl: '🇵🇱',\n es: '🇪🇸',\n it: '🇮🇹',\n zh: '🇨🇳',\n ar: '🇸🇦',\n pt: '🇵🇹',\n nl: '🇳🇱',\n};\n\nexport async function connectedCallback(instance) {\n const p = window.location.protocol;\n const h = window.location.hostname;\n const port = window.location.port;\n try {\n const mod = await import(`${p}//${h}:${port}/mywebui/dist/frontend/common/IobrokerHandler.js`);\n iobrokerHandler = mod.iobrokerHandler;\n } catch(e) {\n console.error('LanguageSelector: import failed', e);\n return;\n }\n await iobrokerHandler.waitForReady();\n _render(instance);\n _langChangedHandler = iobrokerHandler.languageChanged.on(() => _updateActive(instance));\n _translChangedHandler = iobrokerHandler.translationsChanged.on(() => _render(instance));\n}\n\nexport function disconnectedCallback(instance) {\n _langChangedHandler?.dispose();\n _translChangedHandler?.dispose();\n}\n\nfunction _render(instance) {\n const container = instance._getDomElement('lang-selector');\n container.innerHTML = '';\n const langs = Object.keys(iobrokerHandler.translations ?? {});\n if (!langs.length) { container.textContent = 'No translations'; return; }\n const size = parseInt(instance.size ?? 32);\n const dir = instance.direction ?? 'horizontal';\n container.style.flexDirection = dir === 'vertical' ? 'column' : 'row';\n for (const lang of langs) {\n const btn = document.createElement('button');\n btn.className = 'lang-btn' + (lang === iobrokerHandler.language ? ' active' : '');\n btn.dataset.lang = lang;\n btn.title = lang.toUpperCase();\n const flag = document.createElement('span');\n flag.className = 'lang-flag';\n flag.style.fontSize = size + 'px';\n flag.textContent = FLAGS[lang] ?? '🌐';\n const code = document.createElement('span');\n code.className = 'lang-code';\n code.textContent = lang;\n btn.appendChild(flag);\n btn.appendChild(code);\n btn.onclick = () => { iobrokerHandler.setLanguage(lang); };\n container.appendChild(btn);\n }\n}\n\nfunction _updateActive(instance) {\n const container = instance._getDomElement('lang-selector');\n container.querySelectorAll('.lang-btn').forEach(btn => {\n btn.classList.toggle('active', btn.dataset.lang === iobrokerHandler.language);\n });\n}\n","properties":{"size":{"type":"number","default":"32"},"direction":{"type":"string","default":"horizontal"}},"settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}
@@ -0,0 +1 @@
1
+ {"html":"<div id=\"lang-selector\" style=\"display:flex;flex-wrap:wrap;gap:8px;padding:8px;width:100%;height:100%;box-sizing:border-box;align-items:center;justify-content:center;\"></div>\n","style":":host {\n display: block;\n width: 100%;\n height: 100%;\n background: transparent;\n}\n* { box-sizing: border-box; }\n.lang-btn {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n border-radius: 8px;\n border: 2px solid rgba(255,255,255,0.1);\n background: rgba(255,255,255,0.05);\n padding: 6px 10px;\n transition: all 0.2s ease;\n user-select: none;\n gap: 4px;\n min-width: 56px;\n}\n.lang-btn:hover {\n background: rgba(255,255,255,0.15);\n border-color: rgba(255,255,255,0.3);\n transform: translateY(-2px);\n box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n}\n.lang-btn.active {\n border-color: #2196f3;\n background: rgba(33,150,243,0.2);\n box-shadow: 0 0 12px rgba(33,150,243,0.5);\n}\n.lang-flag {\n border-radius: 3px;\n overflow: hidden;\n display: block;\n box-shadow: 0 1px 4px rgba(0,0,0,0.4);\n}\n.lang-code {\n font-size: 11px;\n font-weight: 700;\n color: #aaa;\n letter-spacing: 1px;\n text-transform: uppercase;\n}\n.lang-btn.active .lang-code { color: #64b5f6; }\n","script":"let iobrokerHandler;\r\nlet _langChangedHandler;\r\nlet _translChangedHandler;\r\ndebugger;\r\nconst W = 36, H = 24;\r\n\r\nconst FLAGS = {\r\n az: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"8\" fill=\"#0092BC\"/>\r\n <rect y=\"8\" width=\"36\" height=\"8\" fill=\"#E8112D\"/>\r\n <rect y=\"16\" width=\"36\" height=\"8\" fill=\"#509E2F\"/>\r\n <circle cx=\"18\" cy=\"12\" r=\"4.5\" fill=\"white\"/>\r\n <circle cx=\"19.5\" cy=\"12\" r=\"3.5\" fill=\"#E8112D\"/>\r\n <polygon fill=\"white\" points=\"22,10 23.5,12 22,14 24.5,12\"/>\r\n <polygon fill=\"white\" points=\"21.5,10.5 22.2,12 21.5,13.5 23.5,12\"/>\r\n </svg>`,\r\n ru: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"8\" fill=\"#FFFFFF\"/>\r\n <rect y=\"8\" width=\"36\" height=\"8\" fill=\"#0039A6\"/>\r\n <rect y=\"16\" width=\"36\" height=\"8\" fill=\"#D52B1E\"/>\r\n </svg>`,\r\n en: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#012169\"/>\r\n <line x1=\"0\" y1=\"0\" x2=\"36\" y2=\"24\" stroke=\"white\" stroke-width=\"4.8\"/>\r\n <line x1=\"36\" y1=\"0\" x2=\"0\" y2=\"24\" stroke=\"white\" stroke-width=\"4.8\"/>\r\n <line x1=\"0\" y1=\"0\" x2=\"36\" y2=\"24\" stroke=\"#C8102E\" stroke-width=\"3.2\"/>\r\n <line x1=\"36\" y1=\"0\" x2=\"0\" y2=\"24\" stroke=\"#C8102E\" stroke-width=\"3.2\"/>\r\n <rect x=\"15\" y=\"0\" width=\"6\" height=\"24\" fill=\"white\"/>\r\n <rect y=\"9\" width=\"36\" height=\"6\" fill=\"white\"/>\r\n <rect x=\"16\" y=\"0\" width=\"4\" height=\"24\" fill=\"#C8102E\"/>\r\n <rect y=\"10\" width=\"36\" height=\"4\" fill=\"#C8102E\"/>\r\n </svg>`,\r\n de: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"8\" fill=\"#000000\"/>\r\n <rect y=\"8\" width=\"36\" height=\"8\" fill=\"#DD0000\"/>\r\n <rect y=\"16\" width=\"36\" height=\"8\" fill=\"#FFCE00\"/>\r\n </svg>`,\r\n fr: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#ED2939\"/>\r\n <rect width=\"24\" height=\"24\" fill=\"#FFFFFF\"/>\r\n <rect width=\"12\" height=\"24\" fill=\"#002395\"/>\r\n </svg>`,\r\n tr: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#E30A17\"/>\r\n <circle cx=\"14\" cy=\"12\" r=\"6\" fill=\"white\"/>\r\n <circle cx=\"16\" cy=\"12\" r=\"4.5\" fill=\"#E30A17\"/>\r\n <polygon fill=\"white\" points=\"22,12 19.5,10.5 20.5,13.5 20.5,10.5 19.5,13.5\"/>\r\n <polygon fill=\"white\" transform=\"rotate(-18,20.5,12)\" points=\"20.5,8 21.2,10.2 23.5,10.2 21.7,11.5 22.4,13.7 20.5,12.5 18.6,13.7 19.3,11.5 17.5,10.2 19.8,10.2\"/>\r\n </svg>`,\r\n uk: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"12\" fill=\"#005BBB\"/>\r\n <rect y=\"12\" width=\"36\" height=\"12\" fill=\"#FFD500\"/>\r\n </svg>`,\r\n pl: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"12\" fill=\"#FFFFFF\"/>\r\n <rect y=\"12\" width=\"36\" height=\"12\" fill=\"#DC143C\"/>\r\n </svg>`,\r\n es: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#AA151B\"/>\r\n <rect y=\"6\" width=\"36\" height=\"12\" fill=\"#F1BF00\"/>\r\n </svg>`,\r\n it: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#CE2B37\"/>\r\n <rect width=\"24\" height=\"24\" fill=\"#FFFFFF\"/>\r\n <rect width=\"12\" height=\"24\" fill=\"#009246\"/>\r\n </svg>`,\r\n zh: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#DE2910\"/>\r\n <polygon fill=\"#FFDE00\" points=\"4,2 5,5.5 8.5,5.5 5.8,7.5 6.8,11 4,9 1.2,11 2.2,7.5 -0.5,5.5 3,5.5\"/>\r\n <polygon fill=\"#FFDE00\" transform=\"rotate(30,12,3)\" points=\"12,1 12.5,2.5 14,2.5 12.8,3.3 13.3,4.8 12,4 10.7,4.8 11.2,3.3 10,2.5 11.5,2.5\"/>\r\n <polygon fill=\"#FFDE00\" transform=\"rotate(-20,15,6)\" points=\"15,4 15.5,5.5 17,5.5 15.8,6.3 16.3,7.8 15,7 13.7,7.8 14.2,6.3 13,5.5 14.5,5.5\"/>\r\n </svg>`,\r\n ar: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#006C35\"/>\r\n <line x1=\"6\" y1=\"12\" x2=\"30\" y2=\"12\" stroke=\"white\" stroke-width=\"1.5\"/>\r\n <text x=\"18\" y=\"15\" text-anchor=\"middle\" fill=\"white\" font-size=\"7\" font-family=\"serif\">ع</text>\r\n </svg>`,\r\n pt: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#FF0000\"/>\r\n <rect width=\"14\" height=\"24\" fill=\"#006600\"/>\r\n <circle cx=\"14\" cy=\"12\" r=\"5\" fill=\"#FFFF00\" stroke=\"#006600\" stroke-width=\"0.8\"/>\r\n <circle cx=\"14\" cy=\"12\" r=\"3\" fill=\"#0033AA\"/>\r\n </svg>`,\r\n nl: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"8\" fill=\"#AE1C28\"/>\r\n <rect y=\"8\" width=\"36\" height=\"8\" fill=\"#FFFFFF\"/>\r\n <rect y=\"16\" width=\"36\" height=\"8\" fill=\"#21468B\"/>\r\n </svg>`,\r\n};\r\n\r\nexport async function connectedCallback(instance) {\r\n const p = window.location.protocol;\r\n const h = window.location.hostname;\r\n const port = window.location.port;\r\n try {\r\n const mod = await import(`${p}//${h}:${port}/mywebui/dist/frontend/common/IobrokerHandler.js`);\r\n iobrokerHandler = mod.iobrokerHandler;\r\n } catch(e) {\r\n console.error('LanguageSelector: import failed', e);\r\n return;\r\n }\r\n await iobrokerHandler.waitForReady();\r\n _render(instance);\r\n _langChangedHandler = iobrokerHandler.languageChanged.on(() => _updateActive(instance));\r\n _translChangedHandler = iobrokerHandler.translationsChanged.on(() => _render(instance));\r\n iobrokerHandler.setState('0_userdata.0.language.selectedlanguage', { val: iobrokerHandler.language });\r\n}\r\n\r\nexport function disconnectedCallback(instance) {\r\n _langChangedHandler?.dispose();\r\n _translChangedHandler?.dispose();\r\n}\r\n\r\nfunction _render(instance) {\r\n const container = instance._getDomElement('lang-selector');\r\n container.innerHTML = '';\r\n const langs = Object.keys(iobrokerHandler.translations ?? {});\r\n if (!langs.length) {\r\n container.innerHTML = '<span style=\"color:#888;font-size:12px;\">No translations defined</span>';\r\n return;\r\n }\r\n const size = parseInt(instance.size ?? 36);\r\n const dir = instance.direction ?? 'horizontal';\r\n container.style.flexDirection = dir === 'vertical' ? 'column' : 'row';\r\n for (const lang of langs) {\r\n const btn = document.createElement('button');\r\n btn.className = 'lang-btn' + (lang === iobrokerHandler.language ? ' active' : '');\r\n btn.dataset.lang = lang;\r\n btn.title = lang.toUpperCase();\r\n const flagWrap = document.createElement('span');\r\n flagWrap.className = 'lang-flag';\r\n const svgStr = FLAGS[lang];\r\n if (svgStr) {\r\n const scaled = svgStr.replace(/width=\"\\d+\"/, `width=\"${size}\"`).replace(/height=\"\\d+\"/, `height=\"${Math.round(size*2/3)}\"`);\r\n flagWrap.innerHTML = scaled;\r\n } else {\r\n flagWrap.style.cssText = `width:${size}px;height:${Math.round(size*2/3)}px;background:#555;border-radius:3px;display:flex;align-items:center;justify-content:center;`;\r\n flagWrap.textContent = lang.toUpperCase();\r\n }\r\n const code = document.createElement('span');\r\n code.className = 'lang-code';\r\n code.textContent = lang;\r\n btn.appendChild(flagWrap);\r\n btn.appendChild(code);\r\n btn.onclick = () => {\r\n iobrokerHandler.setLanguage(lang);\r\n iobrokerHandler.setState('0_userdata.0.language.selectedlanguage', { val: lang });\r\n };\r\n container.appendChild(btn);\r\n }\r\n}\r\n\r\nfunction _updateActive(instance) {\r\n const container = instance._getDomElement('lang-selector');\r\n container.querySelectorAll('.lang-btn').forEach(btn => {\r\n btn.classList.toggle('active', btn.dataset.lang === iobrokerHandler.language);\r\n });\r\n}\r\n","properties":{"size":{"type":"number","default":"36"},"direction":{"type":"string","default":"horizontal"}},"settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":"","useGlobalStyle":"false"}}
@@ -0,0 +1 @@
1
+ {"html":"<div id=\"reportSelectorContainer\" style=\"width:100%;height:100%;display:flex;align-items:center;gap:12px;padding:16px;border-radius:8px;\">\n <button id=\"btn1\" class=\"report-btn\" style=\"flex:1;cursor:pointer;border-radius:8px;font-size:15px;font-weight:600;box-shadow:0 2px 4px rgba(0,0,0,0.1);transition:0.3s;border:2px solid;padding:14px 20px;\">SnapShot Report</button>\n <button id=\"btn2\" class=\"report-btn\" style=\"flex:1;cursor:pointer;border-radius:8px;font-size:15px;font-weight:600;box-shadow:0 2px 4px rgba(0,0,0,0.1);transition:0.3s;border:2px solid;padding:14px 20px;\">Daily Report</button>\n <button id=\"btn3\" class=\"report-btn\" style=\"flex:1;cursor:pointer;border-radius:8px;font-size:15px;font-weight:600;box-shadow:0 2px 4px rgba(0,0,0,0.1);transition:0.3s;border:2px solid;padding:14px 20px;\">Hourly Report</button>\n <button id=\"btn4\" class=\"report-btn\" style=\"flex:1;cursor:pointer;border-radius:8px;font-size:15px;font-weight:600;box-shadow:0 2px 4px rgba(0,0,0,0.1);transition:0.3s;border:2px solid;padding:14px 20px;\">Prover Report</button>\n</div>\n","style":":host {\n display: block;\n width: 100%;\n}\n\n.report-btn:hover:not(.active) {\n transform: translateY(-2px);\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n}\n\n.report-btn:active {\n transform: scale(0.98);\n}\n\n.report-btn.active {\n transform: translateY(-2px) scale(1.02);\n box-shadow: 0 6px 20px rgba(0,0,0,0.25);\n font-weight: 700;\n}\n","script":"/**\n * @param {BaseScreenViewerAndControl} instance\n */\nexport function init(instance) {\n const btn1 = instance._getDomElement('btn1');\n const btn2 = instance._getDomElement('btn2');\n const btn3 = instance._getDomElement('btn3');\n const btn4 = instance._getDomElement('btn4');\n const container = instance._getDomElement('reportSelectorContainer');\n\n const buttons = [btn1, btn2, btn3, btn4];\n let currentIndex = parseInt(instance.activeButton) || 1;\n let currentTheme = instance.theme || 'default';\n\n // Button mətnlərini yeniləyirik\n if (instance.button1Text) btn1.textContent = instance.button1Text;\n if (instance.button2Text) btn2.textContent = instance.button2Text;\n if (instance.button3Text) btn3.textContent = instance.button3Text;\n if (instance.button4Text) btn4.textContent = instance.button4Text;\n\n // İlk theming və active button\n applyTheming(instance, currentTheme);\n setActiveButton(currentIndex);\n loadReport(currentIndex);\n\n // Button click event-ləri\n buttons.forEach((btn, index) => {\n btn.addEventListener('click', () => {\n const btnIndex = index + 1;\n setActiveButton(btnIndex);\n loadReport(btnIndex);\n });\n });\n\n function applyTheming(inst, theme) {\n const themes = {\n 'default': {\n containerBg: '#f5f5f5',\n btnBg: '#ffffff',\n btnText: '#2d3436',\n btnBorder: '#dfe6e9',\n activeBg: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',\n activeText: '#ffffff',\n activeBorder: '#667eea'\n },\n 'dark': {\n containerBg: '#2d3436',\n btnBg: '#636e72',\n btnText: '#dfe6e9',\n btnBorder: '#000000',\n activeBg: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',\n activeText: '#ffffff',\n activeBorder: '#667eea'\n },\n 'light': {\n containerBg: '#ffffff',\n btnBg: '#f8f9fa',\n btnText: '#212529',\n btnBorder: '#dee2e6',\n activeBg: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',\n activeText: '#ffffff',\n activeBorder: '#667eea'\n },\n 'blue': {\n containerBg: '#dfe6e9',\n btnBg: '#ffffff',\n btnText: '#0984e3',\n btnBorder: '#74b9ff',\n activeBg: 'linear-gradient(135deg, #0984e3 0%, #0652DD 100%)',\n activeText: '#ffffff',\n activeBorder: '#0984e3'\n },\n 'green': {\n containerBg: '#d4f1e8',\n btnBg: '#ffffff',\n btnText: '#00b894',\n btnBorder: '#55efc4',\n activeBg: 'linear-gradient(135deg, #00b894 0%, #00a878 100%)',\n activeText: '#ffffff',\n activeBorder: '#00b894'\n },\n 'red': {\n containerBg: '#ffcccc',\n btnBg: '#ffffff',\n btnText: '#d63031',\n btnBorder: '#ff7675',\n activeBg: 'linear-gradient(135deg, #d63031 0%, #c0392b 100%)',\n activeText: '#ffffff',\n activeBorder: '#d63031'\n },\n 'purple': {\n containerBg: '#e5dbff',\n btnBg: '#ffffff',\n btnText: '#6c5ce7',\n btnBorder: '#a29bfe',\n activeBg: 'linear-gradient(135deg, #6c5ce7 0%, #5f27cd 100%)',\n activeText: '#ffffff',\n activeBorder: '#6c5ce7'\n }\n };\n\n const colors = themes[theme] || themes['default'];\n\n // Container\n if (container) {\n container.style.background = colors.containerBg;\n }\n\n // Buttons - inactive state\n buttons.forEach(btn => {\n if (btn && !btn.classList.contains('active')) {\n btn.style.background = colors.btnBg;\n btn.style.color = colors.btnText;\n btn.style.borderColor = colors.btnBorder;\n }\n });\n\n // Store colors for active state\n inst._themeColors = colors;\n\n // Re-apply active button styling\n setActiveButton(currentIndex);\n }\n\n function setActiveButton(index) {\n currentIndex = index;\n const colors = instance._themeColors || {\n btnBg: '#ffffff',\n btnText: '#2d3436',\n btnBorder: '#dfe6e9',\n activeBg: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',\n activeText: '#ffffff',\n activeBorder: '#667eea'\n };\n\n buttons.forEach((btn, i) => {\n if (i + 1 === index) {\n btn.classList.add('active');\n btn.style.background = colors.activeBg;\n btn.style.color = colors.activeText;\n btn.style.borderColor = colors.activeBorder;\n } else {\n btn.classList.remove('active');\n btn.style.background = colors.btnBg;\n btn.style.color = colors.btnText;\n btn.style.borderColor = colors.btnBorder;\n }\n });\n }\n\n function loadReport(index) {\n const reportStates = [instance.reportState1, instance.reportState2, instance.reportState3, instance.reportState4];\n const outputStates = [instance.btn1Output, instance.btn2Output, instance.btn3Output, instance.btn4Output];\n const sourceState = reportStates[index - 1];\n const targetState = outputStates[index - 1];\n if (!sourceState || !targetState) {\n console.warn('ReportSelector: Source or target state not configured');\n return;\n }\n window.IOB.getState(sourceState).then(state => {\n if (state && state.val !== null && state.val !== undefined) {\n window.IOB.setState(targetState, state.val); console.log('ReportSelector: Written to', targetState);\n }\n else {\n console.warn('ReportSelector: No data in source state:', sourceState);\n window.IOB.setState(targetState, 'No report data available');\n }\n }).catch(err => {\n console.error('ReportSelector: Error loading report:', err);\n window.IOB.setState(targetState, 'Error: ' + err.message);\n });\n }\n\n // Source state-dən məlumatı oxuyuruq\n window.IOB.getState(sourceState).then(state => {\n if (state && state.val !== null && state.val !== undefined) {\n const data = state.val;\n\n // Hər 4 target state-ə yazırıq\n targetStates.forEach((targetState, i) => {\n if (targetState) {\n window.IOB.setState(targetState, data);\n console.log(`ReportSelector: Written to output ${i + 1}:`, targetState);\n }\n });\n\n // Əgər outputState (legacy) varsa ona da yazırıq\n if (instance.outputState) {\n window.IOB.setState(instance.outputState, data);\n }\n } else {\n console.warn('ReportSelector: No data in source state:', sourceState);\n // Boş məlumat da yazırıq\n targetStates.forEach(targetState => {\n if (targetState) {\n window.IOB.setState(targetState, 'No report data available');\n }\n });\n }\n }).catch(err => {\n console.error('ReportSelector: Error loading report:', err);\n targetStates.forEach(targetState => {\n if (targetState) {\n window.IOB.setState(targetState, 'Error loading report: ' + err.message);\n }\n });\n });\n\n // Property dəyişiklikləri üçün event-lər\ninstance._assignEvent('button1-text-changed', () => {\n if (btn1) btn1.textContent = instance.button1Text || 'Report 1';\n});\n\ninstance._assignEvent('button2-text-changed', () => {\n if (btn2) btn2.textContent = instance.button2Text || 'Report 2';\n});\n\ninstance._assignEvent('button3-text-changed', () => {\n if (btn3) btn3.textContent = instance.button3Text || 'Report 3';\n});\n\ninstance._assignEvent('button4-text-changed', () => {\n if (btn4) btn4.textContent = instance.button4Text || 'Report 4';\n});\n\ninstance._assignEvent('active-button-changed', () => {\n const newIndex = parseInt(instance.activeButton) || 1;\n setActiveButton(newIndex);\n loadReport(newIndex);\n});\n\ninstance._assignEvent('theme-changed', () => {\n currentTheme = instance.theme || 'default';\n applyTheming(instance, currentTheme);\n});\n}\n\n\n/**\n * @param {BaseScreenViewerAndControl} instance\n */\nexport function disconnectedCallback(instance) {\n // Cleanup if needed\n}\n","properties":{"reportState1":{"type":"ioBrokerObject"},"reportState2":{"type":"ioBrokerObject"},"reportState3":{"type":"ioBrokerObject"},"reportState4":{"type":"ioBrokerObject"},"btn1Output":{"type":"ioBrokerObject"},"btn2Output":{"type":"ioBrokerObject"},"btn3Output":{"type":"ioBrokerObject"},"btn4Output":{"type":"ioBrokerObject"},"button1Text":{"type":"string","default":"SnapShot Report"},"button2Text":{"type":"string","default":"Daily Report"},"button3Text":{"type":"string","default":"Hourly Report"},"button4Text":{"type":"string","default":"Prover Report"},"activeButton":{"type":"string","default":"1"},"theme":{"type":"enum","default":"default","values":["default","dark","light","blue","green","red","purple"]}},"settings":{"width":"auto","height":"60px","visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}
@@ -0,0 +1 @@
1
+ {"html":"<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"100%\" height=\"100%\" viewBox=\"0 0 112.385 112.5\" enable-background=\"new 0 0 112.385 112.5\" xml:space=\"preserve\">\n <g id=\"Group_TankBody\">\n <linearGradient id=\"SVGID_1_\" gradientUnits=\"userSpaceOnUse\" x1=\"9.2798\" y1=\"2.5664\" x2=\"103.1064\" y2=\"2.5664\">\n <stop offset=\"0\" style=\"stop-color:#383838;\"></stop>\n <stop offset=\"0.52\" style=\"stop-color:#A6A6A6;\"></stop>\n <stop offset=\"0.69\" style=\"stop-color:#7B7B7B;\"></stop>\n <stop offset=\"0.9\" style=\"stop-color:#4B4B4B;\"></stop>\n <stop offset=\"1\" style=\"stop-color:#383838;\"></stop>\n </linearGradient>\n <polygon fill=\"url(#SVGID_1_)\" points=\"103.106,4.668 56.193,0 9.28,4.668 56.136,5.132 \"></polygon>\n <linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"-0.1709\" y1=\"60.0615\" x2=\"112.4424\" y2=\"60.0615\">\n <stop offset=\"0\" style=\"stop-color:#383838;\"></stop>\n <stop offset=\"0.5\" style=\"stop-color:#ECECEC;\"></stop>\n <stop offset=\"0.57\" style=\"stop-color:#D9D9D9;\"></stop>\n <stop offset=\"0.71\" style=\"stop-color:#A8A8A8;\"></stop>\n <stop offset=\"0.92\" style=\"stop-color:#5A5A5A;\"></stop>\n <stop offset=\"1\" style=\"stop-color:#383838;\"></stop>\n </linearGradient>\n <path fill=\"url(#SVGID_2_)\" d=\"M112.263,11.592c0,0-16.956-3.97-50.376-3.97c-3.559,0-10.675,0-14.234,0 C13.92,7.623,0,11.688,0,11.688l-0.171,0.04v93.826l0.114,0.341c0,0,0.908,6.605,52.393,6.605c2.388,0,7.163,0,9.551,0 c45.697,0,50.442-6.605,50.442-6.605l0.113-0.341V11.728L112.263,11.592z\"></path>\n <linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"2.7427\" y1=\"22.688\" x2=\"108.5949\" y2=\"-3.7039\">\n <stop offset=\"0\" style=\"stop-color:#383838;\"></stop>\n <stop offset=\"0.52\" style=\"stop-color:#BFBFBF;\"></stop>\n <stop offset=\"0.74\" style=\"stop-color:#7C7C7C;\"></stop>\n <stop offset=\"0.91\" style=\"stop-color:#4B4B4B;\"></stop>\n <stop offset=\"1\" style=\"stop-color:#383838;\"></stop>\n </linearGradient>\n <path fill=\"url(#SVGID_3_)\" d=\"M103.106,4.668C94.256,4.453,59.433,3.389,56.193,3.53C52.953,3.389,17.849,4.46,9.28,4.668 c0,0-7.117,4.867-9.28,7.02c24.067-2.875,52.192-2.346,56.194-2.351c5.915,0.141,40.311-0.274,56.249,2.391 C110.279,9.575,103.106,4.668,103.106,4.668z\"></path>\n </g>\n</svg>\n","style":":host {\n background: transparent;\n}\n\n* {\n box-sizing: border-box;\n}","settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}
@@ -0,0 +1 @@
1
+ {"html":"<div height=\"100%\" width=\"100%\">\n <svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"100%\" height=\"100%\" viewBox=\"0 0 102.744 112.5\" enable-background=\"new 0 0 102.744 112.5\" xml:space=\"preserve\">\n <g id=\"Group_Pipe\">\n <g>\n <defs>\n <rect id=\"SVGID_1_\" x=\"47.403\" y=\"32.435\" width=\"7.485\" height=\"53.979\"></rect>\n </defs>\n <clipPath id=\"SVGID_2_\">\n <use xlink:href=\"#SVGID_1_\" overflow=\"visible\"></use>\n </clipPath>\n <linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"-0.0396\" y1=\"112.459\" x2=\"0.9604\" y2=\"112.459\" gradientTransform=\"matrix(7.4849 0 0 -7.4849 47.6998 901.1704)\">\n <stop id=\"SVGID_3_1_\" offset=\"0\" style=\"stop-color:#5C5D60;\"></stop>\n <stop offset=\"0.45\" style=\"stop-color:#ECECED;\"></stop>\n <stop offset=\"0.57\" style=\"stop-color:#D9DADB;\"></stop>\n <stop offset=\"0.77\" style=\"stop-color:#B1B3B6;\"></stop>\n <stop id=\"SVGID_3_5_\" offset=\"1\" style=\"stop-color:#5C5D60;\"></stop>\n </linearGradient>\n <rect id=\"SVGID_rect_1_\" x=\"47.403\" y=\"32.435\" clip-path=\"url(#SVGID_2_)\" fill=\"url(#SVGID_3_)\" width=\"7.485\" height=\"53.979\"></rect>\n </g>\n </g>\n <g id=\"Group_Port2\">\n <linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"25.917\" y1=\"112.9697\" x2=\"25.917\" y2=\"60.3926\">\n <stop offset=\"0\" style=\"stop-color:#383838;\"></stop>\n <stop offset=\"0.45\" style=\"stop-color:#ECECEC;\"></stop>\n <stop offset=\"0.55\" style=\"stop-color:#D9D9D9;\"></stop>\n <stop offset=\"0.76\" style=\"stop-color:#A8A8A8;\"></stop>\n <stop offset=\"0.77\" style=\"stop-color:#A6A6A6;\"></stop>\n <stop offset=\"1\" style=\"stop-color:#383838;\"></stop>\n </linearGradient>\n <path id=\"SVGID_path_1_\" fill=\"url(#SVGID_4_)\" d=\"M0,60.393l51.834,26.097L0,112.97V60.393\"></path>\n </g>\n <g id=\"Group_Port1\">\n <linearGradient id=\"SVGID_5_\" gradientUnits=\"userSpaceOnUse\" x1=\"76.8262\" y1=\"112.9697\" x2=\"76.8262\" y2=\"60.3926\">\n <stop offset=\"0\" style=\"stop-color:#383838;\"></stop>\n <stop offset=\"0.45\" style=\"stop-color:#ECECEC;\"></stop>\n <stop offset=\"0.55\" style=\"stop-color:#D9D9D9;\"></stop>\n <stop offset=\"0.76\" style=\"stop-color:#A8A8A8;\"></stop>\n <stop offset=\"0.77\" style=\"stop-color:#A6A6A6;\"></stop>\n <stop offset=\"1\" style=\"stop-color:#383838;\"></stop>\n </linearGradient>\n <path id=\"SVGID_path_2_\" fill=\"url(#SVGID_5_)\" d=\"M102.744,60.393L50.909,86.489l51.835,26.48V60.393\"></path>\n </g>\n <g id=\"Group_Box\">\n <g>\n <defs>\n <path id=\"SVGID_6_\" d=\"M91.622,16.348l1.239,16.314l4.545,3.386c-0.073-1.252-0.01-30.573-0.01-32.629l-3.725-3.417 L91.622,16.348z\"></path>\n </defs>\n <clipPath id=\"SVGID_7_\">\n <use xlink:href=\"#SVGID_6_\" overflow=\"visible\"></use>\n </clipPath>\n <linearGradient id=\"SVGID_8_\" gradientUnits=\"userSpaceOnUse\" x1=\"-0.0288\" y1=\"112.4717\" x2=\"0.9712\" y2=\"112.4717\" gradientTransform=\"matrix(5.7842 0 0 5.7842 91.7858 -632.5346)\">\n <stop offset=\"0\" style=\"stop-color:#D1D3D4;\"></stop>\n <stop offset=\"0.36\" style=\"stop-color:#BEC0C2;\"></stop>\n <stop offset=\"1\" style=\"stop-color:#939598;\"></stop>\n </linearGradient>\n <rect id=\"SVGID_rect_2_\" x=\"91.622\" y=\"0.002\" clip-path=\"url(#SVGID_7_)\" fill=\"url(#SVGID_8_)\" width=\"5.784\" height=\"36.046\"></rect>\n </g>\n <g>\n <defs>\n <path id=\"SVGID_9_\" d=\"M8.846,32.662l3.75,3.192c3.174-0.033,83.323,0.118,84.81,0.195l-3.734-3.965l-42.3-1.56L8.846,32.662z\"></path>\n </defs>\n <clipPath id=\"SVGID_10_\">\n <use xlink:href=\"#SVGID_9_\" overflow=\"visible\"></use>\n </clipPath>\n <linearGradient id=\"SVGID_11_\" gradientUnits=\"userSpaceOnUse\" x1=\"0.0205\" y1=\"112.5215\" x2=\"1.021\" y2=\"112.5215\" gradientTransform=\"matrix(0 5.5244 5.5244 0 -568.4863 30.4067)\">\n <stop offset=\"0\" style=\"stop-color:#D1D3D4;\"></stop>\n <stop offset=\"0.28\" style=\"stop-color:#BEC0C2;\"></stop>\n <stop offset=\"0.85\" style=\"stop-color:#8E9092;\"></stop>\n <stop offset=\"1\" style=\"stop-color:#808285;\"></stop>\n </linearGradient>\n <rect id=\"SVGID_rect_3_\" x=\"8.846\" y=\"30.524\" clip-path=\"url(#SVGID_10_)\" fill=\"url(#SVGID_11_)\" width=\"88.56\" height=\"5.525\"></rect>\n </g>\n <g>\n <defs>\n <rect id=\"SVGID_12_\" x=\"8.846\" y=\"0.032\" width=\"84.826\" height=\"32.63\"></rect>\n </defs>\n <clipPath id=\"SVGID_13_\">\n <use xlink:href=\"#SVGID_12_\" overflow=\"visible\"></use>\n </clipPath>\n <linearGradient id=\"SVGID_14_\" gradientUnits=\"userSpaceOnUse\" x1=\"-0.002\" y1=\"112.498\" x2=\"0.998\" y2=\"112.498\" gradientTransform=\"matrix(-84.8257 0 0 84.8257 93.5221 -9526.4209)\">\n <stop offset=\"0\" style=\"stop-color:#E6E7E8;\"></stop>\n <stop offset=\"0.01\" style=\"stop-color:#E6E7E8;\"></stop>\n <stop offset=\"0.5\" style=\"stop-color:#D3D5D6;\"></stop>\n <stop offset=\"1\" style=\"stop-color:#BCBEC0;\"></stop>\n </linearGradient>\n <rect id=\"SVGID_rect_4_\" x=\"8.846\" y=\"0.032\" clip-path=\"url(#SVGID_13_)\" fill=\"url(#SVGID_14_)\" width=\"84.826\" height=\"32.63\"></rect>\n </g>\n </g>\n </svg>\n</div>\n","style":":host {\n background-color: transparent;\n}\n\nbody{\n background-color: transparent; \n}\n* {\n box-sizing: border-box;\n}","script":"\n/*function setDefault(instance)\n{\n let lineargradient1 = instance._getDomElement('SVGID_3_');\n lineargradient1.children[0].style['stop-color']= \"#5C5D60\";\n lineargradient1.children[4].style['stop-color']= \"#5C5D60\";\n\n let lineargradient2 = instance._getDomElement('SVGID_4_');\n lineargradient2.children[0].style['stop-color']= \"#383838\";\n lineargradient2.children[4].style['stop-color']= \"#383838\";\n\n let lineargradient3 = instance._getDomElement('SVGID_5_');\n lineargradient3.children[0].style['stop-color']= \"#383838\";\n lineargradient3.children[4].style['stop-color']= \"#383838\";\n\n let lineargradient4 = instance._getDomElement('SVGID_8_');\n lineargradient4.children[2].style['stop-color']= \"#939598\";\n\n let lineargradient5 = instance._getDomElement('SVGID_11_');\n lineargradient5.children[2].style['stop-color']= \"#8E9092\";\n lineargradient5.children[3].style['stop-color']= \"#808285\";\n\n let lineargradient6 = instance._getDomElement('SVGID_14_');\n lineargradient6.children[2].style['stop-color']= \"#D3D5D6\";\n lineargradient6.children[3].style['stop-color']= \"#BCBEC0\";\n}\n\nfunction setGreen(instance)\n{\n let lineargradient1 = instance._getDomElement('SVGID_3_');\n lineargradient1.children[0].style['stop-color']= \"green\";\n lineargradient1.children[4].style['stop-color']= \"green\";\n\n let lineargradient2 = instance._getDomElement('SVGID_4_');\n lineargradient2.children[0].style['stop-color']= \"green\";\n lineargradient2.children[4].style['stop-color']= \"green\";\n\n let lineargradient3 = instance._getDomElement('SVGID_5_');\n lineargradient3.children[0].style['stop-color']= \"green\";\n lineargradient3.children[4].style['stop-color']= \"green\";\n\n let lineargradient4 = instance._getDomElement('SVGID_8_');\n lineargradient4.children[2].style['stop-color']= \"green\";\n\n let lineargradient5 = instance._getDomElement('SVGID_11_');\n lineargradient5.children[2].style['stop-color']= \"green\";\n lineargradient5.children[3].style['stop-color']= \"green\";\n\n let lineargradient6 = instance._getDomElement('SVGID_14_');\n lineargradient6.children[2].style['stop-color']= \"green\";\n lineargradient6.children[3].style['stop-color']= \"green\";\n}\n*/\n\nfunction fillgreen(instance)\n{\n let fillurl1 = instance._getDomElement('SVGID_rect_1_');\n fillurl1.style['fill']= \"#00ff00\";\n let fillurl2 = instance._getDomElement('SVGID_path_1_');\n fillurl2.style['fill']= \"#00ff00\";\n let fillurl3 = instance._getDomElement('SVGID_path_2_');\n fillurl3.style['fill']= \"#00ff00\";\n let fillurl4 = instance._getDomElement('SVGID_rect_2_');\n fillurl4.style['fill']= \"#00ff00\";\n let fillurl5 = instance._getDomElement('SVGID_rect_3_');\n fillurl5.style['fill']= \"#00ff00\";\n let fillurl6 = instance._getDomElement('SVGID_rect_4_');\n fillurl6.style['fill']= \"#00ff00\";\n}\n\n\nfunction fillred(instance)\n{\n let fillurl1 = instance._getDomElement('SVGID_rect_1_');\n fillurl1.style['fill']= \"red\";\n let fillurl2 = instance._getDomElement('SVGID_path_1_');\n fillurl2.style['fill']= \"red\";\n let fillurl3 = instance._getDomElement('SVGID_path_2_');\n fillurl3.style['fill']= \"red\";\n let fillurl4 = instance._getDomElement('SVGID_rect_2_');\n fillurl4.style['fill']= \"red\";\n let fillurl5 = instance._getDomElement('SVGID_rect_3_');\n fillurl5.style['fill']= \"red\";\n let fillurl6 = instance._getDomElement('SVGID_rect_4_');\n fillurl6.style['fill']= \"red\";\n}\n\n\nfunction fillyellow(instance)\n{\n let fillurl1 = instance._getDomElement('SVGID_rect_1_');\n fillurl1.style['fill']= \"yellow\";\n let fillurl2 = instance._getDomElement('SVGID_path_1_');\n fillurl2.style['fill']= \"yellow\";\n let fillurl3 = instance._getDomElement('SVGID_path_2_');\n fillurl3.style['fill']= \"yellow\";\n let fillurl4 = instance._getDomElement('SVGID_rect_2_');\n fillurl4.style['fill']= \"yellow\";\n let fillurl5 = instance._getDomElement('SVGID_rect_3_');\n fillurl5.style['fill']= \"yellow\";\n let fillurl6 = instance._getDomElement('SVGID_rect_4_');\n fillurl6.style['fill']= \"yellow\";\n}\n\nfunction fillorange(instance)\n{\n let fillurl1 = instance._getDomElement('SVGID_rect_1_');\n fillurl1.style['fill']= \"orange\";\n let fillurl2 = instance._getDomElement('SVGID_path_1_');\n fillurl2.style['fill']= \"orange\";\n let fillurl3 = instance._getDomElement('SVGID_path_2_');\n fillurl3.style['fill']= \"orange\";\n let fillurl4 = instance._getDomElement('SVGID_rect_2_');\n fillurl4.style['fill']= \"orange\";\n let fillurl5 = instance._getDomElement('SVGID_rect_3_');\n fillurl5.style['fill']= \"orange\";\n let fillurl6 = instance._getDomElement('SVGID_rect_4_');\n fillurl6.style['fill']= \"orange\";\n}\n\nfunction filldefault(instance)\n{\n let fillurl1 = instance._getDomElement('SVGID_rect_1_');\n fillurl1.style['fill']= \"url(#SVGID_3_)\";\n let fillurl2 = instance._getDomElement('SVGID_path_1_');\n fillurl2.style['fill']= \"url(#SVGID_4_)\";\n let fillurl3 = instance._getDomElement('SVGID_path_2_');\n fillurl3.style['fill']= \"url(#SVGID_5_)\";\n let fillurl4 = instance._getDomElement('SVGID_rect_2_');\n fillurl4.style['fill']= \"url(#SVGID_8_)\";\n let fillurl5 = instance._getDomElement('SVGID_rect_3_');\n fillurl5.style['fill']= \"url(#SVGID_11_)\";\n let fillurl6 = instance._getDomElement('SVGID_rect_4_');\n fillurl6.style['fill']= \"url(#SVGID_14_)\";\n}\n let valveid;\n // this.add(colormy,\"red\");\n\nexport async function init(instance) {\n \n instance._internals = instance.attachInternals();\n //instance.bird='chickens';\n //let ghg = this;//'IobrokerWebuiCustomControl'Valve\n checkstartup(instance);\n instance._assignEvent('colorstopped-changed', (ev) => checkstartup(instance));\n instance._assignEvent('coloropened-changed', (ev) => checkstartup(instance) );\n instance._assignEvent('colorclosed-changed', (ev) => checkstartup(instance));\n instance._assignEvent('colorfault-changed', (ev) => checkstartup(instance));\n /*let vbv = instance.getAttribute(\"inputID\");\n instance.setAttribute(\"Kamran\", 123456);\n let vbv1 = instance.getAttribute(\"Kamran\");*/\n //instance._parseAttributesToProperties();\n //instance.push({ name: 'hello', type: 'string' });\n //instance.properties.push({ name: 'hello', type: 'string' });\n //instance._bindingsRefresh();\n\n}\n\nasync function checkstartup(instance)\n{\n\nlet result = 0;\nlet valveid = await window.IOB.getState(\"local_test\").then(function(result) {\n return result;\n});\n\n\n\n let colorstoppedNodeValue =instance.attributes[\"bind-prop:colorstopped\"].nodeValue;\n let coloropenedNodeValue =instance.attributes[\"bind-prop:coloropened\"].nodeValue;\n let colorclosedNodeValue =instance.attributes[\"bind-prop:colorclosed\"].nodeValue;\n let colorfaultNodeValue =instance.attributes[\"bind-prop:colorfault\"].nodeValue;\n let st = colorstoppedNodeValue.replace(/\\s?\\{[^}]+\\}/g, valveid.val);\n let op = coloropenedNodeValue.replace(/\\s?\\{[^}]+\\}/g, valveid.val);\n let cl = colorclosedNodeValue.replace(/\\s?\\{[^}]+\\}/g, valveid.val);\n let fl = colorfaultNodeValue.replace(/\\s?\\{[^}]+\\}/g, valveid.val);\n \n let stopped = await window.IOB.getState(st);\n let opened = await window.IOB.getState(op);\n let closed = await window.IOB.getState(cl);\n let fault = await window.IOB.getState(fl);\n\n /*let stopped = instance._initialPropertyCache.get(\"colorstopped\");\n let opened = instance._initialPropertyCache.get(\"coloropened\");\n let closed = instance._initialPropertyCache.get(\"colorclosed\");\n let fault = instance._initialPropertyCache.get(\"colorfault\");*/\n if(stopped.val==true)\n {\n fillyellow(instance);\n }\n if(opened.val==true)\n {\n fillgreen(instance);\n }\n if(closed.val==true)\n {\n fillred(instance);\n }\n\n if(fault.val==true)\n {\n fillorange(instance);\n }\n \n if(opened.val==true && closed.val==true)\n {\n fillorange(instance);\n }\n\n if(opened.val==true && closed.val==true && stopped.val==true)\n {\n fillorange(instance);\n }\n if(opened.val==false && closed.val==false && stopped.val==false && fault.val==false)\n {\n filldefault(instance);\n }\n\n\n}\n\nfunction check(instance)\n{\n let stopped = instance.colorstopped;\n let opened = instance.coloropened;\n let closed = instance.colorclosed;\n let fault = instance.colorfault;\n if(stopped==true)\n {\n fillyellow(instance);\n }\n if(opened==true)\n {\n fillgreen(instance);\n }\n if(closed==true)\n {\n fillred(instance);\n }\n\n if(fault==true)\n {\n fillorange(instance);\n }\n \n if(opened==true && closed==true)\n {\n fillorange(instance);\n }\n\n if(opened==true && closed==true && stopped==true)\n {\n fillorange(instance);\n }\n if(opened==false && closed==false && stopped==false && fault==false)\n {\n filldefault(instance);\n }\n\n\n}\nexport function connectedCallback(instance) {\n debugger;\n //instance._bindingsRefresh();\n //updateLevel(instance.level, instance);\n}\n\n/**\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function disconnectedCallback(instance) {\n}\n\nexport function attributeChangedCallback (name, oldValue, newValue) {\n \n\tconsole.log('attribute changed', name, oldValue, newValue, this);\n}","properties":{"colorstopped":{"type":"number"},"coloropened":{"type":"number"},"colorclosed":{"type":"number"},"colorfault":{"type":"number"}},"settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}
@@ -0,0 +1 @@
1
+ {"html":"<node-projects-chart-js bind-prop:data='{\"signal\":\"?signal\",\"expression\":\"__0.map(x =&gt; [x.val, x.ts])\",\"historic\":{\"limit\":30,\"returnNewestEntries\":true,\"instance\":\"history.0\",\"reloadInterval\":250}}' border-color=\"[[this.color]]\" background-color=\"[[this.fillColor]]\" type=\"[[this.type]]\" style=\"width:calc(100% + 4px);height:70%;position:absolute;left:-2px;bottom:-3px;\"></node-projects-chart-js>\n<div style=\"position:absolute;left:5px;top:3px;\">[[this?.name ?? this?.signal]]</div>\n<div bind-content:text=\"?signal;??unit;?$signal;__0 + '' + (__1 ? __1 : (__2?.common?.unit ?? ''))\" style=\"position:absolute;left:12px;top:26px;font-size:16px;font-weight:800;\"></div>\n","style":":host {\n border: solid 1px gray;\n box-shadow: 10px 5px 5px lightgray;\n user-select: none;\n cursor: pointer;\n overflow: hidden;\n}\n\n* {\n box-sizing: border-box;\n}\n\n:host(:hover) {background-color: lightgray}\n\n:host(:active) {\n background-color: lightgray;\n transform: translateY(2px) translateX(1px);\n box-shadow: none;\n}","properties":{"signal":{"type":"signal"},"name":{"type":"string"},"unit":{"type":"string"},"color":{"type":"color"},"fillColor":{"type":"color"},"type":{"type":"enum","default":"line","values":["line","bar"]}},"settings":{}}
@@ -0,0 +1 @@
1
+ {"html":"<div id=\"monitor\" class=\"connection-monitor\"><div class=\"status-indicator\">\n <svg class=\"status-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\">\n <circle id=\"circle\" cx=\"12\" cy=\"12\" r=\"9\"></circle>\n <path id=\"check\" d=\"M9 12l2 2 4-4\" display=\"none\"></path>\n <path id=\"cross\" d=\"M9 9l6 6 M15 9l-6 6\" display=\"none\"></path>\n </svg>\n<span id=\"statusText\" class=\"status-text\"></span>\n</div><div class=\"pulse-ring\"></div></div>\n<!-- Popup Modal -->\n<div id=\"connectionPopup\" class=\"connection-popup\" style=\"display:none;\">\n <div class=\"popup-overlay\"></div>\n <div class=\"popup-content\">\n <div class=\"popup-icon-wrapper\">\n <svg id=\"popupIcon\" class=\"popup-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"9\"></circle>\n <path id=\"popupCheck\" d=\"M9 12l2 2 4-4\" display=\"none\"></path>\n <path id=\"popupCross\" d=\"M9 9l6 6 M15 9l-6 6\" display=\"none\"></path>\n </svg>\n<div class=\"popup-pulse-ring\"></div>\n </div>\n <h2 id=\"popupTitle\" class=\"popup-title\"></h2>\n <p id=\"popupMessage\" class=\"popup-message\"></p>\n </div>\n</div>\n","style":":host {\n display: inline-block;\n font-family: system-ui, -apple-system, sans-serif;\n}\n\n* {\n box-sizing: border-box;\n}\n\n.connection-monitor {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 10px 16px;\n border-radius: 10px;\n background: rgba(255, 255, 255, 0.95);\n box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);\n backdrop-filter: blur(8px);\n transition: all 0.3s ease;\n border: 2px solid transparent;\n}\n\n.status-indicator {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.status-icon {\n width: 24px;\n height: 24px;\n transition: all 0.3s ease;\n}\n\n.status-text {\n font-size: 14px;\n font-weight: 600;\n white-space: nowrap;\n}\n\n/* Bağlı vəziyyət (Yaşıl) */\n.connection-monitor.connected {\n background: rgba(16, 185, 129, 0.1);\n border-color: #10b981;\n}\n\n.connection-monitor.connected .status-icon {\n color: #10b981;\n}\n\n.connection-monitor.connected .status-text {\n color: #10b981;\n}\n\n.connection-monitor.connected .pulse-ring {\n animation: pulse-green 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n/* Kəsilmiş vəziyyət (Qırmızı) */\n.connection-monitor.disconnected {\n background: rgba(239, 68, 68, 0.1);\n border-color: #ef4444;\n animation: shake 0.5s ease;\n}\n\n.connection-monitor.disconnected .status-icon {\n color: #ef4444;\n}\n\n.connection-monitor.disconnected .status-text {\n color: #ef4444;\n}\n\n.connection-monitor.disconnected .pulse-ring {\n animation: pulse-red 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n/* Yoxlanma vəziyyəti (Sarı) */\n.connection-monitor.checking {\n background: rgba(251, 191, 36, 0.1);\n border-color: #fbbf24;\n}\n\n.connection-monitor.checking .status-icon {\n color: #fbbf24;\n animation: rotate 1.5s linear infinite;\n}\n\n.connection-monitor.checking .status-text {\n color: #fbbf24;\n}\n\n/* Pulse halqası */\n.pulse-ring {\n position: absolute;\n top: 50%;\n left: 15px;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n pointer-events: none;\n transform: translate(-50%, -50%);\n}\n\n/* ==================== POPUP STYLES ==================== */\n\n.connection-popup {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 999999;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: fadeIn 0.3s ease;\n}\n\n.popup-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n backdrop-filter: blur(8px);\n}\n\n.popup-content {\n position: relative;\n background: white;\n border-radius: 24px;\n padding: 48px 64px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);\n text-align: center;\n max-width: 500px;\n width: 90%;\n animation: slideUp 0.4s ease;\n}\n\n.popup-icon-wrapper {\n position: relative;\n width: 120px;\n height: 120px;\n margin: 0 auto 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.popup-icon {\n width: 120px;\n height: 120px;\n transition: all 0.3s ease;\n}\n\n.popup-pulse-ring {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 120px;\n height: 120px;\n border-radius: 50%;\n pointer-events: none;\n transform: translate(-50%, -50%);\n}\n\n.popup-title {\n font-size: 32px;\n font-weight: 700;\n margin: 0 0 16px 0;\n color: #1f2937;\n}\n\n.popup-message {\n font-size: 20px;\n font-weight: 500;\n margin: 0;\n color: #6b7280;\n}\n\n/* Popup Connected State */\n.connection-popup.connected .popup-icon {\n color: #10b981;\n}\n\n.connection-popup.connected .popup-pulse-ring {\n animation: pulse-green-big 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n.connection-popup.connected .popup-title {\n color: #10b981;\n}\n\n.connection-popup.connected .popup-message {\n color: #059669;\n}\n\n/* Popup Disconnected State */\n.connection-popup.disconnected .popup-icon {\n color: #ef4444;\n animation: shake-big 0.5s ease;\n}\n\n.connection-popup.disconnected .popup-pulse-ring {\n animation: pulse-red-big 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n.connection-popup.disconnected .popup-title {\n color: #ef4444;\n}\n\n.connection-popup.disconnected .popup-message {\n color: #dc2626;\n}\n\n/* Popup Checking State */\n.connection-popup.checking .popup-icon {\n color: #fbbf24;\n animation: rotate 1.5s linear infinite;\n}\n\n.connection-popup.checking .popup-title {\n color: #fbbf24;\n}\n\n.connection-popup.checking .popup-message {\n color: #f59e0b;\n}\n\n/* Animasiyalar */\n@keyframes pulse-green {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7);\n }\n 50% {\n box-shadow: 0 0 0 12px rgba(16, 185, 129, 0);\n }\n}\n\n@keyframes pulse-red {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);\n }\n 50% {\n box-shadow: 0 0 0 12px rgba(239, 68, 68, 0);\n }\n}\n\n@keyframes pulse-green-big {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7);\n }\n 50% {\n box-shadow: 0 0 0 30px rgba(16, 185, 129, 0);\n }\n}\n\n@keyframes pulse-red-big {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);\n }\n 50% {\n box-shadow: 0 0 0 30px rgba(239, 68, 68, 0);\n }\n}\n\n@keyframes shake {\n 0%, 100% { transform: translateX(0); }\n 25% { transform: translateX(-4px); }\n 75% { transform: translateX(4px); }\n}\n\n@keyframes shake-big {\n 0%, 100% { transform: translateX(0) rotate(0deg); }\n 25% { transform: translateX(-10px) rotate(-5deg); }\n 75% { transform: translateX(10px) rotate(5deg); }\n}\n\n@keyframes rotate {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(50px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Qaranlıq tema */\n@media (prefers-color-scheme: dark) {\n .connection-monitor {\n background: rgba(31, 41, 55, 0.95);\n box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4);\n }\n \n .popup-content {\n background: #1f2937;\n }\n \n .popup-title {\n color: #f3f4f6;\n }\n \n .popup-message {\n color: #d1d5db;\n }\n}\n\n/* Kiçik ölçü */\n.connection-monitor.size-small {\n padding: 6px 12px;\n font-size: 12px;\n}\n\n.connection-monitor.size-small .status-icon {\n width: 18px;\n height: 18px;\n}\n\n/* Böyük ölçü */\n.connection-monitor.size-large {\n padding: 14px 20px;\n font-size: 16px;\n}\n\n.connection-monitor.size-large .status-icon {\n width: 32px;\n height: 32px;\n}","script":"let checkInterval = null;\nlet isConnected = false;\nlet onConnectHandler = null;\nlet onDisconnectHandler = null;\nlet onReconnectHandler = null;\nlet onErrorHandler = null;\nlet popupTimeout = null;\nlet currentStatus = 'checking';\nlet _langChangedHandler = null;\n\n/**\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function connectedCallback(instance) {\n const monitorEl = instance._getDomElement('monitor');\n const statusTextEl = instance._getDomElement('statusText');\n const checkIcon = instance._getDomElement('check');\n const crossIcon = instance._getDomElement('cross');\n const circleIcon = instance._getDomElement('circle');\n \n // Popup elementləri\n const popupEl = instance._getDomElement('connectionPopup');\n const popupTitleEl = instance._getDomElement('popupTitle');\n const popupMessageEl = instance._getDomElement('popupMessage');\n const popupCheckIcon = instance._getDomElement('popupCheck');\n const popupCrossIcon = instance._getDomElement('popupCross');\n \n // Ölçü classı əlavə et\n if (instance.size) {\n monitorEl.classList.add('size-' + instance.size);\n }\n \n // IOB connection hazır olana qədər gözlə\n const waitForConnection = setInterval(() => {\n if (window.IOB && window.IOB.connection) {\n clearInterval(waitForConnection);\n setupMonitoring();\n }\n }, 100);\n \n function setupMonitoring() {\n const connection = window.IOB.connection;\n \n console.log('[ConnectionMonitor] 🚀 Monitoring başladı');\n console.log('[ConnectionMonitor] Connection obyekti:', connection);\n console.log('[ConnectionMonitor] İlkin status:', connection.connected);\n \n // İlk status yoxlaması\n checkConnection();\n \n // Dövri yoxlama\n const interval = instance.checkInterval || 5000;\n checkInterval = setInterval(() => {\n checkConnection();\n }, interval);\n \n // Socket event-lərinə abunə ol\n if (connection._socket) {\n const socket = connection._socket;\n \n onConnectHandler = function() {\n console.log('[ConnectionMonitor] ✅ Socket CONNECT event');\n handleConnectionSuccess();\n };\n \n onReconnectHandler = function() {\n console.log('[ConnectionMonitor] 🔄 Socket RECONNECT event');\n handleConnectionSuccess();\n };\n \n onDisconnectHandler = function(reason) {\n console.log('[ConnectionMonitor] ❌ Socket DISCONNECT event:', reason);\n handleConnectionFailure();\n };\n \n onErrorHandler = function(error) {\n console.error('[ConnectionMonitor] ⚠️ Socket ERROR event:', error);\n handleConnectionFailure();\n };\n \n socket.on('connect', onConnectHandler);\n socket.on('reconnect', onReconnectHandler);\n socket.on('disconnect', onDisconnectHandler);\n socket.on('connect_error', onErrorHandler);\n socket.on('error', onErrorHandler);\n }\n }\n \n function checkConnection() {\n if (!window.IOB || !window.IOB.connection) {\n handleConnectionFailure();\n return;\n }\n \n const connection = window.IOB.connection;\n \n // Connection obyektinin connected property-sini yoxla\n if (connection.connected === true) {\n handleConnectionSuccess();\n } \n // Socket-in connected statusunu yoxla\n else if (connection._socket && connection._socket.connected === true) {\n handleConnectionSuccess();\n }\n // Navigator.onLine yoxla (fallback)\n else if (navigator.onLine === false) {\n handleConnectionFailure();\n }\n else {\n // Əlaqə statusu qeyri-müəyyəndirsə, yoxlanılır statusu göstər\n updateStatus('checking');\n }\n }\n \n function handleConnectionSuccess() {\n const wasDisconnected = !isConnected;\n \n if (wasDisconnected) {\n console.log('[ConnectionMonitor] 💚 Server əlaqəsi BƏRPA OLUNDU');\n isConnected = true;\n \n // Əlaqə bərpa olundu popup-u göstər (3 saniyə)\n showPopup('connected');\n if (popupTimeout) clearTimeout(popupTimeout);\n popupTimeout = setTimeout(() => {\n hidePopup();\n }, 3000);\n }\n \n updateStatus('connected');\n }\n \n function handleConnectionFailure() {\n if (isConnected) {\n console.error('[ConnectionMonitor] 💔 Server əlaqəsi KƏSİLDİ');\n isConnected = false;\n \n // Əlaqə kəsildi popup-u göstər (daimi)\n showPopup('disconnected');\n }\n \n updateStatus('disconnected');\n }\n \n function updateStatus(status) {\n currentStatus = status;\n monitorEl.classList.remove('connected', 'disconnected', 'checking');\n monitorEl.classList.add(status);\n \n checkIcon.style.display = 'none';\n crossIcon.style.display = 'none';\n circleIcon.style.display = 'block';\n \n if (status === 'connected') {\n statusTextEl.textContent = t('WebuiConnectionMonitor.connectedtext');\n checkIcon.style.display = 'block';\n circleIcon.style.display = 'none';\n } else if (status === 'disconnected') {\n statusTextEl.textContent = t('WebuiConnectionMonitor.disconnectedtext');\n crossIcon.style.display = 'block';\n circleIcon.style.display = 'none';\n } else if (status === 'checking') {\n statusTextEl.textContent = t('WebuiConnectionMonitor.checkingtext');\n }\n \n if (instance.showText === false) {\n statusTextEl.style.display = 'none';\n } else {\n statusTextEl.style.display = 'inline';\n }\n }\n \n function showPopup(status) {\n popupEl.style.display = 'flex';\n popupEl.classList.remove('connected', 'disconnected', 'checking');\n popupEl.classList.add(status);\n \n // İkonları yenilə\n popupCheckIcon.style.display = 'none';\n popupCrossIcon.style.display = 'none';\n \n if (status === 'connected') {\n popupTitleEl.textContent = t('WebuiConnectionMonitor.connectedtext');\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessageconnected');\n popupCheckIcon.style.display = 'block';\n } else if (status === 'disconnected') {\n popupTitleEl.textContent = t('WebuiConnectionMonitor.disconnectedtext');\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessagedisconnected');\n popupCrossIcon.style.display = 'block';\n } else if (status === 'checking') {\n popupTitleEl.textContent = t('WebuiConnectionMonitor.checkingtext');\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessagechecking');\n }\n }\n \n function hidePopup() {\n popupEl.style.display = 'none';\n if (popupTimeout) {\n clearTimeout(popupTimeout);\n popupTimeout = null;\n }\n }\n\n _langChangedHandler = () => updateStatus(currentStatus);\n window.addEventListener('languageChanged', _langChangedHandler);\n}\n\n/**\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function disconnectedCallback(instance) {\n if (checkInterval) {\n clearInterval(checkInterval);\n checkInterval = null;\n }\n \n if (_langChangedHandler) {\n window.removeEventListener('languageChanged', _langChangedHandler);\n _langChangedHandler = null;\n }\n\n if (popupTimeout) {\n clearTimeout(popupTimeout);\n popupTimeout = null;\n }\n \n // Event listener-ləri təmizlə\n if (window.IOB && window.IOB.connection && window.IOB.connection._socket) {\n const socket = window.IOB.connection._socket;\n \n if (onConnectHandler) socket.off('connect', onConnectHandler);\n if (onReconnectHandler) socket.off('reconnect', onReconnectHandler);\n if (onDisconnectHandler) socket.off('disconnect', onDisconnectHandler);\n if (onErrorHandler) {\n socket.off('connect_error', onErrorHandler);\n socket.off('error', onErrorHandler);\n }\n }\n}","properties":{"checkInterval":{"type":"number","default":"5000"},"showText":{"type":"boolean","default":"false"},"size":{"type":"enum","default":"medium","values":["small","medium","large"]}},"settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}
@@ -0,0 +1 @@
1
+ {"html":"<div id=\"editor-container\" style=\"width:100%;height:100%;position:relative;overflow:hidden;\"><div id=\"viewport\" style=\"width:100%;height:100%;background:#333;\"></div><div id=\"status\" style=\"position:absolute;bottom:0;left:0;padding:8px;color:#0f0;font-family:monospace;font-size:11px;background:rgba(0,0,0,0.5);\">3D Editor Ready</div></div>","style":":host {\n display: block;\n width: 100%;\n height: 100%;\n background: #333;\n}\n\n#viewport {\n width: 100%;\n height: 100%;\n}\n\n#status {\n font-size: 12px;\n color: #0f0;\n z-index: 10;\n}","script":"let scene, camera, renderer, controls, gltfLoader, raycaster, mouse;\nlet loadedModel = null;\nlet selectedObject = null;\nlet objectAnimations = [];\n\n/**\n * Load Three.js from mywebui.0.widgets packages\n */\nasync function loadThreeJs() {\n try {\n const THREE = await import('/mywebui.0.widgets/node_modules/three/build/three.module.js');\n const { OrbitControls } = await import('/mywebui.0.widgets/node_modules/three/examples/jsm/controls/OrbitControls.js');\n const { GLTFLoader } = await import('/mywebui.0.widgets/node_modules/three/examples/jsm/loaders/GLTFLoader.js');\n \n return { THREE, OrbitControls, GLTFLoader };\n } catch (error) {\n console.error('❌ Three.js import failed:', error);\n throw error;\n }\n}\n\n/**\n * Extract 3D properties from object\n */\nfunction extractObjectProperties(obj) {\n const props = {\n name: obj.name || 'Object',\n type: obj.type,\n visible: obj.visible,\n position: {\n x: obj.position.x.toFixed(3),\n y: obj.position.y.toFixed(3),\n z: obj.position.z.toFixed(3)\n },\n rotation: {\n x: (obj.rotation.x * 180 / Math.PI).toFixed(1),\n y: (obj.rotation.y * 180 / Math.PI).toFixed(1),\n z: (obj.rotation.z * 180 / Math.PI).toFixed(1)\n },\n scale: {\n x: obj.scale.x.toFixed(3),\n y: obj.scale.y.toFixed(3),\n z: obj.scale.z.toFixed(3)\n }\n };\n \n if (obj.material) {\n props.material = {\n type: obj.material.type,\n color: obj.material.color ? '#' + obj.material.color.getHexString() : 'N/A',\n emissive: obj.material.emissive ? '#' + obj.material.emissive.getHexString() : 'N/A',\n metalness: obj.material.metalness?.toFixed(2) || 'N/A',\n roughness: obj.material.roughness?.toFixed(2) || 'N/A'\n };\n }\n \n return props;\n}\n\n/**\n * Select object and update property panel\n */\nfunction selectObject(obj, instance, THREE) {\n if (selectedObject && selectedObject.userData.originalMaterial) {\n selectedObject.material = selectedObject.userData.originalMaterial;\n }\n \n selectedObject = obj;\n \n if (!obj.userData.originalMaterial && obj.material) {\n obj.userData.originalMaterial = obj.material;\n const highlightMat = new THREE.MeshBasicMaterial({ \n color: 0xffff00, \n wireframe: false,\n transparent: true,\n opacity: 0.8\n });\n obj.material = highlightMat;\n }\n \n const props = extractObjectProperties(obj);\n \n const event = new CustomEvent('object-selected', {\n detail: {\n object: obj,\n properties: props,\n animations: objectAnimations\n }\n });\n instance.dispatchEvent(event);\n \n console.log('✅ Object selected:', obj.name, props);\n}\n\n/**\n * Handle mouse click - raycasting\n */\nfunction onMouseClick(event, viewport, instance, THREE) {\n if (!scene || !camera) return;\n \n const rect = viewport.getBoundingClientRect();\n mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;\n mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;\n \n raycaster.setFromCamera(mouse, camera);\n const intersects = raycaster.intersectObjects(scene.children, true);\n \n if (intersects.length > 0) {\n selectObject(intersects[0].object, instance, THREE);\n }\n}\n\n/**\n * Load GLB model\n */\nfunction loadGLBModel(url, statusEl, instance) {\n if (!url || !gltfLoader) return;\n \n statusEl.textContent = '⏳ Loading model...';\n \n gltfLoader.load(\n url,\n (gltf) => {\n if (loadedModel) {\n scene.remove(loadedModel);\n }\n \n loadedModel = gltf.scene;\n scene.add(loadedModel);\n \n objectAnimations = gltf.animations || [];\n \n statusEl.textContent = `✅ Model loaded (${objectAnimations.length} animations)`;\n console.log('✅ GLB model loaded:', url);\n },\n (progress) => {\n const percent = Math.round((progress.loaded / progress.total) * 100);\n statusEl.textContent = `⏳ Loading ${percent}%`;\n },\n (error) => {\n console.error('❌ Model load failed:', error);\n statusEl.textContent = '❌ Model load failed';\n }\n );\n}\n\n/**\n * Initialize 3D Editor\n * @param {BaseScreenViewerAndControl} instance\n */\nexport async function connectedCallback(instance) {\n const viewport = instance._getDomElement('viewport');\n const statusEl = instance._getDomElement('status');\n \n try {\n statusEl.textContent = '⏳ Loading Three.js...';\n \n const { THREE, OrbitControls, GLTFLoader } = await loadThreeJs();\n \n statusEl.textContent = '⏳ Initializing...';\n \n raycaster = new THREE.Raycaster();\n mouse = new THREE.Vector2();\n gltfLoader = new GLTFLoader();\n \n scene = new THREE.Scene();\n scene.background = new THREE.Color(0x333333);\n \n const width = viewport.clientWidth;\n const height = viewport.clientHeight;\n camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);\n camera.position.set(10, 10, 10);\n \n renderer = new THREE.WebGLRenderer({ antialias: true });\n renderer.setSize(width, height);\n renderer.shadowMap.enabled = true;\n viewport.appendChild(renderer.domElement);\n \n const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);\n scene.add(ambientLight);\n \n const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);\n directionalLight.position.set(10, 10, 10);\n directionalLight.castShadow = true;\n directionalLight.shadow.mapSize.width = 2048;\n directionalLight.shadow.mapSize.height = 2048;\n scene.add(directionalLight);\n \n const gridHelper = new THREE.GridHelper(20, 20, 0x888888, 0x444444);\n gridHelper.position.y = -0.01;\n scene.add(gridHelper);\n \n const axesHelper = new THREE.AxesHelper(5);\n scene.add(axesHelper);\n \n controls = new OrbitControls(camera, renderer.domElement);\n controls.autoRotate = false;\n controls.enableZoom = true;\n controls.enablePan = true;\n \n renderer.domElement.addEventListener('click', (e) => onMouseClick(e, viewport, instance, THREE));\n \n let animating = true;\n function animate() {\n if (!animating) return;\n requestAnimationFrame(animate);\n if (controls) controls.update();\n renderer.render(scene, camera);\n }\n animate();\n \n const observer = new MutationObserver(() => {\n const url = instance.modelUrl;\n if (url && url.trim()) {\n loadGLBModel(url, statusEl, instance);\n }\n });\n \n observer.observe(instance, {\n attributes: true,\n attributeFilter: ['modelurl']\n });\n \n if (instance.modelUrl && instance.modelUrl.trim()) {\n loadGLBModel(instance.modelUrl, statusEl, instance);\n } else {\n statusEl.textContent = '✅ 3D Editor Ready (click objects)';\n }\n \n instance._editor3d = { scene, camera, renderer, controls, observer, THREE };\n instance._stopAnimation = () => { animating = false; };\n \n console.log('✅ 3D Editor (Custom Control) initialized (v1.41.8)');\n } catch (error) {\n console.error('❌ Error:', error);\n statusEl.textContent = '❌ ' + error.message;\n }\n}\n\n/**\n * Cleanup\n * @param {BaseScreenViewerAndControl} instance\n */\nexport function disconnectedCallback(instance) {\n if (instance._stopAnimation) instance._stopAnimation();\n if (instance._editor3d) {\n if (instance._editor3d.observer) {\n instance._editor3d.observer.disconnect();\n }\n if (instance._editor3d.renderer) {\n instance._editor3d.renderer.dispose();\n instance._editor3d.renderer.domElement.remove();\n }\n }\n}","properties":{"modelUrl":{"type":"string"}},"settings":{}}
@@ -0,0 +1 @@
1
+ {"html":"<div id=\"root\" @click='{\"commands\":[{\"type\":\"ToggleSignalValue\",\"signal\":{\"source\":\"property\",\"name\":\"signalName\"}}]}'>\n <div style=\"align-self:end;\">[[this.text]]</div>\n <img src=\"[[this.icon]]\" style=\"width:32px;height:32px;margin:auto;\">\n <div bind-content:text=\"?signalName\" style=\"align-self:start;color:var(--primary-color);\"></div>\n</div>\n","style":"* {\n box-sizing: border-box;\n font-size: 14px;\n text-align: center;\n}\n\n#root{\n width: 100%;\n height: 100%;\n display: grid;\n grid-template-columns: 1fr;\n grid-template-rows: 1fr 32px 1fr;\n justify-items: center;\n}","properties":{"signalName":{"type":"signal"},"icon":{"type":"string"},"text":{"type":"string"}},"settings":{}}
@@ -0,0 +1 @@
1
+ {"html":"<div id=\"root\">\n <div style=\"align-self:end;\">[[this.text]]</div>\n <img src=\"[[this.icon]]\" style=\"width:32px;height:32px;margin:auto;\">\n <div bind-content:text=\"?signalName\" style=\"align-self:start;color:var(--primary-color);\"></div>\n</div>\n","style":"* {\n box-sizing: border-box;\n font-size: 14px;\n text-align: center;\n}\n\n#root{\n width: 100%;\n height: 100%;\n display: grid;\n grid-template-columns: 1fr;\n grid-template-rows: 1fr 32px 1fr;\n justify-items: center;\n}","properties":{"signalName":{"type":"signal"},"icon":{"type":"string"},"text":{"type":"string","default":"test"}},"settings":{}}
@@ -0,0 +1 @@
1
+ {"html":"<div id=\"ctl\" bind-content:text=\"??time\" style=\"width:100%;height:100%;\">00:00:00</div>\n","style":"* {\n box-sizing: border-box;\n}","script":"let iv;\n\n/**\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function connectedCallback(instance) {\n iv = setInterval(() => {\n instance._getDomElement('ctl').textContent = dayjs().format(instance.format);\n }, 250);\n}\n\n/**\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function disconnectedCallback(instance) {\n clearInterval(iv);\n}\n","properties":{"format":{"type":"string","default":"HH:mm:ss"}},"settings":{}}
@@ -0,0 +1 @@
1
+ {"html":"<div data-visibility-enabled=\"false\" data-visibility-condition=\"==\" data-visibility-action=\"disable\">\n <mypopup-dialog id=\"mydialog2\" isdraggable=\"true\" style=\"display:none;\">\n <webui-valve_faceplate bind-prop:valveid=\"??valveid\" bind-prop:relativesignalpath=\"??relativesignalpath\" data-visibility-enabled=\"false\" data-visibility-condition=\"==\" data-visibility-action=\"hide\" data-visibility-value=\"true\" bind-prop:opened=\"??coloropened\" bind-prop:closed=\"??colorclosed\" style=\"position:absolute;\"></webui-valve_faceplate>\n <!-- <iobroker-webui-screen-viewer id=\"iobwebscreenviewer1\" stretch screen-name=\"Webui_siyirtme\"></iobroker-webui-screen-viewer>-->\n </mypopup-dialog>\n <div id=\"container_kmn_1\" height=\"100%\" width=\"100%\">\n <svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"100%\" height=\"100%\" viewBox=\"0 0 102.744 112.5\" enable-background=\"new 0 0 102.744 112.5\" xml:space=\"preserve\" @click=\"openPopup\">\n <g id=\"Group_Pipe\">\n <g>\n <defs>\n <rect id=\"SVGID_1_\" x=\"47.403\" y=\"32.435\" width=\"7.485\" height=\"53.979\"></rect>\n </defs>\n <clipPath id=\"SVGID_2_\">\n <use xlink:href=\"#SVGID_1_\" overflow=\"visible\"></use>\n </clipPath>\n <linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"-0.0396\" y1=\"112.459\" x2=\"0.9604\" y2=\"112.459\" gradientTransform=\"matrix(7.4849 0 0 -7.4849 47.6998 901.1704)\">\n <stop id=\"SVGID_3_1_\" offset=\"0\" style=\"stop-color:#5C5D60;\"></stop>\n <stop offset=\"0.45\" style=\"stop-color:#ECECED;\"></stop>\n <stop offset=\"0.57\" style=\"stop-color:#D9DADB;\"></stop>\n <stop offset=\"0.77\" style=\"stop-color:#B1B3B6;\"></stop>\n <stop id=\"SVGID_3_5_\" offset=\"1\" style=\"stop-color:#5C5D60;\"></stop>\n </linearGradient>\n <rect id=\"SVGID_rect_1_\" x=\"47.403\" y=\"32.435\" clip-path=\"url(#SVGID_2_)\" fill=\"url(#SVGID_3_)\" width=\"7.485\" height=\"53.979\" bind-css:fill='{\"signal\":\"??colorstopped;??coloropened;??colorclosed;??colorfault\",\"expression\":\"if(__0 &amp;&amp;__1 &amp;&amp; __2)\\r\\nreturn \\\"orange\\\";\\r\\nif(__1 &amp;&amp; __2)\\r\\nreturn \\\"orange\\\";\\r\\nif(__0)\\r\\nreturn \\\"yellow\\\";\\r\\nif(__1)\\r\\nreturn \\\"#00ff00\\\";\\r\\nif(__2)\\r\\nreturn \\\"red\\\";\\r\\nif(__3)\\r\\nreturn \\\"orange\\\";\\r\\nif(__0 ==false &amp;&amp; __1==false &amp;&amp; __2==false &amp;&amp; __3==false)\\r\\nreturn \\\"url(#SVGID_3_)\\\";\"}'></rect>\n </g>\n </g>\n <g id=\"Group_Port2\">\n <linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"25.917\" y1=\"112.9697\" x2=\"25.917\" y2=\"60.3926\">\n <stop offset=\"0\" style=\"stop-color:#383838;\"></stop>\n <stop offset=\"0.45\" style=\"stop-color:#ECECEC;\"></stop>\n <stop offset=\"0.55\" style=\"stop-color:#D9D9D9;\"></stop>\n <stop offset=\"0.76\" style=\"stop-color:#A8A8A8;\"></stop>\n <stop offset=\"0.77\" style=\"stop-color:#A6A6A6;\"></stop>\n <stop offset=\"1\" style=\"stop-color:#383838;\"></stop>\n </linearGradient>\n <path id=\"SVGID_path_1_\" fill=\"url(#SVGID_4_)\" d=\"M0,60.393l51.834,26.097L0,112.97V60.393\" bind-css:fill='{\"signal\":\"??colorstopped;??coloropened;??colorclosed;??colorfault\",\"expression\":\"if(__0 &amp;&amp;__1 &amp;&amp; __2)\\r\\nreturn \\\"orange\\\";\\r\\nif(__1 &amp;&amp; __2)\\r\\nreturn \\\"orange\\\";\\r\\nif(__0)\\r\\nreturn \\\"yellow\\\";\\r\\nif(__1)\\r\\nreturn \\\"#00ff00\\\";\\r\\nif(__2)\\r\\nreturn \\\"red\\\";\\r\\nif(__3)\\r\\nreturn \\\"orange\\\";\\r\\nif(__0 ==false &amp;&amp; __1==false &amp;&amp; __2==false &amp;&amp; __3==false)\\r\\nreturn \\\"url(#SVGID_4_)\\\";\"}'></path>\n </g>\n <g id=\"Group_Port1\">\n <linearGradient id=\"SVGID_5_\" gradientUnits=\"userSpaceOnUse\" x1=\"76.8262\" y1=\"112.9697\" x2=\"76.8262\" y2=\"60.3926\">\n <stop offset=\"0\" style=\"stop-color:#383838;\"></stop>\n <stop offset=\"0.45\" style=\"stop-color:#ECECEC;\"></stop>\n <stop offset=\"0.55\" style=\"stop-color:#D9D9D9;\"></stop>\n <stop offset=\"0.76\" style=\"stop-color:#A8A8A8;\"></stop>\n <stop offset=\"0.77\" style=\"stop-color:#A6A6A6;\"></stop>\n <stop offset=\"1\" style=\"stop-color:#383838;\"></stop>\n </linearGradient>\n <path id=\"SVGID_path_2_\" fill=\"url(#SVGID_5_)\" d=\"M102.744,60.393L50.909,86.489l51.835,26.48V60.393\" bind-css:fill='{\"signal\":\"??colorstopped;??coloropened;??colorclosed;??colorfault\",\"expression\":\"if(__0 &amp;&amp;__1 &amp;&amp; __2)\\r\\nreturn \\\"orange\\\";\\r\\nif(__1 &amp;&amp; __2)\\r\\nreturn \\\"orange\\\";\\r\\nif(__0)\\r\\nreturn \\\"yellow\\\";\\r\\nif(__1)\\r\\nreturn \\\"#00ff00\\\";\\r\\nif(__2)\\r\\nreturn \\\"red\\\";\\r\\nif(__3)\\r\\nreturn \\\"orange\\\";\\r\\nif(__0==false &amp;&amp; __1==false &amp;&amp; __2==false &amp;&amp; __3==false)\\r\\nreturn \\\"url(#SVGID_5_)\\\";\"}'></path>\n </g>\n <g id=\"Group_Box\">\n <g>\n <defs>\n <path id=\"SVGID_6_\" d=\"M91.622,16.348l1.239,16.314l4.545,3.386c-0.073-1.252-0.01-30.573-0.01-32.629l-3.725-3.417 L91.622,16.348z\"></path>\n </defs>\n <clipPath id=\"SVGID_7_\">\n <use xlink:href=\"#SVGID_6_\" overflow=\"visible\"></use>\n </clipPath>\n <linearGradient id=\"SVGID_8_\" gradientUnits=\"userSpaceOnUse\" x1=\"-0.0288\" y1=\"112.4717\" x2=\"0.9712\" y2=\"112.4717\" gradientTransform=\"matrix(5.7842 0 0 5.7842 91.7858 -632.5346)\">\n <stop offset=\"0\" style=\"stop-color:#D1D3D4;\"></stop>\n <stop offset=\"0.36\" style=\"stop-color:#BEC0C2;\"></stop>\n <stop offset=\"1\" style=\"stop-color:#939598;\"></stop>\n </linearGradient>\n <rect id=\"SVGID_rect_2_\" x=\"91.622\" y=\"0.002\" clip-path=\"url(#SVGID_7_)\" fill=\"url(#SVGID_8_)\" width=\"5.784\" height=\"36.046\" bind-css:fill='{\"signal\":\"??colorstopped;??coloropened;??colorclosed;??colorfault\",\"expression\":\"if(__0 &amp;&amp;__1 &amp;&amp; __2)\\r\\nreturn \\\"orange\\\";\\r\\nif(__1 &amp;&amp; __2)\\r\\nreturn \\\"orange\\\";\\r\\nif(__0)\\r\\nreturn \\\"yellow\\\";\\r\\nif(__1)\\r\\nreturn \\\"#00ff00\\\";\\r\\nif(__2)\\r\\nreturn \\\"red\\\";\\r\\nif(__3)\\r\\nreturn \\\"orange\\\";\\r\\nif(__0 ==false &amp;&amp; __1==false &amp;&amp; __2==false &amp;&amp; __3==false)\\r\\nreturn \\\"url(#SVGID_8_)\\\";\"}'></rect>\n </g>\n <g>\n <defs>\n <path id=\"SVGID_9_\" d=\"M8.846,32.662l3.75,3.192c3.174-0.033,83.323,0.118,84.81,0.195l-3.734-3.965l-42.3-1.56L8.846,32.662z\"></path>\n </defs>\n <clipPath id=\"SVGID_10_\">\n <use xlink:href=\"#SVGID_9_\" overflow=\"visible\"></use>\n </clipPath>\n <linearGradient id=\"SVGID_11_\" gradientUnits=\"userSpaceOnUse\" x1=\"0.0205\" y1=\"112.5215\" x2=\"1.021\" y2=\"112.5215\" gradientTransform=\"matrix(0 5.5244 5.5244 0 -568.4863 30.4067)\">\n <stop offset=\"0\" style=\"stop-color:#D1D3D4;\"></stop>\n <stop offset=\"0.28\" style=\"stop-color:#BEC0C2;\"></stop>\n <stop offset=\"0.85\" style=\"stop-color:#8E9092;\"></stop>\n <stop offset=\"1\" style=\"stop-color:#808285;\"></stop>\n </linearGradient>\n <rect id=\"SVGID_rect_3_\" x=\"8.846\" y=\"30.524\" clip-path=\"url(#SVGID_10_)\" fill=\"url(#SVGID_11_)\" width=\"88.56\" height=\"5.525\" bind-css:fill='{\"signal\":\"??colorstopped;??coloropened;??colorclosed;??colorfault\",\"expression\":\"if(__0 &amp;&amp;__1 &amp;&amp; __2)\\r\\nreturn \\\"orange\\\";\\r\\nif(__1 &amp;&amp; __2)\\r\\nreturn \\\"orange\\\";\\r\\nif(__0)\\r\\nreturn \\\"yellow\\\";\\r\\nif(__1)\\r\\nreturn \\\"#00ff00\\\";\\r\\nif(__2)\\r\\nreturn \\\"red\\\";\\r\\nif(__3)\\r\\nreturn \\\"orange\\\";\\r\\nif(__0 ==false &amp;&amp; __1==false &amp;&amp; __2==false &amp;&amp; __3==false)\\r\\nreturn \\\"url(#SVGID_11_)\\\";\"}'></rect>\n </g>\n <g>\n <defs>\n <rect id=\"SVGID_12_\" x=\"8.846\" y=\"0.032\" width=\"84.826\" height=\"32.63\"></rect>\n </defs>\n <clipPath id=\"SVGID_13_\">\n <use xlink:href=\"#SVGID_12_\" overflow=\"visible\"></use>\n </clipPath>\n <linearGradient id=\"SVGID_14_\" gradientUnits=\"userSpaceOnUse\" x1=\"-0.002\" y1=\"112.498\" x2=\"0.998\" y2=\"112.498\" gradientTransform=\"matrix(-84.8257 0 0 84.8257 93.5221 -9526.4209)\">\n <stop offset=\"0\" style=\"stop-color:#E6E7E8;\"></stop>\n <stop offset=\"0.01\" style=\"stop-color:#E6E7E8;\"></stop>\n <stop offset=\"0.5\" style=\"stop-color:#D3D5D6;\"></stop>\n <stop offset=\"1\" style=\"stop-color:#BCBEC0;\"></stop>\n </linearGradient>\n <rect id=\"SVGID_rect_4_\" x=\"8.846\" y=\"0.032\" clip-path=\"url(#SVGID_13_)\" fill=\"url(#SVGID_14_)\" width=\"84.826\" height=\"32.63\" bind-css:fill='{\"signal\":\"??colorstopped;??coloropened;??colorclosed;??colorfault\",\"expression\":\"if(__0 &amp;&amp;__1 &amp;&amp; __2)\\r\\nreturn \\\"orange\\\";\\r\\nif(__1 &amp;&amp; __2)\\r\\nreturn \\\"orange\\\";\\r\\nif(__0)\\r\\nreturn \\\"yellow\\\";\\r\\nif(__1)\\r\\nreturn \\\"#00ff00\\\";\\r\\nif(__2)\\r\\nreturn \\\"red\\\";\\r\\nif(__3)\\r\\nreturn \\\"orange\\\";\\r\\nif(__0 ==false &amp;&amp; __1==false &amp;&amp; __2==false &amp;&amp; __3==false)\\r\\nreturn \\\"url(#SVGID_14_)\\\";\"}'></rect>\n </g>\n </g>\n </svg>\n </div>\n</div>\n","style":":host {\n background-color: transparent;\n}\n\n\n* {\n box-sizing: border-box;\n}\n","script":"let protocol = window.location.protocol;\nlet ip = window.location.hostname;\nlet port = window.location.port;\ndebugger;\n\nlet BaseCustomControl;\nlet webuiCustomControlSymbol;\nlet path =(`${protocol}//${ip}:${port}/mywebui/dist/frontend/runtime/CustomControls.js`);\nconst module = await import(path) \n .then(module => { \n BaseCustomControl = module.BaseCustomControl;\n webuiCustomControlSymbol=module.webuiCustomControlSymbol;\n }) \n .catch(err => { \n console.error('Error loading module:', err); \n }); \n\n//import (`${protocol}//${ip}:${port}/webui.0.data/config/additionalfiles/svg_popup.js`);\n\n\n\n \n\n/*function fillgreen(instance)\n{\n let fillurl1 = instance._getDomElement('SVGID_rect_1_');\n fillurl1.style['fill']= \"#00ff00\";\n let fillurl2 = instance._getDomElement('SVGID_path_1_');\n fillurl2.style['fill']= \"#00ff00\";\n let fillurl3 = instance._getDomElement('SVGID_path_2_');\n fillurl3.style['fill']= \"#00ff00\";\n let fillurl4 = instance._getDomElement('SVGID_rect_2_');\n fillurl4.style['fill']= \"#00ff00\";\n let fillurl5 = instance._getDomElement('SVGID_rect_3_');\n fillurl5.style['fill']= \"#00ff00\";\n let fillurl6 = instance._getDomElement('SVGID_rect_4_');\n fillurl6.style['fill']= \"#00ff00\";\n}\n\n\nfunction fillred(instance)\n{\n let fillurl1 = instance._getDomElement('SVGID_rect_1_');\n fillurl1.style['fill']= \"red\";\n let fillurl2 = instance._getDomElement('SVGID_path_1_');\n fillurl2.style['fill']= \"red\";\n let fillurl3 = instance._getDomElement('SVGID_path_2_');\n fillurl3.style['fill']= \"red\";\n let fillurl4 = instance._getDomElement('SVGID_rect_2_');\n fillurl4.style['fill']= \"red\";\n let fillurl5 = instance._getDomElement('SVGID_rect_3_');\n fillurl5.style['fill']= \"red\";\n let fillurl6 = instance._getDomElement('SVGID_rect_4_');\n fillurl6.style['fill']= \"red\";\n}\n\n\nfunction fillyellow(instance)\n{\n let fillurl1 = instance._getDomElement('SVGID_rect_1_');\n fillurl1.style['fill']= \"yellow\";\n let fillurl2 = instance._getDomElement('SVGID_path_1_');\n fillurl2.style['fill']= \"yellow\";\n let fillurl3 = instance._getDomElement('SVGID_path_2_');\n fillurl3.style['fill']= \"yellow\";\n let fillurl4 = instance._getDomElement('SVGID_rect_2_');\n fillurl4.style['fill']= \"yellow\";\n let fillurl5 = instance._getDomElement('SVGID_rect_3_');\n fillurl5.style['fill']= \"yellow\";\n let fillurl6 = instance._getDomElement('SVGID_rect_4_');\n fillurl6.style['fill']= \"yellow\";\n}\n\nfunction fillorange(instance)\n{\n let fillurl1 = instance._getDomElement('SVGID_rect_1_');\n fillurl1.style['fill']= \"orange\";\n let fillurl2 = instance._getDomElement('SVGID_path_1_');\n fillurl2.style['fill']= \"orange\";\n let fillurl3 = instance._getDomElement('SVGID_path_2_');\n fillurl3.style['fill']= \"orange\";\n let fillurl4 = instance._getDomElement('SVGID_rect_2_');\n fillurl4.style['fill']= \"orange\";\n let fillurl5 = instance._getDomElement('SVGID_rect_3_');\n fillurl5.style['fill']= \"orange\";\n let fillurl6 = instance._getDomElement('SVGID_rect_4_');\n fillurl6.style['fill']= \"orange\";\n}\n\nfunction filldefault(instance)\n{\n let fillurl1 = instance._getDomElement('SVGID_rect_1_');\n fillurl1.style['fill']= \"url(#SVGID_3_)\";\n let fillurl2 = instance._getDomElement('SVGID_path_1_');\n fillurl2.style['fill']= \"url(#SVGID_4_)\";\n let fillurl3 = instance._getDomElement('SVGID_path_2_');\n fillurl3.style['fill']= \"url(#SVGID_5_)\";\n let fillurl4 = instance._getDomElement('SVGID_rect_2_');\n fillurl4.style['fill']= \"url(#SVGID_8_)\";\n let fillurl5 = instance._getDomElement('SVGID_rect_3_');\n fillurl5.style['fill']= \"url(#SVGID_11_)\";\n let fillurl6 = instance._getDomElement('SVGID_rect_4_');\n fillurl6.style['fill']= \"url(#SVGID_14_)\";\n}*/\n let valveid;\n // this.add(colormy,\"red\");\n\n/*function isNullOrUndefined(value) {\n return value === undefined || value === null;\n}\n\n// Function to handle state changes\nfunction handleStateChange(id, state,instance) {\n checkstartup(instance);\n}*/\n\nexport async function init(instance) {\n debugger;\n \n /*let myiframe = instance._getDomElement('myiframe1');\n myiframe.src=`/webui/runtime.html?use_id=${instance.valveid}#screenName=${instance.screenSelect}`;\n myiframe.style.width='100%';\n myiframe.style.height='100%';*/\n\n /*let colorstoppedNodeValue =checkisnullorumdefined(instance.attributes[\"bind-prop:colorstopped\"]);\n let coloropenedNodeValue =checkisnullorumdefined(instance.attributes[\"bind-prop:coloropened\"]);\n let colorclosedNodeValue =checkisnullorumdefined(instance.attributes[\"bind-prop:colorclosed\"]);\n let colorfaultNodeValue =checkisnullorumdefined(instance.attributes[\"bind-prop:colorfault\"]);\n let st = (!isNullOrUndefined(colorstoppedNodeValue)) ? window.IOB.subscribeState(colorstoppedNodeValue,(id, state)=> handleStateChange(id,state,instance)) : {'val':false};\n let op = (!isNullOrUndefined(coloropenedNodeValue)) ? window.IOB.subscribeState(coloropenedNodeValue,(id, state)=> handleStateChange(id,state,instance)) : {'val':false};\n let cl = (!isNullOrUndefined(colorclosedNodeValue)) ? window.IOB.subscribeState(colorclosedNodeValue,(id, state)=> handleStateChange(id,state,instance)) : {'val':false};\n let fl = (!isNullOrUndefined(colorfaultNodeValue)) ? window.IOB.subscribeState(colorfaultNodeValue, (id, state)=> handleStateChange(id,state,instance)) : {'val':false};\n */\n \n\n //checkstartup(instance);\n /*instance._assignEvent('colorstopped-changed', (ev) => checkstartup(instance));\n instance._assignEvent('coloropened-changed', (ev) => checkstartup(instance));\n instance._assignEvent('colorclosed-changed', (ev) => checkstartup(instance));\n instance._assignEvent('colorfault-changed', (ev) => checkstartup(instance));*/\n \n \n /*let screenviewer1 = instance._getDomElement('iobwebscreenviewer1');\n if (isNullOrUndefined(instance.screenSelect)) {\n //screenviewer1.screenName='';\n } else {\n screenviewer1.screenName=instance.screenSelect;\n /*let stringify =JSON.stringify(instance.parameter);\n screenviewer1.setAttribute(\"Parameters\", stringify);*/\n //}*/\n\n /*let vbv = instance.getAttribute(\"inputID\");\n instance.setAttribute(\"Kamran\", 123456);\n let vbv1 = instance.getAttribute(\"Kamran\");*/\n //instance._bindingsRefresh();\n}\n\n/*function addvalveid()\n{\n let myscreenviewer = instance._getDomElement('iobwebscreenviewer1');\n myscreenviewer.valveid=instance.valveid;\n}*/\n\n/*function checkisnullorumdefined(value)\n{\n if(!isNullOrUndefined(value))\n return value.nodeValue;\n}*/\nasync function checkstartup(instance)\n{\n //debugger;\n /*let result = 0;\n let colorstoppedNodeValue =checkisnullorumdefined(instance.attributes[\"bind-prop:colorstopped\"]);\n let coloropenedNodeValue =checkisnullorumdefined(instance.attributes[\"bind-prop:coloropened\"]);\n let colorclosedNodeValue =checkisnullorumdefined(instance.attributes[\"bind-prop:colorclosed\"]);\n let colorfaultNodeValue =checkisnullorumdefined(instance.attributes[\"bind-prop:colorfault\"]);*/\n /*let st = colorstoppedNodeValue.replace(/\\s?\\{[^}]+\\}/g, valveid.val);\n let op = coloropenedNodeValue.replace(/\\s?\\{[^}]+\\}/g, valveid.val);\n let cl = colorclosedNodeValue.replace(/\\s?\\{[^}]+\\}/g, valveid.val);\n let fl = colorfaultNodeValue.replace(/\\s?\\{[^}]+\\}/g, valveid.val);*/\n \n /*let stopped = (!isNullOrUndefined(colorstoppedNodeValue)) ? await window.IOB.getState(colorstoppedNodeValue) : {'val':false};\n let opened = (!isNullOrUndefined(coloropenedNodeValue)) ? await window.IOB.getState(coloropenedNodeValue) : {'val':false};\n let closed = (!isNullOrUndefined(colorclosedNodeValue)) ? await window.IOB.getState(colorclosedNodeValue) : {'val':false};\n let fault = (!isNullOrUndefined(colorfaultNodeValue)) ? await window.IOB.getState(colorfaultNodeValue) : {'val':false};*/\n \n\n /*let stopped = instance._initialPropertyCache.get(\"colorstopped\");\n let opened = instance._initialPropertyCache.get(\"coloropened\");\n let closed = instance._initialPropertyCache.get(\"colorclosed\");\n let fault = instance._initialPropertyCache.get(\"colorfault\");*/\n /*if(stopped.val==true)\n {\n fillyellow(instance);\n }\n if(opened.val==true)\n {\n fillgreen(instance);\n }\n if(closed.val==true)\n {\n fillred(instance);\n }\n\n if(fault.val==true)\n {\n fillorange(instance);\n }\n \n if(opened.val==true && closed.val==true)\n {\n fillorange(instance);\n }\n\n if(opened.val==true && closed.val==true && stopped.val==true)\n {\n fillorange(instance);\n }\n if(opened.val==false && closed.val==false && stopped.val==false && fault.val==false)\n {\n filldefault(instance);\n }*/\n\n\n}\n\n/*function check(instance)\n{\n let stopped = instance.colorstopped;\n let opened = instance.coloropened;\n let closed = instance.colorclosed;\n let fault = instance.colorfault;\n if(stopped==true)\n {\n fillyellow(instance);\n }\n if(opened==true)\n {\n fillgreen(instance);\n }\n if(closed==true)\n {\n fillred(instance);\n }\n\n if(fault==true)\n {\n fillorange(instance);\n }\n \n if(opened==true && closed==true)\n {\n fillorange(instance);\n }\n\n if(opened==true && closed==true && stopped==true)\n {\n fillorange(instance);\n }\n if(opened==false && closed==false && stopped==false && fault==false)\n {\n filldefault(instance);\n }\n\n\n}*/\n\nexport function connectedCallback(instance) {\n\ndebugger;\nlet instancem = Reflect.construct(BaseCustomControl, [], window['IobrokerWebuiCustomControlWebui_Valve']);\nlet p= { type: 'string'};\nlet newKey = 'salam'; \nlet p1= { type: 'screen'};\nlet newKey1 = 'screenSelect';\n\n\n\nif(instancem.constructor[webuiCustomControlSymbol].control.properties.hasOwnProperty(newKey)){\n}\nelse{\n instancem.constructor[webuiCustomControlSymbol].control.properties[newKey]= p;\n}\n\nif(instancem.constructor[webuiCustomControlSymbol].control.properties.hasOwnProperty(newKey1)){\n}\nelse{\ninstancem.constructor[webuiCustomControlSymbol].control.properties[newKey1]= p1;\n} \n instance._bindingsRefresh();\n}\n\n/**\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function disconnectedCallback(instance) {\n}\n\nexport function attributeChangedCallback (name, oldValue, newValue) {\n \n\tconsole.log('attribute changed', name, oldValue, newValue, this);\n}\n\n\n/*let left_kmn = \"100px\";\nlet top_kmn = \"100px\";\nlet container_kmn = \"container_kmn_1\";\nlet width_kmn = \"250px\";\nlet height_kmn = \"2000px\";*/\n\n\n\nexport function openPopup(event, eventRaisingElement, shadowRoot, instance, parameters) {\n debugger;\n\n const container = instance._getDomElement('container_kmn_1');\n const popup = instance._getDomElement('mydialog2');\n const svg = container.querySelector('svg');\n\n // Remove pointer-events: none from SVG\n svg.style.pointerEvents = 'auto';\n\n event.stopPropagation();\n if (popup) { \n popup.open(event);\n popup.setTitle('Valve');\n //popup.setPosition(instance.popupLeft,instance.popupTop);\n popup.setSize(instance.popupWidth, instance.popupHeight);\n //popup.setAttribute('draggable','false');\n popup.setAttribute('visible', 'true');\n }\n}\n","properties":{"colorstopped":{"type":"boolean"},"coloropened":{"type":"boolean"},"colorclosed":{"type":"boolean"},"colorfault":{"type":"boolean"},"popupWidth":{"type":"number","default":"500"},"popupHeight":{"type":"number","default":"500"},"popupLeft":{"type":"number","default":"100"},"popupTop":{"type":"number","default":"100"},"popupTitle":{"type":"string"},"valveid":{"type":"string"},"salam":{"type":"string"},"screenSelect":{"type":"screen"},"relativesignalpath":{"type":"string"}},"settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}
@@ -0,0 +1 @@
1
+ {"html":"<div height=\"100%\" width=\"100%\">\n <svg version=\"1.1\" id=\"Default\" xmlns:agg=\"http://www.example.com\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" width=\"100%\" height=\"100%\" viewBox=\"0 0 127.21 127.21\" enable-background=\"new 0 0 127.21 127.21\" xml:space=\"preserve\">\n <defs>\n <circle id=\"body\" cx=\"63.604\" cy=\"63.604\" r=\"44\"></circle>\n <clipPath id=\"bodyClip\">\n <use xlink:href=\"#body\" overflow=\"visible\"></use>\n </clipPath>\n </defs>\n <circle cx=\"63.604\" cy=\"63.604\" r=\"46.221\"></circle>\n <radialGradient id=\"SVGID_1_\" cx=\"29.7715\" cy=\"33.4385\" r=\"91.0111\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0\" style=\"stop-color:white;\"></stop>\n <stop offset=\"1\" class bind-attr:class='{\"signal\":\"??fillcolor\",\"converter\":{\"true\":\"maincolor\"}}'></stop>\n </radialGradient>\n <use xlink:href=\"#body\" fill=\"url(#SVGID_1_)\"></use>\n <path opacity=\"0.5\" fill=\"#FFFFFF\" d=\"M25.532,74.503c-5.46-31.579,13.149-50.338,31.092-53.169\n\tC36.639,22.692,20.8,41.055,20.8,63.566c0,3.784,0.46,7.45,1.299,10.938H25.532z\"></path>\n <path opacity=\"0.5\" fill=\"#FFFFFF\" d=\"M22.867,77.693c4.647,12.701,14.21,22.453,25.966,26.129\n\tc-8.619-3.168-20.089-15.555-22.671-26.16L22.867,77.693z\"></path>\n <path opacity=\"0.3\" fill=\"white\" clip-path=\"url(#bodyClip)\" d=\"M103.365,45.808h15.782v-45H10.397v41.411h13.435\n\tc1.109,0.263,2.217,0.612,3.325,1.038c3.221,1.239,6.44,3.129,9.661,5.021c3.221,1.89,6.44,3.781,9.661,5.019\n\tc3.221,1.239,6.441,1.825,9.662,1.555c3.221-0.269,6.441-1.394,9.662-2.983c3.221-1.591,6.441-3.648,9.662-5.462\n\tc3.219-1.814,6.439-3.385,9.66-4.171c3.222-0.786,6.444-0.786,9.662,0C97.645,42.933,100.501,44.252,103.365,45.808z\"></path>\n </svg>\n</div>\n","style":"\n\n* {\n box-sizing: border-box;\n}\n\n.maincolor {\n fill:green;\n stop-color: green;\n}\n","script":"export function init(instance){\n /*debugger;\n let vbv = instance.fillcolor;*/\n}","properties":{"fillcolor":{"type":"boolean"},"stopcolor":{"type":"boolean"}},"settings":{}}
@@ -0,0 +1 @@
1
+ {"html":"<span id=\"jkj\" style=\"position:absolute;left:118px;top:46px;\">hello world</span>\n<span id=\"jkj2\" bind-content:text=\"??testprop\" style=\"position:absolute;left:374px;top:98px;\"></span>\n<input type=\"text\" bind-prop:value=\"=??testprop\" style=\"width:140px;position:absolute;left:559px;top:40px;\">\n","style":":host {\n}\n\n* {\n box-sizing: border-box;\n}","script":"// ════════════════════════════════════════════════════════════════════════════\n// Custom Control — Lifecycle Script\n// Each exported function is called by the runtime with (instance, shadowRoot)\n// instance = the web component element (shadow host)\n// shadowRoot = instance.shadowRoot\n// ════════════════════════════════════════════════════════════════════════════\n\n// ── Import (needed only if you use iobrokerHandler directly) ─────────────────\n// import { iobrokerHandler } from '../common/IobrokerHandler.js';\n\n// ── 1. DOM ───────────────────────────────────────────────────────────────────\n// instance._getDomElement('myId')\n// getElementById inside shadow root — returns the element or null\n// NOTE: pass the bare ID, NOT '#myId'\n//\n// instance._getDomElements('.myClass')\n// querySelectorAll inside shadow root — returns NodeList\n//\n// shadowRoot.querySelector('tag')\n// raw shadow root query (any CSS selector)\n\n// ── 2. Events ────────────────────────────────────────────────────────────────\n// instance._assignEvent('click', handler)\n// Adds a listener on the host element itself.\n// → Auto-removed when the control is disconnected from DOM.\n// Returns { remove() } for early removal.\n//\n// el.addEventListener('click', handler) ← on a child element (manual cleanup)\n\n// ── 3. Properties (defined in Properties tab) ────────────────────────────────\n// instance.myProp read / write a declared property\n// instance['myProp'] same — useful for dynamic names\n\n// ── 4. Relative OID path ─────────────────────────────────────────────────────\n// instance._getRelativeSignalsPath()\n// Returns the OID prefix set by the parent screen/viewer.\n// Relative signals start with '.' → prepend this prefix yourself, or let\n// iobrokerHandler.getNormalizedSignalName() do it.\n\n// ── 5. ioBroker state (iobrokerHandler) ──────────────────────────────────────\n// Must import at top: import { iobrokerHandler } from '../common/IobrokerHandler.js';\n//\n// Read a state once:\n// const state = await iobrokerHandler.getState('hm-rpc.0.ABC.STATE');\n// console.log(state.val, state.ts, state.ack);\n//\n// Write a state:\n// await iobrokerHandler.setState('hm-rpc.0.ABC.STATE', true);\n// await iobrokerHandler.setState('hm-rpc.0.ABC.STATE', { val: 42, ack: false });\n//\n// Subscribe to state changes (remember to unsubscribe on disconnect!):\n// const handler = (id, state) => { console.log(id, state.val); };\n// await iobrokerHandler.subscribeState('hm-rpc.0.ABC.STATE', handler);\n// // in disconnectedCallback:\n// iobrokerHandler.unsubscribeState('hm-rpc.0.ABC.STATE', handler);\n//\n// Get an ioBroker object (type/role/unit/min/max/…):\n// const obj = await iobrokerHandler.getObject('hm-rpc.0.ABC.STATE');\n//\n// Current user and groups:\n// const user = await iobrokerHandler.getCurrentUser(); // { id, groups }\n//\n// Historic data (requires History adapter):\n// const { values } = await iobrokerHandler.getHistoricData(\n// 'hm-rpc.0.ABC.STATE',\n// { start: Date.now() - 3600000, end: Date.now(), count: 100, aggregate: 'none' }\n// );\n//\n// Language:\n// console.log(iobrokerHandler.language); // 'en' | 'de' | 'ru' | …\n// iobrokerHandler.languageChanged.on(lang => {}); // subscribe to language switch\n//\n// Low-level socket connection (for advanced use):\n// iobrokerHandler.connection.subscribeState(id, handler);\n// iobrokerHandler.connection.getStates(pattern); // wildcard query\n\n// ── 6. Navigation & Dialogs (window.RUNTIME) ────────────────────────────────\n// Open a screen (replaces current view):\n// RUNTIME.openScreen({ screen: 'myScreen', relativeSignalsPath: 'hm-rpc.0.device', noHistory: false });\n//\n// Open a screen in a dialog:\n// RUNTIME.openDialog({ screen: 'myScreen', title: 'Details', width: '600px', height: '400px',\n// moveable: true, closeable: true });\n//\n// Run any script command object:\n// await RUNTIME.runSimpleScriptCommand({ type: 'SetSignalValue',\n// signal: 'hm-rpc.0.ABC.STATE', target: 'signal', value: true });\n//\n// Find a parent screen/control:\n// const parentScreen = RUNTIME.getParentScreen(instance); // 1 level up\n// const grandParent = RUNTIME.getParentScreen(instance, 2); // 2 levels up\n\n// ── 7. Bindings (applyBinding) ───────────────────────────────────────────────\n// The framework applies all [[signal]] bindings automatically via applyAllBindings.\n// For dynamic / programmatic bindings you can call it manually:\n//\n// const cleanup = window.appShell.bindingsHelper.applyAllBindings(\n// shadowRoot, // root element to scan\n// instance._getRelativeSignalsPath(), // relative OID prefix\n// instance // property source for ?prop bindings\n// );\n// // cleanup is an array of unsubscribe functions — call them on disconnect:\n// // cleanup.forEach(fn => fn());\n//\n// Single-element binding (low-level — normally not needed):\n// window.appShell.bindingsHelper.applyBinding(\n// element, // target DOM element\n// binding, // binding descriptor (parsed from bind-prop:* attribute)\n// relPath, // relative signal path\n// instance, // root for ?property lookups\n// null // optional specialValueHandler\n// );\n\n// ════════════════════════════════════════════════════════════════════════════\n\nexport function init(instance, shadowRoot) {\n // Called ONCE when the script module is first loaded (before connectedCallback).\n // Use for one-time setup that does not depend on the element being in the DOM.\n debugger;\n let kmn =instance._getDomElement('jkj');\n let jkj2 =instance._getDomElement('jkj2');\n instance._assignEvent('testprop-changed', handler);\n\n console.log(kmn.textContent);\n //jkj2.textContent='ramil';\n}\nfunction handler(e)\n{\n debugger;\n}\n\nexport function connectedCallback(instance, shadowRoot) {\n // Called each time the control is inserted into the DOM.\n // Start subscriptions, set up event listeners, render initial state.\n //\n // Example:\n // const label = instance._getDomElement('statusLabel');\n // instance._assignEvent('click', async () => {\n // await iobrokerHandler.setState('hm-rpc.0.device.SWITCH', true);\n // });\n}\n\nexport function disconnectedCallback(instance, shadowRoot) {\n // Called each time the control is removed from the DOM.\n // _assignEvent listeners are removed automatically.\n // Manually unsubscribe from iobrokerHandler.subscribeState calls here.\n}\n","properties":{"testprop":{"type":"string","default":"salam"}},"settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}
@@ -0,0 +1 @@
1
+ {"html":"<div id=\"panel-container\" style=\"width:100%;height:100%;border:2px solid rgb(58, 90, 90);border-radius:4px;padding:8px;display:flex;flex-direction:column;font:&quot;Times New Roman&quot;, serif;background:rgb(90, 122, 122);\">\n <div style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;flex-shrink:0;\">\n <div id=\"point-name\" style=\"color:#fff;text-align:center;flex:1;font:bold 14px &quot;Times New Roman&quot;, serif;\">Point</div>\n <svg id=\"trend-icon\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" title=\"Show Trend\" style=\"cursor:pointer;fill:none;stroke:#fff;stroke-width:2;margin-left:4px;flex-shrink:0;\">\n <polyline points=\"3 17 9 11 13 15 21 7\"></polyline>\n <polyline points=\"14 7 21 7 21 14\"></polyline>\n </svg>\n </div>\n <div style=\"display:grid;grid-template-columns:auto 1fr auto;gap:4px 8px;align-items:center;flex:1;\">\n <div id=\"label1\" style=\"color:#e0e0e0;font:italic 12px &quot;Times New Roman&quot;, serif;\"></div>\n <div id=\"value1\" bind-content:text=\"??value1_value\" style=\"color:#fff;text-align:center;font:bold 16px &quot;Times New Roman&quot;, serif;\">NaN</div>\n <div id=\"unit1\" style=\"color:#e0e0e0;font:italic 11px &quot;Times New Roman&quot;, serif;\"></div>\n <div id=\"label2\" style=\"color:#e0e0e0;display:none;font:italic 12px &quot;Times New Roman&quot;, serif;\"></div>\n <div id=\"value2\" style=\"color:#fff;text-align:center;display:none;font:bold 16px &quot;Times New Roman&quot;, serif;\"></div>\n <div id=\"unit2\" style=\"color:#e0e0e0;display:none;font:italic 11px &quot;Times New Roman&quot;, serif;\"></div>\n </div>\n <div id=\"action-button\" style=\"margin-top:8px;text-align:center;display:none;flex-shrink:0;\">\n<button id=\"action-btn\" style=\"border:1px solid rgb(58, 90, 90);color:#fff;border-radius:3px;cursor:pointer;background:rgb(74, 106, 106);font:11px &quot;Times New Roman&quot;, serif;padding:4px 12px;\">Button</button>\n </div>\n</div>\n<dialog id=\"trend-dialog\" bind-css:width=\"??grafana_dialogwidth\" bind-css:height=\"??grafana_dialogheight\" style=\"overflow:hidden;border:2px solid rgb(58, 90, 90);border-radius:8px;padding:0px;background:rgb(255, 255, 255);\">\n <div id=\"dialog-header\" style=\"color:#fff;padding:12px;display:flex;justify-content:space-between;align-items:center;background:rgb(90, 122, 122);\">\n <h3 id=\"dialog-title\" style=\"margin:0px;font:16px &quot;Times New Roman&quot;, serif;\">Trend</h3>\n <button id=\"close-dialog\" style=\"border:initial none initial;color:#fff;cursor:pointer;padding:0px;width:30px;height:30px;background:transparent;font:24px &quot;Times New Roman&quot;, serif;\">×</button>\n </div>\n <div id=\"iframecontainerdiv1\" style=\"padding:0px;width:100%;height:91%;\">\n<iframe id=\"grafana-iframe\" frameborder=\"0\" style=\"width:100%;height:100%;border:initial none initial;\"></iframe>\n </div>\n</dialog>\n","style":"#trend-dialog{\r\n width: 600px;\r\n height: 600px;\r\n}\r\n","script":"let protocol = window.location.protocol;\nlet ip = window.location.hostname;\nlet port = window.location.port;\nlet baseaddress = protocol+\"//\"+ip+\":\"+port;\nfunction formatValue(value, options) {\n if (value === null || value === undefined || value === '' || isNaN(value)) {\n return 'NaN';\n }\n\n let numValue = Number(value);\n\n // Apply scaling\n if (options.scale && options.scale !== 1) {\n numValue = numValue * Number(options.scale);\n }\n\n // Apply offset\n if (options.offset && options.offset !== 0) {\n numValue = numValue + Number(options.offset);\n }\n\n // Apply min/max limits\n if (options.minLimit !== undefined && options.minLimit !== null && options.minLimit !== '') {\n const min = Number(options.minLimit);\n if (numValue < min) numValue = min;\n }\n if (options.maxLimit !== undefined && options.maxLimit !== null && options.maxLimit !== '') {\n const max = Number(options.maxLimit);\n if (numValue > max) numValue = max;\n }\n\n // Apply rounding\n let formattedValue;\n const decimals = options.decimals !== undefined ? Number(options.decimals) : 2;\n\n switch (options.roundMode) {\n case 'floor':\n formattedValue = Math.floor(numValue * Math.pow(10, decimals)) / Math.pow(10, decimals);\n break;\n case 'ceil':\n formattedValue = Math.ceil(numValue * Math.pow(10, decimals)) / Math.pow(10, decimals);\n break;\n case 'round':\n default:\n formattedValue = Math.round(numValue * Math.pow(10, decimals)) / Math.pow(10, decimals);\n break;\n }\n\n // Format to fixed decimals\n let result = formattedValue.toFixed(decimals);\n\n // Add thousand separator if enabled\n if (options.thousandSep) {\n const parts = result.split('.');\n parts[0] = parts[0].replace(/\\B(?=(\\d{3})+(?!\\d))/g, options.thousandSep);\n result = parts.join('.');\n }\n\n // Add prefix/suffix\n if (options.prefix) result = options.prefix + result;\n if (options.suffix) result = result + options.suffix;\n\n return result;\n}\nexport async function init(instance) {\n instance._bindingsRefresh();\n\n applyTheming(instance);\n updatePanel(instance);\n\n instance._assignEvent('point-name-changed', () => updatePanel(instance));\n instance._assignEvent('value1_label-changed', () => updatePanel(instance));\n instance._assignEvent('value1_value-changed', () => updatePanel(instance));\n instance._assignEvent('value1_unit-changed', () => updatePanel(instance));\n instance._assignEvent('value2_label-changed', () => updatePanel(instance));\n instance._assignEvent('value2_value-changed', () => updatePanel(instance));\n instance._assignEvent('value2_unit-changed', () => updatePanel(instance));\n instance._assignEvent('theme-changed', () => applyTheming(instance));\n instance._assignEvent('grafana_url-changed', () => { });\n\n instance._assignEvent('show-button-changed', () => updatePanel(instance));\n instance._assignEvent('button-text-changed', () => updatePanel(instance));\n\n // SCADA functions event listeners\n instance._assignEvent('value1_decimals-changed', () => updatePanel(instance));\n instance._assignEvent('value1_roundmode-changed', () => updatePanel(instance));\n instance._assignEvent('value1_scale-changed', () => updatePanel(instance));\n instance._assignEvent('value1_offset-changed', () => updatePanel(instance));\n instance._assignEvent('value1_minlimit-changed', () => updatePanel(instance));\n instance._assignEvent('value1_maxlimit-changed', () => updatePanel(instance));\n instance._assignEvent('value1_prefix-changed', () => updatePanel(instance));\n instance._assignEvent('value1_suffix-changed', () => updatePanel(instance));\n instance._assignEvent('value1_thousandsep-changed', () => updatePanel(instance));\n instance._assignEvent('value2_decimals-changed', () => updatePanel(instance));\n instance._assignEvent('value2_roundmode-changed', () => updatePanel(instance));\n instance._assignEvent('value2_scale-changed', () => updatePanel(instance));\n instance._assignEvent('value2_offset-changed', () => updatePanel(instance));\n instance._assignEvent('value2_minlimit-changed', () => updatePanel(instance));\n instance._assignEvent('value2_maxlimit-changed', () => updatePanel(instance));\n instance._assignEvent('value2_prefix-changed', () => updatePanel(instance));\n instance._assignEvent('value2_suffix-changed', () => updatePanel(instance));\n instance._assignEvent('value2_thousandsep-changed', () => updatePanel(instance));\n\n const trendIcon = instance._getDomElement('trend-icon');\n const dialog = instance._getDomElement('trend-dialog');\n const closeBtn = instance._getDomElement('close-dialog');\n const iframe = instance._getDomElement('grafana-iframe');\n const dialogTitle = instance._getDomElement('dialog-title');\n\n /*if (!instance.grafanaUrl || instance.grafanaUrl.trim() === \"\") {\n console.log(\"Grafana URL boşdur!\");\n } else {\n debugger;\n // URL boş deyilsə, parametrləri əlavə et\n const params = new URLSearchParams({\n \"var-selected_state\": instance.grafana_selected_state_name1,\n \"var-selected_state2\": instance.grafana_selected_state_name2,\n \"var-tag_name\": instance.pointName,\n kiosk: \"\" // kiosk parametri əlavə olunur\n });\n\n\n\n const finalUrl = `${instance.grafanaUrl}?${params.toString()}`;\n // instance.grafanaUrl = finalUrl;\n }*/\n\n\n\n\n\n if (trendIcon && dialog) {\n trendIcon.addEventListener('click', async() => {\n if (!instance.grafana_url || instance.grafana_url.trim() === \"\") {\n console.log(\"Grafana URL boşdur!\");\n } else {\n debugger;\n // URL obyektini qururuq\n const url = new URL(instance.grafana_url, baseaddress); // baza hostu əlavə et\n\n // Mövcud parametrləri götürürük\n const params = url.searchParams;\n\n // İstədiyin parametrlərə dəyər ver\n params.set(\"var-selected_state\", instance.grafana_selected_state_name1);\n params.set(\"var-selected_state2\", instance.grafana_selected_state_name2);\n params.set(\"var-tag_name\", instance.pointName);\n\n\n // Yenilənmiş URL\n const finalUrl = `${url.href}&kiosk`;\n //console.log(finalUrl);\n\n //const grafanaUrl = instance.grafanaUrl || 'about:blank';\n // ioBroker-dən cari useri al\n iframe.src = finalUrl;\n dialogTitle.textContent = (instance.pointName || 'Trend') + ' - Trend';\n dialog.showModal();\n \n debugger;\n\n /*const pointName = instance.pointName || 'Trend';\n iframe.src = finalUrl;\n dialogTitle.textContent = pointName + ' - Trend';\n dialog.showModal();*/\n }\n });\n }\n\n if (closeBtn && dialog) {\n closeBtn.addEventListener('click', () => {\n dialog.close();\n iframe.src = 'about:blank';\n });\n }\n\n if (dialog) {\n dialog.addEventListener('click', (e) => {\n const rect = dialog.getBoundingClientRect();\n if (e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom) {\n dialog.close();\n iframe.src = 'about:blank';\n }\n });\n }\n\n const actionBtn = instance._getDomElement('action-btn');\n if (actionBtn) {\n actionBtn.addEventListener('click', () => {\n const event = new CustomEvent('scada-point-button-clicked', {\n detail: { pointName: instance.pointName },\n bubbles: true\n });\n instance.dispatchEvent(event);\n });\n }\n}\n\nfunction updatePanel(instance) {\n const nameEl = instance._getDomElement('point-name');\n if (nameEl) nameEl.textContent = instance.pointName || 'Point';\n\n const label1El = instance._getDomElement('label1');\n const value1El = instance._getDomElement('value1');\n const unit1El = instance._getDomElement('unit1');\n if (label1El) label1El.textContent = instance.value1_label || '';\n\n // Format value1 with SCADA functions\n if (value1El) {\n const value1Options = {\n decimals: instance.value1_decimals,\n roundMode: instance.value1_roundmode,\n scale: instance.value1_scale,\n offset: instance.value1_offset,\n minLimit: instance.value1_minlimit,\n maxLimit: instance.value1_maxlimit,\n prefix: instance.value1_prefix,\n suffix: instance.value1_suffix,\n thousandSep: instance.value1_thousandsep\n };\n value1El.textContent = formatValue(instance.value1_value, value1Options);\n }\n\n if (unit1El) unit1El.textContent = instance.value1_unit || '';\n\n const label2El = instance._getDomElement('label2');\n const value2El = instance._getDomElement('value2');\n const unit2El = instance._getDomElement('unit2');\n const hasRow2 = instance.value2_label || instance.value2_value || instance.value2_unit;\n\n if (label2El) {\n label2El.textContent = instance.value2_label || '';\n label2El.style.display = hasRow2 ? 'block' : 'none';\n }\n if (value2El) {\n const value2Options = {\n decimals: instance.value2_decimals,\n roundMode: instance.value2_roundmode,\n scale: instance.value2_scale,\n offset: instance.value2_offset,\n minLimit: instance.value2_minlimit,\n maxLimit: instance.value2_maxlimit,\n prefix: instance.value2_prefix,\n suffix: instance.value2_suffix,\n thousandSep: instance.value2_thousandsep\n };\n value2El.textContent = formatValue(instance.value2_value, value2Options);\n value2El.style.display = hasRow2 ? 'block' : 'none';\n }\n if (unit2El) {\n unit2El.textContent = instance.value2_unit || '';\n unit2El.style.display = hasRow2 ? 'block' : 'none';\n }\n\n const buttonEl = instance._getDomElement('action-button');\n const buttonTextEl = instance._getDomElement('action-btn');\n if (buttonEl && buttonTextEl) {\n const showButton = instance.showButton === true || instance.showButton === 'true';\n buttonEl.style.display = showButton ? 'block' : 'none';\n buttonTextEl.textContent = instance.buttonText || 'Button';\n }\n}\n\nfunction applyTheming(instance) {\n const theme = instance.theme || 'default';\n const container = instance._getDomElement('panel-container');\n if (!container) return;\n\n const themes = {\n 'default': { bg: '#5a7a7a', border: '#3a5a5a', text: '#fff', label: '#e0e0e0', btnBg: '#4a6a6a' },\n 'dark': { bg: '#2d3436', border: '#000', text: '#fff', label: '#dfe6e9', btnBg: '#636e72' },\n 'light': { bg: '#dfe6e9', border: '#b2bec3', text: '#2d3436', label: '#636e72', btnBg: '#b2bec3' },\n 'blue': { bg: '#0984e3', border: '#0652DD', text: '#fff', label: '#dfe6e9', btnBg: '#74b9ff' },\n 'green': { bg: '#00b894', border: '#00a383', text: '#fff', label: '#d4f1e8', btnBg: '#55efc4' },\n 'red': { bg: '#d63031', border: '#b71c1c', text: '#fff', label: '#ffcccc', btnBg: '#ff7675' },\n 'purple': { bg: '#6c5ce7', border: '#5f3dc4', text: '#fff', label: '#e5dbff', btnBg: '#a29bfe' }\n };\n\n const colors = themes[theme] || themes['default'];\n container.style.background = colors.bg;\n container.style.borderColor = colors.border;\n\n const nameEl = instance._getDomElement('point-name');\n const value1El = instance._getDomElement('value1');\n const value2El = instance._getDomElement('value2');\n if (nameEl) nameEl.style.color = colors.text;\n if (value1El) value1El.style.color = colors.text;\n if (value2El) value2El.style.color = colors.text;\n\n const label1El = instance._getDomElement('label1');\n const label2El = instance._getDomElement('label2');\n const unit1El = instance._getDomElement('unit1');\n const unit2El = instance._getDomElement('unit2');\n if (label1El) label1El.style.color = colors.label;\n if (label2El) label2El.style.color = colors.label;\n if (unit1El) unit1El.style.color = colors.label;\n if (unit2El) unit2El.style.color = colors.label;\n\n const trendIcon = instance._getDomElement('trend-icon');\n if (trendIcon) trendIcon.style.stroke = colors.text;\n\n const actionBtn = instance._getDomElement('action-btn');\n if (actionBtn) {\n actionBtn.style.background = colors.btnBg;\n actionBtn.style.borderColor = colors.border;\n actionBtn.style.color = colors.text;\n }\n\n const dialog = instance._getDomElement('trend-dialog');\n if (dialog) {\n dialog.style.borderColor = colors.border;\n const header = instance._getDomElement('dialog-header');\n if (header) {\n header.style.background = colors.bg;\n header.style.color = colors.text;\n }\n }\n}\n\nexport function connectedCallback(instance)\n{\n \n}\n\nfunction getResolvedSignalId() {\n // bind-prop:value1 atributundan ham pattern-i oxu\n const raw = element.getAttribute('bind-prop:value1');\n if (!raw) return null;\n\n // {??propName} → screen[propName] ilə replace et\n return raw.replace(/\\{(\\?\\?([^}]+))\\}/g, (_, __, propName) => {\n return screen?.[propName] ?? '';\n });\n }","properties":{"pointName":{"type":"string","default":"DPT 010"},"value1_value":{"type":"number"},"value1_label":{"type":"string"},"value1_unit":{"type":"string","default":"kPa"},"value2_label":{"type":"string"},"value2_value":{"type":"number"},"theme":{"type":"enum","default":"default","values":["default","dark","light","blue","green","red","purple"]},"value2_unit":{"type":"string"},"grafana_url":{"type":"string"},"grafana_selected_state_name1":{"type":"string"},"grafana_selected_state_name2":{"type":"string"},"showButton":{"type":"boolean"},"buttonText":{"type":"string","default":"Button"},"grafana_dialogheight":{"type":"string"},"grafana_dialogwidth":{"type":"string"},"value1_decimals":{"type":"number","default":"2"},"value1_roundmode":{"type":"enum","default":"round","values":["round","floor","ceil"]},"value1_scale":{"type":"number","default":"1"},"value1_offset":{"type":"number"},"value1_maxlimit":{"type":"number"},"value1_minlimit":{"type":"number"},"value1_prefix":{"type":"string"},"value1_suffix":{"type":"string"},"value1_thousandsep":{"type":"string"},"value2_decimals":{"type":"number","default":"2"},"value2_roundmode":{"type":"enum","default":"round","values":["round","floor","ceil"]},"value2_scale":{"type":"number","default":"1"},"value2_offset":{"type":"number"},"value2_minlimit":{"type":"number"},"value2_maxlimit":{"type":"number"},"value2_prefix":{"type":"string"},"value2_suffix":{"type":"string"},"value2_thousandsep":{"type":"string"}},"settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}
@@ -0,0 +1 @@
1
+ {"style":":host {\n background: transparent;\n}\n\n\n* {\n box-sizing: border-box;\n}","script":"debugger;\r\n// Original emit funksiyasını saxla\r\nconst originalEmit = window.IOB.connection._socket.emit;\r\n\r\n// Permission bitləri\r\nconst PERMISSION = {\r\n OWNER_WRITE: 0x200,\r\n GROUP_WRITE: 0x020,\r\n EVERYONE_WRITE: 0x002\r\n};\r\n\r\n// User qruplarını tapmaq üçün helper funksiya\r\nasync function getUserGroups(userName) {\r\n try {\r\n const userId = `system.user.${userName}`;\r\n const userGroups = [];\r\n\r\n // Bütün qrupları əldə et və user-in hansı qruplarda olduğunu yoxla\r\n const groups = await new Promise((resolve, reject) => {\r\n window.IOB.connection._socket.emit(\r\n 'getObjectView',\r\n 'system',\r\n 'group',\r\n { startkey: 'system.group.', endkey: 'system.group.\\u9999' },\r\n (err, res) => {\r\n if (err) reject(err);\r\n else resolve(res);\r\n }\r\n );\r\n });\r\n\r\n if (groups && groups.rows) {\r\n for (const row of groups.rows) {\r\n const groupObj = row.value;\r\n const groupId = row.id.replace('system.group.', '');\r\n \r\n // User bu qrupun üzvüdürsə\r\n if (groupObj.common?.members && groupObj.common.members.includes(userId)) {\r\n userGroups.push(groupId);\r\n }\r\n }\r\n }\r\n\r\n return userGroups;\r\n } catch (err) {\r\n console.error('User groups əldə edilərkən xəta:', err);\r\n return [];\r\n }\r\n}\r\n\r\n// Write permission yoxlama funksiyası\r\nasync function hasWritePermission(stateId, currentUserName, userGroups) {\r\n try {\r\n const obj = await window.IOB.getObject(stateId);\r\n if (!obj) {\r\n console.error('Obyekt tapılmadı:', stateId);\r\n return false;\r\n }\r\n\r\n const currentUserId = `system.user.${currentUserName}`;\r\n const acl = obj.acl || {};\r\n const permission = acl.state || 0;\r\n const owner = acl.owner || 'system.user.admin';\r\n const ownerGroup = acl.ownerGroup || 'system.group.administrator';\r\n\r\n console.log('📝 Permission yoxlanılır:', {\r\n stateId,\r\n currentUser: currentUserId,\r\n userGroups,\r\n owner,\r\n ownerGroup,\r\n permission: '0x' + permission.toString(16)\r\n });\r\n\r\n // Owner yoxla\r\n if (currentUserId === owner) {\r\n const hasOwnerWrite = !!(permission & PERMISSION.OWNER_WRITE);\r\n console.log('👤 Owner check:', hasOwnerWrite);\r\n if (hasOwnerWrite) return true;\r\n }\r\n\r\n // Group yoxla\r\n const ownerGroupName = ownerGroup.replace('system.group.', '');\r\n if (userGroups.includes(ownerGroupName)) {\r\n const hasGroupWrite = !!(permission & PERMISSION.GROUP_WRITE);\r\n console.log('👥 Group check:', hasGroupWrite);\r\n if (hasGroupWrite) return true;\r\n }\r\n\r\n // Everyone yoxla\r\n const hasEveryoneWrite = !!(permission & PERMISSION.EVERYONE_WRITE);\r\n console.log('🌍 Everyone check:', hasEveryoneWrite);\r\n if (hasEveryoneWrite) return true;\r\n\r\n return false;\r\n } catch (err) {\r\n console.error('Permission yoxlanılarkən xəta:', err);\r\n return false;\r\n }\r\n}\r\n\r\n// setState intercept\r\nwindow.IOB.connection._socket.emit = async function(event, ...args) {\r\n if (event === 'setState') {\r\n const [id, value] = args;\r\n \r\n try {\r\n // Current user əldə et\r\n const currentUserName = await window.IOB.connection.getCurrentUser();\r\n console.log('🔐 Current user:', currentUserName);\r\n\r\n // Admin istifadəçiləri yoxlama\r\n if (currentUserName === 'admin') {\r\n console.log('✅ Admin istifadəçi - icazə yoxlanmır');\r\n originalEmit.call(window.IOB.connection._socket, event, ...args);\r\n return;\r\n }\r\n\r\n // User qruplarını tap\r\n const userGroups = await getUserGroups(currentUserName);\r\n console.log('👥 User groups:', userGroups);\r\n\r\n // Administrator qrupunda olanlar üçün yoxlama etmə\r\n if (userGroups.includes('administrator')) {\r\n console.log('✅ Administrator qrupunda - icazə yoxlanmır');\r\n originalEmit.call(window.IOB.connection._socket, event, ...args);\r\n return;\r\n }\r\n\r\n // Write permission yoxla\r\n const hasPermission = await hasWritePermission(id, currentUserName, userGroups);\r\n \r\n if (!hasPermission) {\r\n console.warn('⛔ Write permission yoxdur:', id);\r\n alert('Yazmaq mümkün deyil, icazə yoxdur!');\r\n return;\r\n }\r\n\r\n console.log('✅ Write icazəsi təsdiqləndi');\r\n originalEmit.call(window.IOB.connection._socket, event, ...args);\r\n } catch (err) {\r\n console.error('❌ setState yoxlanılarkən xəta:', err);\r\n alert('İcazə yoxlanılarkən xəta baş verdi!');\r\n }\r\n } else {\r\n // Digər event-lər normal getsin\r\n originalEmit.call(window.IOB.connection._socket, event, ...args);\r\n }\r\n};\r\n\r\nconsole.log('✅ setState permission yoxlaması aktivləşdirildi');","settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}
@@ -0,0 +1 @@
1
+ {"html":"<svg xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" id=\"_x30_\" x=\"0px\" y=\"0px\" width=\"100\" height=\"35\" viewBox=\"0 0 203 65\" enable-background=\"new 0 0 75 282\" xml:space=\"preserve\">\n <defs id=\"defs5\">\n <linearGradient id=\"cyl\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0.0051\" id=\"stop8\" style=\"stop-color:#4C5B75;\"></stop>\n <stop offset=\"0.0323\" id=\"stop10\" style=\"stop-color:#616F86;\"></stop>\n <stop offset=\"0.098\" id=\"stop12\" style=\"stop-color:#8E99AA;\"></stop>\n <stop offset=\"0.1641\" id=\"stop14\" style=\"stop-color:#B4BCC7;\"></stop>\n <stop offset=\"0.2298\" id=\"stop16\" style=\"stop-color:#D1D7DE;\"></stop>\n <stop offset=\"0.2946\" id=\"stop18\" style=\"stop-color:#E5EAEF;\"></stop>\n <stop offset=\"0.3584\" id=\"stop20\" style=\"stop-color:#F2F6F9;\"></stop>\n <stop offset=\"0.42\" id=\"stop22\" style=\"stop-color:#F6FAFC;\"></stop>\n <stop offset=\"0.4709\" id=\"stop24\" style=\"stop-color:#F2F6F8;\"></stop>\n <stop offset=\"0.5236\" id=\"stop26\" style=\"stop-color:#E6E9EC;\"></stop>\n <stop offset=\"0.5772\" id=\"stop28\" style=\"stop-color:#D3D5D7;\"></stop>\n <stop offset=\"0.6314\" id=\"stop30\" style=\"stop-color:#B7B8BB;\"></stop>\n <stop offset=\"0.6856\" id=\"stop32\" style=\"stop-color:#949396;\"></stop>\n <stop offset=\"0.7005\" id=\"stop34\" style=\"stop-color:#89878B;\"></stop>\n <stop offset=\"1\" id=\"stop36\" style=\"stop-color:#F6FAFC;\"></stop>\n </linearGradient>\n <linearGradient id=\"cyl_1\" xlink:href=\"#cyl\" x1=\"237.6904\" y1=\"-198.28101\" x2=\"284.91599\" y2=\"-198.28101\" gradientTransform=\"matrix(1,0,0,-1,-229.6201,-4.0747)\"></linearGradient>\n <linearGradient id=\"cyl_2\" xlink:href=\"#cyl\" x1=\"249.4922\" y1=\"-120.9465\" x2=\"273.11331\" y2=\"-120.9465\" gradientTransform=\"matrix(1,0,0,-1.4268629,-225.59296,-272.06735)\"></linearGradient>\n <linearGradient id=\"cyl_3\" xlink:href=\"#cyl\" x1=\"230.6201\" y1=\"-64.7146\" x2=\"291.98541\" y2=\"-64.7146\" gradientTransform=\"matrix(1,0,0,-1,-229.6201,-4.0747)\"></linearGradient>\n <linearGradient id=\"cyl_4\" xlink:href=\"#cyl\" x1=\"302.61911\" y1=\"-63.978298\" x2=\"302.61911\" y2=\"-36.616299\" gradientTransform=\"matrix(1,0,0,-1,-229.6201,-4.0747)\"></linearGradient>\n <linearGradient id=\"cyl_5\" xlink:href=\"#cyl\" x1=\"291.15631\" y1=\"-42.905499\" x2=\"291.15631\" y2=\"-57.266998\" gradientTransform=\"matrix(0,1,1,0,288.3133,-131.47953)\"></linearGradient>\n <linearGradient id=\"cyl_6\" xlink:href=\"#cyl\" x1=\"230.6201\" y1=\"-6.9001002\" x2=\"291.98541\" y2=\"-6.9001002\" gradientTransform=\"matrix(1,0,0,-1,-229.6201,-4.0747)\"></linearGradient>\n <linearGradient id=\"cyl_7\" xlink:href=\"#cyl\" x1=\"239.01759\" y1=\"-35.802502\" x2=\"283.96289\" y2=\"-35.802502\" gradientTransform=\"matrix(0,1,1,0,335.46745,-94.685002)\"></linearGradient>\n <linearGradient id=\"cyl_8\" xlink:href=\"#cyl\" x1=\"237.6904\" y1=\"-178.6501\" x2=\"284.91599\" y2=\"-178.6501\" gradientTransform=\"matrix(1,0,0,-1,-229.6201,-4.0747)\"></linearGradient>\n <linearGradient id=\"cyl_9\" xlink:href=\"#cyl\" x1=\"248.2188\" y1=\"-270.7522\" x2=\"274.36621\" y2=\"-270.7522\" gradientTransform=\"matrix(0.93168845,0,0,-1,-137.54183,-275.35227)\"></linearGradient>\n <linearGradient id=\"con_1\" xlink:href=\"#cyl\" x1=\"235.57809\" y1=\"-196.8567\" x2=\"284.2084\" y2=\"-178.7756\" gradientTransform=\"matrix(1,0,0,-1,-229.6201,-4.0747)\"></linearGradient>\n <linearGradient id=\"window_1\" xlink:href=\"#cyl\" x1=\"252.6523\" y1=\"-35.813702\" x2=\"298.16669\" y2=\"-35.813702\" gradientTransform=\"matrix(0,1,1,0,339.69776,-161.32446)\"></linearGradient>\n <linearGradient gradientUnits=\"userSpaceOnUse\" id=\"cyl_8-0\" xlink:href=\"#cyl\" x1=\"4204.1602\" y1=\"-2699.9953\" x2=\"5038.8649\" y2=\"-2699.9953\" gradientTransform=\"scale(2.6719336,0.3742608)\"></linearGradient>\n <linearGradient inkscape:collect=\"always\" xlink:href=\"#cyl\" id=\"linearGradient4762\" x1=\"8.0699997\" y1=\"174.5755\" x2=\"55.296001\" y2=\"174.5755\" gradientUnits=\"userSpaceOnUse\"></linearGradient>\n <linearGradient inkscape:collect=\"always\" xlink:href=\"#cyl\" id=\"linearGradient4482\" x1=\"23.399132\" y1=\"-99.493668\" x2=\"48.02013\" y2=\"-99.493668\" gradientUnits=\"userSpaceOnUse\"></linearGradient>\n </defs>\n <rect id=\"rect59\" height=\"155.2327\" width=\"23.621\" y=\"-177.11002\" x=\"23.899132\" transform=\"matrix(0,1,-1,0,0,0)\" style=\"fill:url(#linearGradient4482);stroke:#000000;stroke-opacity:1;fill-opacity:1.0;\"></rect>\n <polygon id=\"polygon70\" points=\"8.07,177.883 8.07,171.268 55.296,171.268 55.296,177.883 \" transform=\"matrix(0,1,-1.7161506,0,316.15626,2.5982347)\" style=\"fill:url(#cyl_8);stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;\"></polygon>\n <polygon id=\"polygon70-8\" points=\"55.296,177.883 8.07,177.883 8.07,171.268 55.296,171.268 \" transform=\"matrix(0,1,-1.6559406,0,471.29931,1.8808984)\" style=\"fill:url(#linearGradient4762);fill-opacity:1;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;\"></polygon>\n</svg>\n","style":":host {\n background: transparent;\n}\n\n* {\n box-sizing: border-box;\n}","settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}