iobroker.mywebui 1.134.0 → 1.162.7

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 (337) hide show
  1. package/README.md +6 -0
  2. package/admin/jsonConfig.json +68 -0
  3. package/default-controls/controls/Language/LanguageSelector2.control +1 -1
  4. package/default-controls/controls/WebuiConnectionMonitor1.control +1 -1
  5. package/default-controls/controls/navigation/sidebar.control +1 -1
  6. package/default-controls/controls/svg/svg_base64_imagecontainer.control +1 -0
  7. package/default-controls/controls/svg/svg_base64_viewer.control +1 -0
  8. package/default-controls/controls/valve_faceplate.control +1 -1
  9. package/dist/backend/3d-editor-integration.js +1 -1
  10. package/dist/backend/fonts/Roboto-Italic.ttf +0 -0
  11. package/dist/backend/fonts/Roboto-Medium.ttf +0 -0
  12. package/dist/backend/fonts/Roboto-MediumItalic.ttf +0 -0
  13. package/dist/backend/fonts/Roboto-Regular.ttf +0 -0
  14. package/dist/backend/main.js +1 -1
  15. package/dist/backend/reportCsv.js +1 -0
  16. package/dist/backend/reportGenerate.js +1 -0
  17. package/dist/backend/reportOutput.js +1 -0
  18. package/dist/backend/reportPdf.js +1 -0
  19. package/dist/backend/reportRenderer.js +1 -0
  20. package/dist/backend/reportSandbox.js +1 -0
  21. package/dist/backend/reportSandboxWorker.js +1 -0
  22. package/dist/backend/vendor/report-dom.iife.js +19 -0
  23. package/dist/backend/vendor/report-web.iife.js +1 -0
  24. package/docs/README.md +6 -0
  25. package/docs/bindings.md +28 -0
  26. package/docs/custom-controls.md +72 -1
  27. package/docs/npm-widgets.md +15 -0
  28. package/docs/permissions.md +14 -0
  29. package/docs/reports.md +258 -0
  30. package/docs/screens.md +10 -0
  31. package/docs/template-directives.md +269 -8
  32. package/docs/theming.md +7 -0
  33. package/docs/translations.md +33 -0
  34. package/io-package.json +7 -2
  35. package/native/mywebui_scada_engine.darwin-arm64.node +0 -0
  36. package/native/mywebui_scada_engine.darwin-x64.node +0 -0
  37. package/native/mywebui_scada_engine.linux-arm-gnueabihf.node +0 -0
  38. package/native/mywebui_scada_engine.linux-arm64-gnu.node +0 -0
  39. package/native/mywebui_scada_engine.linux-arm64-musl.node +0 -0
  40. package/native/mywebui_scada_engine.linux-x64-gnu.node +0 -0
  41. package/native/mywebui_scada_engine.linux-x64-musl.node +0 -0
  42. package/native/mywebui_scada_engine.win32-x64-msvc.node +0 -0
  43. package/package.json +6 -1
  44. package/www/3d-editor/js/Animation.js +1 -1
  45. package/www/3d-editor/js/AnimationResizer.js +1 -1
  46. package/www/3d-editor/js/Command.js +1 -1
  47. package/www/3d-editor/js/Config.js +1 -1
  48. package/www/3d-editor/js/Editor.js +1 -1
  49. package/www/3d-editor/js/EditorControls.js +1 -1
  50. package/www/3d-editor/js/GLTFImportDialog.js +1 -1
  51. package/www/3d-editor/js/History.js +1 -1
  52. package/www/3d-editor/js/Loader.js +1 -1
  53. package/www/3d-editor/js/LoaderUtils.js +1 -1
  54. package/www/3d-editor/js/Menubar.Add.js +1 -1
  55. package/www/3d-editor/js/Menubar.Edit.js +1 -1
  56. package/www/3d-editor/js/Menubar.File.js +1 -1
  57. package/www/3d-editor/js/Menubar.Help.js +1 -1
  58. package/www/3d-editor/js/Menubar.Render.js +1 -1
  59. package/www/3d-editor/js/Menubar.Status.js +1 -1
  60. package/www/3d-editor/js/Menubar.View.js +1 -1
  61. package/www/3d-editor/js/Menubar.js +1 -1
  62. package/www/3d-editor/js/Player.js +1 -1
  63. package/www/3d-editor/js/Resizer.js +1 -1
  64. package/www/3d-editor/js/Script.js +1 -1
  65. package/www/3d-editor/js/Selector.js +1 -1
  66. package/www/3d-editor/js/Sidebar.Geometry.BoxGeometry.js +1 -1
  67. package/www/3d-editor/js/Sidebar.Geometry.BufferGeometry.js +1 -1
  68. package/www/3d-editor/js/Sidebar.Geometry.CapsuleGeometry.js +1 -1
  69. package/www/3d-editor/js/Sidebar.Geometry.CircleGeometry.js +1 -1
  70. package/www/3d-editor/js/Sidebar.Geometry.CylinderGeometry.js +1 -1
  71. package/www/3d-editor/js/Sidebar.Geometry.DodecahedronGeometry.js +1 -1
  72. package/www/3d-editor/js/Sidebar.Geometry.ExtrudeGeometry.js +1 -1
  73. package/www/3d-editor/js/Sidebar.Geometry.IcosahedronGeometry.js +1 -1
  74. package/www/3d-editor/js/Sidebar.Geometry.LatheGeometry.js +1 -1
  75. package/www/3d-editor/js/Sidebar.Geometry.Modifiers.js +1 -1
  76. package/www/3d-editor/js/Sidebar.Geometry.OctahedronGeometry.js +1 -1
  77. package/www/3d-editor/js/Sidebar.Geometry.PlaneGeometry.js +1 -1
  78. package/www/3d-editor/js/Sidebar.Geometry.RingGeometry.js +1 -1
  79. package/www/3d-editor/js/Sidebar.Geometry.ShapeGeometry.js +1 -1
  80. package/www/3d-editor/js/Sidebar.Geometry.SphereGeometry.js +1 -1
  81. package/www/3d-editor/js/Sidebar.Geometry.TetrahedronGeometry.js +1 -1
  82. package/www/3d-editor/js/Sidebar.Geometry.TextGeometry.js +1 -1
  83. package/www/3d-editor/js/Sidebar.Geometry.TorusGeometry.js +1 -1
  84. package/www/3d-editor/js/Sidebar.Geometry.TorusKnotGeometry.js +1 -1
  85. package/www/3d-editor/js/Sidebar.Geometry.TubeGeometry.js +1 -1
  86. package/www/3d-editor/js/Sidebar.Geometry.js +1 -1
  87. package/www/3d-editor/js/Sidebar.Material.BooleanProperty.js +1 -1
  88. package/www/3d-editor/js/Sidebar.Material.ColorProperty.js +1 -1
  89. package/www/3d-editor/js/Sidebar.Material.ConstantProperty.js +1 -1
  90. package/www/3d-editor/js/Sidebar.Material.MapProperty.js +1 -1
  91. package/www/3d-editor/js/Sidebar.Material.NumberProperty.js +1 -1
  92. package/www/3d-editor/js/Sidebar.Material.Program.js +1 -1
  93. package/www/3d-editor/js/Sidebar.Material.RangeValueProperty.js +1 -1
  94. package/www/3d-editor/js/Sidebar.Material.js +1 -1
  95. package/www/3d-editor/js/Sidebar.Object.js +1 -1
  96. package/www/3d-editor/js/Sidebar.Project.App.js +1 -1
  97. package/www/3d-editor/js/Sidebar.Project.Materials.js +1 -1
  98. package/www/3d-editor/js/Sidebar.Project.Renderer.js +1 -1
  99. package/www/3d-editor/js/Sidebar.Project.Resources.js +1 -1
  100. package/www/3d-editor/js/Sidebar.Project.js +1 -1
  101. package/www/3d-editor/js/Sidebar.Properties.js +1 -1
  102. package/www/3d-editor/js/Sidebar.Scene.js +1 -1
  103. package/www/3d-editor/js/Sidebar.Script.js +1 -1
  104. package/www/3d-editor/js/Sidebar.Settings.History.js +1 -1
  105. package/www/3d-editor/js/Sidebar.Settings.Shortcuts.js +1 -1
  106. package/www/3d-editor/js/Sidebar.Settings.js +1 -1
  107. package/www/3d-editor/js/Sidebar.js +1 -1
  108. package/www/3d-editor/js/Storage.js +1 -1
  109. package/www/3d-editor/js/Strings.js +1 -1
  110. package/www/3d-editor/js/TextureParametersDialog.js +1 -1
  111. package/www/3d-editor/js/Toolbar.js +1 -1
  112. package/www/3d-editor/js/Viewport.Controls.js +1 -1
  113. package/www/3d-editor/js/Viewport.Info.js +1 -1
  114. package/www/3d-editor/js/Viewport.Pathtracer.js +1 -1
  115. package/www/3d-editor/js/Viewport.ViewHelper.js +1 -1
  116. package/www/3d-editor/js/Viewport.XR.js +1 -1
  117. package/www/3d-editor/js/Viewport.js +1 -1
  118. package/www/3d-editor/js/commands/AddObjectCommand.js +1 -1
  119. package/www/3d-editor/js/commands/AddScriptCommand.js +1 -1
  120. package/www/3d-editor/js/commands/Commands.js +1 -1
  121. package/www/3d-editor/js/commands/MoveObjectCommand.js +1 -1
  122. package/www/3d-editor/js/commands/MultiCmdsCommand.js +1 -1
  123. package/www/3d-editor/js/commands/RemoveObjectCommand.js +1 -1
  124. package/www/3d-editor/js/commands/RemoveScriptCommand.js +1 -1
  125. package/www/3d-editor/js/commands/SetColorCommand.js +1 -1
  126. package/www/3d-editor/js/commands/SetGeometryCommand.js +1 -1
  127. package/www/3d-editor/js/commands/SetGeometryValueCommand.js +1 -1
  128. package/www/3d-editor/js/commands/SetMaterialColorCommand.js +1 -1
  129. package/www/3d-editor/js/commands/SetMaterialCommand.js +1 -1
  130. package/www/3d-editor/js/commands/SetMaterialMapCommand.js +1 -1
  131. package/www/3d-editor/js/commands/SetMaterialRangeCommand.js +1 -1
  132. package/www/3d-editor/js/commands/SetMaterialValueCommand.js +1 -1
  133. package/www/3d-editor/js/commands/SetMaterialVectorCommand.js +1 -1
  134. package/www/3d-editor/js/commands/SetPositionCommand.js +1 -1
  135. package/www/3d-editor/js/commands/SetRotationCommand.js +1 -1
  136. package/www/3d-editor/js/commands/SetScaleCommand.js +1 -1
  137. package/www/3d-editor/js/commands/SetSceneCommand.js +1 -1
  138. package/www/3d-editor/js/commands/SetScriptValueCommand.js +1 -1
  139. package/www/3d-editor/js/commands/SetShadowValueCommand.js +1 -1
  140. package/www/3d-editor/js/commands/SetTextureParametersCommand.js +1 -1
  141. package/www/3d-editor/js/commands/SetUuidCommand.js +1 -1
  142. package/www/3d-editor/js/commands/SetValueCommand.js +1 -1
  143. package/www/assets/icons/neon/file-report.svg +1 -0
  144. package/www/assets/icons/neon/folder-reports.svg +1 -0
  145. package/www/dist/frontend/bundle/HistoryDirectives-RFNWGNZD.js +1 -0
  146. package/www/dist/frontend/bundle/ReportViewer-4ZI6KOQN.js +197 -0
  147. package/www/dist/frontend/bundle/Sha256-BSH7CIPW.js +1 -0
  148. package/www/dist/frontend/bundle/chunk-FBQ2Z2FQ.js +1 -0
  149. package/www/dist/frontend/bundle/chunk-KVPT4J7H.js +1 -0
  150. package/www/dist/frontend/bundle/chunk-R2IYVC73.js +21 -0
  151. package/www/dist/frontend/bundle/common/IobrokerHandler.js +1 -1
  152. package/www/dist/frontend/bundle/runtime/CustomControls.js +1 -1
  153. package/www/dist/frontend/bundle/runtime/ScreenViewer.js +1 -1
  154. package/www/dist/frontend/bundle/runtime/controls.js +4 -4
  155. package/www/dist/frontend/bundle/runtime/init.js +3 -3
  156. package/www/dist/frontend/common/BindingExpression.js +1 -0
  157. package/www/dist/frontend/common/Common.globals.js +1 -1
  158. package/www/dist/frontend/common/EChartsOption.js +1 -0
  159. package/www/dist/frontend/common/HistoryDirective.js +1 -0
  160. package/www/dist/frontend/common/HistoryOptions.js +1 -0
  161. package/www/dist/frontend/common/HistoryQuery.js +1 -0
  162. package/www/dist/frontend/common/HistoryQueryViews.js +1 -0
  163. package/www/dist/frontend/common/IobrokerHandler.js +1 -1
  164. package/www/dist/frontend/common/PlantPaxResolve.js +1 -1
  165. package/www/dist/frontend/common/ReportAllowlist.js +1 -0
  166. package/www/dist/frontend/common/ReportSchedule.js +1 -0
  167. package/www/dist/frontend/common/ReportTheme.js +1 -0
  168. package/www/dist/frontend/common/Runtime.js +1 -1
  169. package/www/dist/frontend/common/Sha256.js +1 -0
  170. package/www/dist/frontend/config/CommandHandling.js +1 -1
  171. package/www/dist/frontend/config/ConfigureWebcomponentDesigner.js +1 -1
  172. package/www/dist/frontend/config/DockHelper.js +1 -1
  173. package/www/dist/frontend/config/HistoryQueryWindow.js +1 -0
  174. package/www/dist/frontend/config/IobrokerWebui3DDriveEngine.js +1 -1
  175. package/www/dist/frontend/config/IobrokerWebui3DScreenEditor.js +2 -2
  176. package/www/dist/frontend/config/IobrokerWebui3DScreenPropertiesPanel.js +3 -3
  177. package/www/dist/frontend/config/IobrokerWebui3DScreenViewer.js +3 -3
  178. package/www/dist/frontend/config/IobrokerWebuiAppShell.js +13 -3
  179. package/www/dist/frontend/config/IobrokerWebuiBindingsEditor.js +1 -1
  180. package/www/dist/frontend/config/IobrokerWebuiConfirmationWrapper.js +3 -3
  181. package/www/dist/frontend/config/IobrokerWebuiControlPropertiesEditor.js +3 -3
  182. package/www/dist/frontend/config/IobrokerWebuiDynamicPropsEditor.js +1 -1
  183. package/www/dist/frontend/config/IobrokerWebuiEventAssignment.js +1 -1
  184. package/www/dist/frontend/config/IobrokerWebuiIconsView.js +2 -2
  185. package/www/dist/frontend/config/IobrokerWebuiMonacoEditor.js +3 -3
  186. package/www/dist/frontend/config/IobrokerWebuiPlantPaxTypesEditor.js +1 -1
  187. package/www/dist/frontend/config/IobrokerWebuiPropertyGrid.js +1 -1
  188. package/www/dist/frontend/config/IobrokerWebuiScreenEditor.js +1 -1
  189. package/www/dist/frontend/config/IobrokerWebuiScreensView.js +3 -3
  190. package/www/dist/frontend/config/IobrokerWebuiSecurityEditor.js +1 -1
  191. package/www/dist/frontend/config/IobrokerWebuiSignalPropertyEditor.js +1 -1
  192. package/www/dist/frontend/config/IobrokerWebuiSolutionExplorer.js +3 -3
  193. package/www/dist/frontend/config/IobrokerWebuiTranslationEditor.js +1 -1
  194. package/www/dist/frontend/config/IobrokerWebuiWidgetGallery.js +3 -3
  195. package/www/dist/frontend/config/RightDockStrip.js +1 -0
  196. package/www/dist/frontend/helper/DialogHelper.js +2 -2
  197. package/www/dist/frontend/helper/EditorOfDesignItem.js +1 -0
  198. package/www/dist/frontend/helper/EsprimaHelper.js +1 -1
  199. package/www/dist/frontend/helper/Helper.js +1 -1
  200. package/www/dist/frontend/helper/LayoutHelper.js +1 -1
  201. package/www/dist/frontend/helper/XmlHelper.js +1 -1
  202. package/www/dist/frontend/helper/ZipHelper.js +1 -1
  203. package/www/dist/frontend/runtime/AnimationService.js +1 -1
  204. package/www/dist/frontend/runtime/CustomControls.js +1 -1
  205. package/www/dist/frontend/runtime/DynamicElementProperties.js +1 -1
  206. package/www/dist/frontend/runtime/EChart.js +1 -0
  207. package/www/dist/frontend/runtime/HabPanelLikeMenu.js +3 -3
  208. package/www/dist/frontend/runtime/HistoryDirectives.js +1 -0
  209. package/www/dist/frontend/runtime/NavigationShell.js +1 -1
  210. package/www/dist/frontend/runtime/ReportViewer.js +197 -0
  211. package/www/dist/frontend/runtime/RuntimeTheme.js +1 -1
  212. package/www/dist/frontend/runtime/ScreenViewer.js +3 -3
  213. package/www/dist/frontend/runtime/TranslateableText.js +2 -2
  214. package/www/dist/frontend/runtime/VisibilityService.js +1 -1
  215. package/www/dist/frontend/runtime/controls.js +1 -1
  216. package/www/dist/frontend/runtime/init.js +1 -1
  217. package/www/dist/frontend/scripting/IobrokerWebuiScriptSystem.js +1 -1
  218. package/www/dist/frontend/scripting/blockly/OpenScreen.js +1 -1
  219. package/www/dist/frontend/scripting/blockly/webuiBlocklyToolbox.js +1 -1
  220. package/www/dist/frontend/services/DynamicPropertiesHelper.js +1 -1
  221. package/www/dist/frontend/services/IobrokerWebuiBindableLocalObjectsService.js +1 -1
  222. package/www/dist/frontend/services/IobrokerWebuiBindableObjectDragDropService.js +1 -1
  223. package/www/dist/frontend/services/IobrokerWebuiBindableObjectsForPropertiesService.js +1 -1
  224. package/www/dist/frontend/services/IobrokerWebuiBindableObjectsService.js +1 -1
  225. package/www/dist/frontend/services/IobrokerWebuiConfigButtonProvider.js +1 -1
  226. package/www/dist/frontend/services/IobrokerWebuiCopyPasteService.js +1 -1
  227. package/www/dist/frontend/services/IobrokerWebuiCustomControlEventsService.js +1 -1
  228. package/www/dist/frontend/services/IobrokerWebuiCustomElementContextMenu.js +1 -1
  229. package/www/dist/frontend/services/IobrokerWebuiDemoProviderService.js +1 -1
  230. package/www/dist/frontend/services/IobrokerWebuiDynamicPropertiesService.js +1 -1
  231. package/www/dist/frontend/services/IobrokerWebuiEChartPropertiesService.js +1 -0
  232. package/www/dist/frontend/services/IobrokerWebuiEventsService.js +1 -1
  233. package/www/dist/frontend/services/IobrokerWebuiExternalDragDropService.js +1 -1
  234. package/www/dist/frontend/services/IobrokerWebuiLitPropertiesService.js +1 -1
  235. package/www/dist/frontend/services/IobrokerWebuiPropertiesService.js +1 -1
  236. package/www/dist/frontend/services/IobrokerWebuiPropertyGroupsService.js +1 -1
  237. package/www/dist/frontend/services/IobrokerWebuiRefactorService.js +1 -1
  238. package/www/dist/frontend/services/IobrokerWebuiReportContextMenu.js +1 -0
  239. package/www/dist/frontend/services/IobrokerWebuiScreenContextMenu.js +1 -1
  240. package/www/dist/frontend/services/IobrokerWebuiSpecialPropertiesService.js +1 -1
  241. package/www/dist/frontend/services/IobrokerWebuiSpectrumEditorService.js +1 -1
  242. package/www/dist/frontend/services/IobrokerWebuiVisibilityPropertiesService.js +1 -1
  243. package/www/dist/frontend/services/LayoutComponentsPropertiesService.js +1 -1
  244. package/www/dist/frontend/widgets/OrthoFlowLineTool.js +1 -1
  245. package/www/dist/frontend/widgets/ReportToolbox.js +21 -0
  246. package/www/dist/frontend/widgets/customElementsObserver.js +1 -1
  247. package/www/dist/frontend/widgets/importWidgetFiles.js +1 -1
  248. package/www/dist/frontend/widgets/importWidgetFilesRuntime.js +1 -1
  249. package/www/dist/frontend/widgets/layout-components.js +1 -1
  250. package/www/dist/frontend/widgets/layout-placement-services.js +1 -1
  251. package/www/dist/frontend/widgets/testElement.js +1 -1
  252. package/www/libs/@gokturk413/base-custom-webcomponent/dist/BaseCustomWebComponent.d.ts +3 -0
  253. package/www/libs/@gokturk413/base-custom-webcomponent/dist/BaseCustomWebComponent.js +121 -85
  254. package/www/libs/@gokturk413/web-component-designer/dist/elements/widgets/designerView/designerCanvas.js +3 -1
  255. package/www/libs/@gokturk413/web-component-designer/dist/index-min.js +1 -1
  256. package/www/libs/@gokturk413/web-component-designer-visualization-addons/dist/helpers/BindingsHelper.js +3 -1
  257. package/www/node_modules/echarts/LICENSE +222 -0
  258. package/www/node_modules/echarts/dist/echarts.min.js +45 -0
  259. package/www/runtime.html +37 -0
  260. package/www/dist/frontend/bundle/chunk-34JJK367.js +0 -21
  261. package/www/dist/frontend/bundle/chunk-3AS7GRBW.js +0 -21
  262. package/www/dist/frontend/bundle/chunk-3C6XC7RW.js +0 -1
  263. package/www/dist/frontend/bundle/chunk-3R27DWVW.js +0 -21
  264. package/www/dist/frontend/bundle/chunk-3WGNLIWJ.js +0 -21
  265. package/www/dist/frontend/bundle/chunk-3YK3Y2SJ.js +0 -1
  266. package/www/dist/frontend/bundle/chunk-43HEBO5G.js +0 -21
  267. package/www/dist/frontend/bundle/chunk-5MUGMRXU.js +0 -1
  268. package/www/dist/frontend/bundle/chunk-5UBRCTRX.js +0 -21
  269. package/www/dist/frontend/bundle/chunk-65A3PNMG.js +0 -1
  270. package/www/dist/frontend/bundle/chunk-66EU64WD.js +0 -1
  271. package/www/dist/frontend/bundle/chunk-6BL3X2VD.js +0 -21
  272. package/www/dist/frontend/bundle/chunk-6L2YCGEX.js +0 -1
  273. package/www/dist/frontend/bundle/chunk-6PNYA23N.js +0 -21
  274. package/www/dist/frontend/bundle/chunk-7CWDQGAG.js +0 -21
  275. package/www/dist/frontend/bundle/chunk-7PT7IGUJ.js +0 -21
  276. package/www/dist/frontend/bundle/chunk-AI5PDLDE.js +0 -21
  277. package/www/dist/frontend/bundle/chunk-AUTVRE23.js +0 -1
  278. package/www/dist/frontend/bundle/chunk-AWKVUUKN.js +0 -21
  279. package/www/dist/frontend/bundle/chunk-B6NXWGSB.js +0 -1
  280. package/www/dist/frontend/bundle/chunk-BHSHNRXW.js +0 -1
  281. package/www/dist/frontend/bundle/chunk-BVONDEOL.js +0 -21
  282. package/www/dist/frontend/bundle/chunk-C7OGVQZC.js +0 -1
  283. package/www/dist/frontend/bundle/chunk-CHDKROX5.js +0 -21
  284. package/www/dist/frontend/bundle/chunk-DNCD2F6X.js +0 -21
  285. package/www/dist/frontend/bundle/chunk-FFW7WOVM.js +0 -1
  286. package/www/dist/frontend/bundle/chunk-FHF6DL5U.js +0 -1
  287. package/www/dist/frontend/bundle/chunk-FO56TEZH.js +0 -21
  288. package/www/dist/frontend/bundle/chunk-FRCSTZQC.js +0 -21
  289. package/www/dist/frontend/bundle/chunk-FSHAYPVR.js +0 -21
  290. package/www/dist/frontend/bundle/chunk-FSRRPQMX.js +0 -21
  291. package/www/dist/frontend/bundle/chunk-GLQNGCFL.js +0 -1
  292. package/www/dist/frontend/bundle/chunk-HC7GL5JY.js +0 -1
  293. package/www/dist/frontend/bundle/chunk-HXNIU7FI.js +0 -21
  294. package/www/dist/frontend/bundle/chunk-JBDSDK2C.js +0 -21
  295. package/www/dist/frontend/bundle/chunk-JSC66ZLQ.js +0 -1
  296. package/www/dist/frontend/bundle/chunk-JVPUFDR5.js +0 -21
  297. package/www/dist/frontend/bundle/chunk-K6IV7LKW.js +0 -1
  298. package/www/dist/frontend/bundle/chunk-KJU5I76S.js +0 -21
  299. package/www/dist/frontend/bundle/chunk-KY73TL6O.js +0 -21
  300. package/www/dist/frontend/bundle/chunk-LKS3S4WX.js +0 -1
  301. package/www/dist/frontend/bundle/chunk-M6VVTDVX.js +0 -21
  302. package/www/dist/frontend/bundle/chunk-MECWXZRN.js +0 -1
  303. package/www/dist/frontend/bundle/chunk-N2YIZTB2.js +0 -1
  304. package/www/dist/frontend/bundle/chunk-NQ7L7KNT.js +0 -21
  305. package/www/dist/frontend/bundle/chunk-O2AJL747.js +0 -21
  306. package/www/dist/frontend/bundle/chunk-P7EOR36H.js +0 -1
  307. package/www/dist/frontend/bundle/chunk-PJYK24KZ.js +0 -1
  308. package/www/dist/frontend/bundle/chunk-PYLAW6UA.js +0 -21
  309. package/www/dist/frontend/bundle/chunk-QJJGP23I.js +0 -1
  310. package/www/dist/frontend/bundle/chunk-QV4IBWXA.js +0 -1
  311. package/www/dist/frontend/bundle/chunk-QVMAZVXM.js +0 -1
  312. package/www/dist/frontend/bundle/chunk-R6IQDPAP.js +0 -21
  313. package/www/dist/frontend/bundle/chunk-RRFSEMFS.js +0 -21
  314. package/www/dist/frontend/bundle/chunk-RTTBZOQU.js +0 -21
  315. package/www/dist/frontend/bundle/chunk-S4TLNW5D.js +0 -1
  316. package/www/dist/frontend/bundle/chunk-S7P222YS.js +0 -1
  317. package/www/dist/frontend/bundle/chunk-SE6N4ZEN.js +0 -1
  318. package/www/dist/frontend/bundle/chunk-SQHXO2IT.js +0 -21
  319. package/www/dist/frontend/bundle/chunk-T4AIQBBH.js +0 -21
  320. package/www/dist/frontend/bundle/chunk-TWQQHCU7.js +0 -21
  321. package/www/dist/frontend/bundle/chunk-U5Q332OH.js +0 -1
  322. package/www/dist/frontend/bundle/chunk-UIP324PP.js +0 -1
  323. package/www/dist/frontend/bundle/chunk-V27IRYSL.js +0 -1
  324. package/www/dist/frontend/bundle/chunk-V4C5FGJL.js +0 -21
  325. package/www/dist/frontend/bundle/chunk-V5CHFTYG.js +0 -21
  326. package/www/dist/frontend/bundle/chunk-VHXWCBNP.js +0 -1
  327. package/www/dist/frontend/bundle/chunk-W2KWSW4M.js +0 -1
  328. package/www/dist/frontend/bundle/chunk-W5QV7JN5.js +0 -1
  329. package/www/dist/frontend/bundle/chunk-WED7CGWA.js +0 -21
  330. package/www/dist/frontend/bundle/chunk-WQ5H5LTG.js +0 -21
  331. package/www/dist/frontend/bundle/chunk-WU54NCEU.js +0 -1
  332. package/www/dist/frontend/bundle/chunk-XCRSZKUF.js +0 -1
  333. package/www/dist/frontend/bundle/chunk-XFO5FUCN.js +0 -1
  334. package/www/dist/frontend/bundle/chunk-XWAQ2BWM.js +0 -21
  335. package/www/dist/frontend/bundle/chunk-YTQCHNRQ.js +0 -21
  336. package/www/dist/frontend/bundle/chunk-YUSK4FN3.js +0 -21
  337. package/www/dist/frontend/bundle/chunk-Z34BSG5O.js +0 -21
package/README.md CHANGED
@@ -77,6 +77,12 @@ For detailed setup instructions, see [LICENSE_SETUP_PUBLIC.md](./LICENSE_SETUP_P
77
77
 
78
78
  **Note:** This is a hardware-bound license. The adapter will only work on registered hardware. Contact **gokturk413** for licensing.
79
79
 
80
+ **How the machine is recognised.** The Hardware ID comes from the SMBIOS system UUID and the OS machine id: Windows MachineGuid, `/etc/machine-id` on Linux. USB devices, monitors and network adapters do not change it.
81
+
82
+ The ID is read again at every licence check, so a VM cloned or moved while it runs is noticed. On Windows it is read straight from the firmware table and the registry, with no helper process. If one of the IDs cannot be read for a moment, its last value is used for up to 24 hours, and a passing hiccup never changes the identity.
83
+
84
+ If the SMBIOS UUID stays unreadable, the licence enters a grace period (`identity-unreadable`) while the key still lists this machine. Editing stays possible during grace; the licence becomes *restricted* only when grace runs out.
85
+
80
86
  ### 🔧 SCADA Utility Functions (Auto-Installed)
81
87
 
82
88
  This adapter includes powerful SCADA utility functions for value formatting, unit conversions, and more.
@@ -217,6 +217,74 @@
217
217
  "hidden": "!data.grafanaEnabled || !data.grafanaSsoEnabled"
218
218
  }
219
219
  }
220
+ },
221
+ "tab_reports": {
222
+ "type": "panel",
223
+ "label": "Reports",
224
+ "items": {
225
+ "_repHdr": {
226
+ "type": "header",
227
+ "text": "Generated reports",
228
+ "size": 3
229
+ },
230
+ "_repInfo": {
231
+ "type": "staticText",
232
+ "text": "Each report sets its own output folder and file name in the editor (Report Page → Output). These settings apply to all reports.",
233
+ "style": {
234
+ "fontSize": 13,
235
+ "color": "#666",
236
+ "marginBottom": 12
237
+ }
238
+ },
239
+ "reportTimeZone": {
240
+ "type": "text",
241
+ "label": "Time zone for date tokens",
242
+ "placeholder": "Asia/Baku",
243
+ "sm": 12,
244
+ "md": 6,
245
+ "lg": 4,
246
+ "help": "IANA name, e.g. Asia/Baku, Europe/Berlin. Empty = the host system time zone. Used for {yyyy} {MM} {dd} {HH} {mm} {ss} in paths and file names."
247
+ },
248
+ "reportMinFreeMB": {
249
+ "type": "number",
250
+ "label": "Minimum free disk space (MB)",
251
+ "min": 0,
252
+ "sm": 12,
253
+ "md": 6,
254
+ "lg": 4,
255
+ "newLine": true,
256
+ "help": "A report is not written when less space is free on its target drive. 0 = no check."
257
+ },
258
+ "reportRetentionDays": {
259
+ "type": "number",
260
+ "label": "Delete files older than (days)",
261
+ "min": 0,
262
+ "sm": 12,
263
+ "md": 6,
264
+ "lg": 4,
265
+ "newLine": true,
266
+ "help": "Per report, only files that report produced. 0 = keep."
267
+ },
268
+ "reportMaxFiles": {
269
+ "type": "number",
270
+ "label": "Keep at most (files per report)",
271
+ "min": 0,
272
+ "sm": 12,
273
+ "md": 6,
274
+ "lg": 4,
275
+ "help": "The newest are kept. 0 = no limit."
276
+ },
277
+ "reportFonts": {
278
+ "type": "text",
279
+ "label": "Extra fonts for PDF",
280
+ "placeholder": "D:/fonts/NotoSansArabic-Regular.ttf",
281
+ "sm": 12,
282
+ "md": 12,
283
+ "lg": 8,
284
+ "newLine": true,
285
+ "help": "Font files (.ttf, .otf, .ttc) used in PDF reports in addition to the built-in Roboto (Latin incl. Azerbaijani and Turkish, Cyrillic, Greek). Separate several with ;. Chinese/Japanese/Korean use a CJK font found on this computer automatically (Microsoft YaHei, SimSun, Noto CJK, PingFang)."
286
+ }
287
+ }
220
288
  }
221
289
  }
222
290
  }
@@ -1 +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);-webkit-backdrop-filter:var(--ui-backdrop,none);backdrop-filter:var(--ui-backdrop,none);\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: var(--accent,#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\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"}}
1
+ {"html":"<div id=\"lang-selector\" class=\"lang-row\"></div><button id=\"lang-compact\" class=\"lang-btn lang-compact\" type=\"button\" aria-haspopup=\"listbox\" aria-expanded=\"false\"></button><div id=\"lang-menu\" class=\"lang-menu\" role=\"listbox\" hidden></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);-webkit-backdrop-filter:var(--ui-backdrop,none);backdrop-filter:var(--ui-backdrop,none);\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: var(--accent,#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\n/* ── responsive: one row, or one compact button with a menu ─────────────────────────── */\n:host {\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 0; /* may shrink in a flex bar; then it turns compact */\n position: relative;\n}\n.lang-row {\n display: flex;\n flex-wrap: nowrap; /* never onto a second line */\n gap: 8px;\n padding: 8px;\n width: 100%;\n height: 100%;\n align-items: center;\n justify-content: center;\n}\n.lang-compact { display: none; flex-direction: row; gap: 6px; min-width: 0; }\n.lang-caret { font-size: 10px; color: #aaa; line-height: 1; }\n:host([data-compact]) .lang-row { display: none; }\n:host([data-compact]) .lang-compact { display: flex; }\n.lang-menu {\n position: fixed; /* above everything, never clipped by the bar */\n z-index: 1000001;\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 6px;\n background: var(--ui-surface-2, #2f3545);\n border: 1px solid var(--ui-border, #4a5570);\n border-radius: 10px;\n box-shadow: 0 8px 24px rgba(0,0,0,.35);\n}\n.lang-menu[hidden] { display: none; }\n.lang-menu .lang-btn { flex-direction: row; justify-content: flex-start; gap: 8px; min-width: 96px; }\n.lang-menu .lang-btn:hover { transform: none; }\n","script":"let iobrokerHandler;\r\nlet _langChangedHandler;\r\nlet _translChangedHandler;\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) {\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 iobrokerHandler.setState('0_userdata.0.language.selectedlanguage', { val: iobrokerHandler.language });\n\n // compact when the row does not fit the width the control is given\n const compactBtn = instance._getDomElement('lang-compact');\n compactBtn.onclick = (e) => { e.stopPropagation(); _toggleMenu(instance); };\n instance.__langOutside = (e) => { if (!e.composedPath().includes(instance)) _toggleMenu(instance, false); };\n instance.__langKey = (e) => { if (e.key === 'Escape') _toggleMenu(instance, false); };\n document.addEventListener('pointerdown', instance.__langOutside, true);\n document.addEventListener('keydown', instance.__langKey);\n window.addEventListener('resize', instance.__langClose = () => _toggleMenu(instance, false));\n instance.__langRO = new ResizeObserver(() => _fit(instance));\n instance.__langRO.observe(instance);\n}\n\nexport function disconnectedCallback(instance) {\n _langChangedHandler?.dispose();\n _translChangedHandler?.dispose();\n instance.__langRO?.disconnect();\n if (instance.__langOutside) document.removeEventListener('pointerdown', instance.__langOutside, true);\n if (instance.__langKey) document.removeEventListener('keydown', instance.__langKey);\n if (instance.__langClose) window.removeEventListener('resize', instance.__langClose);\n}\n\nfunction _flag(lang, size) {\n const flagWrap = document.createElement('span');\n flagWrap.className = 'lang-flag';\n const svgStr = FLAGS[lang];\n if (svgStr) {\n flagWrap.innerHTML = svgStr.replace(/width=\"\\d+\"/, `width=\"${size}\"`).replace(/height=\"\\d+\"/, `height=\"${Math.round(size*2/3)}\"`);\n } else {\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;`;\n flagWrap.textContent = lang.toUpperCase();\n }\n return flagWrap;\n}\n\nfunction _button(lang, size) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'lang-btn' + (lang === iobrokerHandler.language ? ' active' : '');\n btn.dataset.lang = lang;\n btn.title = lang.toUpperCase();\n const code = document.createElement('span');\n code.className = 'lang-code';\n code.textContent = lang;\n btn.append(_flag(lang, size), code);\n return btn;\n}\n\nfunction _choose(instance, lang) {\n iobrokerHandler.setLanguage(lang);\n iobrokerHandler.setState('0_userdata.0.language.selectedlanguage', { val: lang });\n _toggleMenu(instance, false);\n}\n\nfunction _render(instance) {\n const container = instance._getDomElement('lang-selector');\n const menu = instance._getDomElement('lang-menu');\n container.innerHTML = '';\n menu.innerHTML = '';\n const langs = Object.keys(iobrokerHandler.translations ?? {});\n if (!langs.length) {\n container.innerHTML = '<span style=\"color:#888;font-size:12px;\">No translations defined</span>';\n return;\n }\n const size = parseInt(instance.size ?? 36);\n const dir = instance.direction ?? 'horizontal';\n container.style.flexDirection = dir === 'vertical' ? 'column' : 'row';\n for (const lang of langs) {\n const btn = _button(lang, size);\n btn.onclick = () => _choose(instance, lang);\n container.appendChild(btn);\n const item = _button(lang, size);\n item.setAttribute('role', 'option');\n item.onclick = () => _choose(instance, lang);\n menu.appendChild(item);\n }\n _updateActive(instance);\n instance.__langNatural = 0; // measure the row again\n _fit(instance);\n}\n\nfunction _updateActive(instance) {\n for (const id of ['lang-selector', 'lang-menu'])\n instance._getDomElement(id)?.querySelectorAll('.lang-btn').forEach(btn => {\n btn.classList.toggle('active', btn.dataset.lang === iobrokerHandler.language);\n btn.setAttribute('aria-selected', String(btn.dataset.lang === iobrokerHandler.language));\n });\n // the compact button shows the language in use\n const compact = instance._getDomElement('lang-compact');\n if (compact && iobrokerHandler) {\n const lang = iobrokerHandler.language;\n const code = document.createElement('span');\n code.className = 'lang-code';\n code.textContent = lang;\n const caret = document.createElement('span');\n caret.className = 'lang-caret';\n caret.textContent = '▾';\n compact.replaceChildren(_flag(lang, parseInt(instance.size ?? 36)), code, caret);\n compact.title = lang.toUpperCase();\n }\n}\n\n// mode: auto (default) | buttons | dropdown\nfunction _fit(instance) {\n const row = instance._getDomElement('lang-selector');\n if (!row) return;\n const mode = instance.mode || 'auto';\n let compact = mode === 'dropdown';\n if (mode === 'auto' && (instance.direction ?? 'horizontal') !== 'vertical') {\n // the row's natural width, measured while it is shown (it has none while hidden)\n if (!instance.__langNatural) {\n const was = instance.hasAttribute('data-compact');\n instance.removeAttribute('data-compact');\n instance.__langNatural = row.scrollWidth;\n if (was) instance.setAttribute('data-compact', '');\n }\n compact = instance.clientWidth > 0 && instance.clientWidth + 1 < instance.__langNatural;\n }\n instance.toggleAttribute('data-compact', compact);\n if (!compact) _toggleMenu(instance, false);\n}\n\nfunction _toggleMenu(instance, open) {\n const menu = instance._getDomElement('lang-menu');\n const btn = instance._getDomElement('lang-compact');\n if (!menu || !btn) return;\n open = (open ?? menu.hidden) && instance.hasAttribute('data-compact'); // a menu only for the compact button\n menu.hidden = !open;\n btn.setAttribute('aria-expanded', String(open));\n if (!open) return;\n // below the button, kept inside the window\n const r = btn.getBoundingClientRect();\n menu.style.top = Math.round(r.bottom + 6) + 'px';\n const w = menu.offsetWidth;\n menu.style.left = Math.round(Math.max(8, Math.min(r.left, window.innerWidth - w - 8))) + 'px';\n}\n","properties":{"size":{"type":"number","default":"36"},"direction":{"type":"string","default":"horizontal"},"mode":{"type":"enum","values":["auto","buttons","dropdown"],"default":"auto"}},"settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":"","useGlobalStyle":"false"}}
@@ -1 +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: var(--ui-ok,#10b981);\n}\n\n.connection-monitor.connected .status-icon {\n color: var(--ui-ok,#10b981);\n}\n\n.connection-monitor.connected .status-text {\n color: var(--ui-ok,#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: var(--ui-error,#ef4444);\n animation: shake 0.5s ease;\n}\n\n.connection-monitor.disconnected .status-icon {\n color: var(--ui-error,#ef4444);\n}\n\n.connection-monitor.disconnected .status-text {\n color: var(--ui-error,#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: var(--ui-warn,#fbbf24);\n}\n\n.connection-monitor.checking .status-icon {\n color: var(--ui-warn,#fbbf24);\n animation: rotate 1.5s linear infinite;\n}\n\n.connection-monitor.checking .status-text {\n color: var(--ui-warn,#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: var(--ui-surface,#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: var(--ui-ok,#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: var(--ui-ok,#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: var(--ui-error,#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: var(--ui-error,#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: var(--ui-warn,#fbbf24);\n animation: rotate 1.5s linear infinite;\n}\n\n.connection-popup.checking .popup-title {\n color: var(--ui-warn,#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 -webkit-backdrop-filter:var(--ui-backdrop,none);backdrop-filter:var(--ui-backdrop,none);background: var(--ui-surface,#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', statusTextEl);\n checkIcon.style.display = 'block';\n circleIcon.style.display = 'none';\n } else if (status === 'disconnected') {\n statusTextEl.textContent = t('WebuiConnectionMonitor.disconnectedtext', statusTextEl);\n crossIcon.style.display = 'block';\n circleIcon.style.display = 'none';\n } else if (status === 'checking') {\n statusTextEl.textContent = t('WebuiConnectionMonitor.checkingtext', statusTextEl);\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', popupTitleEl);\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessageconnected', popupMessageEl);\n popupCheckIcon.style.display = 'block';\n } else if (status === 'disconnected') {\n popupTitleEl.textContent = t('WebuiConnectionMonitor.disconnectedtext', popupTitleEl);\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessagedisconnected', popupMessageEl);\n popupCrossIcon.style.display = 'block';\n } else if (status === 'checking') {\n popupTitleEl.textContent = t('WebuiConnectionMonitor.checkingtext', popupTitleEl);\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessagechecking', popupMessageEl);\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": ""}, "translations": {"az": {"WebuiConnectionMonitor": {"connectedtext": "Serverlə əlaqə var", "disconnectedtext": "Serverlə əlaqə yoxdur", "checkingtext": "Yoxlanılır...", "popupmessageconnected": "Əlaqə uğurla bərpa olundu", "popupmessagedisconnected": "Zəhmət olmasa əlaqənizi yoxlayın", "popupmessagechecking": "Server əlaqəsi yoxlanılır"}}, "ru": {"WebuiConnectionMonitor": {"connectedtext": "Соединение с сервером установлено", "disconnectedtext": "Соединение с сервером потеряно", "checkingtext": "Проверяется...", "popupmessageconnected": "Соединение успешно восстановлено", "popupmessagedisconnected": "Пожалуйста, проверьте подключение", "popupmessagechecking": "Проверка соединения с сервером"}}, "de": {"WebuiConnectionMonitor": {"connectedtext": "Verbindung zum Server hergestellt", "disconnectedtext": "Verbindung zum Server unterbrochen", "checkingtext": "Wird geprüft...", "popupmessageconnected": "Verbindung erfolgreich wiederhergestellt", "popupmessagedisconnected": "Bitte überprüfen Sie Ihre Verbindung", "popupmessagechecking": "Serververbindung wird geprüft"}}, "en": {"WebuiConnectionMonitor": {"connectedtext": "Connected to server", "disconnectedtext": "Disconnected from server", "checkingtext": "Checking...", "popupmessageconnected": "Connection successfully restored", "popupmessagedisconnected": "Please check your connection", "popupmessagechecking": "Checking server connection"}}, "tr": {"WebuiConnectionMonitor": {"connectedtext": "Sunucuya bağlantı kuruldu", "disconnectedtext": "Sunucu bağlantısı kesildi", "checkingtext": "Kontrol ediliyor...", "popupmessageconnected": "Bağlantı başarıyla yeniden kuruldu", "popupmessagedisconnected": "Lütfen bağlantınızı kontrol edin", "popupmessagechecking": "Sunucu bağlantısı kontrol ediliyor"}}}}
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=\"statusBox\" class=\"status-box\"><span id=\"statusText\" class=\"status-text\"></span><span id=\"statusSizer\" class=\"status-sizer\" aria-hidden=\"true\"></span></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/* Every language's texts stacked in one grid cell, hidden: the box is as wide as the longest,\n so a language switch never changes the pill's width (and never moves its neighbours). */\n.status-box {\n display: inline-grid;\n}\n\n.status-box > *,\n.status-sizer > span {\n grid-area: 1 / 1;\n}\n\n.status-sizer {\n display: grid;\n visibility: hidden;\n pointer-events: none;\n}\n\n.status-sizer > span {\n font-size: 14px;\n font-weight: 600;\n white-space: nowrap;\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: var(--ui-ok,#10b981);\n}\n\n.connection-monitor.connected .status-icon {\n color: var(--ui-ok,#10b981);\n}\n\n.connection-monitor.connected .status-text {\n color: var(--ui-ok,#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: var(--ui-error,#ef4444);\n animation: shake 0.5s ease;\n}\n\n.connection-monitor.disconnected .status-icon {\n color: var(--ui-error,#ef4444);\n}\n\n.connection-monitor.disconnected .status-text {\n color: var(--ui-error,#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: var(--ui-warn,#fbbf24);\n}\n\n.connection-monitor.checking .status-icon {\n color: var(--ui-warn,#fbbf24);\n animation: rotate 1.5s linear infinite;\n}\n\n.connection-monitor.checking .status-text {\n color: var(--ui-warn,#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: var(--ui-surface,#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: var(--ui-ok,#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: var(--ui-ok,#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: var(--ui-error,#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: var(--ui-error,#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: var(--ui-warn,#fbbf24);\n animation: rotate 1.5s linear infinite;\n}\n\n.connection-popup.checking .popup-title {\n color: var(--ui-warn,#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 -webkit-backdrop-filter:var(--ui-backdrop,none);backdrop-filter:var(--ui-backdrop,none);background: var(--ui-surface,#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}\n\n/* status-box-narrow: a narrow window shows the icon only; the text is in the tooltip */\n@media (max-width: 1100px) {\n .status-box { display: none !important; }\n .connection-monitor { padding: 8px; }\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// Popups are for operators watching a live screen. Not in the designer (the editor preview runs\n// this script too), not for the first connect of a page (nothing was lost), and not while the\n// page prints: window.print() stops the page's JavaScript, the socket misses its heartbeat and\n// reconnects when the print dialog closes — expected, the subscriptions come back by themselves.\nlet everConnected = false;\nlet printing = false;\nlet quietUntil = 0;\nlet _beforePrint = null, _afterPrint = null, _quietCheck = null;\nconst PRINT_QUIET_MS = 15000;\nconst inDesigner = () => !/runtime\\.html$/.test(window.location.pathname);\nconst quiet = () => inDesigner() || printing || Date.now() < quietUntil;\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 statusBoxEl = instance._getDomElement('statusBox');\n const sizerEl = instance._getDomElement('statusSizer');\n // All status texts in every language this control is translated to, plus the ones shown now.\n function fillSizer() {\n if (!sizerEl) return;\n const keys = ['connectedtext', 'disconnectedtext', 'checkingtext'];\n const texts = new Set(keys.map(k => t('WebuiConnectionMonitor.' + k, statusTextEl)));\n const dict = instance.constructor?.__webuiControlInfo?.control?.translations;\n if (dict) for (const lang of Object.keys(dict))\n for (const k of keys) { const v = dict[lang]?.WebuiConnectionMonitor?.[k]; if (typeof v === 'string') texts.add(v); }\n sizerEl.replaceChildren(...[...texts].map(x => Object.assign(document.createElement('span'), { textContent: x })));\n }\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 _beforePrint = () => { printing = true; };\n _afterPrint = () => {\n printing = false;\n quietUntil = Date.now() + PRINT_QUIET_MS;\n // still down once the quiet time is over: say so, as for any other loss\n if (_quietCheck) clearTimeout(_quietCheck);\n _quietCheck = setTimeout(() => { if (!isConnected && !inDesigner()) showPopup('disconnected'); }, PRINT_QUIET_MS + 500);\n };\n window.addEventListener('beforeprint', _beforePrint);\n window.addEventListener('afterprint', _afterPrint);\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 // \"Restored\" only after a real loss the operator saw — not on the page's first\n // connect, not in the designer, not after printing.\n const showRestored = everConnected && !quiet();\n everConnected = true;\n if (showRestored) {\n showPopup('connected');\n if (popupTimeout) clearTimeout(popupTimeout);\n popupTimeout = setTimeout(() => {\n hidePopup();\n }, 3000);\n } else {\n hidePopup();\n }\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) — not in the designer or while printing\n if (!quiet()) 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', statusTextEl);\n checkIcon.style.display = 'block';\n circleIcon.style.display = 'none';\n } else if (status === 'disconnected') {\n statusTextEl.textContent = t('WebuiConnectionMonitor.disconnectedtext', statusTextEl);\n crossIcon.style.display = 'block';\n circleIcon.style.display = 'none';\n } else if (status === 'checking') {\n statusTextEl.textContent = t('WebuiConnectionMonitor.checkingtext', statusTextEl);\n }\n \n fillSizer();\n if (monitorEl) monitorEl.title = statusTextEl.textContent;\n const box = statusBoxEl || statusTextEl;\n if (instance.showText === false) {\n box.style.display = 'none';\n } else {\n box.style.display = '';\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', popupTitleEl);\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessageconnected', popupMessageEl);\n popupCheckIcon.style.display = 'block';\n } else if (status === 'disconnected') {\n popupTitleEl.textContent = t('WebuiConnectionMonitor.disconnectedtext', popupTitleEl);\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessagedisconnected', popupMessageEl);\n popupCrossIcon.style.display = 'block';\n } else if (status === 'checking') {\n popupTitleEl.textContent = t('WebuiConnectionMonitor.checkingtext', popupTitleEl);\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessagechecking', popupMessageEl);\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 if (_beforePrint) window.removeEventListener('beforeprint', _beforePrint);\n if (_afterPrint) window.removeEventListener('afterprint', _afterPrint);\n _beforePrint = _afterPrint = null;\n if (_quietCheck) { clearTimeout(_quietCheck); _quietCheck = null; }\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":""},"translations":{"az":{"WebuiConnectionMonitor":{"connectedtext":"Serverlə əlaqə var","disconnectedtext":"Serverlə əlaqə yoxdur","checkingtext":"Yoxlanılır...","popupmessageconnected":"Əlaqə uğurla bərpa olundu","popupmessagedisconnected":"Zəhmət olmasa əlaqənizi yoxlayın","popupmessagechecking":"Server əlaqəsi yoxlanılır"}},"ru":{"WebuiConnectionMonitor":{"connectedtext":"Соединение с сервером установлено","disconnectedtext":"Соединение с сервером потеряно","checkingtext":"Проверяется...","popupmessageconnected":"Соединение успешно восстановлено","popupmessagedisconnected":"Пожалуйста, проверьте подключение","popupmessagechecking":"Проверка соединения с сервером"}},"de":{"WebuiConnectionMonitor":{"connectedtext":"Verbindung zum Server hergestellt","disconnectedtext":"Verbindung zum Server unterbrochen","checkingtext":"Wird geprüft...","popupmessageconnected":"Verbindung erfolgreich wiederhergestellt","popupmessagedisconnected":"Bitte überprüfen Sie Ihre Verbindung","popupmessagechecking":"Serververbindung wird geprüft"}},"en":{"WebuiConnectionMonitor":{"connectedtext":"Connected to server","disconnectedtext":"Disconnected from server","checkingtext":"Checking...","popupmessageconnected":"Connection successfully restored","popupmessagedisconnected":"Please check your connection","popupmessagechecking":"Checking server connection"}},"tr":{"WebuiConnectionMonitor":{"connectedtext":"Sunucuya bağlantı kuruldu","disconnectedtext":"Sunucu bağlantısı kesildi","checkingtext":"Kontrol ediliyor...","popupmessageconnected":"Bağlantı başarıyla yeniden kuruldu","popupmessagedisconnected":"Lütfen bağlantınızı kontrol edin","popupmessagechecking":"Sunucu bağlantısı kontrol ediliyor"}}}}
@@ -1 +1 @@
1
- {"properties": {}, "settings": {"width": "220px", "height": "100%"}, "html": "<div id=\"sb\" class=\"sb\"><div id=\"sbHdr\" class=\"sb-hdr\"></div><div id=\"slotTop\" class=\"sb-slot\"></div><div id=\"sbItems\" class=\"sb-items\"></div><div id=\"slotBottom\" class=\"sb-slot\"></div></div>", "style": ":host{display:block;width:100%;height:100%;box-sizing:border-box;}\n.sb{display:flex;flex-direction:column;height:100%;width:100%;background:var(--sb-bg,linear-gradient(180deg,#1e3c72 0%,#2a5298 100%));color:var(--sb-fg,rgba(255,255,255,.9));box-shadow:2px 0 18px rgba(0,0,0,.28);-webkit-backdrop-filter:var(--ui-backdrop,none);backdrop-filter:var(--ui-backdrop,none);box-sizing:border-box;}\n.sb-hdr{padding:18px 16px 14px;font-weight:700;font-size:16px;letter-spacing:.5px;color:var(--sb-hdr-fg,#fff);border-bottom:1px solid rgba(255,255,255,.12);white-space:nowrap;overflow:hidden;}\n.sb-items{flex:1;overflow-y:auto;overflow-x:hidden;padding:8px 0;}\n.sb-items::-webkit-scrollbar{width:6px;}\n.sb-items::-webkit-scrollbar-thumb{background:rgba(255,255,255,.3);border-radius:3px;}\n.sb-slot{display:block;}\n:host([data-theme=\"dark\"]){--sb-bg:linear-gradient(180deg,#0a0e27 0%,#1a1f3a 100%);--sb-fg:rgba(255,255,255,.88);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.04);--mi-hover:rgba(255,255,255,.1);--mi-active:linear-gradient(135deg,rgba(74,158,255,.35),rgba(74,158,255,.14));--mi-active-fg:#fff;--mi-active-icon:#4a9eff;}\n:host([data-theme=\"light\"]){--sb-bg:linear-gradient(180deg,#f5f7fa 0%,#e4e8f0 100%);--sb-fg:#37474f;--sb-hdr-fg:#1a237e;--mi-bg:rgba(0,0,0,.04);--mi-hover:rgba(33,150,243,.1);--mi-active:linear-gradient(135deg,rgba(33,150,243,.22),rgba(33,150,243,.1));--mi-active-fg:#1565c0;--mi-active-icon:#1976d2;}\n:host([data-theme=\"purple\"]){--sb-bg:linear-gradient(180deg,#4a1d6e 0%,#7b2d8e 100%);--sb-fg:rgba(255,255,255,.9);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.07);--mi-hover:rgba(255,255,255,.16);--mi-active:linear-gradient(135deg,rgba(224,64,251,.35),rgba(224,64,251,.15));--mi-active-fg:#fff;--mi-active-icon:#8e24aa;}\n:host([data-theme=\"green\"]){--sb-bg:linear-gradient(180deg,#1b5e20 0%,#2e7d32 100%);--sb-fg:rgba(255,255,255,.9);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.07);--mi-hover:rgba(255,255,255,.16);--mi-active:linear-gradient(135deg,rgba(105,240,174,.3),rgba(105,240,174,.12));--mi-active-fg:#fff;--mi-active-icon:#2e7d32;}\n:host([data-theme=\"orange\"]){--sb-bg:linear-gradient(180deg,#bf360c 0%,#e64a19 100%);--sb-fg:rgba(255,255,255,.92);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.08);--mi-hover:rgba(255,255,255,.18);--mi-active:linear-gradient(135deg,rgba(255,171,64,.32),rgba(255,171,64,.14));--mi-active-fg:#fff;--mi-active-icon:#e64a19;}\n:host([data-theme=\"teal\"]){--sb-bg:linear-gradient(180deg,#004d40 0%,#00695c 100%);--sb-fg:rgba(255,255,255,.9);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.07);--mi-hover:rgba(255,255,255,.16);--mi-active:linear-gradient(135deg,rgba(29,233,182,.3),rgba(29,233,182,.12));--mi-active-fg:#fff;--mi-active-icon:#00897b;}\n:host([data-theme=\"auto\"]){--sb-bg:var(--ui-surface,#252b38);--sb-fg:var(--ui-text,#e2e6ec);--sb-hdr-fg:var(--ui-text,#fff);--mi-bg:color-mix(in srgb,var(--ui-text,#fff) 6%,transparent);--mi-fg:var(--ui-text,#e2e6ec);--mi-hover:color-mix(in srgb,var(--ui-text,#fff) 13%,transparent);--mi-active:color-mix(in srgb,var(--accent,#2680eb) 26%,transparent);--mi-active-border:color-mix(in srgb,var(--accent,#2680eb) 55%,transparent);--mi-active-fg:var(--ui-text,#fff);--mi-active-icon:var(--accent,#2680eb);}", "script": "export function init(instance){\n instance._bindingsRefresh?.();\n const IOB = window.IOB;\n const cfg = (IOB && IOB.config && IOB.config.navigation) || {};\n instance.setAttribute('data-theme', cfg.theme || 'default');\n function htmlFromDef(def){\n let attrs='';\n if(def.defaultAttributes) for(const a in def.defaultAttributes){ const v=def.defaultAttributes[a]; attrs+=' '+a+'=\"'+String(typeof v==='object'?JSON.stringify(v):v).replace(/\"/g,'&quot;')+'\"'; }\n let style='';\n if(def.defaultStyles) for(const k in def.defaultStyles) style+=k+':'+def.defaultStyles[k]+';';\n if(def.defaultWidth) style+='width:'+def.defaultWidth+';';\n if(def.defaultHeight) style+='height:'+def.defaultHeight+';';\n return '<'+def.tag+attrs+(style?' style=\"'+style+'\"':'')+'>'+(def.defaultContent||'')+'</'+def.tag+'>';\n }\n function wireSlot(zone, cfgRoot, key){\n if(!zone) return;\n zone.innerHTML = (cfgRoot && cfgRoot.slots && cfgRoot.slots[key]) || '';\n if(!isDesigner) return;\n if(!zone.innerHTML.trim()){ zone.dataset.empty='1'; zone.style.minHeight='30px'; zone.style.outline='1px dashed rgba(255,255,255,.28)'; zone.style.borderRadius='8px'; zone.style.margin='4px 8px'; }\n (function(){\n let chip=null,target=null,hideT=null;\n const saveFromDom=()=>{ if(!cfgRoot.slots) cfgRoot.slots={}; cfgRoot.slots[key]=Array.from(zone.children).map(c=>c.outerHTML).join(''); if(window.IOB&&window.IOB.saveConfig) window.IOB.saveConfig(); };\n const hideChip=()=>{ if(chip) chip.style.display='none'; target=null; };\n const ensureChip=()=>{ if(chip) return chip; chip=document.createElement('div'); chip.className='nav-slot-delchip'; chip.textContent='✕'; chip.title='Delete'; chip.style.cssText='position:fixed;z-index:2147483647;width:18px;height:18px;line-height:18px;text-align:center;font:700 12px/18px sans-serif;background:#e74c3c;color:#fff;border-radius:50%;cursor:pointer;box-shadow:0 1px 5px rgba(0,0,0,.45);user-select:none;display:none;'; chip.addEventListener('mouseenter',()=>{ if(hideT){clearTimeout(hideT);hideT=null;} }); chip.addEventListener('mouseleave',hideChip); chip.addEventListener('click',e=>{ e.preventDefault(); e.stopPropagation(); if(target&&target.parentNode===zone){ target.remove(); saveFromDom(); } hideChip(); }); document.body.appendChild(chip); return chip; };\n const showChip=(el)=>{ if(hideT){clearTimeout(hideT);hideT=null;} target=el; const c=ensureChip(); const r=el.getBoundingClientRect(); c.style.left=(r.right-9)+'px'; c.style.top=(r.top-9)+'px'; c.style.display='block'; };\n zone.addEventListener('mouseover', e=>{ let el=e.target; while(el && el!==zone && el.parentNode!==zone) el=el.parentNode; if(el && el.parentNode===zone && el.nodeType===1) showChip(el); });\n zone.addEventListener('mouseleave', ()=>{ hideT=setTimeout(()=>{ if(chip && chip.style.display!=='none' && !chip.matches(':hover')) hideChip(); }, 160); });\n })();\n zone.addEventListener('dragover', e=>{ if(e.dataTransfer && e.dataTransfer.types && e.dataTransfer.types.indexOf('text/json/elementdefintion')>=0){ e.preventDefault(); zone.style.background='rgba(255,255,255,.18)'; } });\n zone.addEventListener('dragleave', ()=>{ zone.style.background=''; });\n zone.addEventListener('drop', e=>{ e.preventDefault(); e.stopPropagation(); zone.style.background='';\n try{ const td=e.dataTransfer.getData('text/json/elementdefintion'); if(!td) return; const def=JSON.parse(td); const html=htmlFromDef(def);\n if(!cfgRoot.slots) cfgRoot.slots={}; cfgRoot.slots[key]=(cfgRoot.slots[key]||'')+html;\n if(window.IOB && window.IOB.saveConfig) window.IOB.saveConfig();\n }catch(err){ console.warn('[nav] slot drop failed', err); }\n });\n }\n const isDesigner = !!document.querySelector('iobroker-webui-app-shell');\n if(typeof isDesigner!=='undefined' && isDesigner){ try{ document.querySelectorAll('.nav-slot-delchip').forEach(n=>n.remove()); }catch(e){} }\n const hdr=instance._getDomElement('sbHdr'), items=instance._getDomElement('sbItems');\n if(cfg.bgColor) instance.style.setProperty('--sb-bg', cfg.bgColor);\n if(cfg.textColor){ instance.style.setProperty('--sb-fg', cfg.textColor); instance.style.setProperty('--mi-fg', cfg.textColor); }\n if(cfg.menuHeaderTextColor) instance.style.setProperty('--sb-hdr-fg', cfg.menuHeaderTextColor);\n if(cfg.bgColorSelected){ instance.style.setProperty('--mi-active', cfg.bgColorSelected); instance.style.setProperty('--mi-active-icon', cfg.bgColorSelected); }\n if(cfg.textColorSelected) instance.style.setProperty('--mi-active-fg', cfg.textColorSelected);\n if(hdr){ hdr.textContent=cfg.menuHeaderText||''; hdr.style.display=cfg.menuHeaderText?'':'none'; }\n let state='expanded'; const ICONS_W=64; let burger=null; let hasItems=false;\n const navWidth = cfg.menuWidth ? (/^\\d+$/.test(String(cfg.menuWidth))?cfg.menuWidth+'px':String(cfg.menuWidth)) : '220px';\n const setActive=(name)=>{ if(items) items.querySelectorAll('webui-navigation-menu-item').forEach(mi=>{ mi.selected=(mi.screenName===name); }); };\n const navTo=(name)=>{ if(IOB&&IOB.changeView) IOB.changeView.emit(name); setActive(name); if(cfg.hideAfterSelection) setState('hidden'); };\n async function loadEntries(){\n if(!IOB||!IOB.getAllNames) return [];\n const names=await IOB.getAllNames('screen').catch(()=>[]);\n const list=[];\n for(const n of names){ const o=await IOB.getWebuiObject('screen',n).catch(()=>null); const nv=o&&o.settings&&o.settings.navigation; if(nv&&nv.show) list.push({name:n,title:nv.title||n,order:parseFloat(nv.order)||0,image:nv.image||''}); }\n list.sort((a,b)=>a.order-b.order); return list;\n }\n async function render(){\n const list=await loadEntries(); hasItems=list.length>0;\n if(items){ items.innerHTML='';\n for(const e of list){ const mi=document.createElement('webui-navigation-menu-item'); mi.screenName=e.name; mi.title=e.title; mi.image=e.image; mi.addEventListener('menuitemclick',()=>navTo(e.name)); items.appendChild(mi); }\n }\n if(!isDesigner){ if(hasItems && !cfg.hideMenu) applyRuntimeLayout(); else { const v=document.getElementById('viewer'); if(v){ v.style.left='0'; v.style.width='100%'; } instance.style.display='none'; } }\n }\n function applyRuntimeLayout(){\n const ab=document.getElementById('__nav-appbar-host'); const top=ab?ab.offsetHeight:0;\n instance.style.position='fixed'; instance.style.left='0'; instance.style.bottom='0'; instance.style.top=top+'px';\n instance.style.zIndex='99999'; instance.style.overflow='hidden'; instance.style.transition='width .2s ease,transform .2s ease';\n if(!burger){ burger=document.createElement('div'); burger.id='__nav-burger'; burger.textContent='☰'; const inBar=top>0; burger.style.cssText='position:fixed;top:'+(inBar?0:8)+'px;left:'+(inBar?0:8)+'px;z-index:100002;width:'+(inBar?top:42)+'px;height:'+(inBar?top:42)+'px;display:flex;align-items:center;justify-content:center;font-size:22px;cursor:pointer;'+(inBar?'':'border-radius:8px;')+'background:'+(cfg.showButtonBg||(inBar?'transparent':'rgba(0,0,0,.45)'))+';color:'+(cfg.showButtonColor||'#fff')+';transition:background .15s;'; burger.onclick=cycle; document.body.appendChild(burger); }\n setState(state);\n }\n function setState(s){\n state=s; const v=document.getElementById('viewer'); const top=parseInt(instance.style.top)||0; let left='0px';\n const compact=(state==='icons');\n if(items) items.querySelectorAll('webui-navigation-menu-item').forEach(mi=>mi.compact=compact);\n if(state==='expanded'){ instance.style.width=navWidth; instance.style.transform='translateX(0)'; if(hdr)hdr.style.display=cfg.menuHeaderText?'':'none'; left=navWidth; }\n else if(state==='icons'){ instance.style.width=ICONS_W+'px'; instance.style.transform='translateX(0)'; if(hdr)hdr.style.display='none'; left=ICONS_W+'px'; }\n else { instance.style.transform='translateX(-110%)'; left='0px'; }\n if(v){ v.style.left=left; v.style.width='calc(100% - '+left+')'; v.style.top=top+'px'; v.style.height='calc(100% - '+top+'px)'; }\n }\n function cycle(){ const o=['expanded','icons','hidden']; let n=o[(o.indexOf(state)+1)%o.length]; if(n==='hidden'&&cfg.doNotHideCompletely)n='icons'; setState(n); }\n wireSlot(instance._getDomElement('slotTop'), cfg, 'top');\n wireSlot(instance._getDomElement('slotBottom'), cfg, 'bottom');\n render();\n if(IOB&&IOB.changeView) IOB.changeView.on(v=>setActive(v));\n const sv=document.getElementById('viewer'); if(sv&&sv.screenName) setActive(sv.screenName);\n}"}
1
+ {"properties":{},"settings":{"width":"220px","height":"100%"},"html":"<div id=\"sb\" class=\"sb\"><div id=\"sbHdr\" class=\"sb-hdr\"></div><div id=\"slotTop\" class=\"sb-slot\"></div><div id=\"sbItems\" class=\"sb-items\"></div><div id=\"slotBottom\" class=\"sb-slot\"></div></div>","style":":host{display:block;width:100%;height:100%;box-sizing:border-box;}\n.sb{display:flex;flex-direction:column;height:100%;width:100%;background:var(--sb-bg,linear-gradient(180deg,#1e3c72 0%,#2a5298 100%));color:var(--sb-fg,rgba(255,255,255,.9));box-shadow:2px 0 18px rgba(0,0,0,.28);-webkit-backdrop-filter:var(--ui-backdrop,none);backdrop-filter:var(--ui-backdrop,none);box-sizing:border-box;}\n.sb-hdr{padding:18px 16px 14px;font-weight:700;font-size:16px;letter-spacing:.5px;color:var(--sb-hdr-fg,#fff);border-bottom:1px solid rgba(255,255,255,.12);white-space:nowrap;overflow:hidden;}\n.sb-items{flex:1;overflow-y:auto;overflow-x:hidden;padding:8px 0;}\n.sb-items::-webkit-scrollbar{width:6px;}\n.sb-items::-webkit-scrollbar-thumb{background:rgba(255,255,255,.3);border-radius:3px;}\n.sb-slot{display:block;}\n:host([data-theme=\"dark\"]){--sb-bg:linear-gradient(180deg,#0a0e27 0%,#1a1f3a 100%);--sb-fg:rgba(255,255,255,.88);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.04);--mi-hover:rgba(255,255,255,.1);--mi-active:linear-gradient(135deg,rgba(74,158,255,.35),rgba(74,158,255,.14));--mi-active-fg:#fff;--mi-active-icon:#4a9eff;}\n:host([data-theme=\"light\"]){--sb-bg:linear-gradient(180deg,#f5f7fa 0%,#e4e8f0 100%);--sb-fg:#37474f;--sb-hdr-fg:#1a237e;--mi-bg:rgba(0,0,0,.04);--mi-hover:rgba(33,150,243,.1);--mi-active:linear-gradient(135deg,rgba(33,150,243,.22),rgba(33,150,243,.1));--mi-active-fg:#1565c0;--mi-active-icon:#1976d2;}\n:host([data-theme=\"purple\"]){--sb-bg:linear-gradient(180deg,#4a1d6e 0%,#7b2d8e 100%);--sb-fg:rgba(255,255,255,.9);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.07);--mi-hover:rgba(255,255,255,.16);--mi-active:linear-gradient(135deg,rgba(224,64,251,.35),rgba(224,64,251,.15));--mi-active-fg:#fff;--mi-active-icon:#8e24aa;}\n:host([data-theme=\"green\"]){--sb-bg:linear-gradient(180deg,#1b5e20 0%,#2e7d32 100%);--sb-fg:rgba(255,255,255,.9);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.07);--mi-hover:rgba(255,255,255,.16);--mi-active:linear-gradient(135deg,rgba(105,240,174,.3),rgba(105,240,174,.12));--mi-active-fg:#fff;--mi-active-icon:#2e7d32;}\n:host([data-theme=\"orange\"]){--sb-bg:linear-gradient(180deg,#bf360c 0%,#e64a19 100%);--sb-fg:rgba(255,255,255,.92);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.08);--mi-hover:rgba(255,255,255,.18);--mi-active:linear-gradient(135deg,rgba(255,171,64,.32),rgba(255,171,64,.14));--mi-active-fg:#fff;--mi-active-icon:#e64a19;}\n:host([data-theme=\"teal\"]){--sb-bg:linear-gradient(180deg,#004d40 0%,#00695c 100%);--sb-fg:rgba(255,255,255,.9);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.07);--mi-hover:rgba(255,255,255,.16);--mi-active:linear-gradient(135deg,rgba(29,233,182,.3),rgba(29,233,182,.12));--mi-active-fg:#fff;--mi-active-icon:#00897b;}\n:host([data-theme=\"auto\"]){--sb-bg:var(--ui-surface,#252b38);--sb-fg:var(--ui-text,#e2e6ec);--sb-hdr-fg:var(--ui-text,#fff);--mi-bg:color-mix(in srgb,var(--ui-text,#fff) 6%,transparent);--mi-fg:var(--ui-text,#e2e6ec);--mi-hover:color-mix(in srgb,var(--ui-text,#fff) 13%,transparent);--mi-active:color-mix(in srgb,var(--accent,#2680eb) 26%,transparent);--mi-active-border:color-mix(in srgb,var(--accent,#2680eb) 55%,transparent);--mi-active-fg:var(--ui-text,#fff);--mi-active-icon:var(--accent,#2680eb);}","script":"export function init(instance){\n instance._bindingsRefresh?.();\n const IOB = window.IOB;\n const cfg = (IOB && IOB.config && IOB.config.navigation) || {};\n instance.setAttribute('data-theme', cfg.theme || 'default');\n function htmlFromDef(def){\n let attrs='';\n if(def.defaultAttributes) for(const a in def.defaultAttributes){ const v=def.defaultAttributes[a]; attrs+=' '+a+'=\"'+String(typeof v==='object'?JSON.stringify(v):v).replace(/\"/g,'&quot;')+'\"'; }\n let style='';\n if(def.defaultStyles) for(const k in def.defaultStyles) style+=k+':'+def.defaultStyles[k]+';';\n if(def.defaultWidth) style+='width:'+def.defaultWidth+';';\n if(def.defaultHeight) style+='height:'+def.defaultHeight+';';\n return '<'+def.tag+attrs+(style?' style=\"'+style+'\"':'')+'>'+(def.defaultContent||'')+'</'+def.tag+'>';\n }\n function wireSlot(zone, cfgRoot, key){\n if(!zone) return;\n zone.innerHTML = (cfgRoot && cfgRoot.slots && cfgRoot.slots[key]) || '';\n if(!isDesigner) return;\n if(!zone.innerHTML.trim()){ zone.dataset.empty='1'; zone.style.minHeight='30px'; zone.style.outline='1px dashed rgba(255,255,255,.28)'; zone.style.borderRadius='8px'; zone.style.margin='4px 8px'; }\n (function(){\n let chip=null,target=null,hideT=null;\n const saveFromDom=()=>{ if(!cfgRoot.slots) cfgRoot.slots={}; cfgRoot.slots[key]=Array.from(zone.children).map(c=>c.outerHTML).join(''); if(window.IOB&&window.IOB.saveConfig) window.IOB.saveConfig(); };\n const hideChip=()=>{ if(chip) chip.style.display='none'; target=null; };\n const ensureChip=()=>{ if(chip) return chip; chip=document.createElement('div'); chip.className='nav-slot-delchip'; chip.textContent='✕'; chip.title='Delete'; chip.style.cssText='position:fixed;z-index:2147483647;width:18px;height:18px;line-height:18px;text-align:center;font:700 12px/18px sans-serif;background:#e74c3c;color:#fff;border-radius:50%;cursor:pointer;box-shadow:0 1px 5px rgba(0,0,0,.45);user-select:none;display:none;'; chip.addEventListener('mouseenter',()=>{ if(hideT){clearTimeout(hideT);hideT=null;} }); chip.addEventListener('mouseleave',hideChip); chip.addEventListener('click',e=>{ e.preventDefault(); e.stopPropagation(); if(target&&target.parentNode===zone){ target.remove(); saveFromDom(); } hideChip(); }); document.body.appendChild(chip); return chip; };\n const showChip=(el)=>{ if(hideT){clearTimeout(hideT);hideT=null;} target=el; const c=ensureChip(); const r=el.getBoundingClientRect(); c.style.left=(r.right-9)+'px'; c.style.top=(r.top-9)+'px'; c.style.display='block'; };\n zone.addEventListener('mouseover', e=>{ let el=e.target; while(el && el!==zone && el.parentNode!==zone) el=el.parentNode; if(el && el.parentNode===zone && el.nodeType===1) showChip(el); });\n zone.addEventListener('mouseleave', ()=>{ hideT=setTimeout(()=>{ if(chip && chip.style.display!=='none' && !chip.matches(':hover')) hideChip(); }, 160); });\n })();\n zone.addEventListener('dragover', e=>{ if(e.dataTransfer && e.dataTransfer.types && e.dataTransfer.types.indexOf('text/json/elementdefintion')>=0){ e.preventDefault(); zone.style.background='rgba(255,255,255,.18)'; } });\n zone.addEventListener('dragleave', ()=>{ zone.style.background=''; });\n zone.addEventListener('drop', e=>{ e.preventDefault(); e.stopPropagation(); zone.style.background='';\n try{ const td=e.dataTransfer.getData('text/json/elementdefintion'); if(!td) return; const def=JSON.parse(td); const html=htmlFromDef(def);\n if(!cfgRoot.slots) cfgRoot.slots={}; cfgRoot.slots[key]=(cfgRoot.slots[key]||'')+html;\n if(window.IOB && window.IOB.saveConfig) window.IOB.saveConfig();\n }catch(err){ console.warn('[nav] slot drop failed', err); }\n });\n }\n const isDesigner = !!document.querySelector('iobroker-webui-app-shell');\n if(typeof isDesigner!=='undefined' && isDesigner){ try{ document.querySelectorAll('.nav-slot-delchip').forEach(n=>n.remove()); }catch(e){} }\n const hdr=instance._getDomElement('sbHdr'), items=instance._getDomElement('sbItems');\n if(cfg.bgColor) instance.style.setProperty('--sb-bg', cfg.bgColor);\n if(cfg.textColor){ instance.style.setProperty('--sb-fg', cfg.textColor); instance.style.setProperty('--mi-fg', cfg.textColor); }\n if(cfg.menuHeaderTextColor) instance.style.setProperty('--sb-hdr-fg', cfg.menuHeaderTextColor);\n if(cfg.bgColorSelected){ instance.style.setProperty('--mi-active', cfg.bgColorSelected); instance.style.setProperty('--mi-active-icon', cfg.bgColorSelected); }\n if(cfg.textColorSelected) instance.style.setProperty('--mi-active-fg', cfg.textColorSelected);\n if(hdr){ hdr.textContent=cfg.menuHeaderText||''; hdr.style.display=cfg.menuHeaderText?'':'none'; }\n let state='expanded'; const ICONS_W=64; let burger=null; let hasItems=false;\n const navWidth = cfg.menuWidth ? (/^\\d+$/.test(String(cfg.menuWidth))?cfg.menuWidth+'px':String(cfg.menuWidth)) : '220px';\n const setActive=(name)=>{ if(items) items.querySelectorAll('webui-navigation-menu-item').forEach(mi=>{ mi.selected=(mi.screenName===name); }); };\n const navTo=(name,kind)=>{ if(IOB&&IOB.changeView) IOB.changeView.emit(kind==='report'?{name:name,type:'report'}:name); setActive(name); if(cfg.hideAfterSelection) setState('hidden'); };\n async function loadEntries(){\n if(!IOB||!IOB.getAllNames) return [];\n // Reports carry every screen setting, navigation among them, so they belong in the\n // menu exactly as screens do — the entry remembers which kind it is so the runtime opens\n // a .report in the report viewer rather than looking for a screen of that name.\n // Screens and reports are listed at the same time and every file is read at the same\n // time — each listing is a round trip to the server, so waiting for one before the next\n // is what made the menu slow.\n const kinds=await Promise.all(['screen','report'].map(async k=>[k,await IOB.getAllNames(k).catch(()=>[])]));\n const all=await Promise.all(kinds.flatMap(([kind,names])=>names.map(async n=>{ const o=await IOB.getWebuiObject(kind,n).catch(()=>null); const nv=o&&o.settings&&o.settings.navigation; return nv&&nv.show?{name:n,kind:kind,title:nv.title||n,order:parseFloat(nv.order)||0,image:nv.image||''}:null; })));\n const list=all.filter(Boolean);\n list.sort((a,b)=>a.order-b.order); return list;\n }\n // The menu this browser saw last is drawn at once; the fresh list replaces it when it differs.\n const menuCacheKey='webui.nav.menu.'+((IOB&&IOB.configPath)||'');\n async function render(){\n let cached=null;\n if(!isDesigner){ try{ cached=JSON.parse(localStorage.getItem(menuCacheKey)||'null'); }catch(e){} if(Array.isArray(cached)) draw(cached); }\n const list=await loadEntries();\n const fresh=JSON.stringify(list);\n if(!Array.isArray(cached)||JSON.stringify(cached)!==fresh) draw(list);\n if(!isDesigner){ try{ localStorage.setItem(menuCacheKey,fresh); }catch(e){} }\n }\n function draw(list){\n hasItems=list.length>0;\n if(items){ items.innerHTML='';\n for(const e of list){ const mi=document.createElement('webui-navigation-menu-item'); mi.screenName=e.name; mi.title=e.title; mi.image=e.image; mi.addEventListener('menuitemclick',()=>navTo(e.name,e.kind)); items.appendChild(mi); }\n }\n if(!isDesigner){ if(hasItems && !cfg.hideMenu) applyRuntimeLayout(); else { const v=document.getElementById('viewer'); if(v){ v.style.left='0'; v.style.width='100%'; } instance.style.display='none'; } }\n }\n function applyRuntimeLayout(){\n const ab=document.getElementById('__nav-appbar-host'); const top=ab?ab.offsetHeight:0;\n instance.style.position='fixed'; instance.style.left='0'; instance.style.bottom='0'; instance.style.top=top+'px';\n instance.style.zIndex='99999'; instance.style.overflow='hidden'; instance.style.transition='width .2s ease,transform .2s ease';\n if(!burger){ burger=document.createElement('div'); burger.id='__nav-burger'; burger.textContent='☰'; const inBar=top>0; burger.style.cssText='position:fixed;top:'+(inBar?0:8)+'px;left:'+(inBar?0:8)+'px;z-index:100002;width:'+(inBar?top:42)+'px;height:'+(inBar?top:42)+'px;display:flex;align-items:center;justify-content:center;font-size:22px;cursor:pointer;'+(inBar?'':'border-radius:8px;')+'background:'+(cfg.showButtonBg||(inBar?'transparent':'rgba(0,0,0,.45)'))+';color:'+(cfg.showButtonColor||'#fff')+';transition:background .15s;'; burger.onclick=cycle; document.body.appendChild(burger); }\n setState(state);\n }\n function setState(s){\n state=s; const v=document.getElementById('viewer'); const top=parseInt(instance.style.top)||0; let left='0px';\n const compact=(state==='icons');\n if(items) items.querySelectorAll('webui-navigation-menu-item').forEach(mi=>mi.compact=compact);\n if(state==='expanded'){ instance.style.width=navWidth; instance.style.transform='translateX(0)'; if(hdr)hdr.style.display=cfg.menuHeaderText?'':'none'; left=navWidth; }\n else if(state==='icons'){ instance.style.width=ICONS_W+'px'; instance.style.transform='translateX(0)'; if(hdr)hdr.style.display='none'; left=ICONS_W+'px'; }\n else { instance.style.transform='translateX(-110%)'; left='0px'; }\n if(v){ v.style.left=left; v.style.width='calc(100% - '+left+')'; v.style.top=top+'px'; v.style.height='calc(100% - '+top+'px)'; }\n }\n function cycle(){ const o=['expanded','icons','hidden']; let n=o[(o.indexOf(state)+1)%o.length]; if(n==='hidden'&&cfg.doNotHideCompletely)n='icons'; setState(n); }\n wireSlot(instance._getDomElement('slotTop'), cfg, 'top');\n wireSlot(instance._getDomElement('slotBottom'), cfg, 'bottom');\n render();\n if(IOB&&IOB.changeView) IOB.changeView.on(v=>setActive(v));\n const sv=document.getElementById('viewer'); if(sv&&sv.screenName) setActive(sv.screenName);\n}"}
@@ -0,0 +1 @@
1
+ {"html":"","style":":host {\n}\n\n* {\n box-sizing: border-box;\n}\n img {\n display: block;\n width: 100%;\n height: auto;\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}\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 if (!instance.svg_content) {\n instance.shadowRoot.innerHTML = `<p style=\"color:red;\">SVG yoxdur</p>`;\n return;\n }\n const svg = instance.svg_content;\n\n // Base64‑ə çevir\n const b64 = utf8ToBase64(svg);\n\n // Shadow DOM‑a HTML yaz\n instance.shadowRoot.innerHTML = `\n <img src=\"data:image/svg+xml;base64,${b64}\" \n alt=\"Omega Logo\" />\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\nfunction utf8ToBase64(str) {\n return btoa(unescape(encodeURIComponent(str)));\n}","properties":{"svg_content":{"type":"string"}},"settings":{}}
@@ -0,0 +1 @@
1
+ {"html":"<div class=\"svg-container\">\n <img id=\"svgImg\" src=\"\" bind-css:width=\"??width\" bind-css:height=\"??height\" bind-css:border-color=\"??borderColor\" />\n</div>","style":":host {\n display: inline-block;\n width: 100%;\n height: 100%;\n background-color: transparent;\n}\n\n* {\n box-sizing: border-box;\n}\n\n.svg-container {\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.svg-container img {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n border-style: solid;\n border-width: 1px;\n}","script":"/**\n * @param {BaseScreenViewerAndControl} instance\n */\nexport function init(instance) {\n updateSvg(instance);\n instance._assignEvent('svgContent-changed', () => updateSvg(instance));\n}\n\nexport function connectedCallback(instance) {\n updateSvg(instance);\n}\n\nexport function disconnectedCallback(instance) {\n}\n\n/**\n * @param {BaseScreenViewerAndControl} instance\n */\nfunction updateSvg(instance) {\n const img = instance._getDomElement('svgImg');\n if (!img) return;\n\n const rawSvg = instance.svgContent || '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1024 259\"><path d=\"M44.1303,162.296 44.1303,133.587 72.6946,162.296 z\" style=\"fill:#0000ff;stroke:#0000ff;\" /></svg>';\n\n try {\n const b64 = btoa(unescape(encodeURIComponent(rawSvg)));\n img.src = `data:image/svg+xml;base64,${b64}`;\n } catch (e) {\n console.warn('SVG Base64 encoding error:', e);\n }\n}","properties":{"svgContent":{"type":"string","default":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1024 259\"><path d=\"M44.1303,162.296 44.1303,133.587 72.6946,162.296 z\" style=\"fill:#0000ff;stroke:#0000ff;\" /></svg>"},"width":{"type":"string","default":"500px"},"height":{"type":"string","default":"200px"},"borderColor":{"type":"color","default":"#cccccc"}},"settings":{}}
@@ -1 +1 @@
1
- {"html":"<button type=\"button\" bind-css:background-color='{\"signal\":\"{??relativesignalpath}{??valveid}OIN\",\"converter\":{\"false\":\"#342d2d\",\"true\":\"#09dc33\"}}' class=\"button\" bind-attr: @click='{\"name\":\"SetSignalOpen\"}' data-visibility-enabled=\"false\" data-visibility-condition=\"==\" data-visibility-action=\"disable\" style=\"width:86px;height:51px;position:absolute;left:24px;top:140px;opacity:1;transform-origin:53px 27px;transform:rotate(0deg);translate:0px;\">Aç</button>\n<span bind-content:text='{\"signal\":\"??valveid\",\"converter\":{\"10\":\"Valve 10\",\"11\":\"Valve 11\",\"20\":\"Valve 20\",\"21\":\"Valve 21\",\"30\":\"Valve 30\",\"31\":\"Valve 31\"}}' style=\"position:absolute;left:31px;top:18px;width:145px;height:16px;\">hello world</span>\n<span id=\"myrem\" style=\"position:absolute;left:31px;top:96px;width:79px;height:27px;opacity:0;font:bold 20px &quot;Times New Roman&quot;;\">Remote</span>\n<button type=\"button\" bind-css:background-color='{\"signal\":\"{??relativesignalpath}{??valveid}CIN\",\"converter\":{\"false\":\"#342d2d\",\"true\":\"#09dc33\"}}' class=\"button\" bind-attr: @click=\"SetSignalClose\" data-visibility-enabled=\"false\" data-visibility-condition=\"==\" data-visibility-action=\"disable\" style=\"width:86px;height:51px;position:absolute;left:24px;top:208px;opacity:1;transform-origin:52.9334px 26.9333px;\">Bağla</button>\n<button type=\"button\" class=\"button\" bind-attr: @click=\"SetSignalStop\" style=\"width:86px;height:51px;position:absolute;left:24px;top:278px;opacity:1;transform-origin:52.9334px 26.9333px;\">Stop</button>\n<div style=\"height:50px;width:50px;\"></div>\n<span style=\"position:absolute;left:26px;top:348px;\">Açıqdır</span>\n<span style=\"position:absolute;left:26px;top:391px;\">Bağlıdır</span>\n<webui-webui_led_light1 bind-prop:fillcolor=\"??opened\" style=\"position:absolute;left:86px;top:339px;width:43px;height:41px;\"></webui-webui_led_light1>\n<webui-webui_led_light1 bind-prop:fillcolor=\"??closed\" style=\"position:absolute;left:86px;top:384px;width:43px;height:41px;rotate:0deg;\"></webui-webui_led_light1>\n<span bind-content:text=\"??relativesignalpath{??valveid}OIN\" style=\"position:absolute;left:58px;top:67px;width:112px;\">hello world</span>\n","style":":host {\n background: transparent;\n}\n\n* {\n box-sizing: border-box;\n}\n.button {\n position: relative;\n padding: 20px;\n width: 200px;\n -webkit-transition-duration: 0.4s;\n /* Safari */\n transition-duration: 0.4s;\n text-decoration: none;\n overflow: hidden;\n cursor: pointer;\n background-color: grey;\n color: white;\n}\n\n.button:after {\n content: \"\";\n background: #90EE90;\n display: block;\n position: absolute;\n padding-top: 300%;\n padding-left: 350%;\n margin-left: -20px!important;\n margin-top: -120%;\n opacity: 0;\n transition: all 0.8s;\n}\n\n.button:active:after {\n padding: 0;\n margin: 0;\n opacity: 1;\n transition: 0s;\n}\n\n#myrem {\n font-style: italic;\n font-size: 20px;\n font-family: 'Times New Roman', Times, serif;\n font-weight: bold;\n}\n\n.blinking_live {\n height: 15px;\n width: 15px;\n border-radius: 15px;\n color: green;\n animation: blink-live 2s cubic-bezier(0, 0.2, 0.8, 1) infinite;\n}\n\n@keyframes blink-live {\n 0% {\n opacity: 1.0;\n }\n\n 50% {\n opacity: 0.0;\n }\n\n 100% {\n opacity: 1.0;\n }\n}","script":"export function init(instance){\r\n\r\n}\r\n\r\n/**\r\n* SetSignalOpen - 'click' event of <button>\r\n* @param {PointerEvent} event\r\n* @param {Element} eventRaisingElement\r\n* @param {ShadowRoot} shadowRoot\r\n* @param {BaseScreenViewerAndControl} instance\r\n* @param {Object.<string, *>} parameters\r\n*/\r\nexport function SetSignalOpen(event, eventRaisingElement, shadowRoot, instance, parameters) {\r\n window.IOB.setState(instance.relativesignalpath + instance.valveid + \"OIN\",true);\r\n}\r\n\r\n/**\r\n* SetSignalClose - 'click' event of <button>\r\n* @param {PointerEvent} event\r\n* @param {Element} eventRaisingElement\r\n* @param {ShadowRoot} shadowRoot\r\n* @param {BaseScreenViewerAndControl} instance\r\n* @param {Object.<string, *>} parameters\r\n*/\r\nexport function SetSignalClose(event, eventRaisingElement, shadowRoot, instance, parameters) {\r\n window.IOB.setState(instance.relativesignalpath + instance.valveid + \"CIN\",true);\r\n}\r\n\r\n/**\r\n* SetSignalStop - 'click' event of <button>\r\n* @param {PointerEvent} event\r\n* @param {Element} eventRaisingElement\r\n* @param {ShadowRoot} shadowRoot\r\n* @param {BaseScreenViewerAndControl} instance\r\n* @param {Object.<string, *>} parameters\r\n*/\r\nexport function SetSignalStop(event, eventRaisingElement, shadowRoot, instance, parameters) {\r\n //window.IOB.setState(\"my-opcua.0.ns=2___s=Rslinx_For_IO___Device1___Pipeline___Online___ES\"+instance.valveid+\"S\",1);\r\n}","properties":{"oIN":{"type":"signal"},"cIN":{"type":"signal"},"opened":{"type":"boolean"},"closed":{"type":"boolean"},"valveid":{"type":"string"},"relativesignalpath":{"type":"string"}},"settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}
1
+ {"html":"<button type=\"button\" bind-css:background-color='{\"signal\":\"{??relativesignalpath}{??valveid}OIN\",\"converter\":{\"false\":\"#342d2d\",\"true\":\"#09dc33\"}}' class=\"button\" @click='{\"name\":\"SetSignalOpen\"}' data-visibility-enabled=\"false\" data-visibility-condition=\"==\" data-visibility-action=\"disable\" style=\"width:86px;height:51px;position:absolute;left:24px;top:140px;opacity:1;transform-origin:53px 27px;transform:rotate(0deg);translate:0px;\">Aç</button>\n<span bind-content:text='{\"signal\":\"??valveid\",\"converter\":{\"10\":\"Valve 10\",\"11\":\"Valve 11\",\"20\":\"Valve 20\",\"21\":\"Valve 21\",\"30\":\"Valve 30\",\"31\":\"Valve 31\"}}' style=\"position:absolute;left:31px;top:18px;width:145px;height:16px;\">hello world</span>\n<span id=\"myrem\" style=\"position:absolute;left:31px;top:96px;width:79px;height:27px;opacity:0;font:bold 20px &quot;Times New Roman&quot;;\">Remote</span>\n<button type=\"button\" bind-css:background-color='{\"signal\":\"{??relativesignalpath}{??valveid}CIN\",\"converter\":{\"false\":\"#342d2d\",\"true\":\"#09dc33\"}}' class=\"button\" @click=\"SetSignalClose\" data-visibility-enabled=\"false\" data-visibility-condition=\"==\" data-visibility-action=\"disable\" style=\"width:86px;height:51px;position:absolute;left:24px;top:208px;opacity:1;transform-origin:52.9334px 26.9333px;\">Bağla</button>\n<button type=\"button\" class=\"button\" @click=\"SetSignalStop\" style=\"width:86px;height:51px;position:absolute;left:24px;top:278px;opacity:1;transform-origin:52.9334px 26.9333px;\">Stop</button>\n<div style=\"height:50px;width:50px;\"></div>\n<span style=\"position:absolute;left:26px;top:348px;\">Açıqdır</span>\n<span style=\"position:absolute;left:26px;top:391px;\">Bağlıdır</span>\n<webui-webui_led_light1 bind-prop:fillcolor=\"??opened\" style=\"position:absolute;left:86px;top:339px;width:43px;height:41px;\"></webui-webui_led_light1>\n<webui-webui_led_light1 bind-prop:fillcolor=\"??closed\" style=\"position:absolute;left:86px;top:384px;width:43px;height:41px;rotate:0deg;\"></webui-webui_led_light1>\n<span bind-content:text=\"??relativesignalpath{??valveid}OIN\" style=\"position:absolute;left:58px;top:67px;width:112px;\">hello world</span>\n","style":":host {\n background: transparent;\n}\n\n* {\n box-sizing: border-box;\n}\n.button {\n position: relative;\n padding: 20px;\n width: 200px;\n -webkit-transition-duration: 0.4s;\n /* Safari */\n transition-duration: 0.4s;\n text-decoration: none;\n overflow: hidden;\n cursor: pointer;\n background-color: grey;\n color: white;\n}\n\n.button:after {\n content: \"\";\n background: #90EE90;\n display: block;\n position: absolute;\n padding-top: 300%;\n padding-left: 350%;\n margin-left: -20px!important;\n margin-top: -120%;\n opacity: 0;\n transition: all 0.8s;\n}\n\n.button:active:after {\n padding: 0;\n margin: 0;\n opacity: 1;\n transition: 0s;\n}\n\n#myrem {\n font-style: italic;\n font-size: 20px;\n font-family: 'Times New Roman', Times, serif;\n font-weight: bold;\n}\n\n.blinking_live {\n height: 15px;\n width: 15px;\n border-radius: 15px;\n color: green;\n animation: blink-live 2s cubic-bezier(0, 0.2, 0.8, 1) infinite;\n}\n\n@keyframes blink-live {\n 0% {\n opacity: 1.0;\n }\n\n 50% {\n opacity: 0.0;\n }\n\n 100% {\n opacity: 1.0;\n }\n}","script":"export function init(instance){\r\n\r\n}\r\n\r\n/**\r\n* SetSignalOpen - 'click' event of <button>\r\n* @param {PointerEvent} event\r\n* @param {Element} eventRaisingElement\r\n* @param {ShadowRoot} shadowRoot\r\n* @param {BaseScreenViewerAndControl} instance\r\n* @param {Object.<string, *>} parameters\r\n*/\r\nexport function SetSignalOpen(event, eventRaisingElement, shadowRoot, instance, parameters) {\r\n window.IOB.setState(instance.relativesignalpath + instance.valveid + \"OIN\",true);\r\n}\r\n\r\n/**\r\n* SetSignalClose - 'click' event of <button>\r\n* @param {PointerEvent} event\r\n* @param {Element} eventRaisingElement\r\n* @param {ShadowRoot} shadowRoot\r\n* @param {BaseScreenViewerAndControl} instance\r\n* @param {Object.<string, *>} parameters\r\n*/\r\nexport function SetSignalClose(event, eventRaisingElement, shadowRoot, instance, parameters) {\r\n window.IOB.setState(instance.relativesignalpath + instance.valveid + \"CIN\",true);\r\n}\r\n\r\n/**\r\n* SetSignalStop - 'click' event of <button>\r\n* @param {PointerEvent} event\r\n* @param {Element} eventRaisingElement\r\n* @param {ShadowRoot} shadowRoot\r\n* @param {BaseScreenViewerAndControl} instance\r\n* @param {Object.<string, *>} parameters\r\n*/\r\nexport function SetSignalStop(event, eventRaisingElement, shadowRoot, instance, parameters) {\r\n //window.IOB.setState(\"my-opcua.0.ns=2___s=Rslinx_For_IO___Device1___Pipeline___Online___ES\"+instance.valveid+\"S\",1);\r\n}","properties":{"oIN":{"type":"signal"},"cIN":{"type":"signal"},"opened":{"type":"boolean"},"closed":{"type":"boolean"},"valveid":{"type":"string"},"relativesignalpath":{"type":"string"}},"settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}