iobroker.mywebui 1.133.0 → 1.158.0

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 (358) hide show
  1. package/admin/i18n.json +35 -0
  2. package/admin/jsonConfig.json +75 -6
  3. package/default-controls/controls/WebuiConnectionMonitor1.control +1 -1
  4. package/default-controls/controls/navigation/sidebar.control +1 -1
  5. package/dist/backend/3d-editor-integration.js +1 -1
  6. package/dist/backend/fonts/Roboto-Italic.ttf +0 -0
  7. package/dist/backend/fonts/Roboto-Medium.ttf +0 -0
  8. package/dist/backend/fonts/Roboto-MediumItalic.ttf +0 -0
  9. package/dist/backend/fonts/Roboto-Regular.ttf +0 -0
  10. package/dist/backend/main.js +1 -1
  11. package/dist/backend/reportCsv.js +1 -0
  12. package/dist/backend/reportGenerate.js +1 -0
  13. package/dist/backend/reportOutput.js +1 -0
  14. package/dist/backend/reportPdf.js +1 -0
  15. package/dist/backend/reportRenderer.js +1 -0
  16. package/dist/backend/reportSandbox.js +1 -0
  17. package/dist/backend/reportSandboxWorker.js +1 -0
  18. package/dist/backend/vendor/report-dom.iife.js +19 -0
  19. package/docs/README.md +1 -0
  20. package/docs/bindings.md +19 -0
  21. package/docs/custom-controls.md +66 -1
  22. package/docs/npm-widgets.md +15 -0
  23. package/docs/permissions.md +14 -0
  24. package/docs/template-directives.md +263 -8
  25. package/docs/translations.md +18 -0
  26. package/io-package.json +7 -2
  27. package/native/mywebui_scada_engine.darwin-arm64.node +0 -0
  28. package/native/mywebui_scada_engine.darwin-x64.node +0 -0
  29. package/native/mywebui_scada_engine.linux-arm-gnueabihf.node +0 -0
  30. package/native/mywebui_scada_engine.linux-arm64-gnu.node +0 -0
  31. package/native/mywebui_scada_engine.linux-arm64-musl.node +0 -0
  32. package/native/mywebui_scada_engine.linux-x64-gnu.node +0 -0
  33. package/native/mywebui_scada_engine.linux-x64-musl.node +0 -0
  34. package/native/mywebui_scada_engine.win32-x64-msvc.node +0 -0
  35. package/package.json +5 -1
  36. package/www/3d-editor/js/Animation.js +1 -1
  37. package/www/3d-editor/js/AnimationResizer.js +1 -1
  38. package/www/3d-editor/js/Command.js +1 -1
  39. package/www/3d-editor/js/Config.js +1 -1
  40. package/www/3d-editor/js/Editor.js +1 -1
  41. package/www/3d-editor/js/EditorControls.js +1 -1
  42. package/www/3d-editor/js/GLTFImportDialog.js +1 -1
  43. package/www/3d-editor/js/History.js +1 -1
  44. package/www/3d-editor/js/Loader.js +1 -1
  45. package/www/3d-editor/js/LoaderUtils.js +1 -1
  46. package/www/3d-editor/js/Menubar.Add.js +1 -1
  47. package/www/3d-editor/js/Menubar.Edit.js +1 -1
  48. package/www/3d-editor/js/Menubar.File.js +1 -1
  49. package/www/3d-editor/js/Menubar.Help.js +1 -1
  50. package/www/3d-editor/js/Menubar.Render.js +1 -1
  51. package/www/3d-editor/js/Menubar.Status.js +1 -1
  52. package/www/3d-editor/js/Menubar.View.js +1 -1
  53. package/www/3d-editor/js/Menubar.js +1 -1
  54. package/www/3d-editor/js/Player.js +1 -1
  55. package/www/3d-editor/js/Resizer.js +1 -1
  56. package/www/3d-editor/js/Script.js +1 -1
  57. package/www/3d-editor/js/Selector.js +1 -1
  58. package/www/3d-editor/js/Sidebar.Geometry.BoxGeometry.js +1 -1
  59. package/www/3d-editor/js/Sidebar.Geometry.BufferGeometry.js +1 -1
  60. package/www/3d-editor/js/Sidebar.Geometry.CapsuleGeometry.js +1 -1
  61. package/www/3d-editor/js/Sidebar.Geometry.CircleGeometry.js +1 -1
  62. package/www/3d-editor/js/Sidebar.Geometry.CylinderGeometry.js +1 -1
  63. package/www/3d-editor/js/Sidebar.Geometry.DodecahedronGeometry.js +1 -1
  64. package/www/3d-editor/js/Sidebar.Geometry.ExtrudeGeometry.js +1 -1
  65. package/www/3d-editor/js/Sidebar.Geometry.IcosahedronGeometry.js +1 -1
  66. package/www/3d-editor/js/Sidebar.Geometry.LatheGeometry.js +1 -1
  67. package/www/3d-editor/js/Sidebar.Geometry.Modifiers.js +1 -1
  68. package/www/3d-editor/js/Sidebar.Geometry.OctahedronGeometry.js +1 -1
  69. package/www/3d-editor/js/Sidebar.Geometry.PlaneGeometry.js +1 -1
  70. package/www/3d-editor/js/Sidebar.Geometry.RingGeometry.js +1 -1
  71. package/www/3d-editor/js/Sidebar.Geometry.ShapeGeometry.js +1 -1
  72. package/www/3d-editor/js/Sidebar.Geometry.SphereGeometry.js +1 -1
  73. package/www/3d-editor/js/Sidebar.Geometry.TetrahedronGeometry.js +1 -1
  74. package/www/3d-editor/js/Sidebar.Geometry.TextGeometry.js +1 -1
  75. package/www/3d-editor/js/Sidebar.Geometry.TorusGeometry.js +1 -1
  76. package/www/3d-editor/js/Sidebar.Geometry.TorusKnotGeometry.js +1 -1
  77. package/www/3d-editor/js/Sidebar.Geometry.TubeGeometry.js +1 -1
  78. package/www/3d-editor/js/Sidebar.Geometry.js +1 -1
  79. package/www/3d-editor/js/Sidebar.Material.BooleanProperty.js +1 -1
  80. package/www/3d-editor/js/Sidebar.Material.ColorProperty.js +1 -1
  81. package/www/3d-editor/js/Sidebar.Material.ConstantProperty.js +1 -1
  82. package/www/3d-editor/js/Sidebar.Material.MapProperty.js +1 -1
  83. package/www/3d-editor/js/Sidebar.Material.NumberProperty.js +1 -1
  84. package/www/3d-editor/js/Sidebar.Material.Program.js +1 -1
  85. package/www/3d-editor/js/Sidebar.Material.RangeValueProperty.js +1 -1
  86. package/www/3d-editor/js/Sidebar.Material.js +1 -1
  87. package/www/3d-editor/js/Sidebar.Object.js +1 -1
  88. package/www/3d-editor/js/Sidebar.Project.App.js +1 -1
  89. package/www/3d-editor/js/Sidebar.Project.Materials.js +1 -1
  90. package/www/3d-editor/js/Sidebar.Project.Renderer.js +1 -1
  91. package/www/3d-editor/js/Sidebar.Project.Resources.js +1 -1
  92. package/www/3d-editor/js/Sidebar.Project.js +1 -1
  93. package/www/3d-editor/js/Sidebar.Properties.js +1 -1
  94. package/www/3d-editor/js/Sidebar.Scene.js +1 -1
  95. package/www/3d-editor/js/Sidebar.Script.js +1 -1
  96. package/www/3d-editor/js/Sidebar.Settings.History.js +1 -1
  97. package/www/3d-editor/js/Sidebar.Settings.Shortcuts.js +1 -1
  98. package/www/3d-editor/js/Sidebar.Settings.js +1 -1
  99. package/www/3d-editor/js/Sidebar.js +1 -1
  100. package/www/3d-editor/js/Storage.js +1 -1
  101. package/www/3d-editor/js/Strings.js +1 -1
  102. package/www/3d-editor/js/TextureParametersDialog.js +1 -1
  103. package/www/3d-editor/js/Toolbar.js +1 -1
  104. package/www/3d-editor/js/Viewport.Controls.js +1 -1
  105. package/www/3d-editor/js/Viewport.Info.js +1 -1
  106. package/www/3d-editor/js/Viewport.Pathtracer.js +1 -1
  107. package/www/3d-editor/js/Viewport.ViewHelper.js +1 -1
  108. package/www/3d-editor/js/Viewport.XR.js +1 -1
  109. package/www/3d-editor/js/Viewport.js +1 -1
  110. package/www/3d-editor/js/commands/AddObjectCommand.js +1 -1
  111. package/www/3d-editor/js/commands/AddScriptCommand.js +1 -1
  112. package/www/3d-editor/js/commands/Commands.js +1 -1
  113. package/www/3d-editor/js/commands/MoveObjectCommand.js +1 -1
  114. package/www/3d-editor/js/commands/MultiCmdsCommand.js +1 -1
  115. package/www/3d-editor/js/commands/RemoveObjectCommand.js +1 -1
  116. package/www/3d-editor/js/commands/RemoveScriptCommand.js +1 -1
  117. package/www/3d-editor/js/commands/SetColorCommand.js +1 -1
  118. package/www/3d-editor/js/commands/SetGeometryCommand.js +1 -1
  119. package/www/3d-editor/js/commands/SetGeometryValueCommand.js +1 -1
  120. package/www/3d-editor/js/commands/SetMaterialColorCommand.js +1 -1
  121. package/www/3d-editor/js/commands/SetMaterialCommand.js +1 -1
  122. package/www/3d-editor/js/commands/SetMaterialMapCommand.js +1 -1
  123. package/www/3d-editor/js/commands/SetMaterialRangeCommand.js +1 -1
  124. package/www/3d-editor/js/commands/SetMaterialValueCommand.js +1 -1
  125. package/www/3d-editor/js/commands/SetMaterialVectorCommand.js +1 -1
  126. package/www/3d-editor/js/commands/SetPositionCommand.js +1 -1
  127. package/www/3d-editor/js/commands/SetRotationCommand.js +1 -1
  128. package/www/3d-editor/js/commands/SetScaleCommand.js +1 -1
  129. package/www/3d-editor/js/commands/SetSceneCommand.js +1 -1
  130. package/www/3d-editor/js/commands/SetScriptValueCommand.js +1 -1
  131. package/www/3d-editor/js/commands/SetShadowValueCommand.js +1 -1
  132. package/www/3d-editor/js/commands/SetTextureParametersCommand.js +1 -1
  133. package/www/3d-editor/js/commands/SetUuidCommand.js +1 -1
  134. package/www/3d-editor/js/commands/SetValueCommand.js +1 -1
  135. package/www/assets/icons/neon/file-report.svg +1 -0
  136. package/www/assets/icons/neon/folder-reports.svg +1 -0
  137. package/www/dist/frontend/bundle/ReportViewer-462GGJM3.js +197 -0
  138. package/www/dist/frontend/bundle/ReportViewer-DZCUPA5C.js +194 -0
  139. package/www/dist/frontend/bundle/ReportViewer-EOZ4ZNWX.js +194 -0
  140. package/www/dist/frontend/bundle/ReportViewer-FV3UTFAG.js +197 -0
  141. package/www/dist/frontend/bundle/ReportViewer-LQNEQAY5.js +194 -0
  142. package/www/dist/frontend/bundle/ReportViewer-MXJYBX2O.js +194 -0
  143. package/www/dist/frontend/bundle/ReportViewer-VSZZU3RI.js +197 -0
  144. package/www/dist/frontend/bundle/ReportViewer-WH2QE3WJ.js +197 -0
  145. package/www/dist/frontend/bundle/ReportViewer-ZYNTFL5X.js +197 -0
  146. package/www/dist/frontend/bundle/Sha256-BSH7CIPW.js +1 -0
  147. package/www/dist/frontend/bundle/chunk-2WB36FW5.js +1 -0
  148. package/www/dist/frontend/bundle/chunk-34JJK367.js +3 -3
  149. package/www/dist/frontend/bundle/chunk-3AS7GRBW.js +3 -3
  150. package/www/dist/frontend/bundle/chunk-3C6XC7RW.js +1 -1
  151. package/www/dist/frontend/bundle/chunk-3HNK24O5.js +1 -0
  152. package/www/dist/frontend/bundle/chunk-3OZUBCI3.js +21 -0
  153. package/www/dist/frontend/bundle/chunk-3R27DWVW.js +3 -3
  154. package/www/dist/frontend/bundle/chunk-3WGNLIWJ.js +3 -3
  155. package/www/dist/frontend/bundle/chunk-3YK3Y2SJ.js +1 -1
  156. package/www/dist/frontend/bundle/chunk-43HEBO5G.js +2 -2
  157. package/www/dist/frontend/bundle/chunk-5M6KJ3LC.js +21 -0
  158. package/www/dist/frontend/bundle/chunk-5MUGMRXU.js +1 -1
  159. package/www/dist/frontend/bundle/chunk-5SKI5VSQ.js +21 -0
  160. package/www/dist/frontend/bundle/chunk-5UBRCTRX.js +3 -3
  161. package/www/dist/frontend/bundle/chunk-5X42FDFP.js +21 -0
  162. package/www/dist/frontend/bundle/chunk-65A3PNMG.js +1 -1
  163. package/www/dist/frontend/bundle/chunk-66EU64WD.js +1 -1
  164. package/www/dist/frontend/bundle/chunk-6BL3X2VD.js +2 -2
  165. package/www/dist/frontend/bundle/chunk-6KXIY6CH.js +21 -0
  166. package/www/dist/frontend/bundle/chunk-6L2YCGEX.js +1 -1
  167. package/www/dist/frontend/bundle/chunk-6PNYA23N.js +3 -3
  168. package/www/dist/frontend/bundle/chunk-72PPFGWL.js +21 -0
  169. package/www/dist/frontend/bundle/chunk-7CWDQGAG.js +3 -3
  170. package/www/dist/frontend/bundle/chunk-7HXTD2Z2.js +21 -0
  171. package/www/dist/frontend/bundle/chunk-7PT7IGUJ.js +2 -2
  172. package/www/dist/frontend/bundle/chunk-AI5PDLDE.js +3 -3
  173. package/www/dist/frontend/bundle/chunk-AR7OURA3.js +1 -0
  174. package/www/dist/frontend/bundle/chunk-AUTVRE23.js +1 -1
  175. package/www/dist/frontend/bundle/chunk-AWKVUUKN.js +3 -3
  176. package/www/dist/frontend/bundle/chunk-B6NXWGSB.js +1 -1
  177. package/www/dist/frontend/bundle/chunk-BHSHNRXW.js +1 -1
  178. package/www/dist/frontend/bundle/chunk-BVONDEOL.js +3 -3
  179. package/www/dist/frontend/bundle/chunk-C2MX6ZGS.js +1 -0
  180. package/www/dist/frontend/bundle/chunk-C7OGVQZC.js +1 -1
  181. package/www/dist/frontend/bundle/chunk-CHDKROX5.js +2 -2
  182. package/www/dist/frontend/bundle/chunk-D3YMBTJC.js +21 -0
  183. package/www/dist/frontend/bundle/chunk-DNCD2F6X.js +3 -3
  184. package/www/dist/frontend/bundle/chunk-DV5UA2CC.js +21 -0
  185. package/www/dist/frontend/bundle/chunk-EFPFPS32.js +21 -0
  186. package/www/dist/frontend/bundle/chunk-FFW7WOVM.js +1 -1
  187. package/www/dist/frontend/bundle/chunk-FHF6DL5U.js +1 -1
  188. package/www/dist/frontend/bundle/chunk-FO56TEZH.js +2 -2
  189. package/www/dist/frontend/bundle/chunk-FRCSTZQC.js +3 -3
  190. package/www/dist/frontend/bundle/chunk-FSHAYPVR.js +3 -3
  191. package/www/dist/frontend/bundle/chunk-FSRRPQMX.js +3 -3
  192. package/www/dist/frontend/bundle/chunk-GLQNGCFL.js +1 -1
  193. package/www/dist/frontend/bundle/chunk-GMDVBK2C.js +1 -0
  194. package/www/dist/frontend/bundle/chunk-HC7GL5JY.js +1 -1
  195. package/www/dist/frontend/bundle/chunk-HSA5RDOW.js +1 -0
  196. package/www/dist/frontend/bundle/chunk-HXNIU7FI.js +3 -3
  197. package/www/dist/frontend/bundle/chunk-J4H2NUPG.js +1 -0
  198. package/www/dist/frontend/bundle/chunk-JBDSDK2C.js +3 -3
  199. package/www/dist/frontend/bundle/chunk-JJQSMEFR.js +1 -0
  200. package/www/dist/frontend/bundle/chunk-JSC66ZLQ.js +1 -1
  201. package/www/dist/frontend/bundle/chunk-JVPUFDR5.js +3 -3
  202. package/www/dist/frontend/bundle/chunk-K6IV7LKW.js +1 -1
  203. package/www/dist/frontend/bundle/chunk-KJU5I76S.js +3 -3
  204. package/www/dist/frontend/bundle/chunk-KY73TL6O.js +3 -3
  205. package/www/dist/frontend/bundle/chunk-LKS3S4WX.js +1 -1
  206. package/www/dist/frontend/bundle/chunk-M6VVTDVX.js +3 -3
  207. package/www/dist/frontend/bundle/chunk-MECWXZRN.js +1 -1
  208. package/www/dist/frontend/bundle/chunk-N2YIZTB2.js +1 -1
  209. package/www/dist/frontend/bundle/chunk-NQ7L7KNT.js +3 -3
  210. package/www/dist/frontend/bundle/chunk-O2AJL747.js +3 -3
  211. package/www/dist/frontend/bundle/chunk-P7EOR36H.js +1 -1
  212. package/www/dist/frontend/bundle/chunk-PJYK24KZ.js +1 -1
  213. package/www/dist/frontend/bundle/chunk-PYLAW6UA.js +3 -3
  214. package/www/dist/frontend/bundle/chunk-Q4GLWT4I.js +1 -0
  215. package/www/dist/frontend/bundle/chunk-QJJGP23I.js +1 -1
  216. package/www/dist/frontend/bundle/chunk-QV4IBWXA.js +1 -1
  217. package/www/dist/frontend/bundle/chunk-QVMAZVXM.js +1 -1
  218. package/www/dist/frontend/bundle/chunk-QZZCS7D7.js +1 -0
  219. package/www/dist/frontend/bundle/chunk-R6IQDPAP.js +3 -3
  220. package/www/dist/frontend/bundle/chunk-R6VCTUDJ.js +21 -0
  221. package/www/dist/frontend/bundle/chunk-RQYXUZ7Q.js +21 -0
  222. package/www/dist/frontend/bundle/chunk-RRFSEMFS.js +3 -3
  223. package/www/dist/frontend/bundle/chunk-RTTBZOQU.js +2 -2
  224. package/www/dist/frontend/bundle/chunk-S4TLNW5D.js +1 -1
  225. package/www/dist/frontend/bundle/chunk-S5E33OYY.js +1 -0
  226. package/www/dist/frontend/bundle/chunk-S7P222YS.js +1 -1
  227. package/www/dist/frontend/bundle/chunk-SDWH52K4.js +21 -0
  228. package/www/dist/frontend/bundle/chunk-SE6N4ZEN.js +1 -1
  229. package/www/dist/frontend/bundle/chunk-SQHXO2IT.js +3 -3
  230. package/www/dist/frontend/bundle/chunk-SYJAQ3IA.js +21 -0
  231. package/www/dist/frontend/bundle/chunk-T4AIQBBH.js +2 -2
  232. package/www/dist/frontend/bundle/chunk-THOJBUGZ.js +1 -0
  233. package/www/dist/frontend/bundle/chunk-TKT73GT7.js +21 -0
  234. package/www/dist/frontend/bundle/chunk-TWQQHCU7.js +3 -3
  235. package/www/dist/frontend/bundle/chunk-U5Q332OH.js +1 -1
  236. package/www/dist/frontend/bundle/chunk-UIP324PP.js +1 -1
  237. package/www/dist/frontend/bundle/chunk-V27IRYSL.js +1 -1
  238. package/www/dist/frontend/bundle/chunk-V4C5FGJL.js +3 -3
  239. package/www/dist/frontend/bundle/chunk-V5CHFTYG.js +3 -3
  240. package/www/dist/frontend/bundle/chunk-VHXWCBNP.js +1 -1
  241. package/www/dist/frontend/bundle/chunk-VVN7PNOP.js +21 -0
  242. package/www/dist/frontend/bundle/chunk-W2KWSW4M.js +1 -1
  243. package/www/dist/frontend/bundle/chunk-W5QV7JN5.js +1 -1
  244. package/www/dist/frontend/bundle/chunk-WED7CGWA.js +3 -3
  245. package/www/dist/frontend/bundle/chunk-WPDSHBGY.js +21 -0
  246. package/www/dist/frontend/bundle/chunk-WQ5H5LTG.js +2 -2
  247. package/www/dist/frontend/bundle/chunk-WU54NCEU.js +1 -1
  248. package/www/dist/frontend/bundle/chunk-WWCDOL63.js +21 -0
  249. package/www/dist/frontend/bundle/chunk-XCRSZKUF.js +1 -1
  250. package/www/dist/frontend/bundle/chunk-XDSIXRBO.js +1 -0
  251. package/www/dist/frontend/bundle/chunk-XFO5FUCN.js +1 -1
  252. package/www/dist/frontend/bundle/chunk-XFX6B7GG.js +21 -0
  253. package/www/dist/frontend/bundle/chunk-XMIIFLRP.js +21 -0
  254. package/www/dist/frontend/bundle/chunk-XWAQ2BWM.js +2 -2
  255. package/www/dist/frontend/bundle/chunk-Y42FM7CC.js +1 -0
  256. package/www/dist/frontend/bundle/chunk-YGA5D2BL.js +21 -0
  257. package/www/dist/frontend/bundle/chunk-YLMUM2GI.js +1 -0
  258. package/www/dist/frontend/bundle/chunk-YTQCHNRQ.js +2 -2
  259. package/www/dist/frontend/bundle/chunk-YUSK4FN3.js +2 -2
  260. package/www/dist/frontend/bundle/chunk-Z34BSG5O.js +3 -3
  261. package/www/dist/frontend/bundle/common/IobrokerHandler.js +1 -1
  262. package/www/dist/frontend/bundle/runtime/CustomControls.js +1 -1
  263. package/www/dist/frontend/bundle/runtime/ScreenViewer.js +1 -1
  264. package/www/dist/frontend/bundle/runtime/controls.js +4 -4
  265. package/www/dist/frontend/bundle/runtime/init.js +3 -3
  266. package/www/dist/frontend/common/BindingExpression.js +1 -0
  267. package/www/dist/frontend/common/Common.globals.js +1 -1
  268. package/www/dist/frontend/common/HistoryDirective.js +1 -0
  269. package/www/dist/frontend/common/HistoryOptions.js +1 -0
  270. package/www/dist/frontend/common/IobrokerHandler.js +1 -1
  271. package/www/dist/frontend/common/PlantPaxResolve.js +1 -1
  272. package/www/dist/frontend/common/ReportAllowlist.js +1 -0
  273. package/www/dist/frontend/common/ReportSchedule.js +1 -0
  274. package/www/dist/frontend/common/Runtime.js +1 -1
  275. package/www/dist/frontend/common/Sha256.js +1 -0
  276. package/www/dist/frontend/config/CommandHandling.js +1 -1
  277. package/www/dist/frontend/config/ConfigureWebcomponentDesigner.js +1 -1
  278. package/www/dist/frontend/config/DockHelper.js +1 -1
  279. package/www/dist/frontend/config/IobrokerWebui3DDriveEngine.js +1 -1
  280. package/www/dist/frontend/config/IobrokerWebui3DScreenEditor.js +2 -2
  281. package/www/dist/frontend/config/IobrokerWebui3DScreenPropertiesPanel.js +3 -3
  282. package/www/dist/frontend/config/IobrokerWebui3DScreenViewer.js +2 -2
  283. package/www/dist/frontend/config/IobrokerWebuiAppShell.js +10 -4
  284. package/www/dist/frontend/config/IobrokerWebuiBindingsEditor.js +1 -1
  285. package/www/dist/frontend/config/IobrokerWebuiConfirmationWrapper.js +2 -2
  286. package/www/dist/frontend/config/IobrokerWebuiControlPropertiesEditor.js +2 -2
  287. package/www/dist/frontend/config/IobrokerWebuiDynamicPropsEditor.js +1 -1
  288. package/www/dist/frontend/config/IobrokerWebuiEventAssignment.js +1 -1
  289. package/www/dist/frontend/config/IobrokerWebuiIconsView.js +2 -2
  290. package/www/dist/frontend/config/IobrokerWebuiMonacoEditor.js +2 -2
  291. package/www/dist/frontend/config/IobrokerWebuiPlantPaxTypesEditor.js +1 -1
  292. package/www/dist/frontend/config/IobrokerWebuiPropertyGrid.js +1 -1
  293. package/www/dist/frontend/config/IobrokerWebuiScreenEditor.js +1 -1
  294. package/www/dist/frontend/config/IobrokerWebuiScreensView.js +3 -3
  295. package/www/dist/frontend/config/IobrokerWebuiSecurityEditor.js +1 -1
  296. package/www/dist/frontend/config/IobrokerWebuiSignalPropertyEditor.js +1 -1
  297. package/www/dist/frontend/config/IobrokerWebuiSolutionExplorer.js +3 -3
  298. package/www/dist/frontend/config/IobrokerWebuiTranslationEditor.js +1 -1
  299. package/www/dist/frontend/config/IobrokerWebuiWidgetGallery.js +2 -2
  300. package/www/dist/frontend/config/RightDockStrip.js +1 -0
  301. package/www/dist/frontend/helper/DialogHelper.js +2 -2
  302. package/www/dist/frontend/helper/EditorOfDesignItem.js +1 -0
  303. package/www/dist/frontend/helper/EsprimaHelper.js +1 -1
  304. package/www/dist/frontend/helper/Helper.js +1 -1
  305. package/www/dist/frontend/helper/LayoutHelper.js +1 -1
  306. package/www/dist/frontend/helper/XmlHelper.js +1 -1
  307. package/www/dist/frontend/helper/ZipHelper.js +1 -1
  308. package/www/dist/frontend/runtime/AnimationService.js +1 -1
  309. package/www/dist/frontend/runtime/CustomControls.js +1 -1
  310. package/www/dist/frontend/runtime/DynamicElementProperties.js +1 -1
  311. package/www/dist/frontend/runtime/HabPanelLikeMenu.js +2 -2
  312. package/www/dist/frontend/runtime/HistoryDirectives.js +1 -0
  313. package/www/dist/frontend/runtime/NavigationShell.js +1 -1
  314. package/www/dist/frontend/runtime/ReportViewer.js +197 -0
  315. package/www/dist/frontend/runtime/RuntimeTheme.js +1 -1
  316. package/www/dist/frontend/runtime/ScreenViewer.js +3 -3
  317. package/www/dist/frontend/runtime/TranslateableText.js +2 -2
  318. package/www/dist/frontend/runtime/VisibilityService.js +1 -1
  319. package/www/dist/frontend/runtime/controls.js +1 -1
  320. package/www/dist/frontend/runtime/init.js +1 -1
  321. package/www/dist/frontend/scripting/IobrokerWebuiScriptSystem.js +1 -1
  322. package/www/dist/frontend/scripting/blockly/OpenScreen.js +1 -1
  323. package/www/dist/frontend/scripting/blockly/webuiBlocklyToolbox.js +1 -1
  324. package/www/dist/frontend/services/DynamicPropertiesHelper.js +1 -1
  325. package/www/dist/frontend/services/IobrokerWebuiBindableLocalObjectsService.js +1 -1
  326. package/www/dist/frontend/services/IobrokerWebuiBindableObjectDragDropService.js +1 -1
  327. package/www/dist/frontend/services/IobrokerWebuiBindableObjectsForPropertiesService.js +1 -1
  328. package/www/dist/frontend/services/IobrokerWebuiBindableObjectsService.js +1 -1
  329. package/www/dist/frontend/services/IobrokerWebuiConfigButtonProvider.js +1 -1
  330. package/www/dist/frontend/services/IobrokerWebuiCopyPasteService.js +1 -1
  331. package/www/dist/frontend/services/IobrokerWebuiCustomControlEventsService.js +1 -1
  332. package/www/dist/frontend/services/IobrokerWebuiCustomElementContextMenu.js +1 -1
  333. package/www/dist/frontend/services/IobrokerWebuiDemoProviderService.js +1 -1
  334. package/www/dist/frontend/services/IobrokerWebuiDynamicPropertiesService.js +1 -1
  335. package/www/dist/frontend/services/IobrokerWebuiEventsService.js +1 -1
  336. package/www/dist/frontend/services/IobrokerWebuiExternalDragDropService.js +1 -1
  337. package/www/dist/frontend/services/IobrokerWebuiLitPropertiesService.js +1 -1
  338. package/www/dist/frontend/services/IobrokerWebuiPropertiesService.js +1 -1
  339. package/www/dist/frontend/services/IobrokerWebuiPropertyGroupsService.js +1 -1
  340. package/www/dist/frontend/services/IobrokerWebuiRefactorService.js +1 -1
  341. package/www/dist/frontend/services/IobrokerWebuiReportContextMenu.js +1 -0
  342. package/www/dist/frontend/services/IobrokerWebuiScreenContextMenu.js +1 -1
  343. package/www/dist/frontend/services/IobrokerWebuiSpecialPropertiesService.js +1 -1
  344. package/www/dist/frontend/services/IobrokerWebuiSpectrumEditorService.js +1 -1
  345. package/www/dist/frontend/services/IobrokerWebuiVisibilityPropertiesService.js +1 -1
  346. package/www/dist/frontend/services/LayoutComponentsPropertiesService.js +1 -1
  347. package/www/dist/frontend/widgets/OrthoFlowLineTool.js +1 -1
  348. package/www/dist/frontend/widgets/customElementsObserver.js +1 -1
  349. package/www/dist/frontend/widgets/importWidgetFiles.js +1 -1
  350. package/www/dist/frontend/widgets/importWidgetFilesRuntime.js +1 -1
  351. package/www/dist/frontend/widgets/layout-components.js +1 -1
  352. package/www/dist/frontend/widgets/layout-placement-services.js +1 -1
  353. package/www/dist/frontend/widgets/testElement.js +1 -1
  354. package/www/libs/@gokturk413/base-custom-webcomponent/dist/BaseCustomWebComponent.d.ts +3 -0
  355. package/www/libs/@gokturk413/base-custom-webcomponent/dist/BaseCustomWebComponent.js +121 -85
  356. package/www/libs/@gokturk413/web-component-designer-visualization-addons/dist/helpers/BindingsHelper.js +3 -1
  357. package/www/license-check.obf.js +1 -1
  358. package/www/runtime.html +37 -0
package/admin/i18n.json CHANGED
@@ -147,5 +147,40 @@
147
147
  "ru": "nginx auth_request передаёт сессию ioBroker в Grafana — пользователи автоматически входят в iframe Grafana",
148
148
  "az": "nginx auth_request ioBroker sesiyasını Grafana-ya ötürür — istifadəçilər Grafana iframe-ə avtomatik daxil olur",
149
149
  "tr": "nginx auth_request ioBroker oturumunu Grafana'ya iletir — kullanıcılar Grafana iframe'e otomatik giriş yapar"
150
+ },
151
+ "A 14-day trial starts on the first run. After that the adapter needs a licence key bound to this computer.": {
152
+ "en": "A 14-day trial starts on the first run. After that the adapter needs a licence key bound to this computer.",
153
+ "az": "İlk işə düşmədə 14 günlük sınaq müddəti başlayır. Sonra adapterə bu kompüterə bağlı lisenziya açarı lazımdır.",
154
+ "tr": "İlk çalıştırmada 14 günlük deneme süresi başlar. Ardından adaptör bu bilgisayara bağlı bir lisans anahtarı gerektirir.",
155
+ "ru": "При первом запуске начинается 14-дневный пробный период. После него адаптеру нужен лицензионный ключ, привязанный к этому компьютеру.",
156
+ "de": "Beim ersten Start beginnt eine 14-tägige Testphase. Danach benötigt der Adapter einen an diesen Computer gebundenen Lizenzschlüssel."
157
+ },
158
+ "⚠️ Important: a licence key is bound to the Hardware ID of one computer, VM or container. Each of them needs its own licence; copying or moving a licensed VM is not allowed.": {
159
+ "en": "⚠️ Important: a licence key is bound to the Hardware ID of one computer, VM or container. Each of them needs its own licence; copying or moving a licensed VM is not allowed.",
160
+ "az": "⚠️ Vacib: lisenziya açarı bir kompüterin, VM-in və ya konteynerin Hardware ID-sinə bağlıdır. Hər birinə ayrıca lisenziya lazımdır; lisenziyalı VM-i kopyalamaq və ya köçürmək olmaz.",
161
+ "tr": "⚠️ Önemli: lisans anahtarı tek bir bilgisayarın, VM'in veya konteynerin Hardware ID'sine bağlıdır. Her biri kendi lisansını gerektirir; lisanslı bir VM kopyalanamaz veya taşınamaz.",
162
+ "ru": "⚠️ Важно: лицензионный ключ привязан к Hardware ID одного компьютера, ВМ или контейнера. Каждому нужна своя лицензия; копировать или переносить лицензированную ВМ нельзя.",
163
+ "de": "⚠️ Wichtig: Ein Lizenzschlüssel ist an die Hardware-ID eines Computers, einer VM oder eines Containers gebunden. Jeder braucht eine eigene Lizenz; eine lizenzierte VM darf nicht kopiert oder verschoben werden."
164
+ },
165
+ "Copy the whole line (copy button) and send it to gokturk413. It usually holds two IDs: the mainboard and the operating-system installation.": {
166
+ "en": "Copy the whole line (copy button) and send it to gokturk413. It usually holds two IDs: the mainboard and the operating-system installation.",
167
+ "az": "Sətrin hamısını kopyalayın (kopyalama düyməsi) və gokturk413-ə göndərin. Adətən iki ID olur: ana plata və əməliyyat sisteminin quraşdırılması.",
168
+ "tr": "Satırın tamamını kopyalayın (kopyala düğmesi) ve gokturk413'e gönderin. Genellikle iki ID içerir: anakart ve işletim sistemi kurulumu.",
169
+ "ru": "Скопируйте всю строку (кнопка копирования) и отправьте gokturk413. Обычно в ней два ID: материнская плата и установка операционной системы.",
170
+ "de": "Kopieren Sie die ganze Zeile (Kopier-Schaltfläche) und senden Sie sie an gokturk413. Sie enthält meist zwei IDs: das Mainboard und die Betriebssystem-Installation."
171
+ },
172
+ "ℹ️ Why two IDs: if only one of them changes (a new mainboard, or a reinstalled operating system), the key keeps working for 14 days of grace, so there is time to request a new key. If both change, it is another computer and the key does not work.": {
173
+ "en": "ℹ️ Why two IDs: if only one of them changes (a new mainboard, or a reinstalled operating system), the key keeps working for 14 days of grace, so there is time to request a new key. If both change, it is another computer and the key does not work.",
174
+ "az": "ℹ️ Niyə iki ID: onlardan yalnız biri dəyişsə (yeni ana plata və ya yenidən quraşdırılmış əməliyyat sistemi), açar 14 günlük güzəşt müddəti işləməyə davam edir və yeni açar istəməyə vaxt olur. İkisi də dəyişsə, bu başqa kompüterdir və açar işləmir.",
175
+ "tr": "ℹ️ Neden iki ID: yalnızca biri değişirse (yeni anakart veya yeniden kurulan işletim sistemi), anahtar 14 günlük ek süre boyunca çalışmaya devam eder ve yeni anahtar istemek için zaman olur. İkisi de değişirse bu başka bir bilgisayardır ve anahtar çalışmaz.",
176
+ "ru": "ℹ️ Почему два ID: если меняется только один (новая материнская плата или переустановленная ОС), ключ продолжает работать 14 дней льготного периода, и есть время запросить новый ключ. Если меняются оба, это другой компьютер, и ключ не работает.",
177
+ "de": "ℹ️ Warum zwei IDs: Ändert sich nur eine (neues Mainboard oder neu installiertes Betriebssystem), funktioniert der Schlüssel 14 Tage Kulanzzeit weiter, sodass Zeit bleibt, einen neuen Schlüssel anzufordern. Ändern sich beide, ist es ein anderer Computer und der Schlüssel funktioniert nicht."
178
+ },
179
+ "1. Start the adapter: a 14-day trial starts automatically\n2. Copy the whole Hardware ID line above\n3. Send it to gokturk413 to get a licence key\n4. Enter the licence key here and save (the adapter restarts)\n\n💡 When the trial ends without a key, the runtime stops. A project made during the trial keeps working once the key is entered.\n💡 For a new computer, send its Hardware ID to get a key for it.": {
180
+ "en": "1. Start the adapter: a 14-day trial starts automatically\n2. Copy the whole Hardware ID line above\n3. Send it to gokturk413 to get a licence key\n4. Enter the licence key here and save (the adapter restarts)\n\n💡 When the trial ends without a key, the runtime stops. A project made during the trial keeps working once the key is entered.\n💡 For a new computer, send its Hardware ID to get a key for it.",
181
+ "az": "1. Adapteri işə salın: 14 günlük sınaq avtomatik başlayır\n2. Yuxarıdakı Hardware ID sətrini tam kopyalayın\n3. Lisenziya açarı almaq üçün onu gokturk413-ə göndərin\n4. Açarı buraya daxil edib yadda saxlayın (adapter yenidən başlayır)\n\n💡 Sınaq müddəti açarsız bitəndə runtime dayanır. Sınaqda yaradılmış layihə açar daxil ediləndən sonra işləməyə davam edir.\n💡 Yeni kompüter üçün onun Hardware ID-sini göndərib ayrıca açar alın.",
182
+ "tr": "1. Adaptörü başlatın: 14 günlük deneme otomatik başlar\n2. Yukarıdaki Hardware ID satırının tamamını kopyalayın\n3. Lisans anahtarı almak için gokturk413'e gönderin\n4. Anahtarı buraya girip kaydedin (adaptör yeniden başlar)\n\n💡 Deneme süresi anahtarsız biterse runtime durur. Deneme sırasında yapılan proje, anahtar girildikten sonra çalışmaya devam eder.\n💡 Yeni bir bilgisayar için onun Hardware ID'sini gönderip ayrı bir anahtar alın.",
183
+ "ru": "1. Запустите адаптер: 14-дневный пробный период начнётся автоматически\n2. Скопируйте всю строку Hardware ID выше\n3. Отправьте её gokturk413, чтобы получить лицензионный ключ\n4. Введите ключ здесь и сохраните (адаптер перезапустится)\n\n💡 Если пробный период закончится без ключа, runtime останавливается. Проект, созданный в пробный период, продолжит работать после ввода ключа.\n💡 Для нового компьютера отправьте его Hardware ID и получите для него ключ.",
184
+ "de": "1. Adapter starten: eine 14-tägige Testphase beginnt automatisch\n2. Die ganze Hardware-ID-Zeile oben kopieren\n3. An gokturk413 senden, um einen Lizenzschlüssel zu erhalten\n4. Den Schlüssel hier eingeben und speichern (der Adapter startet neu)\n\n💡 Endet die Testphase ohne Schlüssel, stoppt die Runtime. Ein in der Testphase erstelltes Projekt läuft nach Eingabe des Schlüssels weiter.\n💡 Für einen neuen Computer dessen Hardware-ID senden und einen eigenen Schlüssel erhalten."
150
185
  }
151
186
  }
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "_licenseInfo": {
15
15
  "type": "staticText",
16
- "text": "This adapter requires a valid license key to operate. The license is bound to your hardware for security purposes.",
16
+ "text": "A 14-day trial starts on the first run. After that the adapter needs a licence key bound to this computer.",
17
17
  "style": {
18
18
  "fontSize": 14,
19
19
  "color": "#666",
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "_licenseWarning": {
34
34
  "type": "staticText",
35
- "text": "⚠️ Important: License keys are cryptographically bound to hardware IDs. To add a new computer, contact gokturk413 with your Hardware ID.",
35
+ "text": "⚠️ Important: a licence key is bound to the Hardware ID of one computer, VM or container. Each of them needs its own licence; copying or moving a licensed VM is not allowed.",
36
36
  "style": {
37
37
  "fontSize": 13,
38
38
  "color": "#ff9800",
@@ -56,7 +56,7 @@
56
56
  "sm": 12,
57
57
  "md": 10,
58
58
  "lg": 8,
59
- "help": "Send this ID to gokturk413 to receive a license key for this computer."
59
+ "help": "Copy the whole line (copy button) and send it to gokturk413. It usually holds two IDs: the mainboard and the operating-system installation."
60
60
  },
61
61
  "_getHwidBtn": {
62
62
  "type": "sendTo",
@@ -72,7 +72,7 @@
72
72
  },
73
73
  "_hardwareIdInfo": {
74
74
  "type": "staticText",
75
- "text": "ℹ️ The Hardware ID above identifies this computer. Send it to gokturk413 to get a license key. You cannot manually add hardware IDs — they are cryptographically bound inside the license key.",
75
+ "text": "ℹ️ Why two IDs: if only one of them changes (a new mainboard, or a reinstalled operating system), the key keeps working for 14 days of grace, so there is time to request a new key. If both change, it is another computer and the key does not work.",
76
76
  "style": {
77
77
  "fontSize": 12,
78
78
  "color": "#2196F3",
@@ -92,14 +92,15 @@
92
92
  },
93
93
  "_instructions": {
94
94
  "type": "staticText",
95
- "text": "1. Start the adapter (it will fail on first run)\n2. Check logs: iobroker logs webui --lines 50\n3. Copy your Hardware ID from logs\n4. Send Hardware ID to gokturk413 to get a license\n5. Enter the license key here\n6. Restart the adapter\n\n💡 To add another computer: Send its Hardware ID to get an updated license key.",
95
+ "text": "1. Start the adapter: a 14-day trial starts automatically\n2. Copy the whole Hardware ID line above\n3. Send it to gokturk413 to get a licence key\n4. Enter the licence key here and save (the adapter restarts)\n\n💡 When the trial ends without a key, the runtime stops. A project made during the trial keeps working once the key is entered.\n💡 For a new computer, send its Hardware ID to get a key for it.",
96
96
  "style": {
97
97
  "fontSize": 13,
98
98
  "fontFamily": "monospace",
99
99
  "whiteSpace": "pre-line",
100
100
  "padding": "10px",
101
101
  "backgroundColor": "#f5f5f5",
102
- "borderRadius": "4px"
102
+ "borderRadius": "4px",
103
+ "color": "#333"
103
104
  }
104
105
  },
105
106
  "_contactInfo": {
@@ -216,6 +217,74 @@
216
217
  "hidden": "!data.grafanaEnabled || !data.grafanaSsoEnabled"
217
218
  }
218
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
+ }
219
288
  }
220
289
  }
221
290
  }
@@ -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=\"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// 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 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 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 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 const kinds=[['screen',await IOB.getAllNames('screen').catch(()=>[])],['report',await IOB.getAllNames('report').catch(()=>[])]];\n const list=[];\n for(const [kind,names] of kinds){ for(const n of names){ const o=await IOB.getWebuiObject(kind,n).catch(()=>null); const nv=o&&o.settings&&o.settings.navigation; if(nv&&nv.show) list.push({name:n,kind:kind,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,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}"}