Browse Source

Merge remote-tracking branch 'upstream/master' into feature/mobile-app-bundle

pull/11835/head
Vladyslav_Prykhodko 2 years ago
parent
commit
bbb2531c2e
  1. 1
      README.md
  2. 1210
      application/src/main/data/json/system/scada_symbols/conical-tank.svg
  3. 426
      application/src/main/data/json/system/scada_symbols/extra-long-horizontal-pipe.svg
  4. 438
      application/src/main/data/json/system/scada_symbols/extra-long-vertical-pipe.svg
  5. 97
      application/src/main/data/json/system/scada_symbols/horizontal-broken-pipe.svg
  6. 7
      application/src/main/data/json/system/scada_symbols/horizontal-tank.svg
  7. 1220
      application/src/main/data/json/system/scada_symbols/large-conical-tank.svg
  8. 97
      application/src/main/data/json/system/scada_symbols/long-horizontal-broken-pipe.svg
  9. 97
      application/src/main/data/json/system/scada_symbols/long-vertical-broken-pipe.svg
  10. 1376
      application/src/main/data/json/system/scada_symbols/small-cylindrical-tank.svg
  11. 7
      application/src/main/data/json/system/scada_symbols/small-spherical-tank.svg
  12. 7
      application/src/main/data/json/system/scada_symbols/spherical-tank.svg
  13. 7
      application/src/main/data/json/system/scada_symbols/stand-horizontal-tank.svg
  14. 97
      application/src/main/data/json/system/scada_symbols/vertical-broken-pipe.svg
  15. 9
      application/src/main/data/json/system/widget_bundles/scada_fluid_system.json
  16. 7023
      application/src/main/data/json/tenant/dashboards/gateways.json
  17. 7
      application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DashboardSyncService.java
  18. 76
      application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
  19. 4
      application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java
  20. 65
      application/src/main/java/org/thingsboard/server/service/update/DeprecationService.java
  21. 19
      application/src/test/java/org/thingsboard/server/service/entitiy/dashboard/DashboardSyncServiceTest.java
  22. 5
      application/src/test/java/org/thingsboard/server/service/install/InstallScriptsTest.java
  23. 17
      application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java
  24. 4
      application/src/test/resources/logback-test.xml
  25. 36
      common/data/src/main/java/org/thingsboard/server/common/data/notification/info/GeneralNotificationInfo.java
  26. 1
      common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusAdmin.java
  27. 1
      common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubAdmin.java
  28. 1
      common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqAdmin.java
  29. 1
      common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsAdmin.java
  30. 2
      common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java
  31. 1
      common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java
  32. 12
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java
  33. 8
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java
  34. 15
      dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java
  35. 3
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java
  36. 2
      rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
  37. 67
      ui-ngx/angular.json
  38. 104
      ui-ngx/esbuild/tb-esbuild-plugins.ts
  39. 100
      ui-ngx/extra-webpack.config.js
  40. 40
      ui-ngx/generate-types.js
  41. 31
      ui-ngx/package.json
  42. 43
      ui-ngx/patches/@angular+build+18.2.10.patch
  43. 14
      ui-ngx/patches/jquery.terminal+2.43.1.patch
  44. 35
      ui-ngx/src/app/core/api/alias-controller.ts
  45. 19
      ui-ngx/src/app/core/services/utils.service.ts
  46. 8
      ui-ngx/src/app/core/utils.ts
  47. 2
      ui-ngx/src/app/modules/common/modules-map.ts
  48. 2
      ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.html
  49. 12
      ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts
  50. 2
      ui-ngx/src/app/modules/home/components/vc/entity-version-diff.component.ts
  51. 20
      ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.component.ts
  52. 77
      ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts
  53. 9
      ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.models.ts
  54. 4
      ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts
  55. 4
      ui-ngx/src/app/modules/home/components/widget/lib/qrcode-widget.component.ts
  56. 6
      ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action.models.ts
  57. 15
      ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-provider-settings.component.ts
  58. 26
      ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-settings.component.ts
  59. 28
      ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-widget-settings.component.ts
  60. 8
      ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts
  61. 10
      ui-ngx/src/app/modules/home/components/widget/widget.component.ts
  62. 8
      ui-ngx/src/app/modules/home/models/datasource/attribute-datasource.ts
  63. 12
      ui-ngx/src/app/modules/home/models/widget-component.models.ts
  64. 3
      ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html
  65. 2
      ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.scss
  66. 2
      ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-auth-dialog.component.ts
  67. 3
      ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.ts
  68. 2
      ui-ngx/src/app/shared/components/dialog/confirm-dialog.component.html
  69. 2
      ui-ngx/src/app/shared/components/entity/entity-select.component.ts
  70. 12
      ui-ngx/src/app/shared/components/html.component.ts
  71. 8
      ui-ngx/src/app/shared/components/json-form/json-form.component.ts
  72. 7
      ui-ngx/src/app/shared/components/json-form/react/json-form-ace-editor.tsx
  73. 7
      ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx
  74. 6
      ui-ngx/src/app/shared/components/page.component.ts
  75. 1
      ui-ngx/src/app/shared/decorators/public-api.ts
  76. 23
      ui-ngx/src/app/shared/decorators/tb-inject.ts
  77. 9
      ui-ngx/src/app/shared/models/ace/ace.models.ts
  78. 4
      ui-ngx/src/app/shared/models/beautify.models.ts
  79. 1
      ui-ngx/src/app/shared/models/notification.models.ts
  80. 87
      ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts
  81. 24
      ui-ngx/src/assets/help/en_US/widget/lib/gateway/address-filter_fn.md
  82. 21
      ui-ngx/src/assets/help/en_US/widget/lib/gateway/attribute-name-expression_fn.md
  83. 35
      ui-ngx/src/assets/help/en_US/widget/lib/gateway/byte_fn.md
  84. 21
      ui-ngx/src/assets/help/en_US/widget/lib/gateway/request-expression_fn.md
  85. 1
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  86. 2137
      ui-ngx/src/assets/locale/locale.constant-zh_CN.json
  87. 5
      ui-ngx/src/main.ts
  88. 7
      ui-ngx/src/styles.scss
  89. 4
      ui-ngx/src/tsconfig.app.json
  90. 6
      ui-ngx/src/typings/rawloader.typings.d.ts
  91. 3
      ui-ngx/tailwind.config.js
  92. 1
      ui-ngx/tsconfig.json
  93. 509
      ui-ngx/yarn.lock

1
README.md

@ -1,5 +1,4 @@
# ThingsBoard # ThingsBoard
[![Join the chat at https://gitter.im/thingsboard/chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/thingsboard/chat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![ThingsBoard Builds Server Status](https://img.shields.io/teamcity/build/e/ThingsBoard_Build?label=TB%20builds%20server&server=https%3A%2F%2Fbuilds.thingsboard.io&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAALzAAAC8wHS6QoqAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAB9FJREFUeJzVm3+MXUUVx7+zWwqEtnRLWisQ2lKVUisIQmsqYCohpUhpEGsFKSJJTS0qGiGIISJ/8CNGYzSaEKBQEZUiP7RgVbCVdpE0xYKBWgI2rFLZJZQWtFKobPfjH3Pfdu7s3Pvmzntv3/JNNr3bOXPO+Z6ZO3PumVmjFgEYJWmWpDmSZks6VtIESV3Zv29LWmGMubdVPgw7gEOBJcAaYC/18fd2+zyqngAwXdL7M9keSduMMXgyH5R0laRPSRpbwf62CrLDB8AAS4HnAqP2EvA1YBTwPuBnwP46I70H+DPwALAS+B5wBTCu3VyHIJvG98dMX+B/BW1vAvcAnwdmAp3t5hWFbORXR5AvwmPARcCYdnNJAnCBR+gd7HQ9HZgLfAt4PUB8AzCv3f43DGCTQ6o/RAo43gtCL2Da4W9TAUwEBhxiPymRvcabAR8eTl+biQ7neYokdyTXlvR7xPt9etM8GmZ0FDxL+WD42FdBdkTDJd0jyU1wzi7pd473e0+qA8AM4AbgkrK1BDgOWAc8ChyTaq+eM5ud93ofcHpAZiY2sanhZaDDaTfAZ7HJUmlWCJzm6bqLQM6QBanXkfthcxgPNbTEW9z2AT8AzgTmANdikxwXX/d0XOi0bQEmFNj6GPAfhuKnXkB98kNsNjsITwacKkI3MNrrf4UnswXoiiRfwyqgo4D8L2hVZglMw456DDYCRwR0jCH/KuWCgE2oysjX8KsA+V+2jHzm3CrP4PMBx/4JfAU4qETP+EAQ/gKcA/w7gnwNbl5yD7bG0DLyM7DZXw3d2f9PA+YD5wIzK+gLBSEFA/XIA2cAVwLvbSQAt3mGP5Gs7IDO8dg1ZYDGcAfOwujZuIwDn+ObUx09hHx+v7Eh5nndCyIIDgBbgd0lMiv9IABfIF+LeDnVyU97xj5XR/6bwI5sZEaXyH2UuHd+WSbfRXktYjAIAfL9wGdSA/Cgo+gtSio12IKJa3hNKAgZ+TciyL+AlwECKzI/ioLgTvsa+YtTyXeSz8ZW15E3wN88p3JBwCZNMeShIKkBTsRmmSG4a0o/sDSJfGboBE/5pRF9pgI9oSBUJP8mXpLk2bm6pO9Aw+QzI8s8xVFbXRaEf3h911cgD7Cyjg0/L/GxnoLdoUoA3O1vDxUyLWyO4AehCpYX6D2L/LpUhtsaCkIWxRoeT+g/DVsqT8EWYDowC5jh6FxUUc+tJJblOmSPqWp4JUFHl6TDUoxLOlnSdknPSnK3sA2S9lfQs0zS7SkzwQ/A61U6A6dKWufpSMVg5mmMeUPSXyv2v0zSN6oa7ZAdwRqiA5CRf0TS+KpGAxiQ1OFN4z8l6PErVXUxSvmp1hvTqUnk35adPWskPWSM6fPaq84ASXqscg/gi9gcvJuC6o0nfwrhw5EYvIpNn88HStcN4M6KulfTys/lzKlO0lb8P2Lrf6VbLDAF+DLweEX998aSx372bwP6gPlVA3BEAvm9FJwVYtPqjwDXA08n6AZbOYoeeeAWp++mSlPGGLMLeFjSuRW6Iektx4GDJc2TdJ6khZKOruKDh/skXWSM6a/Q5yjn+dDKFrE1vw0VR2m2039x4kj7uJ+SslyJ/+7rtaly4mCM+a+kBaq2TbnVpfWy216jmCzpkIR+7kK/MymHNsbslX0NYoMweMpsjNklaWuKXQ9zJf2eOocvAbzHee5N/ojIgvBVxY3madh3v4b1iWZ/o3zw5kpaS+SFDGCq8jPguUQ/CmsCZfi403dhwjv/AHAQMAl41mvbGBMEhq4/c1PJTwmQr1f7u97pfzj5EnwUead/KAg/ivD7Zkf+HSBpFwiRfwibI3SXkOj29PgEivAggdU+C8JWR+6+CN9dm1tSyHcBLwbIj87ax1Kcxe0DJmVyY4CdEeR/TXnVeRLwc+C3wHF1fP+Qp/uGlABc6Cl5mPziVi8IzwDfAZ6KIN9LyhQt9v1GT/+sFCXTOVBBXuOTd+TGkp+eqWjKSTBwMPAvR+9TjSibjK35l93mWIxdZFKOxPzFseEgAJd7Olt6v+AC8jdIqwRhLbZM758HRH3tYa/vnoqtKZ4JHIk99tvh6HqNVl3RLSB/JfBEBPnBwxXsJ2uf176qxO7hwE3ALq/PfuyVXhdXt4r8+QHyK7K2cXWCMLiTOPqODwTh2IDdD2CP12LwCnUKMankO8kfiAySd2SKgjCEfEEQ+nznsZc7eyLJA9zddPKZIx0c2NcHgMsL5MZhr83XULiTeCSXAEcG2m4PjPCXsEWWBdhbZ/4h6knN4u07Mxv4MbCojtxo7DW6RTRwopMFxt0xeoCJAblLvCDdlWpzRAG42CO2sET2UUfuVbetsYPF9mKq8zwg6Q8lsm7bRJxt8N0cAPdar5FUupYU9X03B2C782wknVUi+0nneacxZk9rXBpGABO8RXA72demJ7fcWyvubIe/TQN2y11MuJ6wA5v3z8HeMbjba+8n5StwJCDb9lYUEI/Fde3mEQ1svnBKRvp32K/LEPYQd1z3XQJfsG3/Sw/gKElLZev8tb8rnizpBEmF1SDZ06ZbJN0saa+kayQtV77qi6QnJF1njFnXdOebAcIXssvQB3yfcGrcCZwEnAfMC8mMKGArNUVT28VubF4/nyZflx8Jr8BVkr4tm83tzn5ek/S8pM2SnpT0gv8H283C/wGTFfhGtexQwQAAAABJRU5ErkJggg==&labelColor=305680)](https://builds.thingsboard.io/viewType.html?buildTypeId=ThingsBoard_Build&guest=1) [![ThingsBoard Builds Server Status](https://img.shields.io/teamcity/build/e/ThingsBoard_Build?label=TB%20builds%20server&server=https%3A%2F%2Fbuilds.thingsboard.io&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAALzAAAC8wHS6QoqAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAB9FJREFUeJzVm3+MXUUVx7+zWwqEtnRLWisQ2lKVUisIQmsqYCohpUhpEGsFKSJJTS0qGiGIISJ/8CNGYzSaEKBQEZUiP7RgVbCVdpE0xYKBWgI2rFLZJZQWtFKobPfjH3Pfdu7s3Pvmzntv3/JNNr3bOXPO+Z6ZO3PumVmjFgEYJWmWpDmSZks6VtIESV3Zv29LWmGMubdVPgw7gEOBJcAaYC/18fd2+zyqngAwXdL7M9keSduMMXgyH5R0laRPSRpbwf62CrLDB8AAS4HnAqP2EvA1YBTwPuBnwP46I70H+DPwALAS+B5wBTCu3VyHIJvG98dMX+B/BW1vAvcAnwdmAp3t5hWFbORXR5AvwmPARcCYdnNJAnCBR+gd7HQ9HZgLfAt4PUB8AzCv3f43DGCTQ6o/RAo43gtCL2Da4W9TAUwEBhxiPymRvcabAR8eTl+biQ7neYokdyTXlvR7xPt9etM8GmZ0FDxL+WD42FdBdkTDJd0jyU1wzi7pd473e0+qA8AM4AbgkrK1BDgOWAc8ChyTaq+eM5ud93ofcHpAZiY2sanhZaDDaTfAZ7HJUmlWCJzm6bqLQM6QBanXkfthcxgPNbTEW9z2AT8AzgTmANdikxwXX/d0XOi0bQEmFNj6GPAfhuKnXkB98kNsNjsITwacKkI3MNrrf4UnswXoiiRfwyqgo4D8L2hVZglMw456DDYCRwR0jCH/KuWCgE2oysjX8KsA+V+2jHzm3CrP4PMBx/4JfAU4qETP+EAQ/gKcA/w7gnwNbl5yD7bG0DLyM7DZXw3d2f9PA+YD5wIzK+gLBSEFA/XIA2cAVwLvbSQAt3mGP5Gs7IDO8dg1ZYDGcAfOwujZuIwDn+ObUx09hHx+v7Eh5nndCyIIDgBbgd0lMiv9IABfIF+LeDnVyU97xj5XR/6bwI5sZEaXyH2UuHd+WSbfRXktYjAIAfL9wGdSA/Cgo+gtSio12IKJa3hNKAgZ+TciyL+AlwECKzI/ioLgTvsa+YtTyXeSz8ZW15E3wN88p3JBwCZNMeShIKkBTsRmmSG4a0o/sDSJfGboBE/5pRF9pgI9oSBUJP8mXpLk2bm6pO9Aw+QzI8s8xVFbXRaEf3h911cgD7Cyjg0/L/GxnoLdoUoA3O1vDxUyLWyO4AehCpYX6D2L/LpUhtsaCkIWxRoeT+g/DVsqT8EWYDowC5jh6FxUUc+tJJblOmSPqWp4JUFHl6TDUoxLOlnSdknPSnK3sA2S9lfQs0zS7SkzwQ/A61U6A6dKWufpSMVg5mmMeUPSXyv2v0zSN6oa7ZAdwRqiA5CRf0TS+KpGAxiQ1OFN4z8l6PErVXUxSvmp1hvTqUnk35adPWskPWSM6fPaq84ASXqscg/gi9gcvJuC6o0nfwrhw5EYvIpNn88HStcN4M6KulfTys/lzKlO0lb8P2Lrf6VbLDAF+DLweEX998aSx372bwP6gPlVA3BEAvm9FJwVYtPqjwDXA08n6AZbOYoeeeAWp++mSlPGGLMLeFjSuRW6Iektx4GDJc2TdJ6khZKOruKDh/skXWSM6a/Q5yjn+dDKFrE1vw0VR2m2039x4kj7uJ+SslyJ/+7rtaly4mCM+a+kBaq2TbnVpfWy216jmCzpkIR+7kK/MymHNsbslX0NYoMweMpsjNklaWuKXQ9zJf2eOocvAbzHee5N/ojIgvBVxY3madh3v4b1iWZ/o3zw5kpaS+SFDGCq8jPguUQ/CmsCZfi403dhwjv/AHAQMAl41mvbGBMEhq4/c1PJTwmQr1f7u97pfzj5EnwUead/KAg/ivD7Zkf+HSBpFwiRfwibI3SXkOj29PgEivAggdU+C8JWR+6+CN9dm1tSyHcBLwbIj87ax1Kcxe0DJmVyY4CdEeR/TXnVeRLwc+C3wHF1fP+Qp/uGlABc6Cl5mPziVi8IzwDfAZ6KIN9LyhQt9v1GT/+sFCXTOVBBXuOTd+TGkp+eqWjKSTBwMPAvR+9TjSibjK35l93mWIxdZFKOxPzFseEgAJd7Olt6v+AC8jdIqwRhLbZM758HRH3tYa/vnoqtKZ4JHIk99tvh6HqNVl3RLSB/JfBEBPnBwxXsJ2uf176qxO7hwE3ALq/PfuyVXhdXt4r8+QHyK7K2cXWCMLiTOPqODwTh2IDdD2CP12LwCnUKMankO8kfiAySd2SKgjCEfEEQ+nznsZc7eyLJA9zddPKZIx0c2NcHgMsL5MZhr83XULiTeCSXAEcG2m4PjPCXsEWWBdhbZ/4h6knN4u07Mxv4MbCojtxo7DW6RTRwopMFxt0xeoCJAblLvCDdlWpzRAG42CO2sET2UUfuVbetsYPF9mKq8zwg6Q8lsm7bRJxt8N0cAPdar5FUupYU9X03B2C782wknVUi+0nneacxZk9rXBpGABO8RXA72demJ7fcWyvubIe/TQN2y11MuJ6wA5v3z8HeMbjba+8n5StwJCDb9lYUEI/Fde3mEQ1svnBKRvp32K/LEPYQd1z3XQJfsG3/Sw/gKElLZev8tb8rnizpBEmF1SDZ06ZbJN0saa+kayQtV77qi6QnJF1njFnXdOebAcIXssvQB3yfcGrcCZwEnAfMC8mMKGArNUVT28VubF4/nyZflx8Jr8BVkr4tm83tzn5ek/S8pM2SnpT0gv8H283C/wGTFfhGtexQwQAAAABJRU5ErkJggg==&labelColor=305680)](https://builds.thingsboard.io/viewType.html?buildTypeId=ThingsBoard_Build&guest=1)
ThingsBoard is an open-source IoT platform for data collection, processing, visualization, and device management. ThingsBoard is an open-source IoT platform for data collection, processing, visualization, and device management.

1210
application/src/main/data/json/system/scada_symbols/conical-tank.svg

File diff suppressed because it is too large

After

Width:  |  Height:  |  Size: 85 KiB

426
application/src/main/data/json/system/scada_symbols/extra-long-horizontal-pipe.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

438
application/src/main/data/json/system/scada_symbols/extra-long-vertical-pipe.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

97
application/src/main/data/json/system/scada_symbols/horizontal-broken-pipe.svg

@ -0,0 +1,97 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "Horizontal broken pipe",
"description": "Horizontal broken pipe.",
"searchTags": [
"pipe",
"horizontal pipe",
"broken pipe"
],
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
}
],
"behavior": [
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "pipeColor",
"name": "{i18n:scada.symbol.pipe-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<g tb:tag="clickArea">
<path d="m14 64s53.5 0.2293 60.5 0 17-6.5 17-6.5l-14.5 21.5 14.5 6.5-10.5 9 14.5 11-18.5 6.5 10 6-2.5 11.5 7 14s-10-7.5-17-7.5h-60.5v-72z" fill="#fff" tb:tag="pipe-background"/>
<path d="m14 64s53.5 0.2293 60.5 0 17-6.5 17-6.5l-14.5 21.5 14.5 6.5-10.5 9 14.5 11-18.5 6.5 10 6-2.5 11.5 7 14s-10-7.5-17-7.5h-60.5v-72z" fill="url(#paint0_linear_2474_252376)"/>
<path d="m86.266 62.02c0.1879-0.0889 0.3731-0.1777 0.5555-0.2661l-12.062 17.886 13.968 6.2614-8.7036 7.4602-1.4118 1.2101 13.759 10.438-15.869 5.576-3.0127 1.058 2.7382 1.643 9.0774 5.447-2.2715 10.448-0.1123 0.517 0.2365 0.473 4.5114 9.023c-0.4206-0.249-0.8606-0.502-1.3163-0.755-3.3898-1.883-7.9357-3.939-11.854-3.939h-59v-68.994l0.8066 0.0033c1.4767 0.0058 3.5942 0.0139 6.1569 0.0228 5.1255 0.0179 12.032 0.0394 19.158 0.0538 14.23 0.0286 29.393 0.0289 32.927-0.0868 3.8302-0.1255 8.3197-1.8722 11.717-3.4792z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="m105.5 56.5s13.5 7.395 18.5 7.5 62 0 62 0v72s-54.5-0.086-62 0-18.5 4.5-18.5 4.5l6-13.5-15-14 16.5-5-7.5-6 6-13-18-13 19.5-4.5-7.5-15z" fill="#fff" tb:tag="pipe-background"/>
<path d="m105.5 56.5s13.5 7.395 18.5 7.5 62 0 62 0v72s-54.5-0.086-62 0-18.5 4.5-18.5 4.5l6-13.5-15-14 16.5-5-7.5-6 6-13-18-13 19.5-4.5-7.5-15z" fill="url(#paint1_linear_2474_252376)"/>
<path d="m184.5 65.503v68.995l-0.854-2e-3c-1.505-2e-3 -3.663-5e-3 -6.277-8e-3 -5.227-7e-3 -12.274-0.015-19.555-0.02-14.556-0.011-30.069-0.011-33.831 0.032-3.973 0.046-8.768 1.225-12.479 2.346-1.18 0.357-2.264 0.712-3.197 1.031l4.564-10.268 0.434-0.976-0.782-0.73-13.096-12.223 14.008-4.244 2.727-0.827-2.225-1.78-6.583-5.267 5.508-11.933 0.518-1.1215-1.002-0.7231-15.321-11.065 16.28-3.757 1.857-0.4284-0.852-1.704-5.435-10.87c1.009 0.5039 2.144 1.0566 3.335 1.611 2.02 0.9404 4.222 1.8953 6.25 2.6248 1.981 0.7123 3.962 1.2726 5.476 1.3044 2.522 0.0529 18.049 0.0528 32.908 0.0397 7.438-0.0066 14.72-0.0164 20.142-0.0246 2.711-0.0041 4.957-0.0078 6.526-0.0105l0.956-0.0017z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect x="187.5" y="51.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/>
<rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/>
</g><defs>
<linearGradient id="paint0_linear_2474_252376" x1="32.98" x2="32.532" y1="64" y2="136" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
<linearGradient id="paint1_linear_2474_252376" x1="129.76" x2="129.33" y1="64" y2="136" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

7
application/src/main/data/json/system/scada_symbols/horizontal-tank.svg

@ -36,6 +36,11 @@
"stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 17;\n var majorIntervalLength = 568 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(715, y, 747, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 705, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(727, minorY, 747, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 17;\n var majorIntervalLength = 568 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(715, y, 747, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 705, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(727, minorY, 747, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}",
"actions": null "actions": null
}, },
{
"tag": "scale-background",
"stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n}",
"actions": null
},
{ {
"tag": "top-layer", "tag": "top-layer",
"stateRenderFunction": "if (ctx.properties.transparent || !ctx.properties.scale) {\n element.hide();\n}", "stateRenderFunction": "if (ctx.properties.transparent || !ctx.properties.scale) {\n element.hide();\n}",
@ -555,7 +560,7 @@
} }
] ]
}]]></tb:metadata> }]]></tb:metadata>
<path d="m174 600c-174 0-174-151.63-174-239 4.9551e-6 -1.386 9.6938e-6 -2.741 1.4223e-5 -4.065 1.18e-8 -4e-3 -1.4223e-5 -113.93-1.4223e-5 -113.94 0-87.373-8.3346e-7 -243 174-243h653.5c173 0 172.5 156.06 172.5 243v118c0 86.939 0 239-172.5 239h-653.5z" fill="#E5E5E5" tb:tag="background"/><path d="m174 600c-174 0-174-151.63-174-239 4.9551e-6 -1.386 9.6938e-6 -2.741 1.4223e-5 -4.065 1.18e-8 -4e-3 -1.4223e-5 -113.93-1.4223e-5 -113.94 0-87.373-8.3346e-7 -243 174-243h653.5c173 0 172.5 156.06 172.5 243v118c0 86.939 0 239-172.5 239h-653.5z" fill="url(#paint0_linear_1694_158298)"/><path d="m177.27 0.4874c198.56-1.0967 560.65 1.9e-5 640.18 0 79.522-1.9e-5 183.55 15 183.55 220v169c0 192.5-105.53 211.5-184.55 211.5s-516.64-0.5-639.18-0.5c-122.53 0-176.55-68.001-176.55-219 5.26e-4 -151-6.59e-4 -49.502-2.47e-4 -119s-22.006-260.91 176.55-262.01z" fill="#4A4848" fill-opacity=".5"/><mask id="mask0_1694_158298" x="17" y="16" width="968" height="570" style="mask-type:alpha" maskUnits="userSpaceOnUse"> <path d="m174 600c-174 0-174-151.63-174-239 4.9551e-6 -1.386 9.6938e-6 -2.741 1.4223e-5 -4.065 1.18e-8 -4e-3 -1.4223e-5 -113.93-1.4223e-5 -113.94 0-87.373-8.3346e-7 -243 174-243h653.5c173 0 172.5 156.06 172.5 243v118c0 86.939 0 239-172.5 239h-653.5z" fill="#E5E5E5" tb:tag="background"/><path d="m174 600c-174 0-174-151.63-174-239 4.9551e-6 -1.386 9.6938e-6 -2.741 1.4223e-5 -4.065 1.18e-8 -4e-3 -1.4223e-5 -113.93-1.4223e-5 -113.94 0-87.373-8.3346e-7 -243 174-243h653.5c173 0 172.5 156.06 172.5 243v118c0 86.939 0 239-172.5 239h-653.5z" fill="url(#paint0_linear_1694_158298)"/><path d="m177.27 0.4874c198.56-1.0967 560.65 1.9e-5 640.18 0 79.522-1.9e-5 183.55 15 183.55 220v169c0 192.5-105.53 211.5-184.55 211.5s-516.64-0.5-639.18-0.5c-122.53 0-176.55-68.001-176.55-219 5.26e-4 -151-6.59e-4 -49.502-2.47e-4 -119s-22.006-260.91 176.55-262.01z" fill="#4A4848" fill-opacity=".5" tb:tag="scale-background"/><mask id="mask0_1694_158298" x="17" y="16" width="968" height="570" style="mask-type:alpha" maskUnits="userSpaceOnUse">
<path d="m178.68 16h654.82c117.67 0 151.5 98.586 151.5 184.16v204.18c0 135.62-68.502 181.66-162.5 181.66h-627.98c-129 0-177.5-66.558-177.5-181.66s0.0023-129.32 0-212.19c-3e-4 -9.509-5.4994-176.16 161.66-176.16z" fill="#D9D9D9"/> <path d="m178.68 16h654.82c117.67 0 151.5 98.586 151.5 184.16v204.18c0 135.62-68.502 181.66-162.5 181.66h-627.98c-129 0-177.5-66.558-177.5-181.66s0.0023-129.32 0-212.19c-3e-4 -9.509-5.4994-176.16 161.66-176.16z" fill="#D9D9D9"/>
</mask><g mask="url(#mask0_1694_158298)"> </mask><g mask="url(#mask0_1694_158298)">
<rect transform="scale(1,-1)" x="9" y="-585" width="984" height="200" fill="#1EC1F4" fill-opacity=".5" tb:tag="fluid-background"/> <rect transform="scale(1,-1)" x="9" y="-585" width="984" height="200" fill="#1EC1F4" fill-opacity=".5" tb:tag="fluid-background"/>

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 115 KiB

1220
application/src/main/data/json/system/scada_symbols/large-conical-tank.svg

File diff suppressed because it is too large

After

Width:  |  Height:  |  Size: 85 KiB

97
application/src/main/data/json/system/scada_symbols/long-horizontal-broken-pipe.svg

@ -0,0 +1,97 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="200" fill="none" version="1.1" viewBox="0 0 400 200"><tb:metadata xmlns=""><![CDATA[{
"title": "Long horizontal broken pipe",
"description": "Long horizontal broken pipe.",
"searchTags": [
"long pipe",
"horizontal pipe",
"broken pipe"
],
"widgetSizeX": 2,
"widgetSizeY": 1,
"tags": [
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
}
],
"behavior": [
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "pipeColor",
"name": "{i18n:scada.symbol.pipe-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<g tb:tag="clickArea">
<path d="m14 64s158.08 0.2293 165.06 0c6.982-0.2293 16.955-6 16.955-6l-14.461 21 14.461 6.5-10.472 9 14.461 11-18.45 6.5 9.973 6-2.494 11.5 6.982 14.5s-9.973-8-16.955-8h-165.06v-72z" fill="#fff" tb:tag="pipe-background"/>
<path d="m14 64s158.08 0.2293 165.06 0c6.982-0.2293 16.955-6 16.955-6l-14.461 21 14.461 6.5-10.472 9 14.461 11-18.45 6.5 9.973 6-2.494 11.5 6.982 14.5s-9.973-8-16.955-8h-165.06v-72z" fill="url(#paint0_linear_2474_252377)"/>
<path d="m190.75 62.288c0.237-0.1035 0.47-0.2069 0.698-0.3097l-11.136 16.171-1.02 1.4814 1.641 0.7374 12.308 5.5325-8.682 7.4618-1.406 1.2087 13.723 10.439-15.827 5.575-3.004 1.058 11.782 7.089-2.265 10.45-0.11 0.504 0.224 0.465 4.49 9.325c-0.401-0.252-0.818-0.509-1.25-0.766-3.376-2.005-7.926-4.21-11.862-4.21h-163.56v-68.998l0.2749 4e-4 5.029 0.0069c4.3361 0.0058 10.538 0.0139 18.003 0.0228 14.929 0.018 34.907 0.0394 55.103 0.0538 40.363 0.0286 81.673 0.0289 85.197-0.0868 3.794-0.1246 8.256-1.7286 11.646-3.2108z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="m218 57s13.5 6.895 18.5 7 149.5 0 149.5 0v72s-142-0.086-149.5 0-18.5 5-18.5 5l6-14-15-14 16.5-5-7.5-6 6-13-18-13 19.5-4.5-7.5-14.5z" fill="#fff" tb:tag="pipe-background"/>
<path d="m218 57s13.5 6.895 18.5 7 149.5 0 149.5 0v72s-142-0.086-149.5 0-18.5 5-18.5 5l6-14-15-14 16.5-5-7.5-6 6-13-18-13 19.5-4.5-7.5-14.5z" fill="url(#paint1_linear_2474_252377)"/>
<path d="m384.5 65.501v68.998h-0.096l-4.519-3e-3c-3.898-2e-3 -9.474-5e-3 -16.189-8e-3 -13.43-7e-3 -31.414-0.015-49.633-0.02-36.428-0.011-73.818-0.011-77.58 0.032-3.999 0.046-8.811 1.363-12.523 2.61-1.157 0.389-2.223 0.777-3.144 1.127l4.563-10.646 0.415-0.969-0.771-0.719-13.096-12.223 14.008-4.244 2.727-0.827-2.225-1.78-6.583-5.267 5.508-11.933 0.518-1.1215-1.002-0.7231-15.321-11.065 16.28-3.757 1.883-0.4345-0.888-1.7162-5.428-10.495c1.018 0.4748 2.167 0.9969 3.374 1.5206 2.019 0.877 4.219 1.7672 6.244 2.4474 1.982 0.6654 3.95 1.1843 5.446 1.2158 2.522 0.0529 39.926 0.0528 76.658 0.0397 18.375-0.0066 36.594-0.0164 50.219-0.0246 6.812-0.0041 12.476-0.0078 16.437-0.0105l4.595-0.0031 0.123-1e-4z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/>
<rect x="387.5" y="51.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/>
</g><defs>
<linearGradient id="paint0_linear_2474_252377" x1="32.929" x2="32.48" y1="64" y2="136" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
<linearGradient id="paint1_linear_2474_252377" x1="329.76" x2="329.33" y1="64" y2="136" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

97
application/src/main/data/json/system/scada_symbols/long-vertical-broken-pipe.svg

@ -0,0 +1,97 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="400" fill="none" version="1.1" viewBox="0 0 200 400"><tb:metadata xmlns=""><![CDATA[{
"title": "Long vertical broken pipe",
"description": "Long vertical broken pipe.",
"searchTags": [
"long pipe",
"vertical pipe",
"broken pipe"
],
"widgetSizeX": 1,
"widgetSizeY": 2,
"tags": [
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
}
],
"behavior": [
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "pipeColor",
"name": "{i18n:scada.symbol.pipe-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<g tb:tag="clickArea">
<path d="m64 386s0.2293-158.08 0-165.06c-0.2293-6.982-6-16.955-6-16.955l21 14.461 6.5-14.461 9 10.472 11-14.461 6.5 18.45 6-9.973 11.5 2.494 14.5-6.982s-8 9.973-8 16.955v165.06h-72z" fill="#fff" tb:tag="pipe-background"/>
<path d="m64 386s0.2293-158.08 0-165.06c-0.2293-6.982-6-16.955-6-16.955l21 14.461 6.5-14.461 9 10.472 11-14.461 6.5 18.45 6-9.973 11.5 2.494 14.5-6.982s-8 9.973-8 16.955v165.06h-72z" fill="url(#paint0_linear_2474_252379)"/>
<path d="m62.288 209.25c-0.1035-0.237-0.2069-0.47-0.3097-0.698l16.171 11.136 1.4814 1.02 0.7374-1.641 5.5325-12.308 7.4618 8.682 1.2087 1.406 10.439-13.723 5.575 15.827 1.058 3.004 7.089-11.782 10.45 2.265 0.504 0.11 0.465-0.224 9.325-4.49c-0.252 0.401-0.509 0.818-0.766 1.25-2.005 3.376-4.21 7.926-4.21 11.862v163.56h-68.998l4e-4 -0.275 0.0069-5.029c0.0058-4.336 0.0139-10.538 0.0228-18.003 0.018-14.928 0.0394-34.906 0.0538-55.102 0.0286-40.363 0.0289-81.673-0.0868-85.197-0.1246-3.794-1.7286-8.256-3.2108-11.646z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="m57 182s6.895-13.5 7-18.5 0-149.5 0-149.5h72s-0.086 142 0 149.5 5 18.5 5 18.5l-14-6-14 15-5-16.5-6 7.5-13-6-13 18-4.5-19.5-14.5 7.5z" fill="#fff" tb:tag="pipe-background"/>
<path d="m57 182s6.895-13.5 7-18.5 0-149.5 0-149.5h72s-0.086 142 0 149.5 5 18.5 5 18.5l-14-6-14 15-5-16.5-6 7.5-13-6-13 18-4.5-19.5-14.5 7.5z" fill="url(#paint1_linear_2474_252379)"/>
<path d="m65.501 15.5h68.998v0.0957l-3e-3 4.5197c-2e-3 3.8974-5e-3 9.4736-8e-3 16.188-7e-3 13.43-0.015 31.414-0.02 49.633-0.011 36.428-0.011 73.818 0.032 77.58 0.046 3.999 1.363 8.811 2.61 12.523 0.389 1.157 0.777 2.223 1.127 3.144l-10.646-4.563-0.969-0.415-0.719 0.771-12.223 13.096-4.244-14.008-0.827-2.727-1.78 2.225-5.267 6.583-11.933-5.508-1.1215-0.518-0.7231 1.002-11.065 15.321-3.757-16.28-0.4345-1.883-1.7162 0.888-10.495 5.428c0.4748-1.018 0.9969-2.167 1.5206-3.374 0.877-2.019 1.7672-4.219 2.4474-6.244 0.6654-1.982 1.1843-3.95 1.2158-5.446 0.0529-2.522 0.0528-39.926 0.0397-76.658-0.0066-18.375-0.0164-36.594-0.0246-50.219-0.0041-6.8125-0.0078-12.477-0.0105-16.438l-0.0031-4.5947-1e-4 -0.123z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect transform="rotate(-90 51.5 398.5)" x="51.5" y="398.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/>
<rect transform="rotate(-90 51.5 12.5)" x="51.5" y="12.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/>
</g><defs>
<linearGradient id="paint0_linear_2474_252379" x1="64" x2="136" y1="367.07" y2="367.52" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
<linearGradient id="paint1_linear_2474_252379" x1="64" x2="136" y1="70.24" y2="70.67" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

1376
application/src/main/data/json/system/scada_symbols/small-cylindrical-tank.svg

File diff suppressed because it is too large

After

Width:  |  Height:  |  Size: 100 KiB

7
application/src/main/data/json/system/scada_symbols/small-spherical-tank.svg

@ -37,6 +37,11 @@
"stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 23;\n var majorIntervalLength = 560 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(268, y, 300, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 258, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(280, minorY, 300, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 23;\n var majorIntervalLength = 560 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(268, y, 300, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 258, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(280, minorY, 300, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}",
"actions": null "actions": null
}, },
{
"tag": "scale-background",
"stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n}",
"actions": null
},
{ {
"tag": "top-layer", "tag": "top-layer",
"stateRenderFunction": "if (ctx.properties.transparent || !ctx.properties.scale) {\n element.hide();\n}", "stateRenderFunction": "if (ctx.properties.transparent || !ctx.properties.scale) {\n element.hide();\n}",
@ -556,7 +561,7 @@
} }
] ]
}]]></tb:metadata> }]]></tb:metadata>
<circle cx="300" cy="300" r="300" fill="#E5E5E5" tb:tag="background"/><circle cx="300" cy="300" r="300" fill="url(#paint0_radial_1711_268272)"/><circle cx="300" cy="300" r="298.5" stroke="#000" stroke-opacity=".12" stroke-width="3"/><circle cx="300" cy="300" r="300" fill="#4A4848" fill-opacity=".5"/><mask id="mask0_1711_268272" x="16" y="16" width="568" height="568" style="mask-type:alpha" maskUnits="userSpaceOnUse"> <circle cx="300" cy="300" r="300" fill="#E5E5E5" tb:tag="background"/><circle cx="300" cy="300" r="300" fill="url(#paint0_radial_1711_268272)"/><circle cx="300" cy="300" r="298.5" stroke="#000" stroke-opacity=".12" stroke-width="3"/><circle cx="300" cy="300" r="300" fill="#4A4848" fill-opacity=".5" tb:tag="scale-background"/><mask id="mask0_1711_268272" x="16" y="16" width="568" height="568" style="mask-type:alpha" maskUnits="userSpaceOnUse">
<circle cx="300" cy="300" r="284" fill="#D9D9D9"/> <circle cx="300" cy="300" r="284" fill="#D9D9D9"/>
</mask><g mask="url(#mask0_1711_268272)"> </mask><g mask="url(#mask0_1711_268272)">
<rect transform="scale(1,-1)" y="-584" width="600" height="200" fill="#1ec1f4" fill-opacity=".5" tb:tag="fluid-background"/> <rect transform="scale(1,-1)" y="-584" width="600" height="200" fill="#1ec1f4" fill-opacity=".5" tb:tag="fluid-background"/>

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

7
application/src/main/data/json/system/scada_symbols/spherical-tank.svg

@ -37,6 +37,11 @@
"stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 23;\n var majorIntervalLength = 960 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(458, y, 490, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 448, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(470, minorY, 490, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 23;\n var majorIntervalLength = 960 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(458, y, 490, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 448, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(470, minorY, 490, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}",
"actions": null "actions": null
}, },
{
"tag": "scale-background",
"stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n}",
"actions": null
},
{ {
"tag": "top-layer", "tag": "top-layer",
"stateRenderFunction": "if (ctx.properties.transparent || !ctx.properties.scale) {\n element.hide();\n}", "stateRenderFunction": "if (ctx.properties.transparent || !ctx.properties.scale) {\n element.hide();\n}",
@ -556,7 +561,7 @@
} }
] ]
}]]></tb:metadata> }]]></tb:metadata>
<path d="m1e3 500c0 276.14-223.86 500-500 500s-500-223.86-500-500 223.86-500 500-500 500 223.86 500 500z" fill="#E5E5E5" tb:tag="background"/><path d="m1e3 500c0 276.14-223.86 500-500 500s-500-223.86-500-500 223.86-500 500-500 500 223.86 500 500z" fill="url(#paint0_radial_1711_251491)"/><path d="m998.5 500c0 275.31-223.19 498.5-498.5 498.5s-498.5-223.19-498.5-498.5 223.19-498.5 498.5-498.5 498.5 223.19 498.5 498.5z" stroke="#000" stroke-opacity=".12" stroke-width="3"/><circle cx="500" cy="500" r="500" fill="#4A4848" fill-opacity=".5"/><mask id="mask0_1711_251491" x="16" y="16" width="968" height="968" style="mask-type:alpha" maskUnits="userSpaceOnUse"> <path d="m1e3 500c0 276.14-223.86 500-500 500s-500-223.86-500-500 223.86-500 500-500 500 223.86 500 500z" fill="#E5E5E5" tb:tag="background"/><path d="m1e3 500c0 276.14-223.86 500-500 500s-500-223.86-500-500 223.86-500 500-500 500 223.86 500 500z" fill="url(#paint0_radial_1711_251491)"/><path d="m998.5 500c0 275.31-223.19 498.5-498.5 498.5s-498.5-223.19-498.5-498.5 223.19-498.5 498.5-498.5 498.5 223.19 498.5 498.5z" stroke="#000" stroke-opacity=".12" stroke-width="3"/><circle cx="500" cy="500" r="500" fill="#4A4848" fill-opacity=".5" tb:tag="scale-background"/><mask id="mask0_1711_251491" x="16" y="16" width="968" height="968" style="mask-type:alpha" maskUnits="userSpaceOnUse">
<circle cx="500" cy="500" r="484" fill="#D9D9D9"/> <circle cx="500" cy="500" r="484" fill="#D9D9D9"/>
</mask><g mask="url(#mask0_1711_251491)"> </mask><g mask="url(#mask0_1711_251491)">
<rect transform="scale(1,-1)" x="8" y="-984" width="984" height="200" fill="#1EC1F4" fill-opacity=".5" tb:tag="fluid-background"/> <rect transform="scale(1,-1)" x="8" y="-984" width="984" height="200" fill="#1EC1F4" fill-opacity=".5" tb:tag="fluid-background"/>

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 118 KiB

7
application/src/main/data/json/system/scada_symbols/stand-horizontal-tank.svg

@ -37,6 +37,11 @@
"stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 17;\n var majorIntervalLength = 568 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(715, y, 747, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 705, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(727, minorY, 747, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 17;\n var majorIntervalLength = 568 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(715, y, 747, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 705, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(727, minorY, 747, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}",
"actions": null "actions": null
}, },
{
"tag": "scale-background",
"stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n}",
"actions": null
},
{ {
"tag": "top-layer", "tag": "top-layer",
"stateRenderFunction": "if (ctx.properties.transparent || !ctx.properties.scale) {\n element.hide();\n}", "stateRenderFunction": "if (ctx.properties.transparent || !ctx.properties.scale) {\n element.hide();\n}",
@ -556,7 +561,7 @@
} }
] ]
}]]></tb:metadata> }]]></tb:metadata>
<path d="m174 600c-174 0-174-151.63-174-239 4.9551e-6 -1.386 9.6938e-6 -2.741 1.4223e-5 -4.065 1.18e-8 -4e-3 -1.4223e-5 -113.93-1.4223e-5 -113.94 0-87.373-8.3346e-7 -243 174-243h653.5c173 0 172.5 156.06 172.5 243v118c0 86.939 0 239-172.5 239h-653.5z" fill="#E5E5E5" tb:tag="background"/><path d="m174 600c-174 0-174-151.63-174-239 4.9551e-6 -1.386 9.6938e-6 -2.741 1.4223e-5 -4.065 1.18e-8 -4e-3 -1.4223e-5 -113.93-1.4223e-5 -113.94 0-87.373-8.3346e-7 -243 174-243h653.5c173 0 172.5 156.06 172.5 243v118c0 86.939 0 239-172.5 239h-653.5z" fill="url(#paint0_linear_1694_158298)"/><path d="m177.27 0.4874c198.56-1.0967 560.65 1.9e-5 640.18 0 79.522-1.9e-5 183.55 15 183.55 220v169c0 192.5-105.53 211.5-184.55 211.5s-516.64-0.5-639.18-0.5c-122.53 0-176.55-68.001-176.55-219 5.26e-4 -151-6.59e-4 -49.502-2.47e-4 -119s-22.006-260.91 176.55-262.01z" fill="#4A4848" fill-opacity=".5"/><mask id="mask0_1694_158298" x="17" y="16" width="968" height="570" style="mask-type:alpha" maskUnits="userSpaceOnUse"> <path d="m174 600c-174 0-174-151.63-174-239 4.9551e-6 -1.386 9.6938e-6 -2.741 1.4223e-5 -4.065 1.18e-8 -4e-3 -1.4223e-5 -113.93-1.4223e-5 -113.94 0-87.373-8.3346e-7 -243 174-243h653.5c173 0 172.5 156.06 172.5 243v118c0 86.939 0 239-172.5 239h-653.5z" fill="#E5E5E5" tb:tag="background"/><path d="m174 600c-174 0-174-151.63-174-239 4.9551e-6 -1.386 9.6938e-6 -2.741 1.4223e-5 -4.065 1.18e-8 -4e-3 -1.4223e-5 -113.93-1.4223e-5 -113.94 0-87.373-8.3346e-7 -243 174-243h653.5c173 0 172.5 156.06 172.5 243v118c0 86.939 0 239-172.5 239h-653.5z" fill="url(#paint0_linear_1694_158298)"/><path d="m177.27 0.4874c198.56-1.0967 560.65 1.9e-5 640.18 0 79.522-1.9e-5 183.55 15 183.55 220v169c0 192.5-105.53 211.5-184.55 211.5s-516.64-0.5-639.18-0.5c-122.53 0-176.55-68.001-176.55-219 5.26e-4 -151-6.59e-4 -49.502-2.47e-4 -119s-22.006-260.91 176.55-262.01z" fill="#4A4848" fill-opacity=".5" tb:tag="scale-background"/><mask id="mask0_1694_158298" x="17" y="16" width="968" height="570" style="mask-type:alpha" maskUnits="userSpaceOnUse">
<path d="m178.68 16h654.82c117.67 0 151.5 98.586 151.5 184.16v204.18c0 135.62-68.502 181.66-162.5 181.66h-627.98c-129 0-177.5-66.558-177.5-181.66s0.0023-129.32 0-212.19c-3e-4 -9.509-5.4994-176.16 161.66-176.16z" fill="#D9D9D9"/> <path d="m178.68 16h654.82c117.67 0 151.5 98.586 151.5 184.16v204.18c0 135.62-68.502 181.66-162.5 181.66h-627.98c-129 0-177.5-66.558-177.5-181.66s0.0023-129.32 0-212.19c-3e-4 -9.509-5.4994-176.16 161.66-176.16z" fill="#D9D9D9"/>
</mask><g mask="url(#mask0_1694_158298)"> </mask><g mask="url(#mask0_1694_158298)">
<rect transform="scale(1,-1)" x="9" y="-585" width="984" height="200" fill="#1EC1F4" fill-opacity=".5" tb:tag="fluid-background"/> <rect transform="scale(1,-1)" x="9" y="-585" width="984" height="200" fill="#1EC1F4" fill-opacity=".5" tb:tag="fluid-background"/>

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 122 KiB

97
application/src/main/data/json/system/scada_symbols/vertical-broken-pipe.svg

@ -0,0 +1,97 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "Vertical broken pipe",
"description": "Vertical broken pipe.",
"searchTags": [
"pipe",
"vertical pipe",
"broken pipe"
],
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
}
],
"behavior": [
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "pipeColor",
"name": "{i18n:scada.symbol.pipe-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<g tb:tag="clickArea">
<path d="m63.5 186.5s0.2293-53.5 0-60.5-6.5-17-6.5-17l21.5 14.5 6.5-14.5 9 10.5 11-14.5 6.5 18.5 6-10 11.5 2.5 14-7s-7.5 10-7.5 17v60.5h-72z" fill="#fff" tb:tag="pipe-background"/>
<path d="m63.5 186.5s0.2293-53.5 0-60.5-6.5-17-6.5-17l21.5 14.5 6.5-14.5 9 10.5 11-14.5 6.5 18.5 6-10 11.5 2.5 14-7s-7.5 10-7.5 17v60.5h-72z" fill="url(#paint0_linear_2474_252378)"/>
<path d="m61.52 114.23c-0.0889-0.188-0.1777-0.374-0.2661-0.556l16.407 11.066 1.4782 0.997 0.7293-1.627 5.5321-12.341 7.4602 8.703 1.2101 1.412 1.1238-1.481 9.314-12.278 5.576 15.868 1.058 3.013 1.643-2.738 5.447-9.078 10.448 2.272 0.517 0.112 0.473-0.236 9.023-4.512c-0.249 0.421-0.502 0.861-0.755 1.317-1.883 3.389-3.939 7.935-3.939 11.853v59h-68.994l0.0033-0.807c0.0058-1.476 0.0139-3.594 0.0228-6.157 0.0179-5.125 0.0394-12.032 0.0538-19.158 0.0286-14.23 0.0289-29.393-0.0868-32.927-0.1255-3.83-1.8722-8.32-3.4792-11.717z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="m58 94.5s7.395-13.5 7.5-18.5 0-62 0-62h72s-0.086 54.5 0 62 4.5 18.5 4.5 18.5l-13.5-6-14 15-5-16.5-6 7.5-13-6-13 18-4.5-19.5-15 7.5z" fill="#fff" tb:tag="pipe-background"/>
<path d="m58 94.5s7.395-13.5 7.5-18.5 0-62 0-62h72s-0.086 54.5 0 62 4.5 18.5 4.5 18.5l-13.5-6-14 15-5-16.5-6 7.5-13-6-13 18-4.5-19.5-15 7.5z" fill="url(#paint1_linear_2474_252378)"/>
<path d="m67.003 15.5h68.995l-2e-3 0.8543c-2e-3 1.5049-5e-3 3.6632-8e-3 6.2766-7e-3 5.2267-0.015 12.274-0.02 19.555-0.011 14.556-0.011 30.069 0.032 33.831 0.046 3.9731 1.225 8.7675 2.346 12.479 0.357 1.1792 0.712 2.2636 1.031 3.1964l-10.268-4.5635-0.976-0.4341-0.73 0.7813-12.223 13.096-4.244-14.008-0.827-2.7275-1.78 2.2255-5.267 6.5829-13.055-6.0254-0.7231 1.0013-11.065 15.321-3.757-16.28-0.4284-1.8563-12.574 6.2868c0.5039-1.009 1.0566-2.1442 1.611-3.3352 0.9404-2.0204 1.8953-4.222 2.6248-6.2504 0.7123-1.9804 1.2726-3.9616 1.3044-5.4761 0.0529-2.5216 0.0528-18.048 0.0397-32.908-0.0066-7.4379-0.0164-14.72-0.0246-20.142-0.0041-2.711-0.0078-4.9572-0.0105-6.5257l-0.0017-0.9564z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect transform="rotate(-90 51.5 198.5)" x="51.5" y="198.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/>
<rect transform="rotate(-90 51.5 12.5)" x="51.5" y="12.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/>
</g><defs>
<linearGradient id="paint0_linear_2474_252378" x1="63.5" x2="135.5" y1="167.52" y2="167.97" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
<linearGradient id="paint1_linear_2474_252378" x1="65.5" x2="137.5" y1="70.24" y2="70.67" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

9
application/src/main/data/json/system/widget_bundles/scada_fluid_system.json

@ -11,8 +11,10 @@
"widgetTypeFqns": [ "widgetTypeFqns": [
"horizontal_pipe", "horizontal_pipe",
"long_horizontal_pipe", "long_horizontal_pipe",
"extra_long_horizontal_pipe",
"vertical_pipe", "vertical_pipe",
"long_vertical_pipe", "long_vertical_pipe",
"extra_long_vertical_pipe",
"left_bottom_elbow_pipe", "left_bottom_elbow_pipe",
"bottom_right_elbow_pipe", "bottom_right_elbow_pipe",
"top_right_elbow_pipe", "top_right_elbow_pipe",
@ -28,6 +30,10 @@
"right_drain_pipe", "right_drain_pipe",
"short_left_drain_pipe", "short_left_drain_pipe",
"short_right_drain_pipe", "short_right_drain_pipe",
"horizontal_broken_pipe",
"vertical_broken_pipe",
"long_horizontal_broken_pipe",
"long_vertical_broken_pipe",
"top_flow_meter", "top_flow_meter",
"right_flow_meter", "right_flow_meter",
"bottom_flow_meter", "bottom_flow_meter",
@ -58,6 +64,7 @@
"stand_vertical_tank", "stand_vertical_tank",
"cylindrical_tank", "cylindrical_tank",
"stand_cylindrical_tank", "stand_cylindrical_tank",
"small_cylindrical_tank",
"vertical_short_tank", "vertical_short_tank",
"stand_vertical_short_tank", "stand_vertical_short_tank",
"large_cylindrical_tank", "large_cylindrical_tank",
@ -68,6 +75,8 @@
"stand_horizontal_tank", "stand_horizontal_tank",
"spherical_tank", "spherical_tank",
"small_spherical_tank", "small_spherical_tank",
"conical_tank",
"large_conical_tank",
"elevated_tank", "elevated_tank",
"pool" "pool"
] ]

7023
application/src/main/data/json/tenant/dashboards/gateways.json

File diff suppressed because it is too large

7
application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DashboardSyncService.java

@ -18,6 +18,7 @@ package org.thingsboard.server.service.entitiy.dashboard;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -39,6 +40,7 @@ import java.util.stream.Stream;
@TbCoreComponent @TbCoreComponent
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
@ConditionalOnProperty(value = "transport.gateway.dashboard.sync.enabled", havingValue = "true")
public class DashboardSyncService { public class DashboardSyncService {
private final GitSyncService gitSyncService; private final GitSyncService gitSyncService;
@ -46,8 +48,6 @@ public class DashboardSyncService {
private final WidgetsBundleService widgetsBundleService; private final WidgetsBundleService widgetsBundleService;
private final PartitionService partitionService; private final PartitionService partitionService;
@Value("${transport.gateway.dashboard.sync.enabled:true}")
private boolean enabled;
@Value("${transport.gateway.dashboard.sync.repository_url:}") @Value("${transport.gateway.dashboard.sync.repository_url:}")
private String repoUrl; private String repoUrl;
@Value("${transport.gateway.dashboard.sync.branch:main}") @Value("${transport.gateway.dashboard.sync.branch:main}")
@ -60,9 +60,6 @@ public class DashboardSyncService {
@AfterStartUp(order = AfterStartUp.REGULAR_SERVICE) @AfterStartUp(order = AfterStartUp.REGULAR_SERVICE)
public void init() throws Exception { public void init() throws Exception {
if (!enabled) {
return;
}
gitSyncService.registerSync(REPO_KEY, repoUrl, branch, TimeUnit.HOURS.toMillis(fetchFrequencyHours), this::update); gitSyncService.registerSync(REPO_KEY, repoUrl, branch, TimeUnit.HOURS.toMillis(fetchFrequencyHours), this::update);
} }

76
application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java

@ -19,7 +19,6 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -54,8 +53,8 @@ import org.thingsboard.server.service.install.update.ImagesUpdater;
import java.io.IOException; import java.io.IOException;
import java.io.UncheckedIOException; import java.io.UncheckedIOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
@ -170,7 +169,6 @@ public class InstallScripts {
loadRuleChainsFromPath(tenantId, edgeChainsDir); loadRuleChainsFromPath(tenantId, edgeChainsDir);
} }
@SneakyThrows
private void loadRuleChainsFromPath(TenantId tenantId, Path ruleChainsPath) { private void loadRuleChainsFromPath(TenantId tenantId, Path ruleChainsPath) {
findRuleChainsFromPath(ruleChainsPath).forEach(path -> { findRuleChainsFromPath(ruleChainsPath).forEach(path -> {
try { try {
@ -182,12 +180,10 @@ public class InstallScripts {
}); });
} }
List<Path> findRuleChainsFromPath(Path ruleChainsPath) throws IOException { List<Path> findRuleChainsFromPath(Path ruleChainsPath) {
List<Path> paths = new ArrayList<>(); try (Stream<Path> files = listDir(ruleChainsPath).filter(path -> path.toString().endsWith(InstallScripts.JSON_EXT))) {
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(ruleChainsPath, path -> path.toString().endsWith(InstallScripts.JSON_EXT))) { return files.toList();
dirStream.forEach(paths::add);
} }
return paths;
} }
public RuleChain createDefaultRuleChain(TenantId tenantId, String ruleChainName) { public RuleChain createDefaultRuleChain(TenantId tenantId, String ruleChainName) {
@ -211,11 +207,11 @@ public class InstallScripts {
return ruleChain; return ruleChain;
} }
public void loadSystemWidgets() throws Exception { public void loadSystemWidgets() {
log.info("Loading system widgets"); log.info("Loading system widgets");
Map<Path, JsonNode> widgetsBundlesMap = new HashMap<>(); Map<Path, JsonNode> widgetsBundlesMap = new HashMap<>();
Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR); Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR);
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) { try (Stream<Path> dirStream = listDir(widgetBundlesDir).filter(path -> path.toString().endsWith(JSON_EXT))) {
dirStream.forEach( dirStream.forEach(
path -> { path -> {
JsonNode widgetsBundleDescriptorJson; JsonNode widgetsBundleDescriptorJson;
@ -247,12 +243,14 @@ public class InstallScripts {
} }
Path widgetTypesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_TYPES_DIR); Path widgetTypesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_TYPES_DIR);
if (Files.exists(widgetTypesDir)) { if (Files.exists(widgetTypesDir)) {
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(widgetTypesDir, path -> path.toString().endsWith(JSON_EXT))) { try (Stream<Path> dirStream = listDir(widgetTypesDir).filter(path -> path.toString().endsWith(JSON_EXT))) {
dirStream.forEach( dirStream.forEach(
path -> { path -> {
try { try {
JsonNode widgetTypeJson = JacksonUtil.toJsonNode(path.toFile()); String widgetTypeJson = Files.readString(path);
WidgetTypeDetails widgetTypeDetails = JacksonUtil.treeToValue(widgetTypeJson, WidgetTypeDetails.class); widgetTypeJson = resourceService.checkSystemResourcesUsage(widgetTypeJson, ResourceType.JS_MODULE);
WidgetTypeDetails widgetTypeDetails = JacksonUtil.fromString(widgetTypeJson, WidgetTypeDetails.class);
widgetTypeService.saveWidgetType(widgetTypeDetails); widgetTypeService.saveWidgetType(widgetTypeDetails);
} catch (Exception e) { } catch (Exception e) {
log.error("Unable to load widget type from json: [{}]", path.toString()); log.error("Unable to load widget type from json: [{}]", path.toString());
@ -300,12 +298,12 @@ public class InstallScripts {
} }
} }
private void loadSystemScadaSymbols() throws Exception { private void loadSystemScadaSymbols() {
log.info("Loading system SCADA symbols"); log.info("Loading system SCADA symbols");
Path scadaSymbolsDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, SCADA_SYMBOLS_DIR); Path scadaSymbolsDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, SCADA_SYMBOLS_DIR);
if (Files.exists(scadaSymbolsDir)) { if (Files.exists(scadaSymbolsDir)) {
WidgetTypeDetails scadaSymbolWidgetTemplate = widgetTypeService.findWidgetTypeDetailsByTenantIdAndFqn(TenantId.SYS_TENANT_ID, "scada_symbol"); WidgetTypeDetails scadaSymbolWidgetTemplate = widgetTypeService.findWidgetTypeDetailsByTenantIdAndFqn(TenantId.SYS_TENANT_ID, "scada_symbol");
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(scadaSymbolsDir, path -> path.toString().endsWith(SVG_EXT))) { try (Stream<Path> dirStream = listDir(scadaSymbolsDir).filter(path -> path.toString().endsWith(SVG_EXT))) {
dirStream.forEach( dirStream.forEach(
path -> { path -> {
try { try {
@ -404,11 +402,10 @@ public class InstallScripts {
imagesUpdater.updateAssetProfilesImages(); imagesUpdater.updateAssetProfilesImages();
} }
@SneakyThrows
public void loadSystemImages() { public void loadSystemImages() {
log.info("Loading system images..."); log.info("Loading system images...");
Stream<Path> dashboardsFiles = Stream.concat(Files.list(Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR)), Stream<Path> dashboardsFiles = Stream.concat(listDir(Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR)),
Files.list(Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, DASHBOARDS_DIR))); listDir(Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, DASHBOARDS_DIR)));
try (dashboardsFiles) { try (dashboardsFiles) {
dashboardsFiles.forEach(file -> { dashboardsFiles.forEach(file -> {
try { try {
@ -431,25 +428,22 @@ public class InstallScripts {
loadDashboardsFromDir(tenantId, customerId, dashboardsDir); loadDashboardsFromDir(tenantId, customerId, dashboardsDir);
} }
@SneakyThrows
private void loadDashboardsFromDir(TenantId tenantId, CustomerId customerId, Path dashboardsDir) { private void loadDashboardsFromDir(TenantId tenantId, CustomerId customerId, Path dashboardsDir) {
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) { try (Stream<Path> dashboards = listDir(dashboardsDir).filter(path -> path.toString().endsWith(JSON_EXT))) {
dirStream.forEach( dashboards.forEach(path -> {
path -> { try {
try { JsonNode dashboardJson = JacksonUtil.toJsonNode(path.toFile());
JsonNode dashboardJson = JacksonUtil.toJsonNode(path.toFile()); Dashboard dashboard = JacksonUtil.treeToValue(dashboardJson, Dashboard.class);
Dashboard dashboard = JacksonUtil.treeToValue(dashboardJson, Dashboard.class); dashboard.setTenantId(tenantId);
dashboard.setTenantId(tenantId); Dashboard savedDashboard = dashboardService.saveDashboard(dashboard);
Dashboard savedDashboard = dashboardService.saveDashboard(dashboard); if (customerId != null && !customerId.isNullUid()) {
if (customerId != null && !customerId.isNullUid()) { dashboardService.assignDashboardToCustomer(TenantId.SYS_TENANT_ID, savedDashboard.getId(), customerId);
dashboardService.assignDashboardToCustomer(TenantId.SYS_TENANT_ID, savedDashboard.getId(), customerId);
}
} catch (Exception e) {
log.error("Unable to load dashboard from json: [{}]", path.toString());
throw new RuntimeException("Unable to load dashboard from json", e);
}
} }
); } catch (Exception e) {
log.error("Unable to load dashboard from json: [{}]", path.toString());
throw new RuntimeException("Unable to load dashboard from json", e);
}
});
} }
} }
@ -464,9 +458,9 @@ public class InstallScripts {
} }
} }
public void createOAuth2Templates() throws Exception { public void createOAuth2Templates() {
Path oauth2ConfigTemplatesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, OAUTH2_CONFIG_TEMPLATES_DIR); Path oauth2ConfigTemplatesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, OAUTH2_CONFIG_TEMPLATES_DIR);
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(oauth2ConfigTemplatesDir, path -> path.toString().endsWith(JSON_EXT))) { try (Stream<Path> dirStream = listDir(oauth2ConfigTemplatesDir).filter(path -> path.toString().endsWith(JSON_EXT))) {
dirStream.forEach( dirStream.forEach(
path -> { path -> {
try { try {
@ -489,7 +483,7 @@ public class InstallScripts {
public void loadSystemLwm2mResources() { public void loadSystemLwm2mResources() {
Path resourceLwm2mPath = Paths.get(getDataDir(), MODELS_LWM2M_DIR); Path resourceLwm2mPath = Paths.get(getDataDir(), MODELS_LWM2M_DIR);
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(resourceLwm2mPath, path -> path.toString().endsWith(InstallScripts.XML_EXT))) { try (Stream<Path> dirStream = listDir(resourceLwm2mPath).filter(path -> path.toString().endsWith(InstallScripts.XML_EXT))) {
dirStream.forEach( dirStream.forEach(
path -> { path -> {
try { try {
@ -539,9 +533,11 @@ public class InstallScripts {
} }
} }
private Stream<Path> listDir(Path resourcesDir) { private Stream<Path> listDir(Path dir) {
try { try {
return Files.list(resourcesDir); return Files.list(dir);
} catch (NoSuchFileException e) {
return Stream.empty();
} catch (IOException e) { } catch (IOException e) {
throw new UncheckedIOException(e); throw new UncheckedIOException(e);
} }

4
application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java

@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.notification.NotificationRequestConfig
import org.thingsboard.server.common.data.notification.NotificationRequestStats; import org.thingsboard.server.common.data.notification.NotificationRequestStats;
import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.notification.NotificationRequestStatus;
import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.notification.NotificationStatus;
import org.thingsboard.server.common.data.notification.info.GeneralNotificationInfo;
import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo;
import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings; import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings;
@ -187,7 +188,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
} }
@Override @Override
public void sendGeneralWebNotification(TenantId tenantId, UsersFilter recipients, NotificationTemplate template) { public void sendGeneralWebNotification(TenantId tenantId, UsersFilter recipients, NotificationTemplate template, GeneralNotificationInfo info) {
NotificationTarget target = new NotificationTarget(); NotificationTarget target = new NotificationTarget();
target.setTenantId(tenantId); target.setTenantId(tenantId);
PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
@ -198,6 +199,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
.tenantId(tenantId) .tenantId(tenantId)
.template(template) .template(template)
.targets(List.of(EntityId.NULL_UUID)) // this is temporary and will be removed when 'create from scratch' functionality is implemented for recipients .targets(List.of(EntityId.NULL_UUID)) // this is temporary and will be removed when 'create from scratch' functionality is implemented for recipients
.info(info)
.status(NotificationRequestStatus.PROCESSING) .status(NotificationRequestStatus.PROCESSING)
.build(); .build();
try { try {

65
application/src/main/java/org/thingsboard/server/service/update/DeprecationService.java

@ -0,0 +1,65 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.update;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.NotificationCenter;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.info.GeneralNotificationInfo;
import org.thingsboard.server.common.data.notification.targets.platform.SystemAdministratorsFilter;
import org.thingsboard.server.dao.notification.DefaultNotifications;
import org.thingsboard.server.queue.util.AfterStartUp;
import java.util.Map;
@Service
@Slf4j
@RequiredArgsConstructor
public class DeprecationService {
private final NotificationCenter notificationCenter;
@Value("${queue.type}")
private String queueType;
@AfterStartUp(order = Integer.MAX_VALUE)
public void checkDeprecation() {
checkQueueTypeDeprecation();
}
private void checkQueueTypeDeprecation() {
String queueTypeName;
switch (queueType) {
case "aws-sqs" -> queueTypeName = "AWS SQS";
case "pubsub" -> queueTypeName = "PubSub";
case "service-bus" -> queueTypeName = "Azure Service Bus";
case "rabbitmq" -> queueTypeName = "RabbitMQ";
default -> {
return;
}
}
log.warn("WARNING: {} queue type is deprecated and will be removed in ThingsBoard 4.0. Please migrate to Apache Kafka", queueTypeName);
notificationCenter.sendGeneralWebNotification(TenantId.SYS_TENANT_ID, new SystemAdministratorsFilter(),
DefaultNotifications.queueTypeDeprecation.toTemplate(), new GeneralNotificationInfo(Map.of(
"queueType", queueTypeName
)));
}
}

19
application/src/test/java/org/thingsboard/server/service/entitiy/dashboard/DashboardSyncServiceTest.java

@ -15,13 +15,18 @@
*/ */
package org.thingsboard.server.service.entitiy.dashboard; package org.thingsboard.server.service.entitiy.dashboard;
import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.controller.AbstractControllerTest;
import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.dao.sql.resource.TbResourceRepository;
import org.thingsboard.server.dao.sql.widget.WidgetTypeRepository;
import org.thingsboard.server.dao.sql.widget.WidgetsBundleRepository;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -35,6 +40,20 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
}) })
public class DashboardSyncServiceTest extends AbstractControllerTest { public class DashboardSyncServiceTest extends AbstractControllerTest {
@Autowired
private WidgetTypeRepository widgetTypeRepository;
@Autowired
private WidgetsBundleRepository widgetsBundleRepository;
@Autowired
private TbResourceRepository resourceRepository;
@After
public void after() throws Exception {
widgetsBundleRepository.deleteAll();
widgetTypeRepository.deleteAll();
resourceRepository.deleteAll();
}
@Test @Test
public void testGatewaysDashboardSync() throws Exception { public void testGatewaysDashboardSync() throws Exception {
loginTenantAdmin(); loginTenantAdmin();

5
application/src/test/java/org/thingsboard/server/service/install/InstallScriptsTest.java

@ -39,7 +39,6 @@ import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.service.install.update.ImagesUpdater; import org.thingsboard.server.service.install.update.ImagesUpdater;
import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -87,14 +86,14 @@ class InstallScriptsTest {
} }
@Test @Test
void testDefaultRuleChainsTemplates() throws IOException { void testDefaultRuleChainsTemplates() {
Path dir = installScripts.getTenantRuleChainsDir(); Path dir = installScripts.getTenantRuleChainsDir();
installScripts.findRuleChainsFromPath(dir) installScripts.findRuleChainsFromPath(dir)
.forEach(this::validateRuleChainTemplate); .forEach(this::validateRuleChainTemplate);
} }
@Test @Test
void testDefaultEdgeRuleChainsTemplates() throws IOException { void testDefaultEdgeRuleChainsTemplates() {
Path dir = installScripts.getEdgeRuleChainsDir(); Path dir = installScripts.getEdgeRuleChainsDir();
installScripts.findRuleChainsFromPath(dir) installScripts.findRuleChainsFromPath(dir)
.forEach(this::validateRuleChainTemplate); .forEach(this::validateRuleChainTemplate);

17
application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java

@ -56,6 +56,7 @@ import org.thingsboard.server.common.data.notification.NotificationRequestStatus
import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.NotificationType;
import org.thingsboard.server.common.data.notification.info.AlarmCommentNotificationInfo; import org.thingsboard.server.common.data.notification.info.AlarmCommentNotificationInfo;
import org.thingsboard.server.common.data.notification.info.EntityActionNotificationInfo; import org.thingsboard.server.common.data.notification.info.EntityActionNotificationInfo;
import org.thingsboard.server.common.data.notification.info.GeneralNotificationInfo;
import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmCommentNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmCommentNotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.settings.MobileAppNotificationDeliveryMethodConfig; import org.thingsboard.server.common.data.notification.settings.MobileAppNotificationDeliveryMethodConfig;
import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.settings.NotificationSettings;
@ -84,6 +85,7 @@ import org.thingsboard.server.common.data.notification.template.WebDeliveryMetho
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.notification.DefaultNotifications; import org.thingsboard.server.dao.notification.DefaultNotifications;
import org.thingsboard.server.dao.notification.DefaultNotifications.DefaultNotification;
import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.service.notification.channels.MicrosoftTeamsNotificationChannel; import org.thingsboard.server.service.notification.channels.MicrosoftTeamsNotificationChannel;
import org.thingsboard.server.service.notification.channels.TeamsAdaptiveCard; import org.thingsboard.server.service.notification.channels.TeamsAdaptiveCard;
@ -746,14 +748,21 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
getAnotherWsClient().registerWaitForUpdate(); getAnotherWsClient().registerWaitForUpdate();
DefaultNotifications.DefaultNotification expectedNotification = DefaultNotifications.maintenanceWork; NotificationTemplate template = DefaultNotification.builder()
.name("Test")
.subject("Testing ${subjectVariable}")
.text("Testing ${bodyVariable}")
.build().toTemplate();
notificationCenter.sendGeneralWebNotification(TenantId.SYS_TENANT_ID, new SystemAdministratorsFilter(), notificationCenter.sendGeneralWebNotification(TenantId.SYS_TENANT_ID, new SystemAdministratorsFilter(),
expectedNotification.toTemplate()); template, new GeneralNotificationInfo(Map.of(
"subjectVariable", "subject",
"bodyVariable", "body"
)));
getAnotherWsClient().waitForUpdate(true); getAnotherWsClient().waitForUpdate(true);
Notification notification = getAnotherWsClient().getLastDataUpdate().getUpdate(); Notification notification = getAnotherWsClient().getLastDataUpdate().getUpdate();
assertThat(notification.getSubject()).isEqualTo(expectedNotification.getSubject()); assertThat(notification.getSubject()).isEqualTo("Testing subject");
assertThat(notification.getText()).isEqualTo(expectedNotification.getText()); assertThat(notification.getText()).isEqualTo("Testing body");
} }
@Test @Test

4
application/src/test/resources/logback-test.xml

@ -17,9 +17,7 @@
<logger name="org.eclipse.leshan" level="INFO"/> <logger name="org.eclipse.leshan" level="INFO"/>
<logger name="org.thingsboard.server.controller.AbstractWebTest" level="INFO"/> <logger name="org.thingsboard.server.controller.AbstractWebTest" level="INFO"/>
<logger name="org.thingsboard.server.service.script" level="INFO"/> <logger name="org.thingsboard.server.service.script" level="INFO"/>
<logger name="org.thingsboard.server.service.entitiy.dashboard" level="TRACE"/>
<logger name="org.thingsboard.server.service.entitiy.widgets" level="TRACE"/>
<logger name="org.thingsboard.server.service.sync" level="TRACE"/>
<!-- mute TelemetryEdgeSqlTest that causes a lot of randomly generated errors --> <!-- mute TelemetryEdgeSqlTest that causes a lot of randomly generated errors -->
<logger name="org.thingsboard.server.service.edge.rpc.EdgeGrpcSession" level="OFF"/> <logger name="org.thingsboard.server.service.edge.rpc.EdgeGrpcSession" level="OFF"/>

36
common/data/src/main/java/org/thingsboard/server/common/data/notification/info/GeneralNotificationInfo.java

@ -0,0 +1,36 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.notification.info;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Map;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GeneralNotificationInfo implements RuleOriginatedNotificationInfo {
private Map<String, String> data;
@Override
public Map<String, String> getTemplateData() {
return data;
}
}

1
common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusAdmin.java

@ -31,6 +31,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@Slf4j @Slf4j
@Deprecated(forRemoval = true, since = "3.9") // for removal in 4.0
public class TbServiceBusAdmin implements TbQueueAdmin { public class TbServiceBusAdmin implements TbQueueAdmin {
private final String MAX_SIZE = "maxSizeInMb"; private final String MAX_SIZE = "maxSizeInMb";
private final String MESSAGE_TIME_TO_LIVE = "messageTimeToLiveInSec"; private final String MESSAGE_TIME_TO_LIVE = "messageTimeToLiveInSec";

1
common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubAdmin.java

@ -37,6 +37,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@Slf4j @Slf4j
@Deprecated(forRemoval = true, since = "3.9") // for removal in 4.0
public class TbPubSubAdmin implements TbQueueAdmin { public class TbPubSubAdmin implements TbQueueAdmin {
private static final String ACK_DEADLINE = "ackDeadlineInSec"; private static final String ACK_DEADLINE = "ackDeadlineInSec";
private static final String MESSAGE_RETENTION = "messageRetentionInSec"; private static final String MESSAGE_RETENTION = "messageRetentionInSec";

1
common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqAdmin.java

@ -27,6 +27,7 @@ import java.util.Map;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@Slf4j @Slf4j
@Deprecated(forRemoval = true, since = "3.9") // for removal in 4.0
public class TbRabbitMqAdmin implements TbQueueAdmin { public class TbRabbitMqAdmin implements TbQueueAdmin {
private final Channel channel; private final Channel channel;

1
common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsAdmin.java

@ -36,6 +36,7 @@ import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j @Slf4j
@Deprecated(forRemoval = true, since = "3.9") // for removal in 4.0
public class TbAwsSqsAdmin implements TbQueueAdmin { public class TbAwsSqsAdmin implements TbQueueAdmin {
private final Map<String, String> attributes; private final Map<String, String> attributes;

2
common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java

@ -1255,7 +1255,7 @@ public class TbUtils {
if (str == null || str.isEmpty()) { if (str == null || str.isEmpty()) {
return -1; return -1;
} }
return str.matches("-?\\d+(\\.\\d+)?") ? DEC_RADIX : -1; return str.matches("[+-]?\\d+(\\.\\d+)?") ? DEC_RADIX : -1;
} }
public static int isHexadecimal(String str) { public static int isHexadecimal(String str) {

1
common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java

@ -213,6 +213,7 @@ public class TbUtilsTest {
Assertions.assertEquals((Integer) 0, TbUtils.parseInt("0")); Assertions.assertEquals((Integer) 0, TbUtils.parseInt("0"));
Assertions.assertEquals((Integer) 0, TbUtils.parseInt("-0")); Assertions.assertEquals((Integer) 0, TbUtils.parseInt("-0"));
Assertions.assertEquals((Integer) 0, TbUtils.parseInt("+0"));
Assertions.assertEquals(java.util.Optional.of(473).get(), TbUtils.parseInt("473")); Assertions.assertEquals(java.util.Optional.of(473).get(), TbUtils.parseInt("473"));
Assertions.assertEquals(java.util.Optional.of(-255).get(), TbUtils.parseInt("-0xFF")); Assertions.assertEquals(java.util.Optional.of(-255).get(), TbUtils.parseInt("-0xFF"));
Assertions.assertThrows(NumberFormatException.class, () -> TbUtils.parseInt("-0xFF123")); Assertions.assertThrows(NumberFormatException.class, () -> TbUtils.parseInt("-0xFF123"));

12
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java

@ -1101,11 +1101,19 @@ public class DefaultTransportService extends TransportActivityManager implements
} }
private void sendToCore(TenantId tenantId, EntityId entityId, ToCoreMsg msg, UUID routingKey, TransportServiceCallback<Void> callback) { private void sendToCore(TenantId tenantId, EntityId entityId, ToCoreMsg msg, UUID routingKey, TransportServiceCallback<Void> callback) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); TopicPartitionInfo tpi;
try {
tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId);
} catch (Exception e) {
log.warn("Failed to send message to core. Tenant with ID [{}], routingKey [{}], msg [{}]. Message delivery aborted.", tenantId, routingKey, msg, e);
if (callback != null) {
callback.onError(e);
}
return;
}
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
log.trace("[{}][{}] Pushing to topic {} message {}", tenantId, entityId, tpi.getFullTopicName(), msg); log.trace("[{}][{}] Pushing to topic {} message {}", tenantId, entityId, tpi.getFullTopicName(), msg);
} }
TransportTbQueueCallback transportTbQueueCallback = callback != null ? TransportTbQueueCallback transportTbQueueCallback = callback != null ?
new TransportTbQueueCallback(callback) : null; new TransportTbQueueCallback(callback) : null;
tbCoreProducerStats.incrementTotal(); tbCoreProducerStats.incrementTotal();

8
dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java

@ -372,6 +372,14 @@ public class DefaultNotifications {
.build()) .build())
.build(); .build();
public static final DefaultNotification queueTypeDeprecation = DefaultNotification.builder()
.name("Queue type deprecation")
.type(NotificationType.GENERAL)
.subject("WARNING: ${queueType} deprecation")
.text("${queueType} queue type is deprecated and will be removed in ThingsBoard 4.0. Please migrate to Apache Kafka")
.icon("warning").color(RED_COLOR)
.build();
private final NotificationTemplateService templateService; private final NotificationTemplateService templateService;
private final NotificationRuleService ruleService; private final NotificationRuleService ruleService;

15
dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java

@ -17,15 +17,19 @@ package org.thingsboard.server.dao.resource;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.event.TransactionalEventListener; import org.springframework.transaction.event.TransactionalEventListener;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.RegexUtils; import org.thingsboard.common.util.RegexUtils;
import org.thingsboard.server.cache.resourceInfo.ResourceInfoCacheKey; import org.thingsboard.server.cache.resourceInfo.ResourceInfoCacheKey;
import org.thingsboard.server.cache.resourceInfo.ResourceInfoEvictEvent; import org.thingsboard.server.cache.resourceInfo.ResourceInfoEvictEvent;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResource;
@ -54,7 +58,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
@Service("TbResourceDaoService") @Service("TbResourceDaoService")
@Slf4j @Slf4j
@AllArgsConstructor @RequiredArgsConstructor
@Primary @Primary
public class BaseResourceService extends AbstractCachedEntityService<ResourceInfoCacheKey, TbResourceInfo, ResourceInfoEvictEvent> implements ResourceService { public class BaseResourceService extends AbstractCachedEntityService<ResourceInfoCacheKey, TbResourceInfo, ResourceInfoEvictEvent> implements ResourceService {
@ -62,6 +66,8 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
protected final TbResourceDao resourceDao; protected final TbResourceDao resourceDao;
protected final TbResourceInfoDao resourceInfoDao; protected final TbResourceInfoDao resourceInfoDao;
protected final ResourceDataValidator resourceValidator; protected final ResourceDataValidator resourceValidator;
@Autowired @Lazy
private ImageService imageService;
@Override @Override
public TbResource saveResource(TbResource resource, boolean doValidate) { public TbResource saveResource(TbResource resource, boolean doValidate) {
@ -243,6 +249,11 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
public TbResource createOrUpdateSystemResource(ResourceType resourceType, String resourceKey, String data) { public TbResource createOrUpdateSystemResource(ResourceType resourceType, String resourceKey, String data) {
if (resourceType == ResourceType.DASHBOARD) { if (resourceType == ResourceType.DASHBOARD) {
data = checkSystemResourcesUsage(data, ResourceType.JS_MODULE); data = checkSystemResourcesUsage(data, ResourceType.JS_MODULE);
Dashboard dashboard = JacksonUtil.fromString(data, Dashboard.class);
dashboard.setTenantId(TenantId.SYS_TENANT_ID);
imageService.replaceBase64WithImageUrl(dashboard);
data = JacksonUtil.toString(dashboard);
} }
TbResource resource = findResourceByTenantIdAndKey(TenantId.SYS_TENANT_ID, resourceType, resourceKey); TbResource resource = findResourceByTenantIdAndKey(TenantId.SYS_TENANT_ID, resourceType, resourceKey);

3
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NotificationCenter.java

@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequest;
import org.thingsboard.server.common.data.notification.NotificationRequestStats; import org.thingsboard.server.common.data.notification.NotificationRequestStats;
import org.thingsboard.server.common.data.notification.info.GeneralNotificationInfo;
import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter;
import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
@ -32,7 +33,7 @@ public interface NotificationCenter {
NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest, FutureCallback<NotificationRequestStats> callback); NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest, FutureCallback<NotificationRequestStats> callback);
void sendGeneralWebNotification(TenantId tenantId, UsersFilter recipients, NotificationTemplate template); void sendGeneralWebNotification(TenantId tenantId, UsersFilter recipients, NotificationTemplate template, GeneralNotificationInfo info);
void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId); void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId);

2
rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js

File diff suppressed because one or more lines are too long

67
ui-ngx/angular.json

@ -18,12 +18,20 @@
}, },
"architect": { "architect": {
"build": { "build": {
"builder": "@angular-builders/custom-webpack:browser", "builder": "@angular-builders/custom-esbuild:application",
"options": { "options": {
"outputPath": "target/generated-resources/public", "plugins": ["./esbuild/tb-esbuild-plugins.ts"],
"loader": {
".raw": "text"
},
"outputPath": {
"base": "target/generated-resources/public",
"media": ".",
"browser": ""
},
"index": "src/index.html", "index": "src/index.html",
"main": "src/main.ts", "browser": "src/main.ts",
"polyfills": "src/polyfills.ts", "polyfills": ["src/polyfills.ts"],
"tsConfig": "src/tsconfig.app.json", "tsConfig": "src/tsconfig.app.json",
"aot": true, "aot": true,
"assets": [ "assets": [
@ -122,49 +130,8 @@
"node_modules/prismjs/components/prism-typescript.min.js", "node_modules/prismjs/components/prism-typescript.min.js",
"node_modules/prismjs/plugins/line-numbers/prism-line-numbers.min.js" "node_modules/prismjs/plugins/line-numbers/prism-line-numbers.min.js"
], ],
"customWebpackConfig": {
"path": "./extra-webpack.config.js"
},
"allowedCommonJsDependencies": [ "allowedCommonJsDependencies": [
"hammerjs", "*"
"react",
"react-dom",
"reactcss",
"react-ace",
"schema-inspector",
"@flowjs/flow.js",
"@material-ui/icons/Add",
"@material-ui/icons/Clear",
"js-beautify",
"mousetrap",
"prop-types",
"react-is",
"hoist-non-react-statics",
"classnames",
"raf",
"moment-timezone",
"tinycolor2",
"json-schema-defaults",
"leaflet-providers",
"lodash",
"jquery",
"jquery.terminal",
"tooltipster",
"jstree",
"qrcode",
"wcwidth",
"leaflet-polylinedecorator",
"ace-diff",
"messageformat-parser",
"html2canvas",
"jszip",
"moment",
"ace",
"ace-builds",
"diff-match-patch",
"tv4",
"@messageformat/parser",
"sorted-btree"
] ]
}, },
"configurations": { "configurations": {
@ -187,8 +154,6 @@
"sourceMap": false, "sourceMap": false,
"namedChunks": false, "namedChunks": false,
"extractLicenses": true, "extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": false,
"budgets": [ "budgets": [
{ {
"type": "initial", "type": "initial",
@ -198,7 +163,6 @@
] ]
}, },
"development": { "development": {
"buildOptimizer": false,
"optimization": { "optimization": {
"scripts": false, "scripts": false,
"styles": { "styles": {
@ -207,7 +171,6 @@
}, },
"fonts": false "fonts": false
}, },
"vendorChunk": true,
"extractLicenses": false, "extractLicenses": false,
"sourceMap": true, "sourceMap": true,
"namedChunks": true "namedChunks": true
@ -215,7 +178,7 @@
} }
}, },
"serve": { "serve": {
"builder": "@angular-builders/custom-webpack:dev-server", "builder": "@angular-builders/custom-esbuild:dev-server",
"options": { "options": {
"buildTarget": "thingsboard:build", "buildTarget": "thingsboard:build",
"proxyConfig": "proxy.conf.js" "proxyConfig": "proxy.conf.js"
@ -231,7 +194,7 @@
"defaultConfiguration": "development" "defaultConfiguration": "development"
}, },
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular/build:extract-i18n",
"options": { "options": {
"buildTarget": "thingsboard:build" "buildTarget": "thingsboard:build"
} }

104
ui-ngx/esbuild/tb-esbuild-plugins.ts

@ -0,0 +1,104 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import type { Plugin, PluginBuild, OutputFile } from 'esbuild';
import dirTree from 'directory-tree';
import * as packageJson from '../package.json';
import { gzip } from 'node:zlib';
import * as path from 'node:path';
const defineTbVariablesPlugin: Plugin = {
name: 'tb-define-variables',
setup(build: PluginBuild) {
const options = build.initialOptions;
const langs: string[] = [];
dirTree("./src/assets/locale/", {extensions: /\.json$/}, (item) => {
/* It is expected what the name of a locale file has the following format: */
/* 'locale.constant-LANG_CODE[_REGION_CODE].json', e.g. locale.constant-es.json or locale.constant-zh_CN.json*/
langs.push(item.name.slice(item.name.lastIndexOf("-") + 1, -5));
});
options.define.TB_VERSION = JSON.stringify(packageJson.version);
options.define.SUPPORTED_LANGS = JSON.stringify(langs);
options.define.ngJitMode = 'true';
},
};
const resolveJQueryPlugin: Plugin = {
name: 'tb-resolve-jquery-plugin',
setup(build: PluginBuild) {
if (isProduction()) {
const jQueryPath = require.resolve('jquery');
build.onResolve({filter: /^(jquery|\$)$/}, () => {
return {path: jQueryPath};
})
}
}
};
const compressFileTypes = ['.js', '.css', '.html', '.svg', '.png', '.jpg', '.ttf', '.gif', '.woff', '.woff2', '.eot', '.json'];
const compressThreshold = 10240;
const compressorPlugin: Plugin = {
name: 'tb-compressor-plugin',
setup(build) {
build.onEnd(async result => {
if (!result.outputFiles || !isProduction()) return;
const outputExt = '.gz';
const gzippedFiles: OutputFile[] = [];
for (const file of result.outputFiles) {
if (!compressFileTypes.some((ext) => ext === path.extname(file.path))) continue;
if (file.contents.byteLength <= compressThreshold) continue;
const compressedContent = await gzipContent(file.contents);
const compressedFilePath = `${file.path}${outputExt}`;
gzippedFiles.push(
{
path: compressedFilePath,
hash: file.hash,
contents: new Uint8Array(compressedContent),
text: '',
}
);
}
result.outputFiles.push(...gzippedFiles);
});
},
};
async function gzipContent(content): Promise<Buffer> {
return new Promise((resolve, reject) => {
gzip(content, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
function isProduction(): boolean {
const configurationIndex = process.argv.indexOf('--configuration');
let production = false;
if (configurationIndex > -1) {
const configurationValue = process.argv[configurationIndex + 1];
production = configurationValue === 'production';
}
return production;
}
export default [defineTbVariablesPlugin, resolveJQueryPlugin, compressorPlugin];

100
ui-ngx/extra-webpack.config.js

@ -1,100 +0,0 @@
/*
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const CompressionPlugin = require("compression-webpack-plugin");
const JavaScriptOptimizerPlugin = require("@angular-devkit/build-angular/src/tools/webpack/plugins/javascript-optimizer-plugin").JavaScriptOptimizerPlugin;
const webpack = require("webpack");
const dirTree = require("directory-tree");
const ngWebpack = require('@ngtools/webpack');
const keysTransformer = require('ts-transformer-keys/transformer').default;
var langs = [];
dirTree("./src/assets/locale/", {extensions: /\.json$/}, (item) => {
/* It is expected what the name of a locale file has the following format: */
/* 'locale.constant-LANG_CODE[_REGION_CODE].json', e.g. locale.constant-es.json or locale.constant-zh_CN.json*/
langs.push(item.name.slice(item.name.lastIndexOf("-") + 1, -5));
});
module.exports = (config, options) => {
config.ignoreWarnings.push(/Usage of '~' in imports is deprecated/);
config.ignoreWarnings.push(/Did you mean "left" instead?/);
config.ignoreWarnings.push(/autoprefixer/);
config.plugins.push(
new webpack.DefinePlugin({
TB_VERSION: JSON.stringify(require("./package.json").version),
SUPPORTED_LANGS: JSON.stringify(langs),
})
);
config.plugins.push(
new webpack.ProvidePlugin(
{
$: "jquery"
}
)
);
config.plugins.push(
new CompressionPlugin({
filename: "[path][base].gz[query]",
algorithm: "gzip",
test: /\.js$|\.css$|\.html$|\.svg?.+$|\.jpg$|\.ttf?.+$|\.woff?.+$|\.eot?.+$|\.json$/,
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: false,
})
);
config.plugins.push(
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/,
})
);
config.module.rules[2].use[0].options.aot = false;
const index = config.plugins.findIndex(p => p instanceof ngWebpack.AngularWebpackPlugin);
let angularWebpackPlugin = config.plugins[index];
if (config.mode === 'production') {
const angularCompilerOptions = angularWebpackPlugin.pluginOptions;
angularCompilerOptions.emitClassMetadata = true;
angularCompilerOptions.emitNgModuleScope = true;
config.plugins.splice(index, 1);
angularWebpackPlugin = new ngWebpack.AngularWebpackPlugin(angularCompilerOptions);
config.plugins.push(angularWebpackPlugin);
const javascriptOptimizerOptions = config.optimization.minimizer[0].options;
delete javascriptOptimizerOptions.define.ngJitMode;
config.optimization.minimizer.splice(0, 1);
config.optimization.minimizer.unshift(new JavaScriptOptimizerPlugin(javascriptOptimizerOptions));
}
addTransformerToAngularWebpackPlugin(angularWebpackPlugin, keysTransformer);
return config;
};
function addTransformerToAngularWebpackPlugin(plugin, transformer) {
const originalCreateFileEmitter = plugin.createFileEmitter; // private method
plugin.createFileEmitter = function (program, transformers, getExtraDependencies, onAfterEmit) {
if (!transformers) {
transformers = {};
}
if (!transformers.before) {
transformers = { before: [] };
}
transformers.before.push(transformer(program.getProgram()));
return originalCreateFileEmitter.apply(plugin, [program, transformers, getExtraDependencies, onAfterEmit]);
};
}

40
ui-ngx/generate-types.js

@ -19,8 +19,10 @@ const path = require('path');
const typeDir = path.join('.', 'target', 'types'); const typeDir = path.join('.', 'target', 'types');
const srcDir = path.join('.', 'target', 'types', 'src'); const srcDir = path.join('.', 'target', 'types', 'src');
const stylesCss = path.join(srcDir, 'styles.css');
const moduleMapPath = path.join('src', 'app', 'modules', 'common', 'modules-map.ts'); const moduleMapPath = path.join('src', 'app', 'modules', 'common', 'modules-map.ts');
const ngcPath = path.join('.', 'node_modules', '.bin', 'ngc'); const ngcPath = path.join('.', 'node_modules', '.bin', 'ngc');
const tailwindcss = path.join('.', 'node_modules', '.bin', 'tailwindcss');
const tsconfigPath = path.join('src', 'tsconfig.app.json'); const tsconfigPath = path.join('src', 'tsconfig.app.json');
console.log(`Remove directory: ${typeDir}`); console.log(`Remove directory: ${typeDir}`);
@ -31,12 +33,28 @@ try {
} }
const cliCommand = `${ngcPath} --p ${tsconfigPath} --declaration --outDir ${srcDir}`; const cliCommand = `${ngcPath} --p ${tsconfigPath} --declaration --outDir ${srcDir}`;
console.log(cliCommand); executeCliCommand(cliCommand, 'Build types');
try {
child_process.execSync(cliCommand); fromDir(srcDir, /(\.js|\.js\.map)$/, function (filename) {
} catch (err) { try {
console.error("Build types", err); fs.rmSync(filename);
process.exit(1); } catch (err) {
console.error(`Remove file error ${filename}: ${err}`);
}
});
fs.cpSync(moduleMapPath, `${typeDir}/${moduleMapPath}`);
const generateStyleCssCommand = `${tailwindcss} -o ${stylesCss} --minify`;
executeCliCommand(generateStyleCssCommand, 'Generate styles.css');
function executeCliCommand(cliCommand, description) {
console.log(cliCommand);
try {
child_process.execSync(cliCommand);
} catch (err) {
console.error(description, err);
process.exit(1);
}
} }
function fromDir(startPath, filter, callback) { function fromDir(startPath, filter, callback) {
@ -56,13 +74,3 @@ function fromDir(startPath, filter, callback) {
} }
} }
} }
fromDir(srcDir, /(\.js|\.js\.map)$/, function (filename) {
try {
fs.rmSync(filename);
} catch (err) {
console.error(`Remove file error ${filename}: ${err}`);
}
});
fs.cpSync(moduleMapPath, `${typeDir}/${moduleMapPath}`);

31
ui-ngx/package.json

@ -5,7 +5,7 @@
"ng": "ng", "ng": "ng",
"start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open", "start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open",
"build": "ng build", "build": "ng build",
"build:prod": "node --max_old_space_size=4096 ./node_modules/@angular/cli/bin/ng build --configuration production --vendor-chunk", "build:prod": "node --max_old_space_size=4096 ./node_modules/@angular/cli/bin/ng build --configuration production",
"build:types": "node generate-types.js", "build:types": "node generate-types.js",
"build:icon-metadata": "node generate-icon-metadata.js", "build:icon-metadata": "node generate-icon-metadata.js",
"lint": "ng lint", "lint": "ng lint",
@ -103,28 +103,27 @@
"split.js": "^1.6.5", "split.js": "^1.6.5",
"systemjs": "6.15.1", "systemjs": "6.15.1",
"tinycolor2": "^1.6.0", "tinycolor2": "^1.6.0",
"tinymce": "~6.8.4", "tinymce": "~6.8.5",
"tooltipster": "^4.2.8", "tooltipster": "^4.2.8",
"ts-transformer-keys": "^0.4.4",
"tslib": "^2.7.0", "tslib": "^2.7.0",
"tv4": "^1.3.0", "tv4": "^1.3.0",
"typeface-roboto": "^1.1.13", "typeface-roboto": "^1.1.13",
"zone.js": "~0.14.10" "zone.js": "~0.14.10"
}, },
"devDependencies": { "devDependencies": {
"@angular-builders/custom-webpack": "~18.0.0", "@angular-builders/custom-esbuild": "18.0.0",
"@angular-devkit/build-angular": "18.2.7", "@angular-devkit/build-angular": "18.2.10",
"@angular-devkit/core": "18.2.7", "@angular-devkit/core": "18.2.10",
"@angular-devkit/schematics": "18.2.7", "@angular-devkit/schematics": "18.2.10",
"@angular-eslint/builder": "18.3.1", "@angular-eslint/builder": "18.3.1",
"@angular-eslint/eslint-plugin": "18.3.1", "@angular-eslint/eslint-plugin": "18.3.1",
"@angular-eslint/eslint-plugin-template": "18.3.1", "@angular-eslint/eslint-plugin-template": "18.3.1",
"@angular-eslint/schematics": "18.3.1", "@angular-eslint/schematics": "18.3.1",
"@angular-eslint/template-parser": "18.3.1", "@angular-eslint/template-parser": "18.3.1",
"@angular/cli": "18.2.7", "@angular/build": "18.2.10",
"@angular/compiler-cli": "18.2.6", "@angular/cli": "18.2.10",
"@angular/language-service": "18.2.6", "@angular/compiler-cli": "18.2.9",
"@ngtools/webpack": "18.2.7", "@angular/language-service": "18.2.9",
"@types/ace-diff": "^2.1.4", "@types/ace-diff": "^2.1.4",
"@types/canvas-gauges": "^2.1.8", "@types/canvas-gauges": "^2.1.8",
"@types/flot": "^0.0.36", "@types/flot": "^0.0.36",
@ -144,13 +143,13 @@
"@types/systemjs": "6.15.1", "@types/systemjs": "6.15.1",
"@types/tinycolor2": "^1.4.6", "@types/tinycolor2": "^1.4.6",
"@types/tooltipster": "^0.0.35", "@types/tooltipster": "^0.0.35",
"@typescript-eslint/eslint-plugin": "^8.7.0", "@typescript-eslint/eslint-plugin": "^8.11.0",
"@typescript-eslint/parser": "^8.7.0", "@typescript-eslint/parser": "^8.11.0",
"@typescript-eslint/utils": "^8.7.0", "@typescript-eslint/utils": "^8.11.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"compression-webpack-plugin": "^11.1.0", "compression-webpack-plugin": "^11.1.0",
"directory-tree": "^3.5.2", "directory-tree": "^3.5.2",
"eslint": "~8.57.1", "eslint": "~9.13.0",
"eslint-plugin-import": "latest", "eslint-plugin-import": "latest",
"eslint-plugin-jsdoc": "latest", "eslint-plugin-jsdoc": "latest",
"eslint-plugin-prefer-arrow": "latest", "eslint-plugin-prefer-arrow": "latest",
@ -169,6 +168,6 @@
"@types/react": "18.3.10", "@types/react": "18.3.10",
"rc-virtual-list": "3.5.2", "rc-virtual-list": "3.5.2",
"ace-builds": "1.36.2", "ace-builds": "1.36.2",
"tinymce": "6.8.4" "tinymce": "6.8.5"
} }
} }

43
ui-ngx/patches/@angular+build+18.2.10.patch

@ -0,0 +1,43 @@
diff --git a/node_modules/@angular/build/src/tools/angular/compilation/angular-compilation.js b/node_modules/@angular/build/src/tools/angular/compilation/angular-compilation.js
index 625c621..4fc8bd8 100755
--- a/node_modules/@angular/build/src/tools/angular/compilation/angular-compilation.js
+++ b/node_modules/@angular/build/src/tools/angular/compilation/angular-compilation.js
@@ -68,8 +68,6 @@ class AngularCompilation {
allowEmptyCodegenFiles: false,
annotationsAs: 'decorators',
enableResourceInlining: false,
- supportTestBed: false,
- supportJitMode: false,
}));
}
async diagnoseFiles(modes = DiagnosticModes.All) {
diff --git a/node_modules/@angular/build/src/tools/esbuild/angular/compiler-plugin.js b/node_modules/@angular/build/src/tools/esbuild/angular/compiler-plugin.js
index b1bb6ea..c76b4c6 100755
--- a/node_modules/@angular/build/src/tools/esbuild/angular/compiler-plugin.js
+++ b/node_modules/@angular/build/src/tools/esbuild/angular/compiler-plugin.js
@@ -79,7 +79,7 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
sourcemap: !!pluginOptions.sourcemap,
thirdPartySourcemaps: pluginOptions.thirdPartySourcemaps,
advancedOptimizations: pluginOptions.advancedOptimizations,
- jit: pluginOptions.jit,
+ jit: true, // pluginOptions.jit,
}, environment_options_1.maxWorkers, cacheStore?.createCache('jstransformer'));
// Setup defines based on the values used by the Angular compiler-cli
build.initialOptions.define ??= {};
@@ -377,12 +377,14 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
async function hasSideEffects(path) {
if (!pluginOptions.advancedOptimizations) {
return undefined;
+ } else {
+ return true;
}
- const { sideEffects } = await build.resolve(path, {
+ /*const { sideEffects } = await build.resolve(path, {
kind: 'import-statement',
resolveDir: build.initialOptions.absWorkingDir ?? '',
});
- return sideEffects;
+ return sideEffects;*/
}
},
};

14
ui-ngx/patches/jquery.terminal+2.43.1.patch

File diff suppressed because one or more lines are too long

35
ui-ngx/src/app/core/api/alias-controller.ts

@ -61,6 +61,9 @@ export class AliasController implements IAliasController {
resolvedAliases: { [aliasId: string]: AliasInfo } = {}; resolvedAliases: { [aliasId: string]: AliasInfo } = {};
resolvedAliasesObservable: { [aliasId: string]: Observable<AliasInfo> } = {}; resolvedAliasesObservable: { [aliasId: string]: Observable<AliasInfo> } = {};
resolvedDevices: { [deviceId: string]: EntityInfo } = {};
resolvedDevicesObservable: { [deviceId: string]: Observable<EntityInfo> } = {};
resolvedAliasesToStateEntities: { [aliasId: string]: StateEntityInfo } = {}; resolvedAliasesToStateEntities: { [aliasId: string]: StateEntityInfo } = {};
constructor(private utils: UtilsService, constructor(private utils: UtilsService,
@ -261,9 +264,35 @@ export class AliasController implements IAliasController {
} }
resolveSingleEntityInfoForDeviceId(deviceId: string): Observable<EntityInfo> { resolveSingleEntityInfoForDeviceId(deviceId: string): Observable<EntityInfo> {
const entityFilter = singleEntityFilterFromDeviceId(deviceId); let entityInfo = this.resolvedDevices[deviceId];
return this.entityService.findSingleEntityInfoByEntityFilter(entityFilter, if (entityInfo) {
{ignoreLoading: true, ignoreErrors: true}); return of(entityInfo);
} else if (this.resolvedDevicesObservable[deviceId]) {
return this.resolvedDevicesObservable[deviceId];
} else {
const resolvedDeviceSubject = new ReplaySubject<EntityInfo>();
this.resolvedDevicesObservable[deviceId] = resolvedDeviceSubject.asObservable();
const entityFilter = singleEntityFilterFromDeviceId(deviceId);
this.entityService.findSingleEntityInfoByEntityFilter(entityFilter,
{ignoreLoading: true, ignoreErrors: true}).subscribe(
(resolvedEntityInfo) => {
this.resolvedDevices[deviceId] = resolvedEntityInfo;
delete this.resolvedDevicesObservable[deviceId];
resolvedDeviceSubject.next(resolvedEntityInfo);
resolvedDeviceSubject.complete();
},
() => {
resolvedDeviceSubject.error(null);
delete this.resolvedDevicesObservable[deviceId];
}
);
entityInfo = this.resolvedDevices[deviceId];
if (entityInfo) {
return of(entityInfo);
} else {
return this.resolvedDevicesObservable[deviceId];
}
}
} }
resolveSingleEntityInfoForTargetDevice(targetDevice: TargetDevice): Observable<EntityInfo> { resolveSingleEntityInfoForTargetDevice(targetDevice: TargetDevice): Observable<EntityInfo> {

19
ui-ngx/src/app/core/services/utils.service.ts

@ -40,24 +40,15 @@ import { WindowMessage } from '@shared/models/window-message.model';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { customTranslationsPrefix, i18nPrefix } from '@app/shared/models/constants'; import { customTranslationsPrefix, i18nPrefix } from '@app/shared/models/constants';
import { DataKey, Datasource, DatasourceType, KeyInfo } from '@shared/models/widget.models'; import { DataKey, Datasource, DatasourceType, KeyInfo } from '@shared/models/widget.models';
import { DataKeyType } from '@app/shared/models/telemetry/telemetry.models'; import { DataKeyType, SharedTelemetrySubscriber } from '@app/shared/models/telemetry/telemetry.models';
import { import { alarmFields, alarmSeverityTranslations, alarmStatusTranslations } from '@shared/models/alarm.models';
alarmFields,
alarmSeverityTranslations,
alarmStatusTranslations
} from '@shared/models/alarm.models';
import { materialColors } from '@app/shared/models/material.models'; import { materialColors } from '@app/shared/models/material.models';
import { WidgetInfo } from '@home/models/widget-component.models'; import { WidgetInfo } from '@home/models/widget-component.models';
import jsonSchemaDefaults from 'json-schema-defaults'; import jsonSchemaDefaults from 'json-schema-defaults';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { publishReplay, refCount } from 'rxjs/operators'; import { publishReplay, refCount } from 'rxjs/operators';
import { WidgetContext } from '@app/modules/home/models/widget-component.models'; import { WidgetContext } from '@app/modules/home/models/widget-component.models';
import { import { AttributeData, LatestTelemetry, TelemetryType } from '@shared/models/telemetry/telemetry.models';
AttributeData,
LatestTelemetry,
TelemetrySubscriber,
TelemetryType
} from '@shared/models/telemetry/telemetry.models';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { DatePipe, DOCUMENT } from '@angular/common'; import { DatePipe, DOCUMENT } from '@angular/common';
import { entityTypeTranslations } from '@shared/models/entity-type.models'; import { entityTypeTranslations } from '@shared/models/entity-type.models';
@ -483,13 +474,13 @@ export class UtilsService {
if (!entityId && ctx.datasources.length > 0) { if (!entityId && ctx.datasources.length > 0) {
entityId = this.getEntityIdFromDatasource(ctx.datasources[0]); entityId = this.getEntityIdFromDatasource(ctx.datasources[0]);
} }
const subscription = TelemetrySubscriber.createEntityAttributesSubscription(ctx.telemetryWsService, entityId, type, ctx.ngZone, keys); const subscription = SharedTelemetrySubscriber.createEntityAttributesSubscription(ctx.telemetryWsService, entityId, type, ctx.ngZone, keys);
if (!ctx.telemetrySubscribers) { if (!ctx.telemetrySubscribers) {
ctx.telemetrySubscribers = []; ctx.telemetrySubscribers = [];
} }
ctx.telemetrySubscribers.push(subscription); ctx.telemetrySubscribers.push(subscription);
subscription.subscribe(); subscription.subscribe();
return subscription.attributeData$().pipe( return subscription.attributeData$.pipe(
publishReplay(1), publishReplay(1),
refCount() refCount()
); );

8
ui-ngx/src/app/core/utils.ts

@ -894,3 +894,11 @@ export const camelCase = (str: string): string => {
export const convertKeysToCamelCase = (obj: Record<string, any>): Record<string, any> => { export const convertKeysToCamelCase = (obj: Record<string, any>): Record<string, any> => {
return _.mapKeys(obj, (value, key) => _.camelCase(key)); return _.mapKeys(obj, (value, key) => _.camelCase(key));
}; };
export const unwrapModule = (module: any) : any => {
if ('default' in module && Object.keys(module).length === 1) {
return module.default;
} else {
return module;
}
};

2
ui-ngx/src/app/modules/common/modules-map.ts

@ -100,7 +100,6 @@ import * as TruncateWithTooltipDirective from '@shared/directives/truncate-with-
import * as coercion from '@shared/decorators/coercion'; import * as coercion from '@shared/decorators/coercion';
import * as enumerable from '@shared/decorators/enumerable'; import * as enumerable from '@shared/decorators/enumerable';
import * as TbInject from '@shared/decorators/tb-inject';
import * as FooterComponent from '@shared/components/footer.component'; import * as FooterComponent from '@shared/components/footer.component';
import * as LogoComponent from '@shared/components/logo.component'; import * as LogoComponent from '@shared/components/logo.component';
@ -432,7 +431,6 @@ class ModulesMap implements IModulesMap {
'@shared/decorators/coercion': coercion, '@shared/decorators/coercion': coercion,
'@shared/decorators/enumerable': enumerable, '@shared/decorators/enumerable': enumerable,
'@shared/decorators/tb-inject': TbInject,
'@shared/import-export/import-export.service': ImportExportService, '@shared/import-export/import-export.service': ImportExportService,
'@shared/import-export/import-dialog.component': ImportDialogComponent, '@shared/import-export/import-dialog.component': ImportDialogComponent,

2
ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.html

@ -173,7 +173,7 @@
<button mat-icon-button <button mat-icon-button
type="button" type="button"
matSuffix matSuffix
*ngIf="getAlarmCommentFormControl().value" *ngIf="getAlarmCommentValue()"
(click)="saveComment()"> (click)="saveComment()">
<mat-icon color="primary"> <mat-icon color="primary">
send send

12
ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.ts

@ -156,7 +156,7 @@ export class AlarmCommentComponent implements OnInit {
} }
saveComment(): void { saveComment(): void {
const commentInputValue: string = this.getAlarmCommentFormControl().value; const commentInputValue: string = this.getAlarmCommentValue();
if (commentInputValue) { if (commentInputValue) {
const comment: AlarmComment = { const comment: AlarmComment = {
alarmId: { alarmId: {
@ -174,7 +174,7 @@ export class AlarmCommentComponent implements OnInit {
} }
saveEditedComment(commentId: string): void { saveEditedComment(commentId: string): void {
const commentEditInputValue: string = this.getAlarmCommentEditFormControl().value; const commentEditInputValue: string = this.getAlarmCommentEditValue();
if (commentEditInputValue) { if (commentEditInputValue) {
const editedComment: AlarmComment = this.getAlarmCommentById(commentId); const editedComment: AlarmComment = this.getAlarmCommentById(commentId);
editedComment.comment.text = commentEditInputValue; editedComment.comment.text = commentEditInputValue;
@ -277,6 +277,14 @@ export class AlarmCommentComponent implements OnInit {
return this.alarmCommentFormGroup.get('alarmCommentEdit'); return this.alarmCommentFormGroup.get('alarmCommentEdit');
} }
getAlarmCommentValue(): string {
return this.alarmCommentFormGroup.get('alarmComment').value.trim();
}
private getAlarmCommentEditValue(): string {
return this.alarmCommentFormGroup.get('alarmCommentEdit').value.trim();
}
private clearCommentInput(): void { private clearCommentInput(): void {
this.getAlarmCommentFormControl().patchValue(''); this.getAlarmCommentFormControl().patchValue('');
} }

2
ui-ngx/src/app/modules/home/components/vc/entity-version-diff.component.ts

@ -121,7 +121,7 @@ export class EntityVersionDiffComponent extends PageComponent implements OnInit,
this.popoverComponent.updatePosition(); this.popoverComponent.updatePosition();
} }
setTimeout(() => { setTimeout(() => {
this.differ = new aceDiff.default( this.differ = new aceDiff(
{ {
element: this.diffViewerElmRef.nativeElement, element: this.diffViewerElmRef.nativeElement,
mode: 'ace/mode/json', mode: 'ace/mode/json',

20
ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.component.ts

@ -15,16 +15,13 @@
/// ///
import { MatDialogRef } from '@angular/material/dialog'; import { MatDialogRef } from '@angular/material/dialog';
import { Directive, InjectionToken } from '@angular/core'; import { Directive, inject, InjectionToken } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { CustomDialogContainerComponent } from './custom-dialog-container.component'; import { CustomDialogContainerComponent } from './custom-dialog-container.component';
import { UntypedFormBuilder, Validators } from '@angular/forms'; import { UntypedFormBuilder, Validators } from '@angular/forms';
import { TbInject } from '@shared/decorators/tb-inject';
export const CUSTOM_DIALOG_DATA = new InjectionToken<any>('ConfigDialogData'); export const CUSTOM_DIALOG_DATA = new InjectionToken<CustomDialogData>('ConfigDialogData');
export interface CustomDialogData { export interface CustomDialogData {
controller: (instance: CustomDialogComponent) => void; controller: (instance: CustomDialogComponent) => void;
@ -37,12 +34,13 @@ export class CustomDialogComponent extends PageComponent {
[key: string]: any; [key: string]: any;
constructor(@TbInject(Store) protected store: Store<AppState>, protected router = inject(Router);
@TbInject(Router) protected router: Router, public dialogRef = inject(MatDialogRef<CustomDialogContainerComponent>);
@TbInject(MatDialogRef) public dialogRef: MatDialogRef<CustomDialogContainerComponent>, public data = inject(CUSTOM_DIALOG_DATA);
@TbInject(UntypedFormBuilder) public fb: UntypedFormBuilder, public fb = inject(UntypedFormBuilder);
@TbInject(CUSTOM_DIALOG_DATA) public data: CustomDialogData) {
super(store); constructor() {
super();
// @ts-ignore // @ts-ignore
this.validators = Validators; this.validators = Validators;
this.data.controller(this); this.data.controller(this);

77
ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts

@ -15,10 +15,13 @@
/// ///
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { Directive, Injector, OnDestroy, OnInit, TemplateRef } from '@angular/core'; import { Directive, inject, Injector, OnDestroy, OnInit } from '@angular/core';
import { Store } from '@ngrx/store'; import {
import { AppState } from '@core/core.state'; IDynamicWidgetComponent,
import { IDynamicWidgetComponent, WidgetContext } from '@home/models/widget-component.models'; widgetContextToken,
widgetErrorMessagesToken,
widgetTitlePanelToken
} from '@home/models/widget-component.models';
import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { RafService } from '@core/services/raf.service'; import { RafService } from '@core/services/raf.service';
import { import {
@ -45,7 +48,6 @@ import { DatePipe } from '@angular/common';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DomSanitizer } from '@angular/platform-browser'; import { DomSanitizer } from '@angular/platform-browser';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { TbInject } from '@shared/decorators/tb-inject';
import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time-string.pipe'; import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time-string.pipe';
import { UserSettingsService } from '@core/http/user-settings.service'; import { UserSettingsService } from '@core/http/user-settings.service';
import { ImagePipe } from '@shared/pipe/image.pipe'; import { ImagePipe } from '@shared/pipe/image.pipe';
@ -64,38 +66,39 @@ export class DynamicWidgetComponent extends PageComponent implements IDynamicWid
validators = Validators; validators = Validators;
constructor(@TbInject(RafService) public raf: RafService, public raf = inject(RafService);
@TbInject(Store) protected store: Store<AppState>, public fb = inject(UntypedFormBuilder);
@TbInject(UntypedFormBuilder) public fb: UntypedFormBuilder, public readonly $injector = inject(Injector);
@TbInject(Injector) public readonly $injector: Injector, public readonly ctx = inject(widgetContextToken);
@TbInject('widgetContext') public readonly ctx: WidgetContext, public readonly errorMessages = inject(widgetErrorMessagesToken);
@TbInject('errorMessages') public readonly errorMessages: string[], public readonly widgetTitlePanel = inject(widgetTitlePanelToken);
@TbInject('widgetTitlePanel') public readonly widgetTitlePanel: TemplateRef<any>) {
super(store); constructor() {
this.ctx.$injector = $injector; super();
this.ctx.deviceService = $injector.get(DeviceService); this.ctx.$injector = this.$injector;
this.ctx.assetService = $injector.get(AssetService); this.ctx.deviceService = this.$injector.get(DeviceService);
this.ctx.entityViewService = $injector.get(EntityViewService); this.ctx.assetService = this.$injector.get(AssetService);
this.ctx.customerService = $injector.get(CustomerService); this.ctx.entityViewService = this.$injector.get(EntityViewService);
this.ctx.dashboardService = $injector.get(DashboardService); this.ctx.customerService = this.$injector.get(CustomerService);
this.ctx.userService = $injector.get(UserService); this.ctx.dashboardService = this.$injector.get(DashboardService);
this.ctx.attributeService = $injector.get(AttributeService); this.ctx.userService = this.$injector.get(UserService);
this.ctx.entityRelationService = $injector.get(EntityRelationService); this.ctx.attributeService = this.$injector.get(AttributeService);
this.ctx.entityService = $injector.get(EntityService); this.ctx.entityRelationService = this.$injector.get(EntityRelationService);
this.ctx.authService = $injector.get(AuthService); this.ctx.entityService = this.$injector.get(EntityService);
this.ctx.dialogs = $injector.get(DialogService); this.ctx.authService = this.$injector.get(AuthService);
this.ctx.customDialog = $injector.get(CustomDialogService); this.ctx.dialogs = this.$injector.get(DialogService);
this.ctx.resourceService = $injector.get(ResourceService); this.ctx.customDialog = this.$injector.get(CustomDialogService);
this.ctx.userSettingsService = $injector.get(UserSettingsService); this.ctx.resourceService = this.$injector.get(ResourceService);
this.ctx.utilsService = $injector.get(UtilsService); this.ctx.userSettingsService = this.$injector.get(UserSettingsService);
this.ctx.telemetryWsService = $injector.get(TelemetryWebsocketService); this.ctx.utilsService = this.$injector.get(UtilsService);
this.ctx.date = $injector.get(DatePipe); this.ctx.telemetryWsService = this.$injector.get(TelemetryWebsocketService);
this.ctx.imagePipe = $injector.get(ImagePipe); this.ctx.date = this.$injector.get(DatePipe);
this.ctx.milliSecondsToTimeString = $injector.get(MillisecondsToTimeStringPipe); this.ctx.imagePipe = this.$injector.get(ImagePipe);
this.ctx.translate = $injector.get(TranslateService); this.ctx.milliSecondsToTimeString = this.$injector.get(MillisecondsToTimeStringPipe);
this.ctx.http = $injector.get(HttpClient); this.ctx.translate = this.$injector.get(TranslateService);
this.ctx.sanitizer = $injector.get(DomSanitizer); this.ctx.http = this.$injector.get(HttpClient);
this.ctx.router = $injector.get(Router); this.ctx.sanitizer = this.$injector.get(DomSanitizer);
this.ctx.router = this.$injector.get(Router);
this.ctx.$scope = this; this.ctx.$scope = this;
if (this.ctx.defaultSubscription) { if (this.ctx.defaultSubscription) {

9
ui-ngx/src/app/modules/home/components/widget/lib/action/action-widget.models.ts

@ -17,8 +17,7 @@
import { import {
AttributeData, AttributeData,
AttributeScope, AttributeScope,
LatestTelemetry, LatestTelemetry, SharedTelemetrySubscriber,
TelemetrySubscriber,
TelemetryType, TelemetryType,
telemetryTypeTranslationsShort telemetryTypeTranslationsShort
} from '@shared/models/telemetry/telemetry.models'; } from '@shared/models/telemetry/telemetry.models';
@ -436,7 +435,7 @@ export class ExecuteRpcValueGetter<V> extends ValueGetter<V> {
export abstract class TelemetryValueGetter<V, S extends TelemetryValueSettings> extends ValueGetter<V> { export abstract class TelemetryValueGetter<V, S extends TelemetryValueSettings> extends ValueGetter<V> {
protected targetEntityId: EntityId; protected targetEntityId: EntityId;
private telemetrySubscriber: TelemetrySubscriber; private telemetrySubscriber: SharedTelemetrySubscriber;
protected constructor(protected ctx: WidgetContext, protected constructor(protected ctx: WidgetContext,
protected settings: GetValueSettings<V>, protected settings: GetValueSettings<V>,
@ -470,10 +469,10 @@ export abstract class TelemetryValueGetter<V, S extends TelemetryValueSettings>
private subscribeForTelemetryValue(): Observable<V> { private subscribeForTelemetryValue(): Observable<V> {
this.telemetrySubscriber = this.telemetrySubscriber =
TelemetrySubscriber.createEntityAttributesSubscription(this.ctx.telemetryWsService, this.targetEntityId, SharedTelemetrySubscriber.createEntityAttributesSubscription(this.ctx.telemetryWsService, this.targetEntityId,
this.scope(), this.ctx.ngZone, [this.getTelemetryValueSettings().key]); this.scope(), this.ctx.ngZone, [this.getTelemetryValueSettings().key]);
this.telemetrySubscriber.subscribe(); this.telemetrySubscriber.subscribe();
return this.telemetrySubscriber.attributeData$().pipe( return this.telemetrySubscriber.attributeData$.pipe(
map((data) => { map((data) => {
let value: V = null; let value: V = null;
const entry = data.find(attr => attr.key === this.getTelemetryValueSettings().key); const entry = data.find(attr => attr.key === this.getTelemetryValueSettings().key);

4
ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts

@ -24,7 +24,7 @@ import { WidgetContext } from '@home/models/widget-component.models';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import { Observable, Subject } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { MINUTE } from '@shared/models/time/time.models'; import { MINUTE } from '@shared/models/time/time.models';
import { isDefinedAndNotNull, mergeDeep } from '@core/utils'; import { isDefinedAndNotNull, mergeDeep, unwrapModule } from '@core/utils';
import { backgroundStyle, ComponentStyle, overlayStyle } from '@shared/models/widget-settings.models'; import { backgroundStyle, ComponentStyle, overlayStyle } from '@shared/models/widget-settings.models';
import { ImagePipe } from '@shared/pipe/image.pipe'; import { ImagePipe } from '@shared/pipe/image.pipe';
import { DomSanitizer } from '@angular/platform-browser'; import { DomSanitizer } from '@angular/platform-browser';
@ -152,7 +152,7 @@ export class MobileAppQrcodeWidgetComponent extends PageComponent implements OnI
private updateQRCode(link: string) { private updateQRCode(link: string) {
import('qrcode').then((QRCode) => { import('qrcode').then((QRCode) => {
QRCode.toString(link, (_err, svgElement) => { unwrapModule(QRCode).toString(link, (_err, svgElement) => {
this.qrCodeSVG = svgElement; this.qrCodeSVG = svgElement;
this.cd.markForCheck(); this.cd.markForCheck();
}) })

4
ui-ngx/src/app/modules/home/components/widget/lib/qrcode-widget.component.ts

@ -28,7 +28,7 @@ import {
isNumber, isNumber,
isObject, isObject,
parseFunction, parseFunction,
safeExecute safeExecute, unwrapModule
} from '@core/utils'; } from '@core/utils';
interface QrCodeWidgetSettings { interface QrCodeWidgetSettings {
@ -124,7 +124,7 @@ export class QrCodeWidgetComponent extends PageComponent implements OnInit, Afte
private updateCanvas() { private updateCanvas() {
if (this.viewInited) { if (this.viewInited) {
import('qrcode').then((QRCode) => { import('qrcode').then((QRCode) => {
QRCode.toCanvas(this.canvasRef.nativeElement, this.qrCodeText); unwrapModule(QRCode).toCanvas(this.canvasRef.nativeElement, this.qrCodeText);
this.canvasRef.nativeElement.style.width = 'auto'; this.canvasRef.nativeElement.style.width = 'auto';
this.canvasRef.nativeElement.style.height = 'auto'; this.canvasRef.nativeElement.style.height = 'auto';
}); });

6
ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action.models.ts

@ -19,9 +19,9 @@ import { widgetContextCompletions } from '@shared/models/ace/widget-completion.m
import { entityIdHref, entityTypeHref, serviceCompletions } from '@shared/models/ace/service-completion.models'; import { entityIdHref, entityTypeHref, serviceCompletions } from '@shared/models/ace/service-completion.models';
import { CustomActionDescriptor, WidgetAction } from '@shared/models/widget.models'; import { CustomActionDescriptor, WidgetAction } from '@shared/models/widget.models';
import { deepClone, isDefined, isUndefined } from '@core/utils'; import { deepClone, isDefined, isUndefined } from '@core/utils';
import customSampleJs from '!raw-loader!./custom-sample-js.raw'; import customSampleJs from './custom-sample-js.raw';
import customSampleCss from '!raw-loader!./custom-sample-css.raw'; import customSampleCss from './custom-sample-css.raw';
import customSampleHtml from '!raw-loader!./custom-sample-html.raw'; import customSampleHtml from './custom-sample-html.raw';
const customActionCompletions: TbEditorCompletions = { const customActionCompletions: TbEditorCompletions = {
...{ ...{

15
ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-provider-settings.component.ts

@ -29,6 +29,10 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { import {
defaultGoogleMapProviderSettings,
defaultHereMapProviderSettings,
defaultImageMapProviderSettings,
defaultOpenStreetMapProviderSettings, defaultTencentMapProviderSettings,
GoogleMapProviderSettings, GoogleMapProviderSettings,
HereMapProviderSettings, HereMapProviderSettings,
ImageMapProviderSettings, ImageMapProviderSettings,
@ -39,7 +43,6 @@ import {
TencentMapProviderSettings TencentMapProviderSettings
} from '@home/components/widget/lib/maps/map-models'; } from '@home/components/widget/lib/maps/map-models';
import { extractType } from '@core/utils'; import { extractType } from '@core/utils';
import { keys } from 'ts-transformer-keys';
import { IAliasController } from '@core/api/widget-api.models'; import { IAliasController } from '@core/api/widget-api.models';
@Component({ @Component({
@ -128,11 +131,11 @@ export class MapProviderSettingsComponent extends PageComponent implements OnIni
writeValue(value: MapProviderSettings): void { writeValue(value: MapProviderSettings): void {
this.modelValue = value; this.modelValue = value;
const provider = value?.provider; const provider = value?.provider;
const googleProviderSettings = extractType<GoogleMapProviderSettings>(value, keys<GoogleMapProviderSettings>()); const googleProviderSettings = extractType<GoogleMapProviderSettings>(value, Object.keys(defaultGoogleMapProviderSettings) as (keyof GoogleMapProviderSettings)[]);
const openstreetProviderSettings = extractType<OpenStreetMapProviderSettings>(value, keys<OpenStreetMapProviderSettings>()); const openstreetProviderSettings = extractType<OpenStreetMapProviderSettings>(value, Object.keys(defaultOpenStreetMapProviderSettings) as (keyof OpenStreetMapProviderSettings)[]);
const hereProviderSettings = extractType<HereMapProviderSettings>(value, keys<HereMapProviderSettings>()); const hereProviderSettings = extractType<HereMapProviderSettings>(value, Object.keys(defaultHereMapProviderSettings) as (keyof HereMapProviderSettings)[]);
const imageMapProviderSettings = extractType<ImageMapProviderSettings>(value, keys<ImageMapProviderSettings>()); const imageMapProviderSettings = extractType<ImageMapProviderSettings>(value, Object.keys(defaultImageMapProviderSettings) as (keyof ImageMapProviderSettings)[]);
const tencentMapProviderSettings = extractType<TencentMapProviderSettings>(value, keys<TencentMapProviderSettings>()); const tencentMapProviderSettings = extractType<TencentMapProviderSettings>(value, Object.keys(defaultTencentMapProviderSettings) as (keyof TencentMapProviderSettings)[]);
this.providerSettingsFormGroup.patchValue( this.providerSettingsFormGroup.patchValue(
{ {
provider, provider,

26
ui-ngx/src/app/modules/home/components/widget/lib/settings/map/map-settings.component.ts

@ -17,11 +17,11 @@
import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { import {
ControlValueAccessor, ControlValueAccessor,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
UntypedFormBuilder, UntypedFormBuilder,
UntypedFormControl, UntypedFormControl,
UntypedFormGroup, UntypedFormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
Validator Validator
} from '@angular/forms'; } from '@angular/forms';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
@ -31,6 +31,13 @@ import { TranslateService } from '@ngx-translate/core';
import { import {
CircleSettings, CircleSettings,
CommonMapSettings, CommonMapSettings,
defaultCircleSettings,
defaultCommonMapSettings,
defaultMapEditorSettings,
defaultMapProviderSettings,
defaultMarkerClusteringSettings,
defaultMarkersSettings,
defaultPolygonSettings,
MapEditorSettings, MapEditorSettings,
MapProviders, MapProviders,
MapProviderSettings, MapProviderSettings,
@ -41,7 +48,6 @@ import {
UnitedMapSettings UnitedMapSettings
} from '@home/components/widget/lib/maps/map-models'; } from '@home/components/widget/lib/maps/map-models';
import { extractType } from '@core/utils'; import { extractType } from '@core/utils';
import { keys } from 'ts-transformer-keys';
import { IAliasController } from '@core/api/widget-api.models'; import { IAliasController } from '@core/api/widget-api.models';
import { Widget } from '@shared/models/widget.models'; import { Widget } from '@shared/models/widget.models';
@ -140,12 +146,12 @@ export class MapSettingsComponent extends PageComponent implements OnInit, Contr
writeValue(value: UnitedMapSettings): void { writeValue(value: UnitedMapSettings): void {
this.modelValue = value; this.modelValue = value;
const mapProviderSettings = extractType<MapProviderSettings>(value, keys<MapProviderSettings>()); const mapProviderSettings = extractType<MapProviderSettings>(value, Object.keys(defaultMapProviderSettings) as (keyof MapProviderSettings)[]);
const commonMapSettings = extractType<CommonMapSettings>(value, keys<CommonMapSettings>()); const commonMapSettings = extractType<CommonMapSettings>(value, Object.keys(defaultCommonMapSettings) as (keyof CommonMapSettings)[]);
const markersSettings = extractType<MarkersSettings>(value, keys<MarkersSettings>()); const markersSettings = extractType<MarkersSettings>(value, Object.keys(defaultMarkersSettings) as (keyof MarkersSettings)[]);
const polygonSettings = extractType<PolygonSettings>(value, keys<PolygonSettings>()); const polygonSettings = extractType<PolygonSettings>(value, Object.keys(defaultPolygonSettings) as (keyof PolygonSettings)[]);
const circleSettings = extractType<CircleSettings>(value, keys<CircleSettings>()); const circleSettings = extractType<CircleSettings>(value, Object.keys(defaultCircleSettings) as (keyof CircleSettings)[]);
const mapEditorSettings = extractType<MapEditorSettings>(value, keys<MapEditorSettings>()); const mapEditorSettings = extractType<MapEditorSettings>(value, Object.keys(defaultMapEditorSettings) as (keyof MapEditorSettings)[]);
const formValue = { const formValue = {
mapProviderSettings, mapProviderSettings,
commonMapSettings, commonMapSettings,
@ -157,7 +163,7 @@ export class MapSettingsComponent extends PageComponent implements OnInit, Contr
if (this.routeMap) { if (this.routeMap) {
formValue.routeMapSettings = extractType<PolylineSettings>(value, ['strokeWeight', 'strokeOpacity']); formValue.routeMapSettings = extractType<PolylineSettings>(value, ['strokeWeight', 'strokeOpacity']);
} else { } else {
formValue.markerClusteringSettings = extractType<MarkerClusteringSettings>(value, keys<MarkerClusteringSettings>()); formValue.markerClusteringSettings = extractType<MarkerClusteringSettings>(value, Object.keys(defaultMarkerClusteringSettings) as (keyof MarkerClusteringSettings)[]);
} }
this.mapSettingsFormGroup.patchValue( formValue, {emitEvent: false} ); this.mapSettingsFormGroup.patchValue( formValue, {emitEvent: false} );
this.updateValidators(false); this.updateValidators(false);

28
ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-widget-settings.component.ts

@ -21,16 +21,22 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { import {
CircleSettings, CircleSettings,
CommonMapSettings,
defaultCircleSettings,
defaultCommonMapSettings,
defaultMapProviderSettings,
defaultMarkersSettings,
defaultPolygonSettings,
defaultTripAnimationPathSettings,
defaultTripAnimationPointSettings,
defaultTripAnimationSettings, defaultTripAnimationSettings,
MapProviderSettings, MapProviderSettings,
MarkersSettings,
PointsSettings, PointsSettings,
PolygonSettings, PolygonSettings,
PolylineSettings, PolylineSettings
TripAnimationCommonSettings,
TripAnimationMarkerSettings
} from 'src/app/modules/home/components/widget/lib/maps/map-models'; } from 'src/app/modules/home/components/widget/lib/maps/map-models';
import { extractType } from '@core/utils'; import { extractType } from '@core/utils';
import { keys } from 'ts-transformer-keys';
@Component({ @Component({
selector: 'tb-trip-animation-widget-settings', selector: 'tb-trip-animation-widget-settings',
@ -69,13 +75,13 @@ export class TripAnimationWidgetSettingsComponent extends WidgetSettingsComponen
} }
protected prepareInputSettings(settings: WidgetSettings): WidgetSettings { protected prepareInputSettings(settings: WidgetSettings): WidgetSettings {
const mapProviderSettings = extractType<MapProviderSettings>(settings, keys<MapProviderSettings>()); const mapProviderSettings = extractType<MapProviderSettings>(settings, Object.keys(defaultMapProviderSettings) as (keyof MapProviderSettings)[]);
const commonMapSettings = extractType<TripAnimationCommonSettings>(settings, keys<TripAnimationCommonSettings>()); const commonMapSettings = extractType<CommonMapSettings>(settings, Object.keys(defaultCommonMapSettings) as (keyof CommonMapSettings)[]);
const markersSettings = extractType<TripAnimationMarkerSettings>(settings, keys<TripAnimationMarkerSettings>()); const markersSettings = extractType<MarkersSettings>(settings, Object.keys(defaultMarkersSettings) as (keyof MarkersSettings)[]);
const pathSettings = extractType<PolylineSettings>(settings, keys<PolylineSettings>()); const pathSettings = extractType<PolylineSettings>(settings, Object.keys(defaultTripAnimationPathSettings) as (keyof PolylineSettings)[]);
const pointSettings = extractType<PointsSettings>(settings, keys<PointsSettings>()); const pointSettings = extractType<PointsSettings>(settings, Object.keys(defaultTripAnimationPointSettings) as (keyof PointsSettings)[]);
const polygonSettings = extractType<PolygonSettings>(settings, keys<PolygonSettings>()); const polygonSettings = extractType<PolygonSettings>(settings, Object.keys(defaultPolygonSettings) as (keyof PolygonSettings)[]);
const circleSettings = extractType<CircleSettings>(settings, keys<CircleSettings>()); const circleSettings = extractType<CircleSettings>(settings, Object.keys(defaultCircleSettings) as (keyof CircleSettings)[]);
return { return {
mapProviderSettings, mapProviderSettings,
commonMapSettings, commonMapSettings,

8
ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts

@ -129,11 +129,13 @@ export class WidgetComponentService {
w.tinycolor = tinycolor; w.tinycolor = tinycolor;
w.cssjs = cssjs; w.cssjs = cssjs;
w.moment = moment; w.moment = moment;
w.$ = $;
w.jQuery = $;
const widgetModulesTasks: Observable<any>[] = []; const widgetModulesTasks: Observable<any>[] = [];
widgetModulesTasks.push(from(import('jquery.terminal'))); widgetModulesTasks.push(from(import('jquery.terminal')).pipe(
tap((mod) => {
mod.default(window, $);
})
));
widgetModulesTasks.push(from(import('flot/src/jquery.flot.js')).pipe( widgetModulesTasks.push(from(import('flot/src/jquery.flot.js')).pipe(
mergeMap(() => { mergeMap(() => {

10
ui-ngx/src/app/modules/home/components/widget/widget.component.ts

@ -70,9 +70,9 @@ import {
IDynamicWidgetComponent, IDynamicWidgetComponent,
ShowWidgetHeaderActionFunction, ShowWidgetHeaderActionFunction,
updateEntityParams, updateEntityParams,
WidgetContext, WidgetContext, widgetContextToken, widgetErrorMessagesToken,
WidgetHeaderAction, WidgetHeaderAction,
WidgetInfo, WidgetInfo, widgetTitlePanelToken,
WidgetTypeInstance WidgetTypeInstance
} from '@home/models/widget-component.models'; } from '@home/models/widget-component.models';
import { import {
@ -743,15 +743,15 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges,
{ {
providers: [ providers: [
{ {
provide: 'widgetContext', provide: widgetContextToken,
useValue: this.widgetContext useValue: this.widgetContext
}, },
{ {
provide: 'errorMessages', provide: widgetErrorMessagesToken,
useValue: this.errorMessages useValue: this.errorMessages
}, },
{ {
provide: 'widgetTitlePanel', provide: widgetTitlePanelToken,
useValue: this.widgetTitlePanel useValue: this.widgetTitlePanel
} }
], ],

8
ui-ngx/src/app/modules/home/models/datasource/attribute-datasource.ts

@ -25,7 +25,7 @@ import {
AttributeData, AttributeData,
AttributeScope, AttributeScope,
isClientSideTelemetryType, isClientSideTelemetryType,
TelemetrySubscriber, SharedTelemetrySubscriber,
TelemetryType TelemetryType
} from '@shared/models/telemetry/telemetry.models'; } from '@shared/models/telemetry/telemetry.models';
import { AttributeService } from '@core/http/attribute.service'; import { AttributeService } from '@core/http/attribute.service';
@ -42,7 +42,7 @@ export class AttributeDatasource implements DataSource<AttributeData> {
public selection = new SelectionModel<AttributeData>(true, []); public selection = new SelectionModel<AttributeData>(true, []);
private allAttributes: Observable<Array<AttributeData>>; private allAttributes: Observable<Array<AttributeData>>;
private telemetrySubscriber: TelemetrySubscriber; private telemetrySubscriber: SharedTelemetrySubscriber;
constructor(private attributeService: AttributeService, constructor(private attributeService: AttributeService,
private telemetryWsService: TelemetryWebsocketService, private telemetryWsService: TelemetryWebsocketService,
@ -99,10 +99,10 @@ export class AttributeDatasource implements DataSource<AttributeData> {
if (!this.allAttributes) { if (!this.allAttributes) {
let attributesObservable: Observable<Array<AttributeData>>; let attributesObservable: Observable<Array<AttributeData>>;
if (isClientSideTelemetryType.get(attributesScope)) { if (isClientSideTelemetryType.get(attributesScope)) {
this.telemetrySubscriber = TelemetrySubscriber.createEntityAttributesSubscription( this.telemetrySubscriber = SharedTelemetrySubscriber.createEntityAttributesSubscription(
this.telemetryWsService, entityId, attributesScope, this.zone); this.telemetryWsService, entityId, attributesScope, this.zone);
this.telemetrySubscriber.subscribe(); this.telemetrySubscriber.subscribe();
attributesObservable = this.telemetrySubscriber.attributeData$(); attributesObservable = this.telemetrySubscriber.attributeData$;
} else { } else {
attributesObservable = this.attributeService.getEntityAttributes(entityId, attributesScope as AttributeScope); attributesObservable = this.attributeService.getEntityAttributes(entityId, attributesScope as AttributeScope);
} }

12
ui-ngx/src/app/modules/home/models/widget-component.models.ts

@ -47,7 +47,7 @@ import {
WidgetActionsApi, WidgetActionsApi,
WidgetSubscriptionApi WidgetSubscriptionApi
} from '@core/api/widget-api.models'; } from '@core/api/widget-api.models';
import { ChangeDetectorRef, Injector, NgZone, Type } from '@angular/core'; import { ChangeDetectorRef, InjectionToken, Injector, NgZone, TemplateRef, Type } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { RafService } from '@core/services/raf.service'; import { RafService } from '@core/services/raf.service';
import { WidgetTypeId } from '@shared/models/id/widget-type-id'; import { WidgetTypeId } from '@shared/models/id/widget-type-id';
@ -98,7 +98,9 @@ import * as RxJSOperators from 'rxjs/operators';
import { TbPopoverComponent } from '@shared/components/popover.component'; import { TbPopoverComponent } from '@shared/components/popover.component';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { AlarmQuery, AlarmSearchStatus, AlarmStatus } from '@app/shared/models/alarm.models'; import { AlarmQuery, AlarmSearchStatus, AlarmStatus } from '@app/shared/models/alarm.models';
import { ImagePipe, MillisecondsToTimeStringPipe, TelemetrySubscriber } from '@app/shared/public-api'; import { ImagePipe } from '@shared/pipe/image.pipe';
import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time-string.pipe';
import { SharedTelemetrySubscriber, TelemetrySubscriber } from '@shared/models/telemetry/telemetry.models';
import { UserId } from '@shared/models/id/user-id'; import { UserId } from '@shared/models/id/user-id';
import { UserSettingsService } from '@core/http/user-settings.service'; import { UserSettingsService } from '@core/http/user-settings.service';
import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models';
@ -204,7 +206,7 @@ export class WidgetContext {
userSettingsService: UserSettingsService; userSettingsService: UserSettingsService;
utilsService: UtilsService; utilsService: UtilsService;
telemetryWsService: TelemetryWebsocketService; telemetryWsService: TelemetryWebsocketService;
telemetrySubscribers?: TelemetrySubscriber[]; telemetrySubscribers?: Array<TelemetrySubscriber | SharedTelemetrySubscriber>;
date: DatePipe; date: DatePipe;
imagePipe: ImagePipe; imagePipe: ImagePipe;
milliSecondsToTimeString: MillisecondsToTimeStringPipe; milliSecondsToTimeString: MillisecondsToTimeStringPipe;
@ -536,6 +538,10 @@ export class LabelVariablePattern {
} }
} }
export const widgetContextToken = new InjectionToken<WidgetContext>('widgetContext');
export const widgetErrorMessagesToken = new InjectionToken<string[]>('errorMessages');
export const widgetTitlePanelToken = new InjectionToken<TemplateRef<any>>('widgetTitlePanel');
export interface IDynamicWidgetComponent { export interface IDynamicWidgetComponent {
readonly ctx: WidgetContext; readonly ctx: WidgetContext;
readonly errorMessages: string[]; readonly errorMessages: string[];

3
ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html

@ -230,9 +230,10 @@
<tb-icon class="tb-mat-18">mdi:microsoft-teams</tb-icon> <tb-icon class="tb-mat-18">mdi:microsoft-teams</tb-icon>
<div class="tb-form-panel-title" translate>notification.delivery-method.microsoft-teams-preview</div> <div class="tb-form-panel-title" translate>notification.delivery-method.microsoft-teams-preview</div>
</div> </div>
<div class="notification-content mini"> <div class="notification-content mini" [style.background-color]="preview.processedTemplates.MICROSOFT_TEAMS?.themeColor">
<div class="subject">{{ preview.processedTemplates.MICROSOFT_TEAMS.subject }}</div> <div class="subject">{{ preview.processedTemplates.MICROSOFT_TEAMS.subject }}</div>
{{ preview.processedTemplates.MICROSOFT_TEAMS.body }} {{ preview.processedTemplates.MICROSOFT_TEAMS.body }}
<button mat-raised-button>{{ preview.processedTemplates.MICROSOFT_TEAMS.button.text }}</button>
</div> </div>
</section> </section>
</div> </div>

2
ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.scss

@ -190,6 +190,8 @@
&.mini { &.mini {
font-size: 12px; font-size: 12px;
line-height: 1.25; line-height: 1.25;
justify-content: start;
gap: 5px;
.subject { .subject {
font-size: 14px; font-size: 14px;

2
ui-ngx/src/app/modules/home/pages/security/authentication-dialog/backup-code-auth-dialog.component.ts

@ -31,7 +31,7 @@ import { mergeMap, tap } from 'rxjs/operators';
import { ImportExportService } from '@shared/import-export/import-export.service'; import { ImportExportService } from '@shared/import-export/import-export.service';
import { deepClone } from '@core/utils'; import { deepClone } from '@core/utils';
import printTemplate from '!raw-loader!./backup-code-print-template.raw'; import printTemplate from './backup-code-print-template.raw';
@Component({ @Component({
selector: 'tb-backup-code-auth-dialog', selector: 'tb-backup-code-auth-dialog',

3
ui-ngx/src/app/modules/home/pages/security/authentication-dialog/totp-auth-dialog.component.ts

@ -28,6 +28,7 @@ import {
TwoFactorAuthProviderType TwoFactorAuthProviderType
} from '@shared/models/two-factor-auth.models'; } from '@shared/models/two-factor-auth.models';
import { MatStepper } from '@angular/material/stepper'; import { MatStepper } from '@angular/material/stepper';
import { unwrapModule } from '@core/utils';
@Component({ @Component({
selector: 'tb-totp-auth-dialog', selector: 'tb-totp-auth-dialog',
@ -56,7 +57,7 @@ export class TotpAuthDialogComponent extends DialogComponent<TotpAuthDialogCompo
this.totpAuthURL = this.authAccountConfig.authUrl; this.totpAuthURL = this.authAccountConfig.authUrl;
this.authAccountConfig.useByDefault = true; this.authAccountConfig.useByDefault = true;
import('qrcode').then((QRCode) => { import('qrcode').then((QRCode) => {
QRCode.toCanvas(this.canvasRef.nativeElement, this.totpAuthURL); unwrapModule(QRCode).toCanvas(this.canvasRef.nativeElement, this.totpAuthURL);
this.canvasRef.nativeElement.style.width = 'auto'; this.canvasRef.nativeElement.style.width = 'auto';
this.canvasRef.nativeElement.style.height = 'auto'; this.canvasRef.nativeElement.style.height = 'auto';
}); });

2
ui-ngx/src/app/shared/components/dialog/confirm-dialog.component.html

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<h2 mat-dialog-title>{{data.title}}</h2> <h2 mat-dialog-title tbTruncateWithTooltip>{{data.title}}</h2>
<div mat-dialog-content [innerHTML]="data.message | safe: 'html'"></div> <div mat-dialog-content [innerHTML]="data.message | safe: 'html'"></div>
<div mat-dialog-actions class="flex items-center justify-end"> <div mat-dialog-actions class="flex items-center justify-end">
<button mat-button color="primary" [mat-dialog-close]="false">{{data.cancel}}</button> <button mat-button color="primary" [mat-dialog-close]="false">{{data.cancel}}</button>

2
ui-ngx/src/app/shared/components/entity/entity-select.component.ts

@ -139,7 +139,7 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte
}; };
} }
this.entitySelectFormGroup.get('entityType').patchValue(this.modelValue.entityType, {emitEvent: false}); this.entitySelectFormGroup.get('entityType').patchValue(this.modelValue.entityType, {emitEvent: false});
this.entitySelectFormGroup.get('entityId').patchValue(this.modelValue.id, {emitEvent: false}); this.entitySelectFormGroup.get('entityId').patchValue(this.modelValue, {emitEvent: false});
} }
updateView(entityType: EntityType | AliasEntityType | null, entityId: string | null) { updateView(entityType: EntityType | AliasEntityType | null, entityId: string | null) {

12
ui-ngx/src/app/shared/components/html.component.ts

@ -126,7 +126,17 @@ export class HtmlComponent implements OnInit, OnDestroy, ControlValueAccessor, V
}); });
// @ts-ignore // @ts-ignore
this.htmlEditor.session.on('changeAnnotation', () => { this.htmlEditor.session.on('changeAnnotation', () => {
const annotations = this.htmlEditor.session.getAnnotations(); const annotations = this.htmlEditor.session.getAnnotations() || [];
const length = annotations.length;
let i = length;
while (i--) {
if(annotations[i].text.includes('Named entity expected')) {
annotations.splice(i, 1);
}
}
if (length > annotations.length) {
this.htmlEditor.session.setAnnotations(annotations);
}
const hasErrors = annotations.filter(annotation => annotation.type === 'error').length > 0; const hasErrors = annotations.filter(annotation => annotation.type === 'error').length > 0;
if (this.hasErrors !== hasErrors) { if (this.hasErrors !== hasErrors) {
this.hasErrors = hasErrors; this.hasErrors = hasErrors;

8
ui-ngx/src/app/shared/components/json-form/json-form.component.ts

@ -31,7 +31,7 @@ import { ControlValueAccessor, UntypedFormControl, NG_VALIDATORS, NG_VALUE_ACCES
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { deepClone, isString } from '@app/core/utils'; import { deepClone, isString, unwrapModule } from '@app/core/utils';
import { JsonFormProps } from './react/json-form.models'; import { JsonFormProps } from './react/json-form.models';
import inspector from 'schema-inspector'; import inspector from 'schema-inspector';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
@ -270,9 +270,9 @@ export class JsonFormComponent implements ControlValueAccessor, Validator, OnCha
]; ];
forkJoin(reactSchemaFormObservables).subscribe( forkJoin(reactSchemaFormObservables).subscribe(
(modules) => { (modules) => {
const react = modules[0]; const react = unwrapModule(modules[0]);
const reactDomClient = modules[2]; const reactDomClient = unwrapModule(modules[2]);
const jsonFormReact = modules[3].default; const jsonFormReact = unwrapModule(modules[3]);
this.reactRoot = reactDomClient.createRoot(this.reactRootElmRef.nativeElement); this.reactRoot = reactDomClient.createRoot(this.reactRootElmRef.nativeElement);
this.reactRoot.render(react.createElement(jsonFormReact, this.formProps)); this.reactRoot.render(react.createElement(jsonFormReact, this.formProps));
} }

7
ui-ngx/src/app/shared/components/json-form/react/json-form-ace-editor.tsx

@ -20,18 +20,21 @@ import reactCSS from 'reactcss';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models'; import { JsonFormFieldProps, JsonFormFieldState } from '@shared/components/json-form/react/json-form.models';
import { IEditorProps } from 'react-ace/src/types'; import { IEditorProps } from 'react-ace/src/types';
import { mergeMap } from 'rxjs/operators'; import { map, mergeMap } from 'rxjs/operators';
import { getAce } from '@shared/models/ace/ace.models'; import { getAce } from '@shared/models/ace/ace.models';
import { from, lastValueFrom } from 'rxjs'; import { from, lastValueFrom } from 'rxjs';
import { Observable } from 'rxjs/internal/Observable'; import { Observable } from 'rxjs/internal/Observable';
import { CircularProgress, IconButton } from '@mui/material'; import { CircularProgress, IconButton } from '@mui/material';
import { MouseEvent } from 'react'; import { MouseEvent } from 'react';
import { Help, HelpOutline } from '@mui/icons-material'; import { Help, HelpOutline } from '@mui/icons-material';
import { unwrapModule } from '@core/utils';
const ReactAce = React.lazy(() => { const ReactAce = React.lazy(() => {
return lastValueFrom(getAce().pipe( return lastValueFrom(getAce().pipe(
mergeMap(() => { mergeMap(() => {
return from(import('react-ace')); return from(import('react-ace')).pipe(
map((module) => unwrapModule(module)
));
}) })
)); ));
}); });

7
ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx

@ -51,6 +51,7 @@ import { MouseEvent, ReactNode } from 'react';
class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> { class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> {
private hasConditions: boolean; private hasConditions: boolean;
private conditionFunction: Function;
private readonly mapper: {[type: string]: any}; private readonly mapper: {[type: string]: any};
constructor(props: JsonFormProps) { constructor(props: JsonFormProps) {
@ -130,8 +131,10 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> {
} }
if (form.condition) { if (form.condition) {
this.hasConditions = true; this.hasConditions = true;
// tslint:disable-next-line:no-eval if (!this.conditionFunction) {
if (eval(form.condition) === false) { this.conditionFunction = new Function('form', 'model', 'index', `return ${form.condition};`);
}
if (this.conditionFunction(form, model, index) === false) {
return null; return null;
} }
} }

6
ui-ngx/src/app/shared/components/page.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Directive, OnDestroy } from '@angular/core'; import { Directive, inject, OnDestroy } from '@angular/core';
import { select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
@ -25,13 +25,15 @@ import { AbstractControl } from '@angular/forms';
@Directive() @Directive()
export abstract class PageComponent implements OnDestroy { export abstract class PageComponent implements OnDestroy {
protected store: Store<AppState> = inject(Store<AppState>);
isLoading$: Observable<boolean>; isLoading$: Observable<boolean>;
loadingSubscription: Subscription; loadingSubscription: Subscription;
disabledOnLoadFormControls: Array<AbstractControl> = []; disabledOnLoadFormControls: Array<AbstractControl> = [];
showMainLoadingBar = true; showMainLoadingBar = true;
protected constructor(protected store: Store<AppState>) { protected constructor(...args: unknown[]) {
this.isLoading$ = this.store.pipe(delay(0), select(selectIsLoading), share()); this.isLoading$ = this.store.pipe(delay(0), select(selectIsLoading), share());
} }

1
ui-ngx/src/app/shared/decorators/public-api.ts

@ -16,4 +16,3 @@
export * from './coercion'; export * from './coercion';
export * from './enumerable'; export * from './enumerable';
export * from './tb-inject';

23
ui-ngx/src/app/shared/decorators/tb-inject.ts

@ -1,23 +0,0 @@
///
/// Copyright © 2016-2024 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Inject, Type } from '@angular/core';
export function TbInject<T>(token: any): (target: Type<T>, key: any, paramIndex: number) => void {
return (target: Type<T>, key: any, paramIndex: number) => {
Inject(token)(target, key, paramIndex);
};
}

9
ui-ngx/src/app/shared/models/ace/ace.models.ts

@ -18,6 +18,7 @@ import { Ace } from 'ace-builds';
import { Observable } from 'rxjs/internal/Observable'; import { Observable } from 'rxjs/internal/Observable';
import { forkJoin, from, of } from 'rxjs'; import { forkJoin, from, of } from 'rxjs';
import { map, mergeMap, tap } from 'rxjs/operators'; import { map, mergeMap, tap } from 'rxjs/operators';
import { unwrapModule } from '@core/utils';
let aceDependenciesLoaded = false; let aceDependenciesLoaded = false;
let aceModule: any; let aceModule: any;
@ -67,10 +68,10 @@ export function getAce(): Observable<any> {
if (aceModule) { if (aceModule) {
return of(aceModule); return of(aceModule);
} else { } else {
return from(import('ace')).pipe( return from(import('ace-builds/src-noconflict/ace')).pipe(
mergeMap((module) => { mergeMap((module) => {
return loadAceDependencies().pipe( return loadAceDependencies().pipe(
map(() => module) map(() => unwrapModule(module))
); );
}), }),
tap((module) => { tap((module) => {
@ -86,7 +87,9 @@ export function getAceDiff(): Observable<any> {
} else { } else {
return getAce().pipe( return getAce().pipe(
mergeMap((ace) => { mergeMap((ace) => {
return from(import('ace-diff')); return from(import('ace-diff')).pipe(
map((module) => unwrapModule(module))
);
}), }),
tap((module) => { tap((module) => {
aceDiffModule = module; aceDiffModule = module;

4
ui-ngx/src/app/shared/models/beautify.models.ts

@ -17,6 +17,7 @@
import { Observable } from 'rxjs/internal/Observable'; import { Observable } from 'rxjs/internal/Observable';
import { from, of } from 'rxjs'; import { from, of } from 'rxjs';
import { map, tap } from 'rxjs/operators'; import { map, tap } from 'rxjs/operators';
import { unwrapModule } from '@core/utils';
let jsBeautifyModule: any; let jsBeautifyModule: any;
let htmlBeautifyModule: any; let htmlBeautifyModule: any;
@ -27,6 +28,7 @@ function loadJsBeautify(): Observable<any> {
return of(jsBeautifyModule); return of(jsBeautifyModule);
} else { } else {
return from(import('js-beautify/js/lib/beautify.js')).pipe( return from(import('js-beautify/js/lib/beautify.js')).pipe(
map((module) => unwrapModule(module)),
tap((module) => { tap((module) => {
jsBeautifyModule = module; jsBeautifyModule = module;
}) })
@ -39,6 +41,7 @@ function loadHtmlBeautify(): Observable<any> {
return of(htmlBeautifyModule); return of(htmlBeautifyModule);
} else { } else {
return from(import('js-beautify/js/lib/beautify-html.js')).pipe( return from(import('js-beautify/js/lib/beautify-html.js')).pipe(
map((module) => unwrapModule(module)),
tap((module) => { tap((module) => {
htmlBeautifyModule = module; htmlBeautifyModule = module;
}) })
@ -51,6 +54,7 @@ function loadCssBeautify(): Observable<any> {
return of(cssBeautifyModule); return of(cssBeautifyModule);
} else { } else {
return from(import('js-beautify/js/lib/beautify-css.js')).pipe( return from(import('js-beautify/js/lib/beautify-css.js')).pipe(
map((module) => unwrapModule(module)),
tap((module) => { tap((module) => {
cssBeautifyModule = module; cssBeautifyModule = module;
}) })

1
ui-ngx/src/app/shared/models/notification.models.ts

@ -366,6 +366,7 @@ interface SlackDeliveryMethodNotificationTemplate {
interface MicrosoftTeamsDeliveryMethodNotificationTemplate { interface MicrosoftTeamsDeliveryMethodNotificationTemplate {
subject?: string; subject?: string;
button: NotificationButtonConfig; button: NotificationButtonConfig;
themeColor?: string;
} }
interface MobileDeliveryMethodNotificationTemplate { interface MobileDeliveryMethodNotificationTemplate {

87
ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts

@ -17,7 +17,7 @@
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { AggregationType } from '../time/time.models'; import { AggregationType } from '../time/time.models';
import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs'; import { BehaviorSubject, connectable, Observable, ReplaySubject, Subscription } from 'rxjs';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { NgZone } from '@angular/core'; import { NgZone } from '@angular/core';
@ -731,6 +731,91 @@ export class NotificationsUpdate extends CmdUpdate {
} }
} }
interface SharedSubscriptionInfo {
key: string;
subscriber: TelemetrySubscriber;
subscribed: boolean;
sharedSubscribers: Set<SharedTelemetrySubscriber>;
}
export class SharedTelemetrySubscriber {
private static subscribersCache: {[key: string]: SharedSubscriptionInfo} = {};
private static createTelemetrySubscriberKey (entityId: EntityId, attributeScope: TelemetryType, keys: string[] = null): string {
let key = entityId.entityType + '_' + entityId.id + '_' + attributeScope;
if (keys) {
key += '_' + keys.sort().join('_');
}
return key;
}
private subscribed = false;
private attributeDataSubject = connectable(this.sharedSubscriptionInfo.subscriber.attributeData$(),
{ connector: () => new ReplaySubject<Array<AttributeData>>(1)});
private subscriptions = new Array<Subscription>();
public attributeData$: Observable<Array<AttributeData>> = this.attributeDataSubject; //this.attributeDataSubject.asObservable();
public static createEntityAttributesSubscription(telemetryService: TelemetryWebsocketService,
entityId: EntityId, attributeScope: TelemetryType,
zone: NgZone, keys: string[] = null): SharedTelemetrySubscriber {
const key = SharedTelemetrySubscriber.createTelemetrySubscriberKey(entityId, attributeScope, keys);
let info = SharedTelemetrySubscriber.subscribersCache[key];
if (!info) {
const subscriber = TelemetrySubscriber.createEntityAttributesSubscription(
telemetryService, entityId, attributeScope, zone, keys
);
info = {
key,
subscriber,
subscribed: false,
sharedSubscribers: new Set<SharedTelemetrySubscriber>()
};
SharedTelemetrySubscriber.subscribersCache[key] = info;
}
const sharedSubscriber = new SharedTelemetrySubscriber(info);
info.sharedSubscribers.add(sharedSubscriber);
return sharedSubscriber;
}
private constructor(private sharedSubscriptionInfo: SharedSubscriptionInfo) {
}
public subscribe() {
if (!this.subscribed) {
this.subscribed = true;
this.subscriptions.push(this.attributeDataSubject.connect());
if (!this.sharedSubscriptionInfo.subscribed) {
this.sharedSubscriptionInfo.subscriber.subscribe();
this.sharedSubscriptionInfo.subscribed = true;
}
}
}
public unsubscribe() {
if (this.subscribed) {
this.complete();
}
this.sharedSubscriptionInfo.sharedSubscribers.delete(this);
if (!this.sharedSubscriptionInfo.sharedSubscribers.size) {
if (this.sharedSubscriptionInfo.subscribed) {
this.sharedSubscriptionInfo.subscriber.unsubscribe();
this.sharedSubscriptionInfo.subscribed = false;
}
delete SharedTelemetrySubscriber.subscribersCache[this.sharedSubscriptionInfo.key];
}
}
private complete() {
this.subscriptions.forEach(subscription => subscription.unsubscribe());
this.subscriptions.length = 0;
}
}
export class TelemetrySubscriber extends WsSubscriber { export class TelemetrySubscriber extends WsSubscriber {
private dataSubject = new ReplaySubject<SubscriptionUpdate>(1); private dataSubject = new ReplaySubject<SubscriptionUpdate>(1);

24
ui-ngx/src/assets/help/en_US/widget/lib/gateway/address-filter_fn.md

@ -0,0 +1,24 @@
### Address filter field
It is used to filter the allowed IP addresses to connect to the connector.
### **Examples of IP addresses filtering**
Let’s review more examples of IP addresses filtering:
For example, we have a device that has the following IP address: 192.168.0.120:5001. Now let's look at examples of the configuration of the field to allow the connection of different variants of IP addresses:
1. Only one device with a specified IP address and port can connect:
**Address filter**: 192.168.0.120:5001
2. Allow any devices with any IP address and only port 5001:
**Address filter:** *:5001
3. Allow all devices that have the IP address 192.168.0.120 with any port:
**Address filter:** 192.168.0.120:*
4. Allow any devices:
**Address filter:** *:*

21
ui-ngx/src/assets/help/en_US/widget/lib/gateway/attribute-name-expression_fn.md

@ -0,0 +1,21 @@
### **Attribute name expression field**
The expression that is used to get the name of the requested attribute from the received data.
### **Examples of data converting**
Let’s review example of data converting:
We have a device that measures temperature and humidity. And for example, you want to send a request for a shared attribute that stores the firmware version of the device. To do this, we need to specify exactly where in the message the attribute name is located, the value of which we want to get.
In our case, let the requested attribute name is “**FirmwareVersion**”.
Accordingly, the field will contain the following value:
`[16:]`
And if the device sends a message with the following payload:
`myShrAttrRequestFirmwareVersion`
The connector, according to the configuration above, will take bytes starting from **16** to the **end** (from **0** to **16** is the “**Request expression**”) and send a request to the platform for the dispersed name of the shared attribute.

35
ui-ngx/src/assets/help/en_US/widget/lib/gateway/byte_fn.md

@ -0,0 +1,35 @@
## Byte field
The byte field is used to slice received data from the specific index.
### Examples of data converting
Let’s review more examples of data converting:
We have a device that measures temperature and humidity. Device has charasteristic that can be
read and when we receive data from her, the data combine temperature and humidity. So, data
from device have the next view:b’\x08<\x08\x00’ and in human readable format:[8, 34](first array
element is temperature and the second is humidity).
1. We want to read only temperature value
**“Bytes from”: “0”**
**“Bytes to”: “1”**
Data to platform: **8**
2. We want to read only humidity value
**“Bytes from”: “1”**
**“Bytes to”: “-1”**
Data to platform: **34**
3. We want to read all values
**“Bytes from”: “0”**
**“Bytes to”: “-1”**
Data to platform:**834**

21
ui-ngx/src/assets/help/en_US/widget/lib/gateway/request-expression_fn.md

@ -0,0 +1,21 @@
### **Request expression field**
The expression that is used to know if the request from the device is “**Attribute Request**” or not.
### **Examples of data converting**
Let’s review example of data converting:
We have a device that measures temperature and humidity. And for example, you want to send a request for a shared attribute that stores the firmware version of the device. In order for the connector to understand that the received message refers to "**Attribute request**" and not telemetry type, we must specify in the configuration what the message should begin with.
In our case, let the beginning of the message contain “**myShrAttrRequest**”.
Accordingly, the field will contain the following value:
`${[0:16]==myShrAttrRequest}`
And if the device sends a message with the following payload:
`myShrAttrRequestFirmwareVersion`
The connector will take the specified range from 0 to 16 bytes and see that this message belongs to the “**Attribute request**” type.

1
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -2836,6 +2836,7 @@
"gateways": "Gateways", "gateways": "Gateways",
"create-new-gateway": "Create a new gateway", "create-new-gateway": "Create a new gateway",
"create-new-gateway-text": "Are you sure you want create a new gateway with name: '{{gatewayName}}'?", "create-new-gateway-text": "Are you sure you want create a new gateway with name: '{{gatewayName}}'?",
"launch-command": "Launch command",
"no-gateway-found": "No gateway found.", "no-gateway-found": "No gateway found.",
"no-gateway-matching": " '{{item}}' not found." "no-gateway-matching": " '{{item}}' not found."
}, },

2137
ui-ngx/src/assets/locale/locale.constant-zh_CN.json

File diff suppressed because it is too large

5
ui-ngx/src/main.ts

@ -22,6 +22,11 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from '@app/app.module'; import { AppModule } from '@app/app.module';
import { environment } from '@env/environment'; import { environment } from '@env/environment';
import $ from 'jquery';
(window as any).jQuery = $;
(window as any).$ = $;
if (environment.production) { if (environment.production) {
enableProdMode(); enableProdMode();
} }

7
ui-ngx/src/styles.scss

@ -15,8 +15,8 @@
*/ */
/* You can add global styles to this file, and also import other style files */ /* You can add global styles to this file, and also import other style files */
@import '~typeface-roboto/index.css'; @import 'typeface-roboto/index.css';
@import '~font-awesome/css/font-awesome.min.css'; @import 'font-awesome/css/font-awesome.min.css';
@import 'theme.scss'; @import 'theme.scss';
@import './scss/constants'; @import './scss/constants';
@import './scss/animations'; @import './scss/animations';
@ -1050,6 +1050,9 @@ pre.tb-highlight {
margin-bottom: 0; margin-bottom: 0;
padding: 8px; padding: 8px;
} }
.mdc-dialog__surface {
display: block;
}
} }
} }

4
ui-ngx/src/tsconfig.app.json

@ -6,7 +6,9 @@
"react", "react-dom", "raphael", "canvas-gauges", "systemjs"] "react", "react-dom", "raphael", "canvas-gauges", "systemjs"]
}, },
"angularCompilerOptions": { "angularCompilerOptions": {
"fullTemplateTypeCheck": true "fullTemplateTypeCheck": true,
"supportTestBed": true,
"supportJitMode": true
}, },
"files": [ "files": [
"main.ts", "main.ts",

6
ui-ngx/src/typings/rawloader.typings.d.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
declare module '!raw-loader!*' { declare module '*.raw' {
const contents: string; const content: string;
export = contents; export default content;
} }

3
ui-ngx/tailwind.config.js

@ -170,7 +170,8 @@ module.exports = {
'md:!hidden', 'md:!hidden',
'gap-6', 'gap-6',
'gap-7', 'gap-7',
'gap-10' 'gap-10',
'gt-md:justify-center'
], ],
corePlugins: { corePlugins: {
preflight: false preflight: false

1
ui-ngx/tsconfig.json

@ -15,6 +15,7 @@
"module": "es2020", "module": "es2020",
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
"jsx": "react", "jsx": "react",
"resolveJsonModule": true,
"typeRoots": [ "typeRoots": [
"node_modules/@types", "node_modules/@types",
"src/typings" "src/typings"

509
ui-ngx/yarn.lock

@ -24,36 +24,34 @@
ts-node "^10.0.0" ts-node "^10.0.0"
tsconfig-paths "^4.1.0" tsconfig-paths "^4.1.0"
"@angular-builders/custom-webpack@~18.0.0": "@angular-builders/custom-esbuild@18.0.0":
version "18.0.0" version "18.0.0"
resolved "https://registry.yarnpkg.com/@angular-builders/custom-webpack/-/custom-webpack-18.0.0.tgz#b51f8b03d6fff4955f63cbe9e090e0366acc90d2" resolved "https://registry.yarnpkg.com/@angular-builders/custom-esbuild/-/custom-esbuild-18.0.0.tgz#7da11ce47cfa0bbab1e5e07a9bdefb8a6afe8ed8"
integrity sha512-XSynPSXHq5+nrh7J2snfrcbvm6YGwUGQRzr7OuO3wURJ6CHOD9C+xEAmvEUWW8c1YjEslVNG7aLtCGz7LA4ymw== integrity sha512-SdAGatJMJJjPYrdkUupdRazid3xe1sIj/CAHDdDVP094vZ9CrZs6fgcsOF17waAo6uMxrQCWmPDrTjNgR3CvMQ==
dependencies: dependencies:
"@angular-builders/common" "2.0.0" "@angular-builders/common" "2.0.0"
"@angular-devkit/architect" ">=0.1800.0 < 0.1900.0" "@angular-devkit/architect" ">=0.1800.0 < 0.1900.0"
"@angular-devkit/build-angular" "^18.0.0" "@angular-devkit/build-angular" "^18.0.0"
"@angular-devkit/core" "^18.0.0" "@angular-devkit/core" "^18.0.0"
lodash "^4.17.15"
webpack-merge "^5.7.3"
"@angular-devkit/architect@0.1802.7", "@angular-devkit/architect@>=0.1800.0 < 0.1900.0": "@angular-devkit/architect@0.1802.10", "@angular-devkit/architect@>=0.1800.0 < 0.1900.0":
version "0.1802.7" version "0.1802.10"
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1802.7.tgz#eb69e6b94473c3ca3428b71c5f31ee418724a8e5" resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1802.10.tgz#1ae877263261e33a86fb3a047c3741ab09a9c599"
integrity sha512-kpcgXnepEXcoxDTbqbGj7Hg1WJLWj1HLR3/FKmC5TbpBf1xiLxiqfkQNwz3BbE/W9JWMLdrXr3GI9O3O2gWPLg== integrity sha512-/xudcHK2s4J/GcL6qyobmGaWMHQcYLSMqCaWMT+nK6I6tu9VEAj/p3R83Tzx8B/eKi31Pz499uHw9pmqdtbafg==
dependencies: dependencies:
"@angular-devkit/core" "18.2.7" "@angular-devkit/core" "18.2.10"
rxjs "7.8.1" rxjs "7.8.1"
"@angular-devkit/build-angular@18.2.7", "@angular-devkit/build-angular@^18.0.0": "@angular-devkit/build-angular@18.2.10", "@angular-devkit/build-angular@^18.0.0":
version "18.2.7" version "18.2.10"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-18.2.7.tgz#21b72596f270e4a5a16d38224b38e46a609aad80" resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-18.2.10.tgz#7eab5b31856f8d3ce02e89771e3966328d13268f"
integrity sha512-u8PriYdgddK7k+OS/pOFPD1v4Iu5bztUJZXZVcGeXBZFFdnGFFzKmQw9mfcyGvTMJp2ABgBuuJT0YqYgNfAhzw== integrity sha512-47XgJ5fdIqlZUFWAo/XtNsh3y597DtLZWvfsnwShw6/TgyiV0rbL1Z24Rn2TCV1D/b3VhLutAIIZ/i5O5BirxQ==
dependencies: dependencies:
"@ampproject/remapping" "2.3.0" "@ampproject/remapping" "2.3.0"
"@angular-devkit/architect" "0.1802.7" "@angular-devkit/architect" "0.1802.10"
"@angular-devkit/build-webpack" "0.1802.7" "@angular-devkit/build-webpack" "0.1802.10"
"@angular-devkit/core" "18.2.7" "@angular-devkit/core" "18.2.10"
"@angular/build" "18.2.7" "@angular/build" "18.2.10"
"@babel/core" "7.25.2" "@babel/core" "7.25.2"
"@babel/generator" "7.25.0" "@babel/generator" "7.25.0"
"@babel/helper-annotate-as-pure" "7.24.7" "@babel/helper-annotate-as-pure" "7.24.7"
@ -64,7 +62,7 @@
"@babel/preset-env" "7.25.3" "@babel/preset-env" "7.25.3"
"@babel/runtime" "7.25.0" "@babel/runtime" "7.25.0"
"@discoveryjs/json-ext" "0.6.1" "@discoveryjs/json-ext" "0.6.1"
"@ngtools/webpack" "18.2.7" "@ngtools/webpack" "18.2.10"
"@vitejs/plugin-basic-ssl" "1.1.0" "@vitejs/plugin-basic-ssl" "1.1.0"
ansi-colors "4.1.3" ansi-colors "4.1.3"
autoprefixer "10.4.20" autoprefixer "10.4.20"
@ -75,7 +73,7 @@
css-loader "7.1.2" css-loader "7.1.2"
esbuild-wasm "0.23.0" esbuild-wasm "0.23.0"
fast-glob "3.3.2" fast-glob "3.3.2"
http-proxy-middleware "3.0.0" http-proxy-middleware "3.0.3"
https-proxy-agent "7.0.5" https-proxy-agent "7.0.5"
istanbul-lib-instrument "6.0.3" istanbul-lib-instrument "6.0.3"
jsonc-parser "3.3.1" jsonc-parser "3.3.1"
@ -114,18 +112,18 @@
optionalDependencies: optionalDependencies:
esbuild "0.23.0" esbuild "0.23.0"
"@angular-devkit/build-webpack@0.1802.7": "@angular-devkit/build-webpack@0.1802.10":
version "0.1802.7" version "0.1802.10"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1802.7.tgz#07c64d2763b10bedacff29795b778c81e888ca2c" resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1802.10.tgz#cf55fd2bee8a7d86b91bfeb0c8af9c22957c6727"
integrity sha512-VrtbrhZ+dht3f0GjtfRLRGRN4XHN/W+/bA9DqckdxVS6SydsrCWNHonvEPmOs4jJmGIGXIu6tUBMcWleTao2sg== integrity sha512-WRftK/RJ9rBDDmkx5IAtIpyNo0DJiMfgGUTuZNpNUaJfSfGeaSZYgC7o1++axMchID8pncmI3Hr8L8gaP94WQg==
dependencies: dependencies:
"@angular-devkit/architect" "0.1802.7" "@angular-devkit/architect" "0.1802.10"
rxjs "7.8.1" rxjs "7.8.1"
"@angular-devkit/core@18.2.7", "@angular-devkit/core@^18.0.0": "@angular-devkit/core@18.2.10", "@angular-devkit/core@^18.0.0":
version "18.2.7" version "18.2.10"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-18.2.7.tgz#f7133c9c50eb1916f8862370be975e4e2b84cca3" resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-18.2.10.tgz#7565f0c7bf0fa3318d08c4a696e890ab0b670032"
integrity sha512-1ZTi4A6tEC2bkJ/puCIdIPYhesnlCVOMSDJL/lZAd0hC6X22T4pwu0AEvue7mcP5NbXpQDiBaXOZ3MmCA8PwOA== integrity sha512-LFqiNdraBujg8e1lhuB0bkFVAoIbVbeXXwfoeROKH60OPbP8tHdgV6sFTqU7UGBKA+b+bYye70KFTG2Ys8QzKQ==
dependencies: dependencies:
ajv "8.17.1" ajv "8.17.1"
ajv-formats "3.0.1" ajv-formats "3.0.1"
@ -134,12 +132,12 @@
rxjs "7.8.1" rxjs "7.8.1"
source-map "0.7.4" source-map "0.7.4"
"@angular-devkit/schematics@18.2.7": "@angular-devkit/schematics@18.2.10":
version "18.2.7" version "18.2.10"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-18.2.7.tgz#348cb0d790720a66e8183d6876effc262571086c" resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-18.2.10.tgz#2ca7dde6f8c9062c3ef8254a12edeffdd180272e"
integrity sha512-j7198lpkOXMG+Gyfln/5aDgBZV7m4pWMzHFhkO3+w3cbCNUN1TVZW0SyJcF+CYaxANzTbuumfvpsYc/fTeAGLw== integrity sha512-EIm/yCYg3ZYPsPYJxXRX5F6PofJCbNQ5rZEuQEY09vy+ZRTqGezH0qoUP5WxlYeJrjiRLYqADI9WtVNzDyaD4w==
dependencies: dependencies:
"@angular-devkit/core" "18.2.7" "@angular-devkit/core" "18.2.10"
jsonc-parser "3.3.1" jsonc-parser "3.3.1"
magic-string "0.30.11" magic-string "0.30.11"
ora "5.4.1" ora "5.4.1"
@ -206,13 +204,13 @@
dependencies: dependencies:
tslib "^2.3.0" tslib "^2.3.0"
"@angular/build@18.2.7": "@angular/build@18.2.10":
version "18.2.7" version "18.2.10"
resolved "https://registry.yarnpkg.com/@angular/build/-/build-18.2.7.tgz#3bdb7c18e8f3bee00c0ce12a48216ce2df15f87c" resolved "https://registry.yarnpkg.com/@angular/build/-/build-18.2.10.tgz#98eb7f1ced225483693280964482e617eba3eb1b"
integrity sha512-oq6JsVxLP9/w9F2IjKroJwPB9CdlMblu2Xhfq/qQZRSUuM8Ppt1svr2FBTo1HrLIbosqukkVcSSdmKYDneo+cg== integrity sha512-YFBKvAyC5sH17yRYcx7VHCtJ4KUg7xCjCQ4Pe16kiTvW6vuYsgU6Btyti0Qgewd7XaWpTM8hk8N6hE4Z0hpflw==
dependencies: dependencies:
"@ampproject/remapping" "2.3.0" "@ampproject/remapping" "2.3.0"
"@angular-devkit/architect" "0.1802.7" "@angular-devkit/architect" "0.1802.10"
"@babel/core" "7.25.2" "@babel/core" "7.25.2"
"@babel/helper-annotate-as-pure" "7.24.7" "@babel/helper-annotate-as-pure" "7.24.7"
"@babel/helper-split-export-declaration" "7.24.7" "@babel/helper-split-export-declaration" "7.24.7"
@ -246,17 +244,17 @@
optionalDependencies: optionalDependencies:
parse5 "^7.1.2" parse5 "^7.1.2"
"@angular/cli@18.2.7": "@angular/cli@18.2.10":
version "18.2.7" version "18.2.10"
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-18.2.7.tgz#b5fe23d6d9d65d37ba580f1860c8a09c7578bc69" resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-18.2.10.tgz#29ab1344bb496122b2689ebbb862f2b44fb31848"
integrity sha512-KoWgSvhRsU05A2m6B7jw1kdpyoS+Ce5GGLW6xcnX7VF2AckW54vYd/8ZkgpzQrKfvIpVblYd4KJGizKoaLZ5jA== integrity sha512-qW/F3XVZMzzenFzbn+7FGpw8GOt9qW8UxBtYya7gUNdWlcsgGUk+ZaGC2OLbfI5gX6pchW4TOPMsDSMeaCEI2Q==
dependencies: dependencies:
"@angular-devkit/architect" "0.1802.7" "@angular-devkit/architect" "0.1802.10"
"@angular-devkit/core" "18.2.7" "@angular-devkit/core" "18.2.10"
"@angular-devkit/schematics" "18.2.7" "@angular-devkit/schematics" "18.2.10"
"@inquirer/prompts" "5.3.8" "@inquirer/prompts" "5.3.8"
"@listr2/prompt-adapter-inquirer" "2.0.15" "@listr2/prompt-adapter-inquirer" "2.0.15"
"@schematics/angular" "18.2.7" "@schematics/angular" "18.2.10"
"@yarnpkg/lockfile" "1.1.0" "@yarnpkg/lockfile" "1.1.0"
ini "4.1.3" ini "4.1.3"
jsonc-parser "3.3.1" jsonc-parser "3.3.1"
@ -276,14 +274,14 @@
dependencies: dependencies:
tslib "^2.3.0" tslib "^2.3.0"
"@angular/compiler-cli@18.2.6": "@angular/compiler-cli@18.2.9":
version "18.2.6" version "18.2.9"
resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-18.2.6.tgz#84d94852ec5088107a9ffa81908b38c51c576bd5" resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-18.2.9.tgz#637f40e0fbb210ccd8d4ff464ddb6628ffa0fc46"
integrity sha512-b5x9STfjNiNM/S0D+CnqRP9UOxPtSz1+RlCH5WdOMiW/p8j5p6dBix8YYgTe6Wg3OD7eItD2pnFQKgF/dWiopA== integrity sha512-4iMoRvyMmq/fdI/4Gob9HKjL/jvTlCjbS4kouAYHuGO9w9dmUhi1pY1z+mALtCEl9/Q8CzU2W8e5cU2xtV4nVg==
dependencies: dependencies:
"@babel/core" "7.25.2" "@babel/core" "7.25.2"
"@jridgewell/sourcemap-codec" "^1.4.14" "@jridgewell/sourcemap-codec" "^1.4.14"
chokidar "^3.0.0" chokidar "^4.0.0"
convert-source-map "^1.5.1" convert-source-map "^1.5.1"
reflect-metadata "^0.2.0" reflect-metadata "^0.2.0"
semver "^7.0.0" semver "^7.0.0"
@ -318,10 +316,10 @@
dependencies: dependencies:
tslib "^2.3.0" tslib "^2.3.0"
"@angular/language-service@18.2.6": "@angular/language-service@18.2.9":
version "18.2.6" version "18.2.9"
resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-18.2.6.tgz#c1cd36e40579b5bacaf0d666477c138577d2a6dd" resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-18.2.9.tgz#f24f5e7e53036f6c9c4968113a3ea9ea827048f7"
integrity sha512-GBvBvS2llh+/l2YhO7UO5o3GftlvQQoXnw3v0hcNoHKwcnvqXV4CCi+T2WOaZyK0iB8Is4QRbMrpJUC66HokZg== integrity sha512-vC9la5VpvfX27ept36rlc42nGxDak7YfbWtSoZUageyZJUWyIEAvW8rNNPEvoO86RLi011/HmyyIr2GSQLKvxA==
"@angular/material@18.2.6": "@angular/material@18.2.6":
version "18.2.6" version "18.2.6"
@ -1792,30 +1790,56 @@
dependencies: dependencies:
eslint-visitor-keys "^3.3.0" eslint-visitor-keys "^3.3.0"
"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": "@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.11.0":
version "4.11.1" version "4.11.1"
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.1.tgz#a547badfc719eb3e5f4b556325e542fbe9d7a18f" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.1.tgz#a547badfc719eb3e5f4b556325e542fbe9d7a18f"
integrity sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q== integrity sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==
"@eslint/eslintrc@^2.1.4": "@eslint/config-array@^0.18.0":
version "2.1.4" version "0.18.0"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.18.0.tgz#37d8fe656e0d5e3dbaea7758ea56540867fd074d"
integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== integrity sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==
dependencies:
"@eslint/object-schema" "^2.1.4"
debug "^4.3.1"
minimatch "^3.1.2"
"@eslint/core@^0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.7.0.tgz#a1bb4b6a4e742a5ff1894b7ee76fbf884ec72bd3"
integrity sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==
"@eslint/eslintrc@^3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6"
integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==
dependencies: dependencies:
ajv "^6.12.4" ajv "^6.12.4"
debug "^4.3.2" debug "^4.3.2"
espree "^9.6.0" espree "^10.0.1"
globals "^13.19.0" globals "^14.0.0"
ignore "^5.2.0" ignore "^5.2.0"
import-fresh "^3.2.1" import-fresh "^3.2.1"
js-yaml "^4.1.0" js-yaml "^4.1.0"
minimatch "^3.1.2" minimatch "^3.1.2"
strip-json-comments "^3.1.1" strip-json-comments "^3.1.1"
"@eslint/js@8.57.1": "@eslint/js@9.13.0":
version "8.57.1" version "9.13.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.13.0.tgz#c5f89bcd57eb54d5d4fa8b77693e9c28dc97e547"
integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== integrity sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==
"@eslint/object-schema@^2.1.4":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843"
integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==
"@eslint/plugin-kit@^0.2.0":
version "0.2.1"
resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.1.tgz#cd14fe2db79fa639839dfef4105e83bad1814482"
integrity sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==
dependencies:
levn "^0.4.1"
"@floating-ui/core@^1.6.0": "@floating-ui/core@^1.6.0":
version "1.6.8" version "1.6.8"
@ -1880,24 +1904,28 @@
lodash "4.17.21" lodash "4.17.21"
polyclip-ts "^0.16.5" polyclip-ts "^0.16.5"
"@humanwhocodes/config-array@^0.13.0": "@humanfs/core@^0.19.0":
version "0.13.0" version "0.19.0"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.0.tgz#08db7a8c73bb07673d9ebd925f2dad746411fcec"
integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== integrity sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==
"@humanfs/node@^0.16.5":
version "0.16.5"
resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.5.tgz#a9febb7e7ad2aff65890fdc630938f8d20aa84ba"
integrity sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==
dependencies: dependencies:
"@humanwhocodes/object-schema" "^2.0.3" "@humanfs/core" "^0.19.0"
debug "^4.3.1" "@humanwhocodes/retry" "^0.3.0"
minimatch "^3.0.5"
"@humanwhocodes/module-importer@^1.0.1": "@humanwhocodes/module-importer@^1.0.1":
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
"@humanwhocodes/object-schema@^2.0.3": "@humanwhocodes/retry@^0.3.0", "@humanwhocodes/retry@^0.3.1":
version "2.0.3" version "0.3.1"
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a"
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==
"@iconify/types@^2.0.0": "@iconify/types@^2.0.0":
version "2.0.0" version "2.0.0"
@ -2505,10 +2533,10 @@
dependencies: dependencies:
tslib "^2.0.0" tslib "^2.0.0"
"@ngtools/webpack@18.2.7": "@ngtools/webpack@18.2.10":
version "18.2.7" version "18.2.10"
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-18.2.7.tgz#2e8e7a3753ccdf49a3f350e082bdf79c7550db44" resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-18.2.10.tgz#6fd94d33a9de0b5f48f8b900a9d32e4f8c89872e"
integrity sha512-BmnFxss6zGobGyq9Mi7736golbK8RLgF+zYCQZ+4/OfMMA1jKVoELnyJqNyAx+DQn3m1qKVBjtGEL7pTNpPzOw== integrity sha512-CGYr8rdM5ntdb4kLUAhrLBPrhJQ4KBPo3KMT6qJE/S+jJJn5zHzedpuGFOCVhC1Siw+n1pOBSI8leTRJIW/eCQ==
"@ngx-translate/core@^15.0.0": "@ngx-translate/core@^15.0.0":
version "15.0.0" version "15.0.0"
@ -2528,7 +2556,7 @@
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": "@nodelib/fs.walk@^1.2.3":
version "1.2.8" version "1.2.8"
resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
@ -2825,13 +2853,13 @@
resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8"
integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==
"@schematics/angular@18.2.7": "@schematics/angular@18.2.10":
version "18.2.7" version "18.2.10"
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-18.2.7.tgz#0b6caf0491618c2e9b24506cc68fb629845b6c6c" resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-18.2.10.tgz#8ef1e67d266c334e71bac38cdef4cefab141f244"
integrity sha512-WOBzO11qstznHbC9tZXQf6/8+PqmaRI6QYcdTspqXNh9q9nNglvi43Xn4tSIpEhW8aSHea9hgWZV8sG+i/4W9Q== integrity sha512-2pDHT4aSzfs8Up4RQmHHuFd5FeuUebS1ZJwyt46MfXzRMFtzUZV/JKsIvDqyMwnkvFfLvgJyTCkl8JGw5jQObg==
dependencies: dependencies:
"@angular-devkit/core" "18.2.7" "@angular-devkit/core" "18.2.10"
"@angular-devkit/schematics" "18.2.7" "@angular-devkit/schematics" "18.2.10"
jsonc-parser "3.3.1" jsonc-parser "3.3.1"
"@sigstore/bundle@^2.3.2": "@sigstore/bundle@^2.3.2":
@ -3166,7 +3194,7 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
"@types/estree@1.0.6", "@types/estree@^1.0.5": "@types/estree@1.0.6", "@types/estree@^1.0.5", "@types/estree@^1.0.6":
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
@ -3243,7 +3271,7 @@
resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f"
integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==
"@types/http-proxy@^1.17.10", "@types/http-proxy@^1.17.8": "@types/http-proxy@^1.17.15", "@types/http-proxy@^1.17.8":
version "1.17.15" version "1.17.15"
resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.15.tgz#12118141ce9775a6499ecb4c01d02f90fc839d36" resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.15.tgz#12118141ce9775a6499ecb4c01d02f90fc839d36"
integrity sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ== integrity sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==
@ -3262,7 +3290,7 @@
resolved "https://registry.yarnpkg.com/@types/js-beautify/-/js-beautify-1.14.3.tgz#6ced76f79935e37e0d613110dea369881d93c1ff" resolved "https://registry.yarnpkg.com/@types/js-beautify/-/js-beautify-1.14.3.tgz#6ced76f79935e37e0d613110dea369881d93c1ff"
integrity sha512-FMbQHz+qd9DoGvgLHxeqqVPaNRffpIu5ZjozwV8hf9JAGpIOzuAf4wGbRSo8LNITHqGjmmVjaMggTT5P4v4IHg== integrity sha512-FMbQHz+qd9DoGvgLHxeqqVPaNRffpIu5ZjozwV8hf9JAGpIOzuAf4wGbRSo8LNITHqGjmmVjaMggTT5P4v4IHg==
"@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
version "7.0.15" version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
@ -3465,62 +3493,62 @@
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@typescript-eslint/eslint-plugin@^8.7.0": "@typescript-eslint/eslint-plugin@^8.11.0":
version "8.8.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.0.tgz#b2b02a5447cdc885950eb256b3b8a97b92031bd3" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz#c3f087d20715fa94310b30666c08b3349e0ab084"
integrity sha512-wORFWjU30B2WJ/aXBfOm1LX9v9nyt9D3jsSOxC3cCaTQGCW5k4jNpmjFv3U7p/7s4yvdjHzwtv2Sd2dOyhjS0A== integrity sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==
dependencies: dependencies:
"@eslint-community/regexpp" "^4.10.0" "@eslint-community/regexpp" "^4.10.0"
"@typescript-eslint/scope-manager" "8.8.0" "@typescript-eslint/scope-manager" "8.11.0"
"@typescript-eslint/type-utils" "8.8.0" "@typescript-eslint/type-utils" "8.11.0"
"@typescript-eslint/utils" "8.8.0" "@typescript-eslint/utils" "8.11.0"
"@typescript-eslint/visitor-keys" "8.8.0" "@typescript-eslint/visitor-keys" "8.11.0"
graphemer "^1.4.0" graphemer "^1.4.0"
ignore "^5.3.1" ignore "^5.3.1"
natural-compare "^1.4.0" natural-compare "^1.4.0"
ts-api-utils "^1.3.0" ts-api-utils "^1.3.0"
"@typescript-eslint/parser@^8.7.0": "@typescript-eslint/parser@^8.11.0":
version "8.8.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.8.0.tgz#ee4397c70230c4eee030456924c0fba480072f5e" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.11.0.tgz#2ad1481388dc1c937f50b2d138c9ca57cc6c5cce"
integrity sha512-uEFUsgR+tl8GmzmLjRqz+VrDv4eoaMqMXW7ruXfgThaAShO9JTciKpEsB+TvnfFfbg5IpujgMXVV36gOJRLtZg== integrity sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "8.8.0" "@typescript-eslint/scope-manager" "8.11.0"
"@typescript-eslint/types" "8.8.0" "@typescript-eslint/types" "8.11.0"
"@typescript-eslint/typescript-estree" "8.8.0" "@typescript-eslint/typescript-estree" "8.11.0"
"@typescript-eslint/visitor-keys" "8.8.0" "@typescript-eslint/visitor-keys" "8.11.0"
debug "^4.3.4" debug "^4.3.4"
"@typescript-eslint/scope-manager@8.8.0": "@typescript-eslint/scope-manager@8.11.0":
version "8.8.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.8.0.tgz#30b23a6ae5708bd7882e40675ef2f1b2beac741f" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz#9d399ce624118966732824878bc9a83593a30405"
integrity sha512-EL8eaGC6gx3jDd8GwEFEV091210U97J0jeEHrAYvIYosmEGet4wJ+g0SYmLu+oRiAwbSA5AVrt6DxLHfdd+bUg== integrity sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==
dependencies: dependencies:
"@typescript-eslint/types" "8.8.0" "@typescript-eslint/types" "8.11.0"
"@typescript-eslint/visitor-keys" "8.8.0" "@typescript-eslint/visitor-keys" "8.11.0"
"@typescript-eslint/type-utils@8.8.0": "@typescript-eslint/type-utils@8.11.0":
version "8.8.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.8.0.tgz#a0ca1c8a90d94b101176a169d7a0958187408d33" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz#b7f9e6120c1ddee8a1a07615646642ad85fc91b5"
integrity sha512-IKwJSS7bCqyCeG4NVGxnOP6lLT9Okc3Zj8hLO96bpMkJab+10HIfJbMouLrlpyOr3yrQ1cA413YPFiGd1mW9/Q== integrity sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==
dependencies: dependencies:
"@typescript-eslint/typescript-estree" "8.8.0" "@typescript-eslint/typescript-estree" "8.11.0"
"@typescript-eslint/utils" "8.8.0" "@typescript-eslint/utils" "8.11.0"
debug "^4.3.4" debug "^4.3.4"
ts-api-utils "^1.3.0" ts-api-utils "^1.3.0"
"@typescript-eslint/types@8.8.0": "@typescript-eslint/types@8.11.0":
version "8.8.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.8.0.tgz#08ea5df6c01984d456056434641491fbf7a1bf43" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.11.0.tgz#7c766250502097f49bbc2e651132e6bf489e20b8"
integrity sha512-QJwc50hRCgBd/k12sTykOJbESe1RrzmX6COk8Y525C9l7oweZ+1lw9JiU56im7Amm8swlz00DRIlxMYLizr2Vw== integrity sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==
"@typescript-eslint/typescript-estree@8.8.0": "@typescript-eslint/typescript-estree@8.11.0":
version "8.8.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.0.tgz#072eaab97fdb63513fabfe1cf271812affe779e3" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz#35fe5d3636fc5727c52429393415412e552e222b"
integrity sha512-ZaMJwc/0ckLz5DaAZ+pNLmHv8AMVGtfWxZe/x2JVEkD5LnmhWiQMMcYT7IY7gkdJuzJ9P14fRy28lUrlDSWYdw== integrity sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==
dependencies: dependencies:
"@typescript-eslint/types" "8.8.0" "@typescript-eslint/types" "8.11.0"
"@typescript-eslint/visitor-keys" "8.8.0" "@typescript-eslint/visitor-keys" "8.11.0"
debug "^4.3.4" debug "^4.3.4"
fast-glob "^3.3.2" fast-glob "^3.3.2"
is-glob "^4.0.3" is-glob "^4.0.3"
@ -3528,29 +3556,24 @@
semver "^7.6.0" semver "^7.6.0"
ts-api-utils "^1.3.0" ts-api-utils "^1.3.0"
"@typescript-eslint/utils@8.8.0", "@typescript-eslint/utils@^8.7.0": "@typescript-eslint/utils@8.11.0", "@typescript-eslint/utils@^8.11.0":
version "8.8.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.8.0.tgz#bd8607e3a68c461b69169c7a5824637dc9e8b3f1" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.11.0.tgz#4480d1e9f2bb18ea3510c79f870a1aefc118103d"
integrity sha512-QE2MgfOTem00qrlPgyByaCHay9yb1+9BjnMFnSFkUKQfu7adBXDTnCAivURnuPPAG/qiB+kzKkZKmKfaMT0zVg== integrity sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==
dependencies: dependencies:
"@eslint-community/eslint-utils" "^4.4.0" "@eslint-community/eslint-utils" "^4.4.0"
"@typescript-eslint/scope-manager" "8.8.0" "@typescript-eslint/scope-manager" "8.11.0"
"@typescript-eslint/types" "8.8.0" "@typescript-eslint/types" "8.11.0"
"@typescript-eslint/typescript-estree" "8.8.0" "@typescript-eslint/typescript-estree" "8.11.0"
"@typescript-eslint/visitor-keys@8.8.0": "@typescript-eslint/visitor-keys@8.11.0":
version "8.8.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.0.tgz#f93965abd38c82a1a1f5574290a50d02daf1cd2e" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz#273de1cbffe63d9f9cd7dfc20b5a5af66310cb92"
integrity sha512-8mq51Lx6Hpmd7HnA2fcHQo3YgfX1qbccxQOgZcb4tvasu//zXRaA1j5ZRFeCw/VRAdFi4mRM9DnZw0Nu0Q2d1g== integrity sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==
dependencies: dependencies:
"@typescript-eslint/types" "8.8.0" "@typescript-eslint/types" "8.11.0"
eslint-visitor-keys "^3.4.3" eslint-visitor-keys "^3.4.3"
"@ungap/structured-clone@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
"@vitejs/plugin-basic-ssl@1.1.0": "@vitejs/plugin-basic-ssl@1.1.0":
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz#8b840305a6b48e8764803435ec0c716fa27d3802" resolved "https://registry.yarnpkg.com/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz#8b840305a6b48e8764803435ec0c716fa27d3802"
@ -3734,7 +3757,7 @@ acorn-walk@^8.1.1:
dependencies: dependencies:
acorn "^8.11.0" acorn "^8.11.0"
acorn@^8.11.0, acorn@^8.11.3, acorn@^8.12.0, acorn@^8.4.1, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: acorn@^8.11.0, acorn@^8.11.3, acorn@^8.12.0, acorn@^8.4.1, acorn@^8.7.1, acorn@^8.8.2:
version "8.12.1" version "8.12.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
@ -4329,7 +4352,7 @@ chevrotain@~11.0.3:
"@chevrotain/utils" "11.0.3" "@chevrotain/utils" "11.0.3"
lodash-es "4.17.21" lodash-es "4.17.21"
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.0.0, chokidar@^3.5.3, chokidar@^3.6.0: "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3, chokidar@^3.6.0:
version "3.6.0" version "3.6.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
@ -4344,6 +4367,13 @@ chevrotain@~11.0.3:
optionalDependencies: optionalDependencies:
fsevents "~2.3.2" fsevents "~2.3.2"
chokidar@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41"
integrity sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==
dependencies:
readdirp "^4.0.1"
chownr@^2.0.0: chownr@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
@ -5305,13 +5335,6 @@ doctrine@^2.1.0:
dependencies: dependencies:
esutils "^2.0.2" esutils "^2.0.2"
doctrine@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
dependencies:
esutils "^2.0.2"
dom-helpers@^5.0.1: dom-helpers@^5.0.1:
version "5.2.1" version "5.2.1"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
@ -5735,15 +5758,7 @@ eslint-scope@5.1.1:
esrecurse "^4.3.0" esrecurse "^4.3.0"
estraverse "^4.1.1" estraverse "^4.1.1"
eslint-scope@^7.2.2: eslint-scope@^8.0.2, eslint-scope@^8.1.0:
version "7.2.2"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f"
integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==
dependencies:
esrecurse "^4.3.0"
estraverse "^5.2.0"
eslint-scope@^8.0.2:
version "8.1.0" version "8.1.0"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.1.0.tgz#70214a174d4cbffbc3e8a26911d8bf51b9ae9d30" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.1.0.tgz#70214a174d4cbffbc3e8a26911d8bf51b9ae9d30"
integrity sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw== integrity sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==
@ -5751,7 +5766,7 @@ eslint-scope@^8.0.2:
esrecurse "^4.3.0" esrecurse "^4.3.0"
estraverse "^5.2.0" estraverse "^5.2.0"
eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3:
version "3.4.3" version "3.4.3"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
@ -5761,51 +5776,48 @@ eslint-visitor-keys@^4.1.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz#1f785cc5e81eb7534523d85922248232077d2f8c" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz#1f785cc5e81eb7534523d85922248232077d2f8c"
integrity sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg== integrity sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==
eslint@~8.57.1: eslint@~9.13.0:
version "8.57.1" version "9.13.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.13.0.tgz#7659014b7dda1ff876ecbd990f726e11c61596e6"
integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== integrity sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==
dependencies: dependencies:
"@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/eslint-utils" "^4.2.0"
"@eslint-community/regexpp" "^4.6.1" "@eslint-community/regexpp" "^4.11.0"
"@eslint/eslintrc" "^2.1.4" "@eslint/config-array" "^0.18.0"
"@eslint/js" "8.57.1" "@eslint/core" "^0.7.0"
"@humanwhocodes/config-array" "^0.13.0" "@eslint/eslintrc" "^3.1.0"
"@eslint/js" "9.13.0"
"@eslint/plugin-kit" "^0.2.0"
"@humanfs/node" "^0.16.5"
"@humanwhocodes/module-importer" "^1.0.1" "@humanwhocodes/module-importer" "^1.0.1"
"@nodelib/fs.walk" "^1.2.8" "@humanwhocodes/retry" "^0.3.1"
"@ungap/structured-clone" "^1.2.0" "@types/estree" "^1.0.6"
"@types/json-schema" "^7.0.15"
ajv "^6.12.4" ajv "^6.12.4"
chalk "^4.0.0" chalk "^4.0.0"
cross-spawn "^7.0.2" cross-spawn "^7.0.2"
debug "^4.3.2" debug "^4.3.2"
doctrine "^3.0.0"
escape-string-regexp "^4.0.0" escape-string-regexp "^4.0.0"
eslint-scope "^7.2.2" eslint-scope "^8.1.0"
eslint-visitor-keys "^3.4.3" eslint-visitor-keys "^4.1.0"
espree "^9.6.1" espree "^10.2.0"
esquery "^1.4.2" esquery "^1.5.0"
esutils "^2.0.2" esutils "^2.0.2"
fast-deep-equal "^3.1.3" fast-deep-equal "^3.1.3"
file-entry-cache "^6.0.1" file-entry-cache "^8.0.0"
find-up "^5.0.0" find-up "^5.0.0"
glob-parent "^6.0.2" glob-parent "^6.0.2"
globals "^13.19.0"
graphemer "^1.4.0"
ignore "^5.2.0" ignore "^5.2.0"
imurmurhash "^0.1.4" imurmurhash "^0.1.4"
is-glob "^4.0.0" is-glob "^4.0.0"
is-path-inside "^3.0.3"
js-yaml "^4.1.0"
json-stable-stringify-without-jsonify "^1.0.1" json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.4.1"
lodash.merge "^4.6.2" lodash.merge "^4.6.2"
minimatch "^3.1.2" minimatch "^3.1.2"
natural-compare "^1.4.0" natural-compare "^1.4.0"
optionator "^0.9.3" optionator "^0.9.3"
strip-ansi "^6.0.1"
text-table "^0.2.0" text-table "^0.2.0"
espree@^10.1.0: espree@^10.0.1, espree@^10.1.0, espree@^10.2.0:
version "10.2.0" version "10.2.0"
resolved "https://registry.yarnpkg.com/espree/-/espree-10.2.0.tgz#f4bcead9e05b0615c968e85f83816bc386a45df6" resolved "https://registry.yarnpkg.com/espree/-/espree-10.2.0.tgz#f4bcead9e05b0615c968e85f83816bc386a45df6"
integrity sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g== integrity sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==
@ -5814,16 +5826,7 @@ espree@^10.1.0:
acorn-jsx "^5.3.2" acorn-jsx "^5.3.2"
eslint-visitor-keys "^4.1.0" eslint-visitor-keys "^4.1.0"
espree@^9.6.0, espree@^9.6.1: esquery@^1.5.0, esquery@^1.6.0:
version "9.6.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
dependencies:
acorn "^8.9.0"
acorn-jsx "^5.3.2"
eslint-visitor-keys "^3.4.1"
esquery@^1.4.2, esquery@^1.6.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7"
integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==
@ -5996,12 +5999,12 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4:
node-domexception "^1.0.0" node-domexception "^1.0.0"
web-streams-polyfill "^3.0.3" web-streams-polyfill "^3.0.3"
file-entry-cache@^6.0.1: file-entry-cache@^8.0.0:
version "6.0.1" version "8.0.0"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f"
integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==
dependencies: dependencies:
flat-cache "^3.0.4" flat-cache "^4.0.0"
file-selector@^0.6.0: file-selector@^0.6.0:
version "0.6.0" version "0.6.0"
@ -6081,14 +6084,13 @@ find-yarn-workspace-root@^2.0.0:
dependencies: dependencies:
micromatch "^4.0.2" micromatch "^4.0.2"
flat-cache@^3.0.4: flat-cache@^4.0.0:
version "3.2.0" version "4.0.1"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c"
integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==
dependencies: dependencies:
flatted "^3.2.9" flatted "^3.2.9"
keyv "^4.5.3" keyv "^4.5.4"
rimraf "^3.0.2"
flat@^5.0.2: flat@^5.0.2:
version "5.0.2" version "5.0.2"
@ -6317,12 +6319,10 @@ globals@^11.1.0:
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
globals@^13.19.0: globals@^14.0.0:
version "13.24.0" version "14.0.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e"
integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
dependencies:
type-fest "^0.20.2"
globalthis@^1.0.3: globalthis@^1.0.3:
version "1.0.4" version "1.0.4"
@ -6520,17 +6520,17 @@ http-proxy-agent@^7.0.0:
agent-base "^7.1.0" agent-base "^7.1.0"
debug "^4.3.4" debug "^4.3.4"
http-proxy-middleware@3.0.0: http-proxy-middleware@3.0.3:
version "3.0.0" version "3.0.3"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-3.0.0.tgz#550790357d6f92a9b82ab2d63e07343a791cf26b" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-3.0.3.tgz#dc1313c75bd00d81e103823802551ee30130ebd1"
integrity sha512-36AV1fIaI2cWRzHo+rbcxhe3M3jUDCNzc4D5zRl57sEWRAxdXYtw7FSQKYY6PDKssiAKjLYypbssHk+xs/kMXw== integrity sha512-usY0HG5nyDUwtqpiZdETNbmKtw3QQ1jwYFZ9wi5iHzX2BcILwQKtYDJPo7XHTsu5Z0B2Hj3W9NNnbd+AjFWjqg==
dependencies: dependencies:
"@types/http-proxy" "^1.17.10" "@types/http-proxy" "^1.17.15"
debug "^4.3.4" debug "^4.3.6"
http-proxy "^1.18.1" http-proxy "^1.18.1"
is-glob "^4.0.1" is-glob "^4.0.3"
is-plain-obj "^3.0.0" is-plain-object "^5.0.0"
micromatch "^4.0.5" micromatch "^4.0.8"
http-proxy-middleware@^2.0.3: http-proxy-middleware@^2.0.3:
version "2.0.6" version "2.0.6"
@ -6853,11 +6853,6 @@ is-number@^7.0.0:
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
is-path-inside@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
is-plain-obj@^3.0.0: is-plain-obj@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7"
@ -6870,6 +6865,11 @@ is-plain-object@^2.0.4:
dependencies: dependencies:
isobject "^3.0.1" isobject "^3.0.1"
is-plain-object@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
is-regex@^1.1.4: is-regex@^1.1.4:
version "1.1.4" version "1.1.4"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
@ -7267,7 +7267,7 @@ katex@^0.16.0, katex@^0.16.9:
dependencies: dependencies:
commander "^8.3.0" commander "^8.3.0"
keyv@^4.5.3: keyv@^4.5.4:
version "4.5.4" version "4.5.4"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
@ -7534,7 +7534,7 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash@4.17.21, lodash@^4.0.1, lodash@^4.17.14, lodash@^4.17.15: lodash@4.17.21, lodash@^4.0.1, lodash@^4.17.14:
version "4.17.21" version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -7695,7 +7695,7 @@ methods@~1.1.2:
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8:
version "4.0.8" version "4.0.8"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
@ -7755,7 +7755,7 @@ minimatch@9.0.1:
dependencies: dependencies:
brace-expansion "^2.0.1" brace-expansion "^2.0.1"
minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@ -9089,6 +9089,11 @@ readable-stream@^3.0.6, readable-stream@^3.4.0:
string_decoder "^1.1.1" string_decoder "^1.1.1"
util-deprecate "^1.0.1" util-deprecate "^1.0.1"
readdirp@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a"
integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==
readdirp@~3.6.0: readdirp@~3.6.0:
version "3.6.0" version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
@ -9262,13 +9267,6 @@ rimraf@^2.6.3:
dependencies: dependencies:
glob "^7.1.3" glob "^7.1.3"
rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
dependencies:
glob "^7.1.3"
rimraf@^5.0.5: rimraf@^5.0.5:
version "5.0.10" version "5.0.10"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.10.tgz#23b9843d3dc92db71f96e1a2ce92e39fd2a8221c" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.10.tgz#23b9843d3dc92db71f96e1a2ce92e39fd2a8221c"
@ -10163,10 +10161,10 @@ tinyexec@^0.3.0:
resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.0.tgz#ed60cfce19c17799d4a241e06b31b0ec2bee69e6" resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.0.tgz#ed60cfce19c17799d4a241e06b31b0ec2bee69e6"
integrity sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg== integrity sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==
tinymce@6.8.4, "tinymce@^7.0.0 || ^6.0.0 || ^5.5.0", tinymce@~6.8.4: tinymce@6.8.5, "tinymce@^7.0.0 || ^6.0.0 || ^5.5.0", tinymce@~6.8.5:
version "6.8.4" version "6.8.5"
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-6.8.4.tgz#53e1313ebfe5524b24c0fa45937d51fda058632d" resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-6.8.5.tgz#aa9a711c4e0b59d506dd281bade857d35a7b3c59"
integrity sha512-okoJyxuPv1gzASxQDNgQbnUXOdAIyoOSXcXcZZu7tiW0PSKEdf3SdASxPBupRj+64/E3elHwVRnzSdo82Emqbg== integrity sha512-qAL/FxL7cwZHj4BfaF818zeJJizK9jU5IQzTcSLL4Rj5MaJdiVblEj7aDr80VCV1w9h4Lak9hlnALhq/kVtN1g==
tmp@^0.0.33: tmp@^0.0.33:
version "0.0.33" version "0.0.33"
@ -10241,11 +10239,6 @@ ts-node@^10.0.0, ts-node@^10.9.2:
v8-compile-cache-lib "^3.0.1" v8-compile-cache-lib "^3.0.1"
yn "3.1.1" yn "3.1.1"
ts-transformer-keys@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/ts-transformer-keys/-/ts-transformer-keys-0.4.4.tgz#c185508b3ae9b79236aac58f788c85ca3ac807d7"
integrity sha512-LrqgvaFvar01/5mbunRyeLTSIkqoC2xfcpL/90aDY6vR07DGyH+UaYGdIEsUudnlAw2Sr0pxFgdZvE0QIyI4qA==
tsconfig-paths@^3.15.0: tsconfig-paths@^3.15.0:
version "3.15.0" version "3.15.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4"
@ -10301,11 +10294,6 @@ type-check@^0.4.0, type-check@~0.4.0:
dependencies: dependencies:
prelude-ls "^1.2.1" prelude-ls "^1.2.1"
type-fest@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
type-fest@^0.21.3: type-fest@^0.21.3:
version "0.21.3" version "0.21.3"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
@ -10668,15 +10656,6 @@ webpack-merge@6.0.1:
flat "^5.0.2" flat "^5.0.2"
wildcard "^2.0.1" wildcard "^2.0.1"
webpack-merge@^5.7.3:
version "5.10.0"
resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177"
integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==
dependencies:
clone-deep "^4.0.1"
flat "^5.0.2"
wildcard "^2.0.0"
webpack-sources@^3.0.0, webpack-sources@^3.2.3: webpack-sources@^3.0.0, webpack-sources@^3.2.3:
version "3.2.3" version "3.2.3"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
@ -10802,7 +10781,7 @@ which@^4.0.0:
dependencies: dependencies:
isexe "^3.1.1" isexe "^3.1.1"
wildcard@^2.0.0, wildcard@^2.0.1: wildcard@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67"
integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==

Loading…
Cancel
Save