FE: regex validation for cron run schedules

Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
jokob-sk
2025-11-27 12:10:33 +11:00
parent 8acb0a876a
commit b9d3f430fe
64 changed files with 666 additions and 592 deletions

View File

@@ -1,4 +1,4 @@
# NetAlertX API Documentation # API Documentation
This API provides programmatic access to **devices, events, sessions, metrics, network tools, and sync** in NetAlertX. It is implemented as a **REST and GraphQL server**. All requests require authentication via **API Token** (`API_TOKEN` setting) unless explicitly noted. For example, to authorize a GraphQL request, you need to use a `Authorization: Bearer API_TOKEN` header as per example below: This API provides programmatic access to **devices, events, sessions, metrics, network tools, and sync** in NetAlertX. It is implemented as a **REST and GraphQL server**. All requests require authentication via **API Token** (`API_TOKEN` setting) unless explicitly noted. For example, to authorize a GraphQL request, you need to use a `Authorization: Bearer API_TOKEN` header as per example below:

View File

@@ -1,8 +1,8 @@
# NetAlertX - Device Management # Device Management
The Main Info section is where most of the device identifiable information is stored and edited. Some of the information is autodetected via various plugins. Initial values for most of the fields can be specified in the `NEWDEV` plugin. The Main Info section is where most of the device identifiable information is stored and edited. Some of the information is autodetected via various plugins. Initial values for most of the fields can be specified in the `NEWDEV` plugin.
> [!NOTE] > [!NOTE]
> >
> You can multi-edit devices by selecting them in the main Devices view, from the Mainetence section, or via the CSV Export functionality under Maintenance. More info can be found in the [Devices Bulk-editing docs](./DEVICES_BULK_EDITING.md). > You can multi-edit devices by selecting them in the main Devices view, from the Mainetence section, or via the CSV Export functionality under Maintenance. More info can be found in the [Devices Bulk-editing docs](./DEVICES_BULK_EDITING.md).
@@ -14,23 +14,23 @@ The Main Info section is where most of the device identifiable information is st
- **MAC**: MAC addres of the device. Not editable, unless creating a new dummy device. - **MAC**: MAC addres of the device. Not editable, unless creating a new dummy device.
- **Last IP**: IP addres of the device. Not editable, unless creating a new dummy device. - **Last IP**: IP addres of the device. Not editable, unless creating a new dummy device.
- **Name**: Friendly device name. Autodetected via various 🆎 Name discovery [plugins](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md). The app attaches `(IP match)` if the name is discovered via an IP match and not MAC match which could mean the name could be incorrect as IPs might change. - **Name**: Friendly device name. Autodetected via various 🆎 Name discovery [plugins](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md). The app attaches `(IP match)` if the name is discovered via an IP match and not MAC match which could mean the name could be incorrect as IPs might change.
- **Icon**: Partially autodetected. Select an existing or [add a custom icon](./ICONS.md). You can also auto-apply the same icon on all devices of the same type. - **Icon**: Partially autodetected. Select an existing or [add a custom icon](./ICONS.md). You can also auto-apply the same icon on all devices of the same type.
- **Owner**: Device owner (The list is self-populated with existing owners and you can add custom values). - **Owner**: Device owner (The list is self-populated with existing owners and you can add custom values).
- **Type**: Select a device type from the dropdown list (`Smartphone`, `Tablet`, - **Type**: Select a device type from the dropdown list (`Smartphone`, `Tablet`,
`Laptop`, `TV`, `router`, etc.) or add a new device type. If you want the device to act as a **Network device** (and be able to be a network node in the Network view), select a type under Network Devices or add a new Network Device type in Settings. More information can be found in the [Network Setup docs](./NETWORK_TREE.md). `Laptop`, `TV`, `router`, etc.) or add a new device type. If you want the device to act as a **Network device** (and be able to be a network node in the Network view), select a type under Network Devices or add a new Network Device type in Settings. More information can be found in the [Network Setup docs](./NETWORK_TREE.md).
- **Vendor**: The manufacturing vendor. Automatically updated by NetAlertX when empty or unknown, can be edited. - **Vendor**: The manufacturing vendor. Automatically updated by NetAlertX when empty or unknown, can be edited.
- **Group**: Select a group (`Always on`, `Personal`, `Friends`, etc.) or type - **Group**: Select a group (`Always on`, `Personal`, `Friends`, etc.) or type
your own Group name. your own Group name.
- **Location**: Select the location, usually a room, where the device is located (`Kitchen`, `Attic`, `Living room`, etc.) or add a custom Location. - **Location**: Select the location, usually a room, where the device is located (`Kitchen`, `Attic`, `Living room`, etc.) or add a custom Location.
- **Comments**: Add any comments for the device, such as a serial number, or maintenance information. - **Comments**: Add any comments for the device, such as a serial number, or maintenance information.
> [!NOTE] > [!NOTE]
> >
> Please note the above usage of the fields are only suggestions. You can use most of these fields for other purposes, such as storing the network interface, company owning a device, or similar. > Please note the above usage of the fields are only suggestions. You can use most of these fields for other purposes, such as storing the network interface, company owning a device, or similar.
## Dummy devices ## Dummy devices
You can create dummy devices from the Devices listing screen. You can create dummy devices from the Devices listing screen.
![Create Dummy Device](./img/DEVICE_MANAGEMENT/Devices_CreateDummyDevice.png) ![Create Dummy Device](./img/DEVICE_MANAGEMENT/Devices_CreateDummyDevice.png)
@@ -39,12 +39,12 @@ The **MAC** field and the **Last IP** field will then become editable.
![Save Dummy Device](./img/DEVICE_MANAGEMENT/DeviceEdit_SaveDummyDevice.png) ![Save Dummy Device](./img/DEVICE_MANAGEMENT/DeviceEdit_SaveDummyDevice.png)
> [!NOTE] > [!NOTE]
> >
> You can couple this with the `ICMP` plugin which can be used to monitor the status of these devices, if they are actual devices reachable with the `ping` command. If not, you can use a loopback IP address so they appear online, such as `0.0.0.0` or `127.0.0.1`. > You can couple this with the `ICMP` plugin which can be used to monitor the status of these devices, if they are actual devices reachable with the `ping` command. If not, you can use a loopback IP address so they appear online, such as `0.0.0.0` or `127.0.0.1`.
## Copying data from an existing device. ## Copying data from an existing device.
To speed up device population you can also copy data from an existing device. This can be done from the **Tools** tab on the Device details. To speed up device population you can also copy data from an existing device. This can be done from the **Tools** tab on the Device details.

View File

@@ -1,4 +1,4 @@
# NetAlertX Community Helper Scripts Overview # Community Helper Scripts Overview
This page provides an overview of community-contributed scripts for NetAlertX. These scripts are not actively maintained and are provided as-is. This page provides an overview of community-contributed scripts for NetAlertX. These scripts are not actively maintained and are provided as-is.
@@ -14,8 +14,8 @@ You can find all scripts in this [scripts GitHub folder](https://github.com/joko
## Important Notes ## Important Notes
> [!NOTE] > [!NOTE]
> These scripts are community-supplied and not actively maintained. Use at your own discretion. > These scripts are community-supplied and not actively maintained. Use at your own discretion.
For detailed usage instructions, refer to each script's documentation in each [scripts GitHub folder](https://github.com/jokob-sk/NetAlertX/tree/main/scripts). For detailed usage instructions, refer to each script's documentation in each [scripts GitHub folder](https://github.com/jokob-sk/NetAlertX/tree/main/scripts).

View File

@@ -5,7 +5,7 @@ To download and install NetAlertX on the hardware/server directly use the `curl`
> [!NOTE] > [!NOTE]
> This is an Experimental feature 🧪 and it relies on community support. > This is an Experimental feature 🧪 and it relies on community support.
> >
> 🙏 Looking for maintainers for this installation method 🙂 Current community volunteers: > 🙏 Looking for maintainers for this installation method 🙂 Current community volunteers:
> - [slammingprogramming](https://github.com/slammingprogramming) > - [slammingprogramming](https://github.com/slammingprogramming)
> - [ingoratsdorf](https://github.com/ingoratsdorf) > - [ingoratsdorf](https://github.com/ingoratsdorf)
> >
@@ -13,8 +13,7 @@ To download and install NetAlertX on the hardware/server directly use the `curl`
> Data loss is a possibility, **it is recommended to install NetAlertX using the supplied Docker image**. > Data loss is a possibility, **it is recommended to install NetAlertX using the supplied Docker image**.
> [!WARNING] > [!WARNING]
> A warning to the installation method below: Piping to bash is [controversial](https://pi-hole.net/2016/07/25/curling-and-piping-to-bash) and may > A warning to the installation method below: Piping to bash is [controversial](https://pi-hole.net/2016/07/25/curling-and-piping-to-bash) and may be dangerous, as you cannot see the code that's about to be executed on your system.
be dangerous, as you cannot see the code that's about to be executed on your system.
If you trust this repo, you can download the install script via one of the methods (curl/wget) below and it will fo its best to install NetAlertX on your system. If you trust this repo, you can download the install script via one of the methods (curl/wget) below and it will fo its best to install NetAlertX on your system.
@@ -40,7 +39,7 @@ Some facts about what and where something will be changed/installed by the HW in
- Only tested to work on the system listed in the install directory. - Only tested to work on the system listed in the install directory.
- **EXPERIMENTAL** and not recommended way to install NetAlertX. - **EXPERIMENTAL** and not recommended way to install NetAlertX.
> [!TIP] > [!TIP]
> If the below fails try grabbing and installing one of the [previous releases](https://github.com/jokob-sk/NetAlertX/releases) and run the installation from the zip package. > If the below fails try grabbing and installing one of the [previous releases](https://github.com/jokob-sk/NetAlertX/releases) and run the installation from the zip package.
These commands will download the `install.debian12.sh` script from the GitHub repository, make it executable with `chmod`, and then run it using `./install.debian12.sh`. These commands will download the `install.debian12.sh` script from the GitHub repository, make it executable with `chmod`, and then run it using `./install.debian12.sh`.
@@ -81,7 +80,7 @@ wget https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/install/ubuntu24/
> [!NOTE] > [!NOTE]
> Use this on a clean LXC/VM for Debian 13 OR Ubuntu 24. > Use this on a clean LXC/VM for Debian 13 OR Ubuntu 24.
> The Scipt will detect OS and build acordingly. > The Scipt will detect OS and build acordingly.
> Maintained by [JVKeller](https://github.com/JVKeller) > Maintained by [JVKeller](https://github.com/JVKeller)
### Installation via wget ### Installation via wget

View File

@@ -218,7 +218,7 @@ services:
### 1.3 Migration from NetAlertX `v25.10.1` ### 1.3 Migration from NetAlertX `v25.10.1`
Starting from v25.10.1, the container uses a [more secure, read-only runtime environment](./SECURITY_FEATURES.md), which requires all writable paths (e.g., logs, API cache, temporary data) to be mounted as `tmpfs` or permanent writable volumes, with sufficient access [permissions](./FILE_PERMISSIONS.md). Starting from v25.10.1, the container uses a [more secure, read-only runtime environment](./SECURITY_FEATURES.md), which requires all writable paths (e.g., logs, API cache, temporary data) to be mounted as `tmpfs` or permanent writable volumes, with sufficient access [permissions](./FILE_PERMISSIONS.md). The data location has also hanged from `/app/db` and `/app/config` to `/data/db` and `/data/config`. See detailed steps below.
#### STEPS: #### STEPS:
@@ -234,8 +234,8 @@ services:
network_mode: "host" network_mode: "host"
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- /local_data_dir/config:/data/config - /local_data_dir/config:/app/config
- /local_data_dir/db:/data/db - /local_data_dir/db:/app/db
# (optional) useful for debugging if you have issues setting up the container # (optional) useful for debugging if you have issues setting up the container
- /local_data_dir/logs:/tmp/log - /local_data_dir/logs:/tmp/log
environment: environment:
@@ -284,10 +284,8 @@ services:
- NET_BIND_SERVICE # 🆕 New line - NET_BIND_SERVICE # 🆕 New line
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- /local_data_dir/config:/data/config - /local_data_dir/config:/data/config # 🆕 This has changed from /app to /data
- /local_data_dir/db:/data/db - /local_data_dir/db:/data/db # 🆕 This has changed from /app to /data
# (optional) useful for debugging if you have issues setting up the container
#- /local_data_dir/logs:/tmp/log
# Ensuring the timezone is the same as on the server - make sure also the TIMEZONE setting is configured # Ensuring the timezone is the same as on the server - make sure also the TIMEZONE setting is configured
- /etc/localtime:/etc/localtime:ro # 🆕 New line - /etc/localtime:/etc/localtime:ro # 🆕 New line
environment: environment:

View File

@@ -1,62 +1,64 @@
# Sessions Section in Device View # Sessions Section Device View
The **Sessions Section** provides details about a device's connection history. This data is automatically detected and cannot be edited by the user. The **Sessions Section** shows a devices connection history. All data is automatically detected and **cannot be edited**.
![Session info](./img/SESSION_INFO/DeviceDetails_SessionInfo.png) ![Session info](./img/SESSION_INFO/DeviceDetails_SessionInfo.png)
--- ---
## Key Fields ## Key Fields
1. **Date and Time of First Connection** | Field | Description | Editable? |
- **Description:** Displays the first detected connection time for the device. | ------------------------------ | ------------------------------------------------------------------------------------------------ | --------------- |
- **Editability:** Uneditable (auto-detected). | **First Connection** | The first time the device was detected on the network. | ❌ Auto-detected |
- **Source:** Automatically captured when the device is first added to the system. | **Last Connection** | The most recent time the device was online. | ❌ Auto-detected |
2. **Date and Time of Last Connection**
- **Description:** Shows the most recent time the device was online.
- **Editability:** Uneditable (auto-detected).
- **Source:** Updated with every new connection event.
3. **Offline Devices with Missing or Conflicting Data**
- **Description:** Handles cases where a device is offline but has incomplete or conflicting session data (e.g., missing start times).
- **Handling:** The system flags these cases for review and attempts to infer missing details.
--- ---
## How Sessions are Discovered and Calculated ## How Session Information Works
### 1. Detecting New Devices ### 1. Detecting New Devices
When a device is first detected in the network, the system logs it in the events table:
`INSERT INTO Events (eve_MAC, eve_IP, eve_DateTime, eve_EventType, eve_AdditionalInfo, eve_PendingAlertEmail) SELECT cur_MAC, cur_IP, '{startTime}', 'New Device', cur_Vendor, 1 FROM CurrentScan WHERE NOT EXISTS (SELECT 1 FROM Devices WHERE devMac = cur_MAC)` * New devices are automatically detected when they first appear on the network.
* A **New Device** record is created, capturing the MAC, IP, vendor, and detection time.
- Devices scanned in the current cycle (**CurrentScan**) are checked against the **Devices** table. ### 2. Recording Connection Sessions
- If a device is new:
- A **New Device** event is logged.
- The devices MAC, IP, vendor, and detection time are recorded.
### 2. Logging Connection Sessions * Every time a device connects, a session entry is created.
When a new connection is detected, the system creates a session record: * Captured details include:
`INSERT INTO Sessions (ses_MAC, ses_IP, ses_EventTypeConnection, ses_DateTimeConnection, ses_EventTypeDisconnection, ses_DateTimeDisconnection, ses_StillConnected, ses_AdditionalInfo) SELECT cur_MAC, cur_IP, 'Connected', '{startTime}', NULL, NULL, 1, cur_Vendor FROM CurrentScan WHERE NOT EXISTS (SELECT 1 FROM Sessions WHERE ses_MAC = cur_MAC)` * Connection type (wired or wireless)
* Connection time
- A new session is logged in the **Sessions** table if no prior session exists. * Device details (MAC, IP, vendor)
- Fields like `MAC`, `IP`, `Connection Type`, and `Connection Time` are populated.
- The `Still Connected` flag is set to `1` (active connection).
### 3. Handling Missing or Conflicting Data ### 3. Handling Missing or Conflicting Data
- Devices with incomplete or conflicting session data (e.g., missing start times) are detected.
- The system flags these records and attempts corrections by inferring details from available data. * **Triggers:**
Devices are flagged when session data is incomplete, inconsistent, or conflicting. Examples include:
* Missing first or last connection timestamps
* Overlapping session records
* Sessions showing a device as connected and disconnected at the same time
* **System response:**
* Automatically highlights affected devices in the **Sessions Section**.
* Attempts to **infer missing information** from available data, such as:
* Estimating first or last connection times from nearby session events
* Correcting overlapping session periods
* Reconciling conflicting connection statuses
* **User impact:**
* Users do **not** need to manually fix session data.
* The system ensures the devices connection history remains as accurate as possible for monitoring and reporting.
### 4. Updating Sessions ### 4. Updating Sessions
- When a device reconnects, its session is updated with a new connection timestamp.
- When a device disconnects:
- The **Disconnection Time** is recorded.
- The `Still Connected` flag is set to `0`.
The session information is then used to display the device presence under **Monitoring** -> **Presence**. * **Reconnect:** Updates session with the new connection timestamp.
* **Disconnect:** Records disconnection time and marks the device as offline.
This session information feeds directly into **Monitoring → Presence**, providing a live view of which devices are currently online.
![Monitoring Device Presence](./img/SESSION_INFO/Monitoring_Presence.png) ![Monitoring Device Presence](./img/SESSION_INFO/Monitoring_Presence.png)

View File

@@ -1,7 +1,8 @@
# Docker Update Strategies to upgrade NetAlertX # Docker Update Strategies to upgrade NetAlertX
> [!WARNING] > [!WARNING]
> For versions prior to `v25.6.7` upgrade to version `v25.5.24` first (`docker pull ghcr.io/jokob-sk/netalertx:25.5.24`) as later versions don't support a full upgrade. Alternatively, devices and settings can be migrated manually, e.g. via [CSV import](./DEVICES_BULK_EDITING.md). > For versions prior to `v25.6.7` upgrade to version `v25.5.24` first (`docker pull ghcr.io/jokob-sk/netalertx:25.5.24`) as later versions don't support a full upgrade. Alternatively, devices and settings can be migrated manually, e.g. via [CSV import](./DEVICES_BULK_EDITING.md).
> See the [Migration guide](./MIGRATION.md) for details.
This guide outlines approaches for updating Docker containers, usually when upgrading to a newer version of NetAlertX. Each method offers different benefits depending on the situation. Here are the methods: This guide outlines approaches for updating Docker containers, usually when upgrading to a newer version of NetAlertX. Each method offers different benefits depending on the situation. Here are the methods:
@@ -15,7 +16,7 @@ You can choose any approach that fits your workflow.
> In the examples I assume that the container name is `netalertx` and the image name is `netalertx` as well. > In the examples I assume that the container name is `netalertx` and the image name is `netalertx` as well.
> [!NOTE] > [!NOTE]
> See also [Backup strategies](./BACKUPS.md) to be on the safe side. > See also [Backup strategies](./BACKUPS.md) to be on the safe side.
## 1. Manual Updates ## 1. Manual Updates
@@ -48,7 +49,7 @@ sudo docker-compose up --pull always -d
## 2. Dockcheck for Bulk Container Updates ## 2. Dockcheck for Bulk Container Updates
Always check the [Dockcheck](https://github.com/mag37/dockcheck) docs if encountering issues with the guide below. Always check the [Dockcheck](https://github.com/mag37/dockcheck) docs if encountering issues with the guide below.
Dockcheck is a useful tool if you have multiple containers to update and some flexibility for handling potential issues that might arise during mass updates. Dockcheck allows you to inspect each container and decide when to update. Dockcheck is a useful tool if you have multiple containers to update and some flexibility for handling potential issues that might arise during mass updates. Dockcheck allows you to inspect each container and decide when to update.
@@ -74,7 +75,7 @@ sudo ./dockcheck.sh
## 3. Automated Updates with Watchtower ## 3. Automated Updates with Watchtower
Always check the [watchtower](https://github.com/containrrr/watchtower) docs if encountering issues with the guide below. Always check the [watchtower](https://github.com/containrrr/watchtower) docs if encountering issues with the guide below.
Watchtower monitors your Docker containers and automatically updates them when new images are available. This is ideal for ongoing updates without manual intervention. Watchtower monitors your Docker containers and automatically updates them when new images are available. This is ideal for ongoing updates without manual intervention.
@@ -96,7 +97,7 @@ docker run -d \
--interval 300 # Check for updates every 5 minutes --interval 300 # Check for updates every 5 minutes
``` ```
#### 3. Run Watchtower to update only NetAlertX: #### 3. Run Watchtower to update only NetAlertX:
You can specify which containers to monitor by listing them. For example, to monitor netalertx only: You can specify which containers to monitor by listing them. For example, to monitor netalertx only:

View File

@@ -1,6 +1,6 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
# NetAlertX # NetAlertX
# Open Source Network Guard / WIFI & LAN intrusion detector # Open Source Network Guard / WIFI & LAN intrusion detector
# #
# app.css - Front module. CSS styles # app.css - Front module. CSS styles
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
@@ -36,7 +36,7 @@ a[target="_blank"] {
display: inline-block; /* Needed for positioning */ display: inline-block; /* Needed for positioning */
padding-right: 0.6em; /* Space for the icon */ padding-right: 0.6em; /* Space for the icon */
} }
a[target="_blank"]::after { a[target="_blank"]::after {
content: '↗'; content: '↗';
position: absolute; position: absolute;
@@ -55,7 +55,7 @@ a[target="_blank"] {
right: -7px; right: -7px;
top: 1px; top: 1px;
} */ } */
/* .select2-container--default .select2-selection--multiple .select2-selection__choice /* .select2-container--default .select2-selection--multiple .select2-selection__choice
{ {
padding-right: 15px !important; padding-right: 15px !important;
@@ -70,6 +70,11 @@ a[target="_blank"] {
opacity: 1; opacity: 1;
} }
[data-is-valid="0"] {
/* border: 1px solid red; */
background-color: #ff4b4b;
}
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Helper Classes Helper Classes
----------------------------------------------------------------------------- */ ----------------------------------------------------------------------------- */
@@ -100,7 +105,7 @@ a[target="_blank"] {
background-color: black; background-color: black;
font-family: 'Courier New', monospace; font-family: 'Courier New', monospace;
font-size: .85em; font-size: .85em;
} }
.logs-row textarea .logs-row textarea
{ {
@@ -110,12 +115,12 @@ a[target="_blank"] {
display:contents; display:contents;
position: relative; position: relative;
padding: 0.4em padding: 0.4em
} }
#tab_Logging .actions .toggle{ #tab_Logging .actions .toggle{
margin: 0.5em; margin: 0.5em;
height: 3em; height: 3em;
} }
@@ -134,8 +139,8 @@ a[target="_blank"] {
} }
.log-area .log-area
{ {
padding: 3px; padding: 3px;
width:100%; width:100%;
border-bottom-width: 1px; border-bottom-width: 1px;
border-bottom-style: solid; border-bottom-style: solid;
border-color: #606060; border-color: #606060;
@@ -246,7 +251,7 @@ a[target="_blank"] {
{ {
padding:8px; padding:8px;
color: white; color: white;
} }
.header-status .header-status
{ {
@@ -262,7 +267,7 @@ a[target="_blank"] {
position: absolute; position: absolute;
top: 3px; top: 3px;
margin-left: 15px; margin-left: 15px;
display: none; display: none;
} }
@@ -298,9 +303,9 @@ body
.NetAlertX-logo .NetAlertX-logo
{ {
border-color:transparent !important; border-color:transparent !important;
height: 50px !important; height: 50px !important;
width: 50px !important; width: 50px !important;
margin-top:15px !important; margin-top:15px !important;
border-radius: 1px !important; border-radius: 1px !important;
} }
@@ -327,7 +332,7 @@ body
.content-wrapper, .content-wrapper,
.right-side, .right-side,
.main-footer { .main-footer {
margin-left: 150px; margin-left: 150px;
} }
@@ -740,7 +745,7 @@ body
text-decoration: underline; text-decoration: underline;
} }
#ticker-message #ticker-message
{ {
color:#FFFFFF; color:#FFFFFF;
} }
@@ -774,7 +779,7 @@ body
.file-checking .icon-wrap{ .file-checking .icon-wrap{
width: 200px; width: 200px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
display: block; display: block;
} }
@@ -788,7 +793,7 @@ body
.file-checking .file-name-wrap{ .file-checking .file-name-wrap{
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
display: flex; display: flex;
padding: 5px; padding: 5px;
} }
@@ -796,7 +801,7 @@ body
.file-checking{ .file-checking{
display: block; display: block;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
@@ -854,16 +859,16 @@ body
.db_tools_table_cell_a { .db_tools_table_cell_a {
display: table-cell; display: table-cell;
text-align: center; text-align: center;
padding: 10px; padding: 10px;
min-width: 180px; min-width: 180px;
width: 20%; width: 20%;
vertical-align: middle; vertical-align: middle;
} }
.db_tools_table_cell_b { .db_tools_table_cell_b {
display: table-cell; display: table-cell;
text-align: justify; text-align: justify;
font-size: 16px; font-size: 16px;
vertical-align: middle; vertical-align: middle;
padding: 10px; padding: 10px;
} }
@@ -876,12 +881,12 @@ height: 50px;
} }
.nav-tabs-custom .tab-content { .nav-tabs-custom .tab-content {
background-color: white; background-color: white;
} }
@media (max-width: 767px) { @media (max-width: 767px) {
.nav-tabs-custom .tab-content { .nav-tabs-custom .tab-content {
overflow: scroll; overflow: scroll;
} }
} }
@@ -898,7 +903,7 @@ height: 50px;
font-size: 16px !important; font-size: 16px !important;
} }
.deviceSelector .deviceSelector
{ {
display: block; display: block;
} }
@@ -935,7 +940,7 @@ height: 50px;
height: 10px; height: 10px;
display: inline-block; display: inline-block;
/* background: #fff; */ /* background: #fff; */
opacity: .75; opacity: .75;
} }
/* --------------------------------------------------------- */ /* --------------------------------------------------------- */
@@ -979,32 +984,32 @@ height: 50px;
} }
/* .setting_input{ /* .setting_input{
width:70%; width:70%;
} }
.setting_name .setting_name
{ {
width:30%; width:30%;
} */ } */
} }
@media (min-width: 768px) { @media (min-width: 768px) {
.setting_description { .setting_description {
/* color: green; */ /* color: green; */
display: block; display: block;
} }
/* .setting_input{ /* .setting_input{
width:40%; width:40%;
} }
.setting_name .setting_name
{ {
width:19%; width:19%;
} */ } */
} }
/* Hide unusable buttons on the settings page for the NEWDEV plugin*/ /* Hide unusable buttons on the settings page for the NEWDEV plugin*/
#settingsPage #add_option_NEWDEV_devGroup, #settingsPage #add_option_NEWDEV_devGroup,
#settingsPage #add_option_NEWDEV_devLocation, #settingsPage #add_option_NEWDEV_devLocation,
#settingsPage #add_option_NEWDEV_devOwner, #settingsPage #add_option_NEWDEV_devOwner,
#settingsPage #copy_icons_NEWDEV_devIcon, #settingsPage #copy_icons_NEWDEV_devIcon,
#settingsPage #add_icon_NEWDEV_devIcon, #settingsPage #add_icon_NEWDEV_devIcon,
@@ -1024,11 +1029,11 @@ height: 50px;
#settingsPage .small-box .inner .card-title { #settingsPage .small-box .inner .card-title {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
color: white; color: white;
} }
.settingswrap .settingswrap
{ {
@@ -1048,13 +1053,13 @@ height: 50px;
.padding-bottom .padding-bottom
{ {
padding-bottom: 100px; padding-bottom: 100px;
} }
.settings-group .settings-group
{ {
font-size: 20px; font-size: 20px;
padding-top: 7px; padding-top: 7px;
padding-bottom: 9px; padding-bottom: 9px;
} }
.overview-section .small-box .icon .overview-section .small-box .icon
@@ -1069,7 +1074,7 @@ height: 50px;
} }
.overview-group .overview-group
{ {
font-size: 20px; font-size: 20px;
padding-top: 7px; padding-top: 7px;
padding-bottom: 9px; padding-bottom: 9px;
@@ -1082,8 +1087,8 @@ height: 50px;
} }
#settingsPage .table_row { #settingsPage .table_row {
padding: 3px; padding: 3px;
/* width:100%; */ /* width:100%; */
/* display: flex; */ /* display: flex; */
border-bottom-width: 1px; border-bottom-width: 1px;
@@ -1102,7 +1107,7 @@ height: 50px;
.setting_name .setting_name
{ {
/* width:19%; */ /* width:19%; */
font-weight: 300; font-weight: 300;
} }
@@ -1111,24 +1116,24 @@ height: 50px;
display:none !important; display:none !important;
} }
.center .center
{ {
margin: 0; margin: 0;
position: relative; position: relative;
left: 50%; left: 50%;
-ms-transform: translate(-50%, -50%); -ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
} }
.top-margin .top-margin
{ {
margin-top: 50px; margin-top: 50px;
} }
/* Settings */ /* Settings */
#settingsPage .overview-setting-value{ #settingsPage .overview-setting-value{
display:unset; display:unset;
} }
@@ -1165,7 +1170,7 @@ height: 50px;
} }
.text-overflow-hidden .text-overflow-hidden
{ {
overflow: hidden; overflow: hidden;
text-overflow: clip; text-overflow: clip;
} }
@@ -1175,9 +1180,9 @@ height: 50px;
padding: 10px; padding: 10px;
/* background-color: #272c30; */ /* background-color: #272c30; */
margin: 10px; margin: 10px;
} }
#settingsPage .panel-heading:hover{ #settingsPage .panel-heading:hover{
background-color: #272c30; background-color: #272c30;
} }
@@ -1185,12 +1190,12 @@ height: 50px;
font-size: medium; font-size: medium;
/* background-color: #272c30; */ /* background-color: #272c30; */
margin: 10px; margin: 10px;
} }
.settings_content input[type=checkbox] .settings_content input[type=checkbox]
{ {
width: auto width: auto
} }
.override{ .override{
@@ -1212,7 +1217,7 @@ height: 50px;
input[readonly] { input[readonly] {
/* Apply styles to the readonly input */ /* Apply styles to the readonly input */
background-color: #646566 !important; background-color: #646566 !important;
color: #e6e6e6; color: #e6e6e6;
cursor: not-allowed; cursor: not-allowed;
} }
@@ -1300,7 +1305,7 @@ input[readonly] {
/* margin-bottom:20px; */ /* margin-bottom:20px; */
} }
#settingsPage .select2-selection #settingsPage .select2-selection
{ {
width: initial; width: initial;
display: inline-block; display: inline-block;
@@ -1314,8 +1319,8 @@ input[readonly] {
#settingsPage .select2-selection #settingsPage .select2-selection
{ {
background-color: rgb(96, 96, 96); background-color: rgb(96, 96, 96);
} }
#settingsPage .select2-container #settingsPage .select2-container
{ {
width: 100% !important; width: 100% !important;
} }
@@ -1398,7 +1403,7 @@ input[readonly] {
backdrop-filter: brightness(50%); backdrop-filter: brightness(50%);
} }
.iconPreviewSelector .iconPreviewSelector
{ {
text-align: center; text-align: center;
padding: 15px; padding: 15px;
@@ -1440,7 +1445,7 @@ input[readonly] {
} }
.dummyDevice .dummyDevice
{ {
text-align: end; text-align: end;
} }
@@ -1461,7 +1466,7 @@ input[readonly] {
} }
.info-icon-nav .info-icon-nav
{ {
top: -6px; top: -6px;
position: absolute; position: absolute;
z-index: 1; z-index: 1;
@@ -1538,7 +1543,7 @@ input[readonly] {
} }
#panDetails .input-group { #panDetails .input-group {
min-height: 40px; min-height: 40px;
} }
@@ -1583,7 +1588,7 @@ input[readonly] {
} }
.devicePropAction .devicePropAction
{ {
width: 1.2em; width: 1.2em;
height: 1.2em; height: 1.2em;
display: inline-block; display: inline-block;
@@ -1593,11 +1598,11 @@ input[readonly] {
} }
.devicePropAction:hover .devicePropAction:hover
{ {
font-size: larger; font-size: larger;
padding: 0em; padding: 0em;
margin: 0em; margin: 0em;
} }
@@ -1607,7 +1612,7 @@ input[readonly] {
display: block; display: block;
float:inline-end; float:inline-end;
height: 2em; height: 2em;
} }
#panDetails .dataTables_wrapper .bottom .dataTables_info #panDetails .dataTables_wrapper .bottom .dataTables_info
{ {
@@ -1636,22 +1641,22 @@ input[readonly] {
height: 14px; height: 14px;
} }
#deviceDetailsEdit .select2-container--default .select2-selection--multiple .select2-selection__choice #deviceDetailsEdit .select2-container--default .select2-selection--multiple .select2-selection__choice
{ {
height: 20px; height: 20px;
} }
#deviceDetailsEdit .select2-container--disabled #deviceDetailsEdit .select2-container--disabled
{ {
background-color: #606060; background-color: #606060;
} }
#deviceDetailsEdit .select2-container--default .select2-selection--multiple .select2-selection__choice span #deviceDetailsEdit .select2-container--default .select2-selection--multiple .select2-selection__choice span
{ {
font-size: 14px; font-size: 14px;
} }
#deviceDetailsEdit .select2-selection #deviceDetailsEdit .select2-selection
{ {
width: initial; width: initial;
display: inline-block; display: inline-block;
@@ -1681,7 +1686,7 @@ input[readonly] {
font-size: 14px; font-size: 14px;
} }
.custom-badge .custom-badge
{ {
border: 1px solid #aaa; border: 1px solid #aaa;
border-radius: 4px; border-radius: 4px;
border-style: solid; border-style: solid;
@@ -1716,7 +1721,7 @@ input[readonly] {
} }
#deviceDetailsEdit .select2-container #deviceDetailsEdit .select2-container
{ {
width: 100% !important; width: 100% !important;
} }
@@ -1799,7 +1804,7 @@ input[readonly] {
z-index: 5; z-index: 5;
} }
#networkTree .netNodeText #networkTree .netNodeText
{ {
position: absolute; position: absolute;
} }
#networkTree .netPort #networkTree .netPort
@@ -1812,7 +1817,7 @@ input[readonly] {
#networkTree .portBckgIcon #networkTree .portBckgIcon
{ {
opacity: 0.3; opacity: 0.3;
display: initial; display: initial;
float: left; float: left;
width: 1em; width: 1em;
} }
@@ -1822,7 +1827,7 @@ input[readonly] {
margin-left: 16px; margin-left: 16px;
/* border: solid; /* border: solid;
border-color:#606060; */ border-color:#606060; */
position: relative; position: relative;
} }
#networkTree .netIcon #networkTree .netIcon
{ {
@@ -1850,8 +1855,8 @@ input[readonly] {
} }
#hover-box .devName #hover-box .devName
{ {
font-size: larger; font-size: larger;
display: contents; display: contents;
} }
@@ -1910,7 +1915,7 @@ input[readonly] {
#networkTree .highlightedNode #networkTree .highlightedNode
{ {
/* border: solid; */ /* border: solid; */
border-color:var(--color-lightblue); border-color:var(--color-lightblue);
box-shadow: var(--color-lightblue) 0px 0px 20px; box-shadow: var(--color-lightblue) 0px 0px 20px;
} }
@@ -1968,7 +1973,7 @@ input[readonly] {
} }
.sort-btn { .sort-btn {
right: 5px; right: 5px;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
@@ -2020,7 +2025,7 @@ input[readonly] {
} }
.plugin-filters .plugin-filters
{ {
margin: 7px; margin: 7px;
margin-right: 7px; margin-right: 7px;
margin-bottom: 9px; margin-bottom: 9px;
@@ -2054,7 +2059,7 @@ input[readonly] {
} }
.plugin-content #tabs-content-location .plugin-content #tabs-content-location
{ {
margin: 0px; margin: 0px;
padding-top: 0; padding-top: 0;
} }
@@ -2066,7 +2071,7 @@ input[readonly] {
} }
.plugin-content .tab-content .plugin-content .tab-content
{ {
padding-top: 10px; padding-top: 10px;
} }
@@ -2103,7 +2108,7 @@ input[readonly] {
@media (max-width: 500px) { @media (max-width: 500px) {
.header-server-time { .header-server-time {
display: none; display: none;
} }
} }
@@ -2234,12 +2239,12 @@ input[readonly] {
display: grid; display: grid;
} }
#workflowContainerWrap .panel-collapse #workflowContainerWrap .panel-collapse
{ {
padding: 5px; padding: 5px;
} }
.workflows .workflows
{ {
max-width: 800px; max-width: 800px;
} }
@@ -2285,7 +2290,7 @@ input[readonly] {
color: #000; color: #000;
} }
.workflows .button-container .workflows .button-container
{ {
/* display: contents; */ /* display: contents; */
text-align: center; text-align: center;
@@ -2305,7 +2310,7 @@ input[readonly] {
margin: 5px; margin: 5px;
} }
.workflows .button-container .workflows .button-container
{ {
padding-right: 0px !important; padding-right: 0px !important;
padding-left: 0px !important; padding-left: 0px !important;
@@ -2318,19 +2323,19 @@ input[readonly] {
/* .button-container button /* .button-container button
{ {
width:100%; width:100%;
} */ } */
.red-hover-text:hover .red-hover-text:hover
{ {
color: var(--color-red) !important; color: var(--color-red) !important;
} }
.green-hover-text:hover .green-hover-text:hover
{ {
color: var(--color-green) !important; color: var(--color-green) !important;
} }
.workflows .bckg-icon-1-line .workflows .bckg-icon-1-line
{ {
font-size: 3em; font-size: 3em;
@@ -2362,7 +2367,7 @@ input[readonly] {
z-index: 1; z-index: 1;
} }
.workflows .workflow-card .workflows .workflow-card
{ {
display: block; display: block;
} }
@@ -2372,7 +2377,7 @@ input[readonly] {
padding: 10px; padding: 10px;
} }
.workflow-card, .actions-list .workflow-card, .actions-list
{ {
display: contents; display: contents;
padding: 5px; padding: 5px;
@@ -2384,7 +2389,7 @@ input[readonly] {
z-index:1; z-index:1;
} }
.condition .condition
{ {
padding: 5px; padding: 5px;
padding-left: 10px; padding-left: 10px;

View File

@@ -96,7 +96,7 @@ function showModalInput(
btnOK = getString("Gen_Okay"), btnOK = getString("Gen_Okay"),
callbackFunction = null, callbackFunction = null,
triggeredBy = null, triggeredBy = null,
defaultValue = "" defaultValue = ""
) { ) {
prefix = "modal-input"; prefix = "modal-input";
@@ -121,7 +121,7 @@ function showModalInput(
setTimeout(function () { setTimeout(function () {
$(`#${prefix}-textarea`).focus(); $(`#${prefix}-textarea`).focus();
}, 500); }, 500);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -143,7 +143,7 @@ function showModalFieldInput(
$(`#${prefix}-OK`).html(btnOK); $(`#${prefix}-OK`).html(btnOK);
if (callbackFunction != null) { if (callbackFunction != null) {
modalCallbackFunction = callbackFunction; modalCallbackFunction = callbackFunction;
} }
@@ -181,11 +181,11 @@ function showModalPopupForm(
$(`#${prefix}-cancel`).html(btnCancel); $(`#${prefix}-cancel`).html(btnCancel);
$(`#${prefix}-OK`).html(btnOK); $(`#${prefix}-OK`).html(btnOK);
// if curValue not null // if curValue not null
if (curValue) if (curValue)
{ {
initialValues = JSON.parse(atob(curValue)); initialValues = JSON.parse(atob(curValue));
} }
outputHtml = ""; outputHtml = "";
@@ -193,7 +193,7 @@ function showModalPopupForm(
if (Array.isArray(popupFormJson)) { if (Array.isArray(popupFormJson)) {
popupFormJson.forEach((field, index) => { popupFormJson.forEach((field, index) => {
// You'll need to define these or map them from `field` // You'll need to define these or map them from `field`
const setKey = field.function || `field_${index}`; const setKey = field.function || `field_${index}`;
const setName = getString(`${parentSettingKey}_popupform_${setKey}_name`); const setName = getString(`${parentSettingKey}_popupform_${setKey}_name`);
const labelClasses = "col-sm-2"; // example, or from your obj.labelClasses const labelClasses = "col-sm-2"; // example, or from your obj.labelClasses
const inputClasses = "col-sm-10"; // example, or from your obj.inputClasses const inputClasses = "col-sm-10"; // example, or from your obj.inputClasses
@@ -207,9 +207,9 @@ function showModalPopupForm(
} }
} }
const fieldOptionsOverride = field.type?.elements[0]?.elementOptions || []; const fieldOptionsOverride = field.type?.elements[0]?.elementOptions || [];
const setValue = initialValue; const setValue = initialValue;
const setType = JSON.stringify(field.type); const setType = JSON.stringify(field.type);
const setEvents = field.events || []; // default to empty array if missing const setEvents = field.events || []; // default to empty array if missing
const setObj = { setKey, setValue, setType, setEvents }; const setObj = { setKey, setValue, setType, setEvents };
@@ -218,17 +218,17 @@ function showModalPopupForm(
<div class="form-group col-xs-12"> <div class="form-group col-xs-12">
<label id="${setKey}_label" class="${labelClasses}"> ${setName} <label id="${setKey}_label" class="${labelClasses}"> ${setName}
<i my-set-key="${parentSettingKey}_popupform_${setKey}" <i my-set-key="${parentSettingKey}_popupform_${setKey}"
title="${getString("Settings_Show_Description")}" title="${getString("Settings_Show_Description")}"
class="fa fa-circle-info pointer helpIconSmallTopRight" class="fa fa-circle-info pointer helpIconSmallTopRight"
onclick="showDescriptionPopup(this)"> onclick="showDescriptionPopup(this)">
</i> </i>
</label> </label>
<div class="${inputClasses}"> <div class="${inputClasses}">
${generateFormHtml( ${generateFormHtml(
null, // settingsData only required for datatables null, // settingsData only required for datatables
setObj, setObj,
null, null,
fieldOptionsOverride, fieldOptionsOverride,
null null
)} )}
</div> </div>
@@ -239,7 +239,7 @@ function showModalPopupForm(
outputHtml += inputFormHtml; outputHtml += inputFormHtml;
}); });
} }
$(`#modal-form-plc`).html(outputHtml); $(`#modal-form-plc`).html(outputHtml);
// Bind OK button click event // Bind OK button click event
@@ -247,12 +247,19 @@ function showModalPopupForm(
let settingsArray = []; let settingsArray = [];
if (Array.isArray(popupFormJson)) { if (Array.isArray(popupFormJson)) {
popupFormJson.forEach(field => { popupFormJson.forEach(field => {
collectSetting( const result = collectSetting(
`${parentSettingKey}_popupform`, // prefix `${parentSettingKey}_popupform`, // prefix
field.function, // setCodeName field.function, // setCodeName
field.type, // setType (object) field.type, // setType (object)
settingsArray settingsArray
); );
settingsArray = result.settingsArray;
if (!result.dataIsValid) {
msg = getString("Gen_Invalid_Value") + ":" + result.failedSettingKey;
console.error(msg);
showModalOk("ERROR", msg);
}
}); });
} }
@@ -276,7 +283,7 @@ function showModalPopupForm(
const newOption = $("<option class='interactable-option'></option>") const newOption = $("<option class='interactable-option'></option>")
.attr("value", encodedValue) .attr("value", encodedValue)
.text(label); .text(label);
$("#" + selectId).append(newOption); $("#" + selectId).append(newOption);
initListInteractionOptions(newOption); initListInteractionOptions(newOption);
} }
@@ -429,10 +436,10 @@ function safeDecodeURIComponent(content) {
return content; // Return the original content if decoding fails return content; // Return the original content if decoding fails
} }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Backend notification Polling // Backend notification Polling
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Function to check for notifications // Function to check for notifications
function checkNotification() { function checkNotification() {
@@ -440,7 +447,7 @@ function checkNotification() {
const phpEndpoint = 'php/server/utilNotification.php'; const phpEndpoint = 'php/server/utilNotification.php';
$.ajax({ $.ajax({
url: notificationEndpoint, url: notificationEndpoint,
type: 'GET', type: 'GET',
success: function(response) { success: function(response) {
// console.log(response); // console.log(response);
@@ -492,7 +499,7 @@ function checkNotification() {
}, },
error: function() { error: function() {
console.warn(`🟥 Error checking ${notificationEndpoint}`) console.warn(`🟥 Error checking ${notificationEndpoint}`)
} }
}); });
} }
@@ -582,7 +589,7 @@ const phpEndpoint = 'php/server/utilNotification.php';
// -------------------------------------------------- // --------------------------------------------------
// Write a notification // Write a notification
function write_notification(content, level) { function write_notification(content, level) {
$.ajax({ $.ajax({
url: phpEndpoint, // Change this to the path of your PHP script url: phpEndpoint, // Change this to the path of your PHP script
@@ -603,8 +610,8 @@ function write_notification(content, level) {
// -------------------------------------------------- // --------------------------------------------------
// Write a notification // Write a notification
function markNotificationAsRead(guid) { function markNotificationAsRead(guid) {
$.ajax({ $.ajax({
url: phpEndpoint, url: phpEndpoint,
type: 'GET', type: 'GET',
@@ -628,8 +635,8 @@ function markNotificationAsRead(guid) {
// -------------------------------------------------- // --------------------------------------------------
// Remove a notification // Remove a notification
function removeNotification(guid) { function removeNotification(guid) {
$.ajax({ $.ajax({
url: phpEndpoint, url: phpEndpoint,
type: 'GET', type: 'GET',

View File

@@ -71,7 +71,7 @@ function getPluginConfig(pluginsData, prefix) {
// Show the description of a setting // Show the description of a setting
function showDescriptionPopup(e) { function showDescriptionPopup(e) {
console.log($(e).attr("my-set-key")); console.log($(e).attr("my-set-key"));
showModalOK("Info", getString($(e).attr("my-set-key") + '_description')) showModalOK("Info", getString($(e).attr("my-set-key") + '_description'))
} }
@@ -92,13 +92,13 @@ function pluginCards(prefixesOfEnabledPlugins, includeSettings) {
prefix + "_" + set prefix + "_" + set
}"> }">
<code>${getSetting(prefix + "_" + set)}</code> <code>${getSetting(prefix + "_" + set)}</code>
</div> </div>
</a> </a>
</div> </div>
`; `;
}); });
html += ` html += `
<div class="col-xs-6 col-sm-4 col-md-3 col-lg-2 col-xxl-1 padding-5px"> <div class="col-xs-6 col-sm-4 col-md-3 col-lg-2 col-xxl-1 padding-5px">
<div class="small-box bg-green col-sm-12 " > <div class="small-box bg-green col-sm-12 " >
<div class="inner col-sm-12"> <div class="inner col-sm-12">
@@ -110,10 +110,10 @@ function pluginCards(prefixesOfEnabledPlugins, includeSettings) {
${includeSettings_html} ${includeSettings_html}
</div> </div>
<a href="#${prefix}_header" onclick="toggleAllSettings('open')"> <a href="#${prefix}_header" onclick="toggleAllSettings('open')">
<div class="icon"> ${getString(prefix + "_icon")} </div> <div class="icon"> ${getString(prefix + "_icon")} </div>
</a> </a>
</div> </div>
</div> </div>
`; `;
}); });
@@ -251,17 +251,17 @@ function settingsCollectedCorrectly(settingsArray, settingsJSON_DB) {
function cloneDataTableRow(el){ function cloneDataTableRow(el){
console.log(el); console.log(el);
const id = "NEWDEV_devCustomProps_table"; // Your table ID const id = "NEWDEV_devCustomProps_table"; // Your table ID
const table = $('#'+id).DataTable(); const table = $('#'+id).DataTable();
// Get the 'my-index' attribute from the closest tr element // Get the 'my-index' attribute from the closest tr element
const myIndex = parseInt($(el).closest("tr").attr("my-index")); const myIndex = parseInt($(el).closest("tr").attr("my-index"));
// Find the row in the table with the matching 'my-index' // Find the row in the table with the matching 'my-index'
const row = table.rows().nodes().to$().filter(`[my-index="${myIndex}"]`).first().get(0); const row = table.rows().nodes().to$().filter(`[my-index="${myIndex}"]`).first().get(0);
// Clone the row (including its data and controls) // Clone the row (including its data and controls)
let clonedRow = $(row).clone(true, true); // The true arguments copy the data and event handlers let clonedRow = $(row).clone(true, true); // The true arguments copy the data and event handlers
@@ -270,7 +270,7 @@ function cloneDataTableRow(el){
console.log(clonedRow); console.log(clonedRow);
// Add the cloned row to the DataTable // Add the cloned row to the DataTable
table.row.add(clonedRow[0]).draw(); table.row.add(clonedRow[0]).draw();
@@ -291,13 +291,13 @@ function removeDataTableRow(el) {
// Find the row in the table with the matching 'my-index' // Find the row in the table with the matching 'my-index'
const row = table.rows().nodes().to$().filter(`[my-index="${myIndex}"]`).first().get(0); const row = table.rows().nodes().to$().filter(`[my-index="${myIndex}"]`).first().get(0);
// Remove the row from the DataTable // Remove the row from the DataTable
table.row(row).remove().draw(); table.row(row).remove().draw();
} }
else else
{ {
showMessage (getString("CustProps_cant_remove"), 3000, "modal_red"); showMessage (getString("CustProps_cant_remove"), 3000, "modal_red");
} }
} }
@@ -308,9 +308,9 @@ function addViaPopupForm(element) {
const toId = $(element).attr("my-input-to"); const toId = $(element).attr("my-input-to");
const curValue = $(`#${toId}`).val(); const curValue = $(`#${toId}`).val();
const parsed = JSON.parse(atob($(`#${toId}`).data("elementoptionsbase64"))); const parsed = JSON.parse(atob($(`#${toId}`).data("elementoptionsbase64")));
const popupFormJson = parsed.find(obj => "popupForm" in obj)?.popupForm ?? null; const popupFormJson = parsed.find(obj => "popupForm" in obj)?.popupForm ?? null;
console.log(`toId | curValue: ${toId} | ${curValue}`); console.log(`toId | curValue: ${toId} | ${curValue}`);
showModalPopupForm( showModalPopupForm(
@@ -393,7 +393,7 @@ function selectAll(element) {
settingsChanged(); settingsChanged();
var selectElement = $(`#${$(element).attr("my-input-to")}`); var selectElement = $(`#${$(element).attr("my-input-to")}`);
// Iterate over each option within the select element // Iterate over each option within the select element
selectElement.find('option').each(function() { selectElement.find('option').each(function() {
// Mark each option as selected // Mark each option as selected
@@ -409,13 +409,13 @@ function selectAll(element) {
function unselectAll(element) { function unselectAll(element) {
settingsChanged(); settingsChanged();
var selectElement = $(`#${$(element).attr("my-input-to")}`); var selectElement = $(`#${$(element).attr("my-input-to")}`);
// Iterate over each option within the select element // Iterate over each option within the select element
selectElement.find('option').each(function() { selectElement.find('option').each(function() {
// Unselect each option // Unselect each option
$(this).prop('selected', false); $(this).prop('selected', false);
}); });
// Trigger the 'change' event to notify Bootstrap Select of the changes // Trigger the 'change' event to notify Bootstrap Select of the changes
selectElement.trigger('change'); selectElement.trigger('change');
} }
@@ -426,7 +426,7 @@ function selectChange(element) {
settingsChanged(); settingsChanged();
var selectElement = $(`#${$(element).attr("my-input-to")}`); var selectElement = $(`#${$(element).attr("my-input-to")}`);
selectElement.parent().find("input").focus().click(); selectElement.parent().find("input").focus().click();
} }
@@ -464,9 +464,9 @@ function initListInteractionOptions(element) {
// Parent has my-transformers="name|base64" // Parent has my-transformers="name|base64"
const toId = $parent.attr("id"); const toId = $parent.attr("id");
const curValue = $option.val(); const curValue = $option.val();
const parsed = JSON.parse(atob($parent.data("elementoptionsbase64"))); const parsed = JSON.parse(atob($parent.data("elementoptionsbase64")));
const popupFormJson = parsed.find(obj => "popupForm" in obj)?.popupForm ?? null; const popupFormJson = parsed.find(obj => "popupForm" in obj)?.popupForm ?? null;
showModalPopupForm( showModalPopupForm(
`<i class="fa fa-pen-to-square"></i> ${getString("Gen_Update_Value")}`, // title `<i class="fa fa-pen-to-square"></i> ${getString("Gen_Update_Value")}`, // title
"", // message "", // message
@@ -515,8 +515,8 @@ function filterRows(inputText) {
var $panelHeader = $panel.find('.panel-heading'); var $panelHeader = $panel.find('.panel-heading');
var $panelBody = $panel.find('.panel-collapse'); var $panelBody = $panel.find('.panel-collapse');
$panel.show() $panel.show()
$panelHeader.show() $panelHeader.show()
$panelBody.collapse('show'); $panelBody.collapse('show');
$panelBody.find(".table_row:not(.docs)").each(function () { $panelBody.find(".table_row:not(.docs)").each(function () {
@@ -525,11 +525,11 @@ function filterRows(inputText) {
var isMetadataRow = rowId && rowId.endsWith("__metadata"); var isMetadataRow = rowId && rowId.endsWith("__metadata");
if (!isMetadataRow) { if (!isMetadataRow) {
$row.show() $row.show()
} }
}); });
}); });
} else{ } else{
// filter // filter
@@ -537,25 +537,25 @@ function filterRows(inputText) {
var $panel = $(this); var $panel = $(this);
var $panelHeader = $panel.find('.panel-heading'); var $panelHeader = $panel.find('.panel-heading');
var $panelBody = $panel.find('.panel-collapse'); var $panelBody = $panel.find('.panel-collapse');
var anyVisible = false; // Flag to check if any row is visible var anyVisible = false; // Flag to check if any row is visible
$panelBody.find(".table_row:not(.docs)").each(function () { $panelBody.find(".table_row:not(.docs)").each(function () {
var $row = $(this); var $row = $(this);
// Check if the row ID ends with "__metadata" // Check if the row ID ends with "__metadata"
var rowId = $row.attr("id"); var rowId = $row.attr("id");
var isMetadataRow = rowId && rowId.endsWith("__metadata"); var isMetadataRow = rowId && rowId.endsWith("__metadata");
// Always hide metadata rows // Always hide metadata rows
if (isMetadataRow) { if (isMetadataRow) {
$row.hide(); $row.hide();
return; // Skip further processing for metadata rows return; // Skip further processing for metadata rows
} }
var description = $row.find(".setting_description").text().toLowerCase(); var description = $row.find(".setting_description").text().toLowerCase();
var setKey = $row.find(".setting_name code").text().toLowerCase(); var setKey = $row.find(".setting_name code").text().toLowerCase();
if ( if (
description.includes(inputText.toLowerCase()) || description.includes(inputText.toLowerCase()) ||
setKey.includes(inputText.toLowerCase()) setKey.includes(inputText.toLowerCase())
@@ -566,7 +566,7 @@ function filterRows(inputText) {
$row.hide(); $row.hide();
} }
}); });
// Determine whether to hide or show the panel based on visibility of rows // Determine whether to hide or show the panel based on visibility of rows
if (anyVisible) { if (anyVisible) {
$panelBody.collapse('show'); // Ensure the panel body is shown if there are visible rows $panelBody.collapse('show'); // Ensure the panel body is shown if there are visible rows
@@ -582,7 +582,7 @@ function filterRows(inputText) {
} }
} }
@@ -661,7 +661,7 @@ function generateOptionsOrSetOptions(
processDataCallback, // Callback function to generate entries based on options processDataCallback, // Callback function to generate entries based on options
targetField, // Target field or element where selected value should be applied or updated targetField, // Target field or element where selected value should be applied or updated
transformers = [], // Transformers to be applied to the values transformers = [], // Transformers to be applied to the values
overrideOptions = null // override options if available overrideOptions = null // override options if available
) { ) {
// console.log(setKey); // console.log(setKey);
@@ -712,7 +712,7 @@ function applyTransformers(val, transformers) {
break; break;
case "getString": case "getString":
// no change // no change
val = val; val = val;
break; break;
default: default:
console.warn(`Unknown transformer: ${transformer}`); console.warn(`Unknown transformer: ${transformer}`);
@@ -745,13 +745,13 @@ function reverseTransformers(val, transformers) {
break; break;
case "getString": case "getString":
// retrieve string // retrieve string
val = getString(val); val = getString(val);
break; break;
case "deviceChip": case "deviceChip":
mac = val // value is mac mac = val // value is mac
val = `${getDevDataByMac(mac, "devName")}` val = `${getDevDataByMac(mac, "devName")}`
break; break;
case "deviceRelType": case "deviceRelType":
val = val; // nothing to do val = val; // nothing to do
break; break;
default: default:
@@ -779,10 +779,11 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
let getStringKey = ""; let getStringKey = "";
let onClick = "console.log('onClick - Not implemented');"; let onClick = "console.log('onClick - Not implemented');";
let onChange = "console.log('onChange - Not implemented');"; let onChange = "console.log('onChange - Not implemented');";
let focusout = "console.log('focusout - Not implemented');";
let customParams = ""; let customParams = "";
let customId = ""; let customId = "";
let columns = []; let columns = [];
let base64Regex = ""; let base64Regex = "";
let elementOptionsBase64 = btoa(JSON.stringify(elementOptions)); let elementOptionsBase64 = btoa(JSON.stringify(elementOptions));
elementOptions.forEach((option) => { elementOptions.forEach((option) => {
@@ -830,6 +831,9 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
if (option.onChange) { if (option.onChange) {
onChange = option.onChange; onChange = option.onChange;
} }
if (option.focusout) {
focusout = option.focusout;
}
if (option.customParams) { if (option.customParams) {
customParams = option.customParams; customParams = option.customParams;
} }
@@ -867,7 +871,8 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
customId, customId,
columns, columns,
base64Regex, base64Regex,
elementOptionsBase64 elementOptionsBase64,
focusout
}; };
}; };
@@ -877,7 +882,7 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// -------------------------------------------------- // --------------------------------------------------
// Creates an object from an array // Creates an object from an array
function arrayToObject(array) { function arrayToObject(array) {
const obj = []; const obj = [];
array.forEach((item, index) => { array.forEach((item, index) => {
@@ -895,18 +900,18 @@ function generateOptions(options, valuesArray, targetField, transformers, placeh
resultArray = [] resultArray = []
selectedArray = [] selectedArray = []
cssClass = "" cssClass = ""
// determine if options or values are used in the listing // determine if options or values are used in the listing
if (valuesArray.length > 0 && options.length > 0){ if (valuesArray.length > 0 && options.length > 0){
// multiselect list -> options only + selected the ones in valuesArray // multiselect list -> options only + selected the ones in valuesArray
resultArray = options; resultArray = options;
selectedArray = valuesArray selectedArray = valuesArray
} else if (valuesArray.length > 0 && options.length == 0){ } else if (valuesArray.length > 0 && options.length == 0){
// editable list -> values only // editable list -> values only
resultArray = arrayToObject(valuesArray) resultArray = arrayToObject(valuesArray)
cssClass = "interactable-option" // generates [1x 📝 | 2x 🚮] cssClass = "interactable-option" // generates [1x 📝 | 2x 🚮]
} else if (options.length > 0){ } else if (options.length > 0){
@@ -914,7 +919,7 @@ function generateOptions(options, valuesArray, targetField, transformers, placeh
// dropdown -> options only (value == 1 STRING not ARRAY) // dropdown -> options only (value == 1 STRING not ARRAY)
resultArray = options; resultArray = options;
} }
// Create a map to track the index of each item in valuesArray // Create a map to track the index of each item in valuesArray
const orderMap = new Map(valuesArray.map((item, index) => [item, index])); const orderMap = new Map(valuesArray.map((item, index) => [item, index]));
@@ -961,7 +966,7 @@ function generateList(options, valuesArray, targetField, transformers, placehold
listHtml += `<li ${selected}>${labelName}</li>`; listHtml += `<li ${selected}>${labelName}</li>`;
}); });
// Place the resulting HTML into the specified placeholder div // Place the resulting HTML into the specified placeholder div
$("#" + placeholder).replaceWith(listHtml); $("#" + placeholder).replaceWith(listHtml);
} }
@@ -972,7 +977,7 @@ function genListWithInputSet(options, valuesArray, targetField, transformers, pl
var listHtml = ""; var listHtml = "";
options.forEach(function(item) { options.forEach(function(item) {
let selected = valuesArray.includes(item.id) ? 'selected' : ''; let selected = valuesArray.includes(item.id) ? 'selected' : '';
@@ -988,9 +993,9 @@ function genListWithInputSet(options, valuesArray, targetField, transformers, pl
} }
listHtml += `<li ${selected}> listHtml += `<li ${selected}>
<a href="javascript:void(0)" onclick="setTextValue('${targetField}','${item.id}')">${labelName}</a> <a href="javascript:void(0)" onclick="setTextValue('${targetField}','${item.id}')">${labelName}</a>
</li>`; </li>`;
}); });
// Place the resulting HTML into the specified placeholder div // Place the resulting HTML into the specified placeholder div
@@ -1001,8 +1006,8 @@ function genListWithInputSet(options, valuesArray, targetField, transformers, pl
// Collects a setting based on code name // Collects a setting based on code name
function collectSetting(prefix, setCodeName, setType, settingsArray) { function collectSetting(prefix, setCodeName, setType, settingsArray) {
// Parse setType if it's a JSON string // Parse setType if it's a JSON string
const setTypeObject = (typeof setType === "string") const setTypeObject = (typeof setType === "string")
? JSON.parse(processQuotes(setType)) ? JSON.parse(processQuotes(setType))
: setType; : setType;
const dataType = setTypeObject.dataType; const dataType = setTypeObject.dataType;
@@ -1015,6 +1020,20 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
const { elementType, elementOptions = [], transformers = [] } = elementWithInputValue; const { elementType, elementOptions = [], transformers = [] } = elementWithInputValue;
// Check if validation failed
if (
$(`#${setCodeName}`)
&& $(`#${setCodeName}`).attr("data-is-valid")
&& $(`#${setCodeName}`).attr("data-is-valid") == 0
)
{
return {
"settingsArray": settingsArray,
"dataIsValid": false,
"failedSettingKey": setCodeName
};
}
const opts = handleElementOptions('none', elementOptions, transformers, val = ""); const opts = handleElementOptions('none', elementOptions, transformers, val = "");
// Map of handlers // Map of handlers
@@ -1038,7 +1057,7 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
let temps = []; let temps = [];
if (opts.isOrdeable) { if (opts.isOrdeable) {
temps = $(`#${setCodeName}`).val(); temps = $(`#${setCodeName}`).val();
} else { } else {
const sel = $(`#${setCodeName}`).attr("my-editable") === "true" ? "" : ":selected"; const sel = $(`#${setCodeName}`).attr("my-editable") === "true" ? "" : ":selected";
$(`#${setCodeName} option${sel}`).each(function() { $(`#${setCodeName} option${sel}`).each(function() {
const vl = $(this).val(); const vl = $(this).val();
@@ -1066,7 +1085,7 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
let handlerKey; let handlerKey;
if (dataType === "string" && elementType === "datatable") { if (dataType === "string" && elementType === "datatable") {
handlerKey = "datatableString"; handlerKey = "datatableString";
} else if (dataType === "string" || } else if (dataType === "string" ||
(dataType === "integer" && (opts.inputType === "number" || opts.inputType === "text"))) { (dataType === "integer" && (opts.inputType === "number" || opts.inputType === "text"))) {
handlerKey = "simpleValue"; handlerKey = "simpleValue";
} else if (opts.inputType === "checkbox") { } else if (opts.inputType === "checkbox") {
@@ -1084,7 +1103,11 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
const value = handlers[handlerKey](); const value = handlers[handlerKey]();
settingsArray.push([prefix, setCodeName, dataType, value]); settingsArray.push([prefix, setCodeName, dataType, value]);
return settingsArray; return {
"settingsArray": settingsArray,
"dataIsValid": true,
"failedSettingKey": ""
};
} }
@@ -1093,22 +1116,22 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
function generateFormHtml(settingsData, set, overrideValue, overrideOptions, originalSetKey) { function generateFormHtml(settingsData, set, overrideValue, overrideOptions, originalSetKey) {
let inputHtml = ''; let inputHtml = '';
isEmpty(overrideValue) ? inVal = set['setValue'] : inVal = overrideValue; isEmpty(overrideValue) ? inVal = set['setValue'] : inVal = overrideValue;
const setKey = set['setKey']; const setKey = set['setKey'];
const setType = set['setType']; const setType = set['setType'];
// if (setKey == '') { // if (setKey == '') {
// console.log(setType); // console.log(setType);
// console.log(setKey); // console.log(setKey);
// console.log(overrideValue); // console.log(overrideValue);
// console.log(inVal); // console.log(inVal);
// } // }
// Parse the setType JSON string // Parse the setType JSON string
// console.log(processQuotes(setType)); // console.log(processQuotes(setType));
const setTypeObject = JSON.parse(processQuotes(setType)) const setTypeObject = JSON.parse(processQuotes(setType))
const dataType = setTypeObject.dataType; const dataType = setTypeObject.dataType;
const elements = setTypeObject.elements || []; const elements = setTypeObject.elements || [];
@@ -1137,20 +1160,21 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
customId, customId,
columns, columns,
base64Regex, base64Regex,
elementOptionsBase64 elementOptionsBase64,
focusout
} = handleElementOptions(setKey, elementOptions, transformers, inVal); } = handleElementOptions(setKey, elementOptions, transformers, inVal);
// Override value // Override value
let val = valRes; let val = valRes;
// if (setKey == '') { // if (setKey == '') {
// console.log(setType); // console.log(setType);
// console.log(setKey); // console.log(setKey);
// console.log(overrideValue); // console.log(overrideValue);
// console.log(inVal); // console.log(inVal);
// console.log(val); // console.log(val);
// } // }
// Generate HTML based on elementType // Generate HTML based on elementType
@@ -1159,16 +1183,17 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
const multi = isMultiSelect ? "multiple" : ""; const multi = isMultiSelect ? "multiple" : "";
const addCss = isOrdeable ? "select2 select2-hidden-accessible" : ""; const addCss = isOrdeable ? "select2 select2-hidden-accessible" : "";
inputHtml += `<select onChange="settingsChanged();${onChange}" inputHtml += `<select onChange="settingsChanged();${onChange}"
my-data-type="${dataType}" onfocusout="${focusout}"
my-editable="${editable}" my-data-type="${dataType}"
class="form-control ${addCss} ${cssClasses}" my-editable="${editable}"
name="${setKey}" class="form-control ${addCss} ${cssClasses}"
id="${setKey}" name="${setKey}"
id="${setKey}"
my-transformers=${transformers} my-transformers=${transformers}
my-customparams="${customParams}" my-customparams="${customParams}"
my-customid="${customId}" my-customid="${customId}"
my-originalSetKey="${originalSetKey}" my-originalSetKey="${originalSetKey}"
data-elementoptionsbase64="${elementOptionsBase64}" data-elementoptionsbase64="${elementOptionsBase64}"
${multi} ${multi}
${readOnly ? "disabled" : ""}> ${readOnly ? "disabled" : ""}>
@@ -1182,31 +1207,32 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
const checked = val === 'True' || val === 'true' || val === '1' ? 'checked' : ''; const checked = val === 'True' || val === 'true' || val === '1' ? 'checked' : '';
const inputClass = inputType === 'checkbox' ? 'checkbox' : 'form-control'; const inputClass = inputType === 'checkbox' ? 'checkbox' : 'form-control';
inputHtml += `<input inputHtml += `<input
class="${inputClass} ${cssClasses}" class="${inputClass} ${cssClasses}"
onChange="settingsChanged();${onChange}" onChange="settingsChanged();${onChange}"
my-data-type="${dataType}" onfocusout="${focusout}"
my-customparams="${customParams}" my-data-type="${dataType}"
my-customid="${customId}" my-customparams="${customParams}"
my-customid="${customId}"
my-originalSetKey="${originalSetKey}" my-originalSetKey="${originalSetKey}"
my-base64Regex="${base64Regex}" my-base64Regex="${base64Regex}"
id="${setKey}${suffix}" id="${setKey}${suffix}"
type="${inputType}" type="${inputType}"
value="${val}" value="${val}"
${readOnly} ${readOnly}
${checked} ${checked}
placeholder="${placeholder}" placeholder="${placeholder}"
/>`; />`;
break; break;
case 'button': case 'button':
inputHtml += `<button inputHtml += `<button
class="btn btn-primary ${cssClasses}" class="btn btn-primary ${cssClasses}"
my-customparams="${customParams}" my-customparams="${customParams}"
my-customid="${customId}" my-customid="${customId}"
my-originalSetKey="${originalSetKey}" my-originalSetKey="${originalSetKey}"
my-input-from="${sourceIds}" my-input-from="${sourceIds}"
my-input-to="${setKey}" my-input-to="${setKey}"
data-elementoptionsbase64="${elementOptionsBase64}" data-elementoptionsbase64="${elementOptionsBase64}"
onclick="${onClick}"> onclick="${onClick}">
${getString(getStringKey)} ${getString(getStringKey)}
@@ -1214,21 +1240,23 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
break; break;
case 'textarea': case 'textarea':
inputHtml += `<textarea inputHtml += `<textarea
class="form-control input" class="form-control input"
my-customparams="${customParams}" onChange="settingsChanged();${onChange}"
my-customid="${customId}" onfocusout="${focusout}"
my-customparams="${customParams}"
my-customid="${customId}"
my-originalSetKey="${originalSetKey}" my-originalSetKey="${originalSetKey}"
my-data-type="${dataType}" my-data-type="${dataType}"
id="${setKey}" id="${setKey}"
${readOnly}>${val}</textarea>`; ${readOnly}>${val}</textarea>`;
break; break;
case 'span': case 'span':
inputHtml += `<span inputHtml += `<span
class="${cssClasses}" class="${cssClasses}"
my-data-type="${dataType}" my-data-type="${dataType}"
my-customparams="${customParams}" my-customparams="${customParams}"
my-customid="${customId}" my-customid="${customId}"
my-originalSetKey="${originalSetKey}" my-originalSetKey="${originalSetKey}"
onclick="${onClick}"> onclick="${onClick}">
@@ -1264,13 +1292,13 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
columnSetting["setOptions"] = getSetting(column.optionsOverride.replace("setting.","")); columnSetting["setOptions"] = getSetting(column.optionsOverride.replace("setting.",""));
} else { } else {
columnSetting["setOptions"] = column.optionsOverride; columnSetting["setOptions"] = column.optionsOverride;
} }
} }
columnSettings.push(columnSetting) columnSettings.push(columnSetting)
// helper for if val is empty // helper for if val is empty
emptyVal.push(''); emptyVal.push('');
}); });
datatableHtml += '</tr></thead>'; datatableHtml += '</tr></thead>';
@@ -1290,7 +1318,7 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
let index = 0; let index = 0;
val.forEach(rowData => { val.forEach(rowData => {
datatableHtml += `<tr my-index="${index}">`; datatableHtml += `<tr my-index="${index}">`;
let j = 0; let j = 0;
columnSettings.forEach(set => { columnSettings.forEach(set => {
// Extract the value for the current column based on the new structure // Extract the value for the current column based on the new structure
@@ -1300,11 +1328,11 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
{ {
columnOverrideValue = "" columnOverrideValue = ""
} }
// Create unique key to prevent dropdown data duplication // Create unique key to prevent dropdown data duplication
const oldKey = set["setKey"]; const oldKey = set["setKey"];
set["setKey"] = oldKey + "_" + index; set["setKey"] = oldKey + "_" + index;
// Generate the cell HTML using the extracted value // Generate the cell HTML using the extracted value
const cellHtml = generateFormHtml( const cellHtml = generateFormHtml(
settingsData, settingsData,
@@ -1314,17 +1342,17 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
oldKey oldKey
); );
datatableHtml += `<td> <div class="input-group"> ${cellHtml} </div></td>`; datatableHtml += `<td> <div class="input-group"> ${cellHtml} </div></td>`;
// Restore the original key // Restore the original key
set["setKey"] = oldKey; set["setKey"] = oldKey;
j++; j++;
}); });
datatableHtml += '</tr>'; datatableHtml += '</tr>';
index++; index++;
}); });
datatableHtml += '</tbody></table>'; datatableHtml += '</tbody></table>';
inputHtml += datatableHtml; inputHtml += datatableHtml;
@@ -1347,8 +1375,8 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
// Generate event HTML if applicable // Generate event HTML if applicable
let eventsHtml = ''; let eventsHtml = '';
const eventsList = createArray(set['setEvents']); const eventsList = createArray(set['setEvents']);
// inline buttons events // inline buttons events
if (eventsList.length > 0) { if (eventsList.length > 0) {
eventsList.forEach(event => { eventsList.forEach(event => {
@@ -1387,7 +1415,7 @@ if (eventsList.length > 0) {
data-myparam-setkey="${setKey}" data-myparam-setkey="${setKey}"
data-myparam="${setKey}" data-myparam="${setKey}"
data-myparam-plugin="${setKey.split('_')[0] || ''}" data-myparam-plugin="${setKey.split('_')[0] || ''}"
data-myevent="${event}" data-myevent="${event}"
onclick="execute_settingEvent(this)"> onclick="execute_settingEvent(this)">
<i title="${getString(event + "_event_tooltip")}" class="fa ${eventIcon}"></i> <i title="${getString(event + "_event_tooltip")}" class="fa ${eventIcon}"></i>
</span>`; </span>`;
@@ -1406,15 +1434,15 @@ function getSetObject(settingsData, setKey) {
result = "" result = ""
settingsData.forEach(function(set) { settingsData.forEach(function(set) {
if (set.setKey == setKey) { if (set.setKey == setKey) {
// console.log(set); // console.log(set);
result = set; result = set;
return; return;
} }
}); });
if(result == "") if(result == "")
@@ -1439,7 +1467,7 @@ function collectTableData(tableSelector) {
cells.each((index, cell) => { cells.each((index, cell) => {
const input = $(cell).find('input, select, textarea'); const input = $(cell).find('input, select, textarea');
if (input.length) { if (input.length) {
if (input.attr('type') === 'checkbox') { if (input.attr('type') === 'checkbox') {
// For checkboxes, check if they are checked // For checkboxes, check if they are checked
@@ -1455,10 +1483,10 @@ function collectTableData(tableSelector) {
} }
}); });
tableData.push(rowData); tableData.push(rowData);
}); });
return tableData; return tableData;
} }

View File

@@ -1,6 +1,6 @@
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* NetAlertX * NetAlertX
* Open Source Network Guard / WIFI & LAN intrusion detector * Open Source Network Guard / WIFI & LAN intrusion detector
* *
* ui_components.js - Front module. Common UI components * ui_components.js - Front module. Common UI components
*------------------------------------------------------------------------------- *-------------------------------------------------------------------------------
@@ -56,7 +56,7 @@ function getRandomBytes(elem, length) {
window.crypto.getRandomValues(array); window.crypto.getRandomValues(array);
// Convert bytes to hexadecimal string // Convert bytes to hexadecimal string
let hexString = Array.from(array, byte => let hexString = Array.from(array, byte =>
byte.toString(16).padStart(2, '0') byte.toString(16).padStart(2, '0')
).join(''); ).join('');
@@ -71,7 +71,7 @@ function getRandomBytes(elem, length) {
} }
// ---------------------------------------------- // ----------------------------------------------
// Updates the icon preview // Updates the icon preview
function updateAllIconPreviews() { function updateAllIconPreviews() {
$(".iconInputVal").each((index, el)=>{ $(".iconInputVal").each((index, el)=>{
updateIconPreview(el) updateIconPreview(el)
@@ -79,7 +79,7 @@ function updateAllIconPreviews() {
} }
// ---------------------------------------------- // ----------------------------------------------
// Updates the icon preview // Updates the icon preview
function updateIconPreview(elem) { function updateIconPreview(elem) {
const previewSpan = $(elem).parent().find(".iconPreview"); const previewSpan = $(elem).parent().find(".iconPreview");
@@ -97,7 +97,7 @@ function updateIconPreview(elem) {
previewSpan.html(atob(newValue)); previewSpan.html(atob(newValue));
}); });
return; // Stop retrying if successful return; // Stop retrying if successful
} }
attempts++; attempts++;
if (attempts < 10) { if (attempts < 10) {
@@ -119,9 +119,9 @@ function validateRegex(elem) {
const iconSpan = $(elem).parent().find(".validityCheck"); const iconSpan = $(elem).parent().find(".validityCheck");
const inputElem = $(elem); const inputElem = $(elem);
const regexTmp = atob($(inputElem).attr("my-base64Regex")); // Decode base64 regex const regexTmp = atob($(inputElem).attr("my-base64Regex")); // Decode base64 regex
const regex = new RegExp(regexTmp); // Convert to a valid RegExp object const regex = new RegExp(regexTmp); // Convert to a valid RegExp object
let attempts = 0; let attempts = 0;
function tryUpdateValidityResultIcon() { function tryUpdateValidityResultIcon() {
@@ -140,8 +140,11 @@ function validateRegex(elem) {
// Validate against regex // Validate against regex
if (regex.test(value)) { if (regex.test(value)) {
iconSpan.html("<i class='fa fa-check'></i>"); iconSpan.html("<i class='fa fa-check'></i>");
inputElem.attr("data-is-valid", "1");
} else { } else {
iconSpan.html("<i class='fa fa-xmark'></i>"); iconSpan.html("<i class='fa fa-xmark'></i>");
showModalOk('WARNING', getString("Gen_Invalid_Value"));
inputElem.attr("data-is-valid", "0");
} }
} }
@@ -175,7 +178,7 @@ function initializeiCheck () {
increaseArea: '20%' increaseArea: '20%'
}); });
} }
@@ -206,7 +209,7 @@ function copyToClipboard(buttonElement) {
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Simple Sortable Table columns // Simple Sortable Table columns
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Function to handle column sorting when a user clicks on a table header // Function to handle column sorting when a user clicks on a table header
@@ -268,9 +271,9 @@ function ipToNum(ip) {
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// handling events // handling events
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
modalEventStatusId = 'modal-message-front-event' modalEventStatusId = 'modal-message-front-event'
@@ -301,41 +304,41 @@ function execute_settingEvent(element) {
updateModalState() updateModalState()
} }
}) })
} else if (["add_option"].includes(feEvent)) { } else if (["add_option"].includes(feEvent)) {
showModalFieldInput ( showModalFieldInput (
'<i class="fa fa-square-plus pointer"></i> ' + getString('Gen_Add'), '<i class="fa fa-square-plus pointer"></i> ' + getString('Gen_Add'),
getString('Gen_Add'), getString('Gen_Add'),
getString('Gen_Cancel'), getString('Gen_Cancel'),
getString('Gen_Okay'), getString('Gen_Okay'),
'', // curValue '', // curValue
'addOptionFromModalInput', 'addOptionFromModalInput',
feSourceId // triggered by id feSourceId // triggered by id
); );
} else if (["add_icon"].includes(feEvent)) { } else if (["add_icon"].includes(feEvent)) {
// Add new icon as base64 string // Add new icon as base64 string
showModalInput ( showModalInput (
'<i class="fa fa-square-plus pointer"></i> ' + getString('DevDetail_button_AddIcon'), '<i class="fa fa-square-plus pointer"></i> ' + getString('DevDetail_button_AddIcon'),
getString('DevDetail_button_AddIcon_Help'), getString('DevDetail_button_AddIcon_Help'),
getString('Gen_Cancel'), getString('Gen_Cancel'),
getString('Gen_Okay'), getString('Gen_Okay'),
() => addIconAsBase64(element), // Wrap in an arrow function () => addIconAsBase64(element), // Wrap in an arrow function
feSourceId // triggered by id feSourceId // triggered by id
); );
} else if (["select_icon"].includes(feEvent)) { } else if (["select_icon"].includes(feEvent)) {
showIconSelection(feSetKey) showIconSelection(feSetKey)
// myparam-setkey // myparam-setkey
} else if (["copy_icons"].includes(feEvent)) { } else if (["copy_icons"].includes(feEvent)) {
// Ask overwrite icon types // Ask overwrite icon types
showModalWarning ( showModalWarning (
getString('DevDetail_button_OverwriteIcons'), getString('DevDetail_button_OverwriteIcons'),
getString('DevDetail_button_OverwriteIcons_Warning'), getString('DevDetail_button_OverwriteIcons_Warning'),
getString('Gen_Cancel'), getString('Gen_Cancel'),
getString('Gen_Okay'), getString('Gen_Okay'),
'overwriteIconType', 'overwriteIconType',
feSourceId // triggered by id feSourceId // triggered by id
); );
@@ -343,30 +346,30 @@ function execute_settingEvent(element) {
goToDevice(feValue); goToDevice(feValue);
} else if (["go_to_node"].includes(feEvent)) { } else if (["go_to_node"].includes(feEvent)) {
goToNetworkNode(feValue); goToNetworkNode(feValue);
} else { } else {
console.warn(`🔺Not implemented: ${feEvent}`) console.warn(`🔺Not implemented: ${feEvent}`)
} }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Go to the correct network node in the Network section // Go to the correct network node in the Network section
function overwriteIconType() function overwriteIconType()
{ {
const mac = getMac(); const mac = getMac();
if (!isValidMac(mac)) { if (!isValidMac(mac)) {
showModalOK("Error", getString("Gen_InvalidMac")) showModalOK("Error", getString("Gen_InvalidMac"))
return; return;
} }
// Construct SQL query // Construct SQL query
const rawSql = ` const rawSql = `
UPDATE Devices UPDATE Devices
SET devIcon = ( SET devIcon = (
SELECT devIcon FROM Devices WHERE devMac = "${mac}" SELECT devIcon FROM Devices WHERE devMac = "${mac}"
) )
@@ -391,24 +394,24 @@ function overwriteIconType()
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Go to the correct network node in the Network section // Go to the correct network node in the Network section
function goToNetworkNode(mac) function goToNetworkNode(mac)
{ {
setCache('activeNetworkTab', mac.replaceAll(":","_")+'_id'); setCache('activeNetworkTab', mac.replaceAll(":","_")+'_id');
window.location.href = './network.php'; window.location.href = './network.php';
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Go to the device // Go to the device
function goToDevice(mac, newtab = false) { function goToDevice(mac, newtab = false) {
const url = './deviceDetails.php?mac=' + encodeURIComponent(mac); const url = './deviceDetails.php?mac=' + encodeURIComponent(mac);
if (newtab) { if (newtab) {
window.open(url, '_blank'); window.open(url, '_blank');
} else { } else {
window.location.href = url; window.location.href = url;
} }
} }
// -------------------------------------------------------- // --------------------------------------------------------
// Updating the execution queue in in modal pop-up // Updating the execution queue in in modal pop-up
@@ -437,7 +440,7 @@ function updateModalState() {
function addOptionFromModalInput() { function addOptionFromModalInput() {
var inputVal = $(`#modal-field-input-field`).val(); var inputVal = $(`#modal-field-input-field`).val();
console.log($('#modal-field-input-field')); console.log($('#modal-field-input-field'));
var triggeredBy = $('#modal-field-input').attr("data-myparam-triggered-by"); var triggeredBy = $('#modal-field-input').attr("data-myparam-triggered-by");
var targetId = $('#' + triggeredBy).attr("data-myparam-setkey"); var targetId = $('#' + triggeredBy).attr("data-myparam-setkey");
@@ -475,16 +478,16 @@ function addIconAsBase64 (el) {
console.log($('#modal-field-input-field')); console.log($('#modal-field-input-field'));
var triggeredBy = $('#modal-input').attr("data-myparam-triggered-by"); var triggeredBy = $('#modal-input').attr("data-myparam-triggered-by");
var targetId = $('#' + triggeredBy).attr("data-myparam-setkey"); var targetId = $('#' + triggeredBy).attr("data-myparam-setkey");
// $('#'+targetId).val(iconHtmlBase64); // $('#'+targetId).val(iconHtmlBase64);
// Add new option and set it as selected // Add new option and set it as selected
$('#' + targetId).append(new Option(iconHtmlBase64, iconHtmlBase64)).val(iconHtmlBase64); $('#' + targetId).append(new Option(iconHtmlBase64, iconHtmlBase64)).val(iconHtmlBase64);
updateIconPreview(el) updateIconPreview(el)
} }
@@ -522,8 +525,8 @@ function showIconSelection(setKey) {
// Populate the icon list // Populate the icon list
Array.from(selectElement.options).forEach(option => { Array.from(selectElement.options).forEach(option => {
if (option.value != "") { if (option.value != "") {
const value = option.value; const value = option.value;
// Decode the base64 value // Decode the base64 value
@@ -566,7 +569,7 @@ function showIconSelection(setKey) {
}); });
// //
} }
@@ -661,7 +664,7 @@ function getRelationshipConf(relType) {
// --color-red: #dd4b39; // --color-red: #dd4b39;
switch (relType) { switch (relType) {
case "child": case "child":
color = "#f39c12"; // yellow color = "#f39c12"; // yellow
cssClass = "text-yellow"; cssClass = "text-yellow";
@@ -673,11 +676,11 @@ function getRelationshipConf(relType) {
case "virtual": case "virtual":
color = "#0060df"; // blue color = "#0060df"; // blue
cssClass = "text-blue"; cssClass = "text-blue";
break; break;
case "logical": case "logical":
color = "#00a65a"; // green color = "#00a65a"; // green
cssClass = "text-green"; cssClass = "text-green";
break; break;
default: default:
color = "#5B5B66"; // grey color = "#5B5B66"; // grey
cssClass = "text-light-grey"; cssClass = "text-light-grey";
@@ -703,13 +706,13 @@ function initSelect2() {
// check if cache ready // check if cache ready
if(isValidJSON(devicesListAll_JSON)) if(isValidJSON(devicesListAll_JSON))
{ {
// -------------------------------------------------------- // --------------------------------------------------------
//Initialize Select2 Elements and make them sortable //Initialize Select2 Elements and make them sortable
$(function () { $(function () {
// Iterate over each Select2 dropdown // Iterate over each Select2 dropdown
$('.select2').each(function() { $('.select2').each(function() {
// handle Device chips, if my-transformers="deviceChip" // handle Device chips, if my-transformers="deviceChip"
if($(this).attr("my-transformers") == "deviceChip") if($(this).attr("my-transformers") == "deviceChip")
{ {
@@ -721,7 +724,7 @@ function initSelect2() {
return m; // Allow HTML return m; // Allow HTML
} }
}); });
} else if($(this).attr("my-transformers") == "deviceRelType") // handling dropdown for relationships } else if($(this).attr("my-transformers") == "deviceRelType") // handling dropdown for relationships
{ {
var selectEl = $(this).select2({ var selectEl = $(this).select2({
@@ -730,26 +733,26 @@ function initSelect2() {
if (!data.id) return data.text; // default for placeholder etc. if (!data.id) return data.text; // default for placeholder etc.
const relConf = getRelationshipConf(data.text); const relConf = getRelationshipConf(data.text);
// Custom HTML // Custom HTML
const html = $(` const html = $(`
<span class="custom-chip ${relConf.cssClass}" > <span class="custom-chip ${relConf.cssClass}" >
${data.text} ${data.text}
</span> </span>
`); `);
return html; return html;
}, },
escapeMarkup: function (m) { escapeMarkup: function (m) {
return m; // Allow HTML return m; // Allow HTML
} }
}); });
} else // default handling - default template } else // default handling - default template
{ {
var selectEl = $(this).select2(); var selectEl = $(this).select2();
} }
// Apply sortable functionality to the dropdown's dropdown-container // Apply sortable functionality to the dropdown's dropdown-container
selectEl.next().children().children().children().sortable({ selectEl.next().children().children().children().sortable({
containment: 'parent', containment: 'parent',
@@ -757,14 +760,14 @@ function initSelect2() {
var sortedValues = $(this).children().map(function() { var sortedValues = $(this).children().map(function() {
return $(this).attr('title'); return $(this).attr('title');
}).get(); }).get();
var sortedOptions = selectEl.find('option').sort(function(a, b) { var sortedOptions = selectEl.find('option').sort(function(a, b) {
return sortedValues.indexOf($(a).text()) - sortedValues.indexOf($(b).text()); return sortedValues.indexOf($(a).text()) - sortedValues.indexOf($(b).text());
}); });
// Replace all options in selectEl // Replace all options in selectEl
selectEl.empty().append(sortedOptions); selectEl.empty().append(sortedOptions);
// Trigger change event on Select2 // Trigger change event on Select2
selectEl.trigger('change'); selectEl.trigger('change');
} }
@@ -776,7 +779,7 @@ function initSelect2() {
setTimeout(() => { setTimeout(() => {
initSelect2() initSelect2()
}, 1000); }, 1000);
} }
} }
// ------------------------------------------ // ------------------------------------------
@@ -816,7 +819,7 @@ function renderDeviceLink(data, container, useName = false) {
'data-alert': device.devAlertDown, 'data-alert': device.devAlertDown,
'data-icon': device.devIcon 'data-icon': device.devIcon
}); });
return ` return `
<a href="${badge.url}" target="_blank"> <a href="${badge.url}" target="_blank">
<span class="custom-chip"> <span class="custom-chip">
@@ -866,7 +869,7 @@ function initHoverNodeInfo() {
$(document).on('mouseenter', '.hover-node-info', function (e) { $(document).on('mouseenter', '.hover-node-info', function (e) {
const $el = $(this); const $el = $(this);
lastTarget = this; lastTarget = this;
// use timeout to prevent a quick hover and exit toi flash a card when navigating to a target node with your mouse // use timeout to prevent a quick hover and exit toi flash a card when navigating to a target node with your mouse
clearTimeout(hoverTimeout); clearTimeout(hoverTimeout);
@@ -893,25 +896,25 @@ function initHoverNodeInfo() {
<div class="line"> <div class="line">
<b>Status:</b> <span>${status}</span><br> <b>Status:</b> <span>${status}</span><br>
</div> </div>
<div class="line"> <div class="line">
<b>IP:</b> <span>${ip}</span><br> <b>IP:</b> <span>${ip}</span><br>
</div> </div>
<div class="line"> <div class="line">
<b>MAC:</b> <span>${mac}</span><br> <b>MAC:</b> <span>${mac}</span><br>
</div> </div>
<div class="line"> <div class="line">
<b>Vendor:</b> <span>${vendor}</span><br> <b>Vendor:</b> <span>${vendor}</span><br>
</div> </div>
<div class="line"> <div class="line">
<b>Type:</b> <span>${type}</span><br> <b>Type:</b> <span>${type}</span><br>
</div> </div>
<div class="line"> <div class="line">
<b>First seen:</b> <span>${firstseen}</span><br> <b>First seen:</b> <span>${firstseen}</span><br>
</div> </div>
<div class="line"> <div class="line">
<b>Last seen:</b> <span>${lastseen}</span><br> <b>Last seen:</b> <span>${lastseen}</span><br>
</div> </div>
<div class="line"> <div class="line">
<b>Relationship:</b> <span class="${getRelationshipConf(relationship).cssClass}">${relationship}</span> <b>Relationship:</b> <span class="${getRelationshipConf(relationship).cssClass}">${relationship}</span>
</div> </div>
`; `;

View File

@@ -18,19 +18,19 @@
</div> </div>
<div class="deviceSelector col-md-11 col-sm-11" style="z-index:5"> <div class="deviceSelector col-md-11 col-sm-11" style="z-index:5">
<div class="db_info_table_row col-sm-12" > <div class="db_info_table_row col-sm-12" >
<div class="form-group" > <div class="form-group" >
<div class="input-group col-sm-12 " > <div class="input-group col-sm-12 " >
<select class="form-control select2 select2-hidden-accessible" multiple="" style="width: 100%;" tabindex="-1" aria-hidden="true"> <select class="form-control select2 select2-hidden-accessible" multiple="" style="width: 100%;" tabindex="-1" aria-hidden="true">
</select> </select>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-1 hoverHighlight"> <div class="col-md-1 hoverHighlight">
<i class="fa-solid fa-circle-check hoverHighlight pointer" onclick="markAllSelected()" title="<?= lang('Gen_Add_All');?>"></i> <i class="fa-solid fa-circle-check hoverHighlight pointer" onclick="markAllSelected()" title="<?= lang('Gen_Add_All');?>"></i>
<i class="fa-solid fa-circle-xmark hoverHighlight pointer" onclick="markAllNotSelected()" title="<?= lang('Gen_Remove_All');?>"></i> <i class="fa-solid fa-circle-xmark hoverHighlight pointer" onclick="markAllNotSelected()" title="<?= lang('Gen_Remove_All');?>"></i>
</div> </div>
</div> </div>
@@ -69,19 +69,19 @@
<script defer> <script defer>
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Get plugin and settings data from API endpoints // Get plugin and settings data from API endpoints
function getData(){ function getData(){
// some race condition, need to implement delay // some race condition, need to implement delay
setTimeout(() => { setTimeout(() => {
$.get('php/server/query_json.php', { file: 'table_settings.json', nocache: Date.now() }, function(res) { $.get('php/server/query_json.php', { file: 'table_settings.json', nocache: Date.now() }, function(res) {
settingsData = res["data"]; settingsData = res["data"];
excludedColumns = ["NEWDEV_devMac", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection", "NEWDEV_devLastNotification", "NEWDEV_devScan", "NEWDEV_devPresentLastScan", "NEWDEV_devCustomProps", "NEWDEV_devChildrenNicsDynamic", "NEWDEV_devChildrenDynamic" ] excludedColumns = ["NEWDEV_devMac", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection", "NEWDEV_devLastNotification", "NEWDEV_devScan", "NEWDEV_devPresentLastScan", "NEWDEV_devCustomProps", "NEWDEV_devChildrenNicsDynamic", "NEWDEV_devChildrenDynamic" ]
const relevantColumns = settingsData.filter(set => const relevantColumns = settingsData.filter(set =>
set.setGroup === "NEWDEV" && set.setGroup === "NEWDEV" &&
set.setKey.includes("_dev") && set.setKey.includes("_dev") &&
@@ -103,7 +103,7 @@
// Append form groups to the column // Append form groups to the column
for (let j = i * elementsPerColumn; j < Math.min((i + 1) * elementsPerColumn, multiEditColumns.length); j++) { for (let j = i * elementsPerColumn; j < Math.min((i + 1) * elementsPerColumn, multiEditColumns.length); j++) {
const setTypeObject = JSON.parse(multiEditColumns[j].setType.replace(/'/g, '"')); const setTypeObject = JSON.parse(multiEditColumns[j].setType.replace(/'/g, '"'));
// get the element with the input value(s) // get the element with the input value(s)
let elements = setTypeObject.elements.filter(element => element.elementHasInputValue === 1); let elements = setTypeObject.elements.filter(element => element.elementHasInputValue === 1);
@@ -118,7 +118,7 @@
} }
const { elementType, elementOptions = [], transformers = [] } = elementWithInputValue; const { elementType, elementOptions = [], transformers = [] } = elementWithInputValue;
const { const {
inputType, inputType,
readOnly, readOnly,
isMultiSelect, isMultiSelect,
@@ -137,10 +137,11 @@
customId, customId,
columns, columns,
base64Regex, base64Regex,
elementOptionsBase64 elementOptionsBase64,
focusout
} = handleElementOptions('none', elementOptions, transformers, val = ""); } = handleElementOptions('none', elementOptions, transformers, val = "");
// render based on element type // render based on element type
if (elementType === 'select') { if (elementType === 'select') {
targetLocation = multiEditColumns[j].setKey + "_generateSetOptions" targetLocation = multiEditColumns[j].setKey + "_generateSetOptions"
@@ -148,7 +149,7 @@
generateOptionsOrSetOptions(multiEditColumns[j].setKey, [], targetLocation, generateOptions, null) generateOptionsOrSetOptions(multiEditColumns[j].setKey, [], targetLocation, generateOptions, null)
console.log(multiEditColumns[j].setKey) console.log(multiEditColumns[j].setKey)
// Handle Icons as they need a preview // Handle Icons as they need a preview
if(multiEditColumns[j].setKey == 'NEWDEV_devIcon') if(multiEditColumns[j].setKey == 'NEWDEV_devIcon')
{ {
input = ` input = `
@@ -157,37 +158,37 @@
onChange="updateIconPreview(this)" onChange="updateIconPreview(this)"
my-customparams="NEWDEV_devIcon,NEWDEV_devIcon_preview" my-customparams="NEWDEV_devIcon,NEWDEV_devIcon_preview"
id="${multiEditColumns[j].setKey}" id="${multiEditColumns[j].setKey}"
data-my-column="${multiEditColumns[j].setKey}" data-my-column="${multiEditColumns[j].setKey}"
data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}" > data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}" >
<option id="${targetLocation}"></option> <option id="${targetLocation}"></option>
</select>` </select>`
} else{ } else{
input = `<select class="form-control" input = `<select class="form-control"
id="${multiEditColumns[j].setKey}" id="${multiEditColumns[j].setKey}"
data-my-column="${multiEditColumns[j].setKey}" data-my-column="${multiEditColumns[j].setKey}"
data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}" > data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}" >
<option id="${targetLocation}"></option> <option id="${targetLocation}"></option>
</select>` </select>`
} }
} else if (elementType === 'input'){ } else if (elementType === 'input'){
// Add classes specifically for checkboxes // Add classes specifically for checkboxes
inputType === 'checkbox' ? inputClass = 'checkbox' : inputClass = 'form-control'; inputType === 'checkbox' ? inputClass = 'checkbox' : inputClass = 'form-control';
input = `<input class="${inputClass}"
id="${multiEditColumns[j].setKey}" input = `<input class="${inputClass}"
my-customid="${multiEditColumns[j].setKey}" id="${multiEditColumns[j].setKey}"
data-my-column="${multiEditColumns[j].setKey}" my-customid="${multiEditColumns[j].setKey}"
data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}" data-my-column="${multiEditColumns[j].setKey}"
data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}"
type="${inputType}">` type="${inputType}">`
} }
const inputEntry = `<div class="form-group col-sm-12" > const inputEntry = `<div class="form-group col-sm-12" >
<label class="col-sm-3 control-label">${multiEditColumns[j].setName}</label> <label class="col-sm-3 control-label">${multiEditColumns[j].setName}</label>
<div class="col-sm-9"> <div class="col-sm-9">
@@ -200,7 +201,7 @@
</div> </div>
</div>` </div>`
column.append(inputEntry); column.append(inputEntry);
} }
@@ -215,11 +216,11 @@
initSelect2(); initSelect2();
initDeviceSelectors(); initDeviceSelectors();
}) })
}, 100); }, 100);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -262,10 +263,10 @@
var option = new Option($('.deviceSelector select option[value="' + mac + '"]').html(), mac, true, true); var option = new Option($('.deviceSelector select option[value="' + mac + '"]').html(), mac, true, true);
$('.deviceSelector select').append(option).trigger('change'); $('.deviceSelector select').append(option).trigger('change');
}); });
}
}
}, 10); }, 10);
} }
@@ -282,7 +283,7 @@
function markAllSelected() { function markAllSelected() {
// Get the <select> element with the class 'deviceSelector' // Get the <select> element with the class 'deviceSelector'
var selectElement = $('.deviceSelector select'); var selectElement = $('.deviceSelector select');
// Iterate over each option within the select element // Iterate over each option within the select element
selectElement.find('option').each(function() { selectElement.find('option').each(function() {
// Mark each option as selected // Mark each option as selected
@@ -298,13 +299,13 @@
function markAllNotSelected() { function markAllNotSelected() {
// Get the <select> element with the class 'deviceSelector' // Get the <select> element with the class 'deviceSelector'
var selectElement = $('.deviceSelector select'); var selectElement = $('.deviceSelector select');
// Iterate over each option within the select element // Iterate over each option within the select element
selectElement.find('option').each(function() { selectElement.find('option').each(function() {
// Unselect each option // Unselect each option
$(this).prop('selected', false); $(this).prop('selected', false);
}); });
// Trigger the 'change' event to notify Bootstrap Select of the changes // Trigger the 'change' event to notify Bootstrap Select of the changes
selectElement.trigger('change'); selectElement.trigger('change');
} }
@@ -341,13 +342,13 @@
// update selected // update selected
if(selectorMacs() != "") if(selectorMacs() != "")
{ {
executeAction('update', 'devMac', selectorMacs(), targetColumns, columnValue ) executeAction('update', 'devMac', selectorMacs(), targetColumns, columnValue )
} }
else else
{ {
showModalWarning(getString("Gen_Error"), getString('Device_MultiEdit_No_Devices')); showModalWarning(getString("Gen_Error"), getString('Device_MultiEdit_No_Devices'));
} }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -380,21 +381,21 @@ function executeAction(action, whereColumnName, key, targetColumns, newTargetCol
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Ask to delete selected devices // Ask to delete selected devices
function askDeleteSelectedDevices () { function askDeleteSelectedDevices () {
// Ask // Ask
showModalWarning( showModalWarning(
getString('Maintenance_Tool_del_alldev_noti'), getString('Maintenance_Tool_del_alldev_noti'),
getString('Gen_AreYouSure'), getString('Gen_AreYouSure'),
getString('Gen_Cancel'), getString('Gen_Cancel'),
getString('Gen_Delete'), getString('Gen_Delete'),
'deleteSelectedDevices'); 'deleteSelectedDevices');
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Delete selected devices // Delete selected devices
function deleteSelectedDevices() function deleteSelectedDevices()
{ {
macs_tmp = selectorMacs() macs_tmp = selectorMacs()
executeAction('delete', 'devMac', macs_tmp ) executeAction('delete', 'devMac', macs_tmp )
write_notification('[Multi edit] Manually deleted devices with MACs:' + macs_tmp, 'info') write_notification('[Multi edit] Manually deleted devices with MACs:' + macs_tmp, 'info')

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "تصفية", "Gen_Filter": "تصفية",
"Gen_Generate": "إنشاء", "Gen_Generate": "إنشاء",
"Gen_InvalidMac": "", "Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "قاعدة البيانات مقفلة", "Gen_LockedDB": "قاعدة البيانات مقفلة",
"Gen_NetworkMask": "", "Gen_NetworkMask": "",
"Gen_Offline": "غير متصل", "Gen_Offline": "غير متصل",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtrar", "Gen_Filter": "Filtrar",
"Gen_Generate": "Generar", "Gen_Generate": "Generar",
"Gen_InvalidMac": "Mac address invàlida.", "Gen_InvalidMac": "Mac address invàlida.",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "ERROR - DB podria estar bloquejada - Fes servir F12 Eines desenvolupament -> Consola o provar-ho més tard.", "Gen_LockedDB": "ERROR - DB podria estar bloquejada - Fes servir F12 Eines desenvolupament -> Consola o provar-ho més tard.",
"Gen_NetworkMask": "Màscara de xarxa", "Gen_NetworkMask": "Màscara de xarxa",
"Gen_Offline": "Fora de línia", "Gen_Offline": "Fora de línia",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtr", "Gen_Filter": "Filtr",
"Gen_Generate": "Vygenerovat", "Gen_Generate": "Vygenerovat",
"Gen_InvalidMac": "", "Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "CHYBA - Databáze je možná zamčená - Zkontrolujte F12 -> Nástroje pro vývojáře -> Konzole. nebo to zkuste později.", "Gen_LockedDB": "CHYBA - Databáze je možná zamčená - Zkontrolujte F12 -> Nástroje pro vývojáře -> Konzole. nebo to zkuste později.",
"Gen_NetworkMask": "", "Gen_NetworkMask": "",
"Gen_Offline": "Offline", "Gen_Offline": "Offline",

View File

@@ -315,6 +315,7 @@
"Gen_Filter": "Filter", "Gen_Filter": "Filter",
"Gen_Generate": "Generieren", "Gen_Generate": "Generieren",
"Gen_InvalidMac": "Ungültige MAC-Adresse.", "Gen_InvalidMac": "Ungültige MAC-Adresse.",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "ERROR - DB eventuell gesperrt - Nutze die Konsole in den Entwickler Werkzeugen (F12) zur Überprüfung oder probiere es später erneut.", "Gen_LockedDB": "ERROR - DB eventuell gesperrt - Nutze die Konsole in den Entwickler Werkzeugen (F12) zur Überprüfung oder probiere es später erneut.",
"Gen_NetworkMask": "", "Gen_NetworkMask": "",
"Gen_Offline": "Offline", "Gen_Offline": "Offline",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filter", "Gen_Filter": "Filter",
"Gen_Generate": "Generate", "Gen_Generate": "Generate",
"Gen_InvalidMac": "Invalid Mac address.", "Gen_InvalidMac": "Invalid Mac address.",
"Gen_Invalid_Value": "An invalid value was entered",
"Gen_LockedDB": "ERROR - DB might be locked - Check F12 Dev tools -> Console or try later.", "Gen_LockedDB": "ERROR - DB might be locked - Check F12 Dev tools -> Console or try later.",
"Gen_NetworkMask": "Network mask", "Gen_NetworkMask": "Network mask",
"Gen_Offline": "Offline", "Gen_Offline": "Offline",

View File

@@ -313,6 +313,7 @@
"Gen_Filter": "Filtro", "Gen_Filter": "Filtro",
"Gen_Generate": "Generar", "Gen_Generate": "Generar",
"Gen_InvalidMac": "", "Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "Fallo - La base de datos puede estar bloqueada - Pulsa F1 -> Ajustes de desarrolladores -> Consola o prueba más tarde.", "Gen_LockedDB": "Fallo - La base de datos puede estar bloqueada - Pulsa F1 -> Ajustes de desarrolladores -> Consola o prueba más tarde.",
"Gen_NetworkMask": "", "Gen_NetworkMask": "",
"Gen_Offline": "Desconectado", "Gen_Offline": "Desconectado",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "", "Gen_Filter": "",
"Gen_Generate": "", "Gen_Generate": "",
"Gen_InvalidMac": "", "Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "", "Gen_LockedDB": "",
"Gen_NetworkMask": "", "Gen_NetworkMask": "",
"Gen_Offline": "", "Gen_Offline": "",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtrer", "Gen_Filter": "Filtrer",
"Gen_Generate": "Générer", "Gen_Generate": "Générer",
"Gen_InvalidMac": "Adresse MAC invalide.", "Gen_InvalidMac": "Adresse MAC invalide.",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "Erreur - La base de données est peut-être verrouillée - Vérifier avec les outils de dév via F12 -> Console ou essayer plus tard.", "Gen_LockedDB": "Erreur - La base de données est peut-être verrouillée - Vérifier avec les outils de dév via F12 -> Console ou essayer plus tard.",
"Gen_NetworkMask": "Masque réseau", "Gen_NetworkMask": "Masque réseau",
"Gen_Offline": "Hors ligne", "Gen_Offline": "Hors ligne",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtro", "Gen_Filter": "Filtro",
"Gen_Generate": "Genera", "Gen_Generate": "Genera",
"Gen_InvalidMac": "Indirizzo Mac non valido.", "Gen_InvalidMac": "Indirizzo Mac non valido.",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "ERRORE: il DB potrebbe essere bloccato, controlla F12 Strumenti di sviluppo -> Console o riprova più tardi.", "Gen_LockedDB": "ERRORE: il DB potrebbe essere bloccato, controlla F12 Strumenti di sviluppo -> Console o riprova più tardi.",
"Gen_NetworkMask": "Maschera di rete", "Gen_NetworkMask": "Maschera di rete",
"Gen_Offline": "Offline", "Gen_Offline": "Offline",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "", "Gen_Filter": "",
"Gen_Generate": "", "Gen_Generate": "",
"Gen_InvalidMac": "", "Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "", "Gen_LockedDB": "",
"Gen_NetworkMask": "", "Gen_NetworkMask": "",
"Gen_Offline": "", "Gen_Offline": "",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filter", "Gen_Filter": "Filter",
"Gen_Generate": "", "Gen_Generate": "",
"Gen_InvalidMac": "", "Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "FEIL - DB kan være låst - Sjekk F12 Dev tools -> Konsoll eller prøv senere.", "Gen_LockedDB": "FEIL - DB kan være låst - Sjekk F12 Dev tools -> Konsoll eller prøv senere.",
"Gen_NetworkMask": "", "Gen_NetworkMask": "",
"Gen_Offline": "Frakoblet", "Gen_Offline": "Frakoblet",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtr", "Gen_Filter": "Filtr",
"Gen_Generate": "Wygeneruj", "Gen_Generate": "Wygeneruj",
"Gen_InvalidMac": "", "Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "Błąd - Baza danych może być zablokowana - Sprawdź narzędzia deweloperskie F12 -> Konsola lub spróbuj później.", "Gen_LockedDB": "Błąd - Baza danych może być zablokowana - Sprawdź narzędzia deweloperskie F12 -> Konsola lub spróbuj później.",
"Gen_NetworkMask": "", "Gen_NetworkMask": "",
"Gen_Offline": "Niedostępne", "Gen_Offline": "Niedostępne",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtro", "Gen_Filter": "Filtro",
"Gen_Generate": "Gerar", "Gen_Generate": "Gerar",
"Gen_InvalidMac": "", "Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "ERRO - O banco de dados pode estar bloqueado - Verifique F12 Ferramentas de desenvolvimento -> Console ou tente mais tarde.", "Gen_LockedDB": "ERRO - O banco de dados pode estar bloqueado - Verifique F12 Ferramentas de desenvolvimento -> Console ou tente mais tarde.",
"Gen_NetworkMask": "", "Gen_NetworkMask": "",
"Gen_Offline": "Offline", "Gen_Offline": "Offline",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtro", "Gen_Filter": "Filtro",
"Gen_Generate": "Gerar", "Gen_Generate": "Gerar",
"Gen_InvalidMac": "", "Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "ERRO - A base de dados pode estar bloqueada - Verifique F12 Ferramentas de desenvolvimento -> Console ou tente mais tarde.", "Gen_LockedDB": "ERRO - A base de dados pode estar bloqueada - Verifique F12 Ferramentas de desenvolvimento -> Console ou tente mais tarde.",
"Gen_NetworkMask": "", "Gen_NetworkMask": "",
"Gen_Offline": "Offline", "Gen_Offline": "Offline",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Фильтр", "Gen_Filter": "Фильтр",
"Gen_Generate": "Генерировать", "Gen_Generate": "Генерировать",
"Gen_InvalidMac": "Неверный Mac-адрес.", "Gen_InvalidMac": "Неверный Mac-адрес.",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "ОШИБКА - Возможно, база данных заблокирована. Проверьте инструменты разработчика F12 -> Консоль или повторите попытку позже.", "Gen_LockedDB": "ОШИБКА - Возможно, база данных заблокирована. Проверьте инструменты разработчика F12 -> Консоль или повторите попытку позже.",
"Gen_NetworkMask": "Маска сети", "Gen_NetworkMask": "Маска сети",
"Gen_Offline": "Оффлайн", "Gen_Offline": "Оффлайн",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "", "Gen_Filter": "",
"Gen_Generate": "", "Gen_Generate": "",
"Gen_InvalidMac": "", "Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "", "Gen_LockedDB": "",
"Gen_NetworkMask": "", "Gen_NetworkMask": "",
"Gen_Offline": "", "Gen_Offline": "",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Filtre", "Gen_Filter": "Filtre",
"Gen_Generate": "Oluştur", "Gen_Generate": "Oluştur",
"Gen_InvalidMac": "", "Gen_InvalidMac": "",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "HATA - Veritabanı kilitlenmiş olabilir - F12 Geliştirici araçlarını -> Konsol kısmını kontrol edin veya daha sonra tekrar deneyin.", "Gen_LockedDB": "HATA - Veritabanı kilitlenmiş olabilir - F12 Geliştirici araçlarını -> Konsol kısmını kontrol edin veya daha sonra tekrar deneyin.",
"Gen_NetworkMask": "", "Gen_NetworkMask": "",
"Gen_Offline": "Çevrimdışı", "Gen_Offline": "Çevrimdışı",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "Фільтр", "Gen_Filter": "Фільтр",
"Gen_Generate": "Генерувати", "Gen_Generate": "Генерувати",
"Gen_InvalidMac": "Недійсна Mac-адреса.", "Gen_InvalidMac": "Недійсна Mac-адреса.",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "ПОМИЛКА БД може бути заблоковано перевірте F12 Інструменти розробника -> Консоль або спробуйте пізніше.", "Gen_LockedDB": "ПОМИЛКА БД може бути заблоковано перевірте F12 Інструменти розробника -> Консоль або спробуйте пізніше.",
"Gen_NetworkMask": "Маска мережі", "Gen_NetworkMask": "Маска мережі",
"Gen_Offline": "Офлайн", "Gen_Offline": "Офлайн",

View File

@@ -311,6 +311,7 @@
"Gen_Filter": "筛选", "Gen_Filter": "筛选",
"Gen_Generate": "生成", "Gen_Generate": "生成",
"Gen_InvalidMac": "无效的 Mac 地址。", "Gen_InvalidMac": "无效的 Mac 地址。",
"Gen_Invalid_Value": "",
"Gen_LockedDB": "错误 - DB 可能被锁定 - 检查 F12 开发工具 -> 控制台或稍后重试。", "Gen_LockedDB": "错误 - DB 可能被锁定 - 检查 F12 开发工具 -> 控制台或稍后重试。",
"Gen_NetworkMask": "网络掩码", "Gen_NetworkMask": "网络掩码",
"Gen_Offline": "离线", "Gen_Offline": "离线",

View File

@@ -91,7 +91,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -377,7 +377,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -225,7 +225,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -100,7 +100,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -148,7 +148,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -185,7 +185,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -125,7 +125,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -175,7 +175,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -596,7 +596,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -397,7 +397,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -148,7 +148,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -103,7 +103,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -180,7 +180,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -203,7 +203,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -468,7 +468,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -103,7 +103,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -130,7 +130,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -130,7 +130,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -148,7 +148,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -227,7 +227,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -476,7 +476,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -148,7 +148,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -92,7 +92,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -84,7 +84,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -89,7 +89,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -182,7 +182,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -512,7 +512,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -98,7 +98,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -100,7 +100,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
@@ -221,7 +221,7 @@
}, },
{ {
"getStringKey": "Gen_Add" "getStringKey": "Gen_Add"
} }
], ],
"transformers": [] "transformers": []
}, },

View File

@@ -140,7 +140,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -90,7 +90,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -425,7 +425,7 @@
"elementType": "input", "elementType": "input",
"elementOptions": [ "elementOptions": [
{ {
"onChange": "validateRegex(this)" "focusout": "validateRegex(this)"
}, },
{ {
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ=" "base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="

View File

@@ -30,22 +30,22 @@ checkPermissions([$dbPath, $confPath]);
// path to your JSON file // path to your JSON file
$apiRoot = rtrim(getenv('NETALERTX_API') ?: '/tmp/api', '/'); $apiRoot = rtrim(getenv('NETALERTX_API') ?: '/tmp/api', '/');
$file = $apiRoot . '/table_settings.json'; $file = $apiRoot . '/table_settings.json';
// put the content of the file in a variable // put the content of the file in a variable
$data = file_get_contents($file); $data = file_get_contents($file);
// JSON decode // JSON decode
$settingsJson = json_decode($data); $settingsJson = json_decode($data);
// get settings from the DB // get settings from the DB
global $db; global $db;
$result = $db->query("SELECT * FROM Settings"); $result = $db->query("SELECT * FROM Settings");
$settings = array(); $settings = array();
while ($row = $result -> fetchArray (SQLITE3_ASSOC)) { while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
// Push row data // Push row data
$settings[] = array( 'setKey' => $row['setKey'], $settings[] = array( 'setKey' => $row['setKey'],
'setName' => $row['setName'], 'setName' => $row['setName'],
'setDescription' => $row['setDescription'], 'setDescription' => $row['setDescription'],
@@ -54,7 +54,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
'setValue' => $row['setValue'], 'setValue' => $row['setValue'],
'setGroup' => $row['setGroup'], 'setGroup' => $row['setGroup'],
'setEvents' => $row['setEvents'] 'setEvents' => $row['setEvents']
); );
} }
$settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
@@ -63,7 +63,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<!-- ----------------------------------------------------------------------- --> <!-- ----------------------------------------------------------------------- -->
<script src="lib/crypto/crypto-js.min.js"></script> <script src="lib/crypto/crypto-js.min.js"></script>
<script src="lib/bcrypt/bcrypt.min.js"></script> <script src="lib/bcrypt/bcrypt.min.js"></script>
@@ -74,21 +74,21 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<a style="cursor:pointer"> <a style="cursor:pointer">
<span> <span>
<i id='toggleSettings' onclick="toggleAllSettings()" class="settings-expand-icon fa fa-angle-double-down"></i> <i id='toggleSettings' onclick="toggleAllSettings()" class="settings-expand-icon fa fa-angle-double-down"></i>
</span> </span>
</a> </a>
<!-- Content header--------------------------------------------------------- --> <!-- Content header--------------------------------------------------------- -->
<section class="content-header"> <section class="content-header">
<div class ="bg-white color-palette box box-solid box-primary col-sm-12 panel panel-default panel-title" > <div class ="bg-white color-palette box box-solid box-primary col-sm-12 panel panel-default panel-title" >
<a data-toggle="collapse" href="#settingsOverview"> <a data-toggle="collapse" href="#settingsOverview">
<div class ="settings-group col-sm-12 panel-heading panel-title"> <div class ="settings-group col-sm-12 panel-heading panel-title">
<i class="<?= lang("settings_enabled_icon");?>"></i> <?= lang("settings_enabled");?> <i class="<?= lang("settings_enabled_icon");?>"></i> <?= lang("settings_enabled");?>
</div> </div>
</a> </a>
<div id="settingsOverview" class="panel-collapse collapse in"> <div id="settingsOverview" class="panel-collapse collapse in">
<div class="panel-body"></div> <div class="panel-body"></div>
<div class =" col-sm-12 " id=""></div> <div class =" col-sm-12 " id=""></div>
</div> </div>
@@ -96,43 +96,43 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<div class="content settingswrap " id="accordion_gen"> <div class="content settingswrap " id="accordion_gen">
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="core_content_header" > <div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="core_content_header" >
<div class ="settings-group col-sm-12"> <div class ="settings-group col-sm-12">
<i class="<?= lang("settings_core_icon");?>"></i> <?= lang("settings_core_label");?> <i class="<?= lang("settings_core_icon");?>"></i> <?= lang("settings_core_label");?>
</div> </div>
<div class =" col-sm-12" id="core_content"></div> <div class =" col-sm-12" id="core_content"></div>
</div> </div>
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="system_content_header" > <div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="system_content_header" >
<div class ="settings-group col-sm-12"> <div class ="settings-group col-sm-12">
<i class="<?= lang("settings_system_icon");?>"></i> <?= lang("settings_system_label");?> <i class="<?= lang("settings_system_icon");?>"></i> <?= lang("settings_system_label");?>
</div> </div>
<div class =" col-sm-12" id="system_content"></div> <div class =" col-sm-12" id="system_content"></div>
</div> </div>
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="device_scanners_content_header" > <div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="device_scanners_content_header" >
<div class ="settings-group col-sm-12"> <div class ="settings-group col-sm-12">
<i class="<?= lang("settings_device_scanners_icon");?>"></i> <?= lang("settings_device_scanners_label");?> <i class="<?= lang("settings_device_scanners_icon");?>"></i> <?= lang("settings_device_scanners_label");?>
</div> </div>
<div class =" col-sm-12" id="device_scanner_content"> <?= lang("settings_device_scanners_info");?> </div> <div class =" col-sm-12" id="device_scanner_content"> <?= lang("settings_device_scanners_info");?> </div>
</div> </div>
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="other_scanners_content_header"> <div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="other_scanners_content_header">
<div class ="settings-group col-sm-12"> <div class ="settings-group col-sm-12">
<i class="<?= lang("settings_other_scanners_icon");?>"></i> <?= lang("settings_other_scanners_label");?> <i class="<?= lang("settings_other_scanners_icon");?>"></i> <?= lang("settings_other_scanners_label");?>
</div> </div>
<div class =" col-sm-12" id="other_content"></div> <div class =" col-sm-12" id="other_content"></div>
</div> </div>
<div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="publishers_content_header" > <div class ="bg-grey-dark color-palette panel panel-default col-sm-12 box-default box-info" id="publishers_content_header" >
<div class ="settings-group col-sm-12"> <div class ="settings-group col-sm-12">
<i class="<?= lang("settings_publishers_icon");?>"></i> <?= lang("settings_publishers_label");?> <i class="<?= lang("settings_publishers_icon");?>"></i> <?= lang("settings_publishers_label");?>
</div> </div>
<div class =" col-sm-12" id="publisher_content"><?= lang("settings_publishers_info");?></div> <div class =" col-sm-12" id="publisher_content"><?= lang("settings_publishers_info");?></div>
</div> </div>
</div> </div>
<!-- /.content --> <!-- /.content -->
<section class=" padding-bottom col-sm-12"> <section class=" padding-bottom col-sm-12">
@@ -141,10 +141,10 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<div class="col-sm-7 settingsImportedTimestamp" style="display:none" title="<?= lang("settings_imported");?> "> <div class="col-sm-7 settingsImportedTimestamp" style="display:none" title="<?= lang("settings_imported");?> ">
<div class="settingsImported "> <div class="settingsImported ">
<?= lang("settings_imported_label");?>: <?= lang("settings_imported_label");?>:
<span id="lastImportedTime"></span> <span id="lastImportedTime"></span>
</div> </div>
</div> </div>
</section> </section>
@@ -156,8 +156,8 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<input type="text" id="settingsSearch" class="form-control input-xs col-xs-12" placeholder="Filter Settings..."> <input type="text" id="settingsSearch" class="form-control input-xs col-xs-12" placeholder="Filter Settings...">
<div class="clear-filter "> <div class="clear-filter ">
<i class="fa-solid fa-circle-xmark" onclick="$('#settingsSearch').val('');filterRows();$('#settingsSearch').focus()"></i> <i class="fa-solid fa-circle-xmark" onclick="$('#settingsSearch').val('');filterRows();$('#settingsSearch').focus()"></i>
</div> </div>
</div> </div>
</div> </div>
<div class="col-xs-4 saveSettingsWrapper"> <div class="col-xs-4 saveSettingsWrapper">
@@ -171,13 +171,13 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<!-- ----------------------------------------------------------------------- --> <!-- ----------------------------------------------------------------------- -->
<?php <?php
require 'php/templates/footer.php'; require 'php/templates/footer.php';
?> ?>
<script> <script>
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Get plugin and settings data from API endpoints // Get plugin and settings data from API endpoints
function getData(){ function getData(){
@@ -217,9 +217,9 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
console.log("Settings:", settingsData); console.log("Settings:", settingsData);
// Wrong number of settings processing // Wrong number of settings processing
if(settingsNumberDB != settingsData.length) if(settingsNumberDB != settingsData.length)
{ {
showModalOk('WARNING', "<?= lang("settings_old")?>"); showModalOk('WARNING', "<?= lang("settings_old")?>");
setTimeout(() => { setTimeout(() => {
clearCache() clearCache()
}, 3000); }, 3000);
@@ -227,7 +227,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
{ {
$.get('php/server/query_json.php', { file: 'plugins.json', nocache: Date.now() }, function(res) { $.get('php/server/query_json.php', { file: 'plugins.json', nocache: Date.now() }, function(res) {
pluginsData = res["data"]; pluginsData = res["data"];
// Sort settingsData alphabetically, ensuring "General" is always first // Sort settingsData alphabetically, ensuring "General" is always first
settingsData.sort((a, b) => { settingsData.sort((a, b) => {
@@ -257,31 +257,31 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
setKey = set['setKey'] setKey = set['setKey']
try { try {
const isMetadata = setKey.includes('__metadata'); const isMetadata = setKey.includes('__metadata');
// if this isn't a metadata entry, get corresponding metadata object from the dummy setting // if this isn't a metadata entry, get corresponding metadata object from the dummy setting
const setObj = isMetadata ? {} : JSON.parse(getSetting(`${setKey}__metadata`)); const setObj = isMetadata ? {} : JSON.parse(getSetting(`${setKey}__metadata`));
} catch (error) { } catch (error) {
console.error(`Error getting setting for ${setKey}:`, error); console.error(`Error getting setting for ${setKey}:`, error);
showModalOk('WARNING', "Outdated cache - refreshing (refresh browser cache if needed)"); showModalOk('WARNING', "Outdated cache - refreshing (refresh browser cache if needed)");
setTimeout(() => { setTimeout(() => {
clearCache() clearCache()
}, 3000); }, 3000);
exception_occurred = true; exception_occurred = true;
} }
}); });
// only proceed if everything was loaded correctly // only proceed if everything was loaded correctly
if(!exception_occurred) if(!exception_occurred)
{ {
initSettingsPage(settingsData, pluginsData); initSettingsPage(settingsData, pluginsData);
} }
}) })
} }
} }
}, },
error: function (xhr, status, error) { error: function (xhr, status, error) {
@@ -296,9 +296,9 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
function initSettingsPage(settingsData, pluginsData){ function initSettingsPage(settingsData, pluginsData){
const settingPluginPrefixes = []; const settingPluginPrefixes = [];
const enabledDeviceScanners = getPluginsByType(pluginsData, "device_scanner", true); const enabledDeviceScanners = getPluginsByType(pluginsData, "device_scanner", true);
const enabledOthers = getPluginsByType(pluginsData, "other", true); const enabledOthers = getPluginsByType(pluginsData, "other", true);
const enabledPublishers = getPluginsByType(pluginsData, "publisher", true); const enabledPublishers = getPluginsByType(pluginsData, "publisher", true);
@@ -310,18 +310,18 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
// settingPluginPrefixes // settingPluginPrefixes
if (!settingPluginPrefixes.includes(set.setGroup)) { if (!settingPluginPrefixes.includes(set.setGroup)) {
settingPluginPrefixes.push(set.setGroup); // = Unique plugin prefix settingPluginPrefixes.push(set.setGroup); // = Unique plugin prefix
} }
}); });
// Init the overview section // Init the overview section
overviewSections = [ overviewSections = [
'device_scanners', 'device_scanners',
'other_scanners', 'other_scanners',
'publishers' 'publishers'
] ]
overviewSectionsHtml = [ overviewSectionsHtml = [
pluginCards(enabledDeviceScanners,['RUN', 'RUN_SCHD']), pluginCards(enabledDeviceScanners,['RUN', 'RUN_SCHD']),
pluginCards(enabledOthers, ['RUN', 'RUN_SCHD']), pluginCards(enabledOthers, ['RUN', 'RUN_SCHD']),
pluginCards(enabledPublishers, []), pluginCards(enabledPublishers, []),
] ]
@@ -334,15 +334,15 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<div class="col-sm-12 " title="${getString("settings_"+section)}"> <div class="col-sm-12 " title="${getString("settings_"+section)}">
<a href="#${section}_content_header"> <a href="#${section}_content_header">
<div class="overview-group col-sm-12 col-xs-12"> <div class="overview-group col-sm-12 col-xs-12">
<i title="${section}" class="${getString("settings_"+section+"_icon")}"></i> <i title="${section}" class="${getString("settings_"+section+"_icon")}"></i>
${getString("settings_"+section+"_label")} ${getString("settings_"+section+"_label")}
</div> </div>
</a> </a>
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
${overviewSectionsHtml[index]} ${overviewSectionsHtml[index]}
</div> </div>
</div>` </div>`
index++; index++;
@@ -350,7 +350,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
$('#settingsOverview .panel-body').append(overviewSections_html); $('#settingsOverview .panel-body').append(overviewSections_html);
// Display warning // Display warning
if(schedulesAreSynchronized(enabledDeviceScanners, pluginsData) == false) if(schedulesAreSynchronized(enabledDeviceScanners, pluginsData) == false)
{ {
$("#device_scanners").append( $("#device_scanners").append(
@@ -359,9 +359,9 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
${getString('Settings_device_Scanners_desync')} ${getString('Settings_device_Scanners_desync')}
</small> </small>
`) `)
} }
for (const prefix of settingPluginPrefixes) { for (const prefix of settingPluginPrefixes) {
// enabled / disabled icons // enabled / disabled icons
enabledHtml = '' enabledHtml = ''
@@ -376,16 +376,16 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<i class="fa fa-${onOff}"></i> <i class="fa fa-${onOff}"></i>
</div> </div>
` `
} }
// console.log(pluginsData); // console.log(pluginsData);
// Start constructing the main settings HTML // Start constructing the main settings HTML
let pluginHtml = ` let pluginHtml = `
<div class="row table_row docs"> <div class="row table_row docs">
<div class="table_cell bold"> <div class="table_cell bold">
<i class="fa fa-book fa-sm"></i> <i class="fa fa-book fa-sm"></i>
${getString(prefix+'_description')} ${getString(prefix+'_description')}
<a href="https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/${getPluginCodeName(pluginsData, prefix)}" target="_blank"> <a href="https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/${getPluginCodeName(pluginsData, prefix)}" target="_blank">
${getString('Gen_ReadDocs')} ${getString('Gen_ReadDocs')}
</a> </a>
@@ -399,28 +399,28 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<div class="panel-heading"> <div class="panel-heading">
<h4 class="panel-title"> <h4 class="panel-title">
<div class="col-sm-1 col-xs-1">${getString(prefix+"_icon")} </div> <div class="col-sm-1 col-xs-1">${getString(prefix+"_icon")} </div>
<div class="col-sm-10 col-xs-8">${getString(prefix+"_display_name")} </div> <div class="col-sm-10 col-xs-8">${getString(prefix+"_display_name")} </div>
<div class="col-sm-1 col-xs-1">${enabledHtml} </div> <div class="col-sm-1 col-xs-1">${enabledHtml} </div>
</h4> </h4>
</div> </div>
</a> </a>
<div id="${prefix}" data-myid="collapsible" class="panel-collapse collapse ${prefix == "General" ? ' in ' : ""}"> <div id="${prefix}" data-myid="collapsible" class="panel-collapse collapse ${prefix == "General" ? ' in ' : ""}">
<div class="panel-body"> <div class="panel-body">
${prefix != "General" ? pluginHtml: ""} ${prefix != "General" ? pluginHtml: ""}
</div> </div>
</div> </div>
</div> </div>
`; `;
// generate headers/sections // generate headers/sections
$('#'+getPluginType(pluginsData, prefix) + "_content").append(headerHtml); $('#'+getPluginType(pluginsData, prefix) + "_content").append(headerHtml);
} }
// generate panel content // generate panel content
for (const prefix of settingPluginPrefixes) { for (const prefix of settingPluginPrefixes) {
// go thru all settings and collect settings per settings prefix // go thru all settings and collect settings per settings prefix
settingsData.forEach((set) => { settingsData.forEach((set) => {
const valIn = set['setValue']; const valIn = set['setValue'];
@@ -443,18 +443,18 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
if(set["setGroup"] == prefix) if(set["setGroup"] == prefix)
{ {
// hide metadata by default by assigning it a special class // hide metadata by default by assigning it a special class
isMetadata ? metadataClass = 'metadata' : metadataClass = ''; isMetadata ? metadataClass = 'metadata' : metadataClass = '';
isMetadata ? showMetadata = '' : showMetadata = `<i isMetadata ? showMetadata = '' : showMetadata = `<i
my-to-toggle="row_${setKey}__metadata" my-to-toggle="row_${setKey}__metadata"
title="${getString("Settings_Metadata_Toggle")}" title="${getString("Settings_Metadata_Toggle")}"
class="fa fa-circle-question pointer hideOnMobile" class="fa fa-circle-question pointer hideOnMobile"
onclick="toggleMetadata(this)"> onclick="toggleMetadata(this)">
</i>` ; </i>` ;
infoIcon = `<i my-to-show="#row_${setKey} .setting_description" infoIcon = `<i my-to-show="#row_${setKey} .setting_description"
title="${getString("Settings_Show_Description")}" title="${getString("Settings_Show_Description")}"
class="fa fa-circle-info pointer hideOnBigScreen" class="fa fa-circle-info pointer hideOnBigScreen"
onclick="showDescription(this)"> onclick="showDescription(this)">
</i>` ; </i>` ;
@@ -477,12 +477,12 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
// surface settings override functionality if the setting is a template that can be overridden with user defined values // surface settings override functionality if the setting is a template that can be overridden with user defined values
// if the setting is a json of the correct structure, handle like a template setting // if the setting is a json of the correct structure, handle like a template setting
let overrideHtml = ""; let overrideHtml = "";
//pre-check if this is a json object that needs value extraction //pre-check if this is a json object that needs value extraction
let overridable = false; // indicates if the setting is overridable let overridable = false; // indicates if the setting is overridable
let override = false; // If the setting is set to be overridden by the user or by default let override = false; // If the setting is set to be overridden by the user or by default
let readonly = ""; // helper variable to make text input readonly let readonly = ""; // helper variable to make text input readonly
let disabled = ""; // helper variable to make checkbox input readonly let disabled = ""; // helper variable to make checkbox input readonly
@@ -490,7 +490,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
if ('override_value' in setObj) { if ('override_value' in setObj) {
overridable = true; overridable = true;
overrideObj = setObj["override_value"] overrideObj = setObj["override_value"]
override = overrideObj["override"]; override = overrideObj["override"];
console.log(setObj) console.log(setObj)
console.log(prefix) console.log(prefix)
@@ -498,8 +498,8 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
// prepare override checkbox and HTML // prepare override checkbox and HTML
if(overridable) if(overridable)
{ {
let checked = override ? 'checked' : ''; let checked = override ? 'checked' : '';
overrideHtml = `<div class="override col-xs-12"> overrideHtml = `<div class="override col-xs-12">
<div class="override-check col-xs-1"> <div class="override-check col-xs-1">
@@ -508,10 +508,10 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
<div class="override-text col-xs-11" title="${getString("Setting_Override_Description")}"> <div class="override-text col-xs-11" title="${getString("Setting_Override_Description")}">
${getString("Setting_Override")} ${getString("Setting_Override")}
</div> </div>
</div>`; </div>`;
}
}
// INPUT // INPUT
inputFormHtml = generateFormHtml(settingsData, set, valIn, null, null); inputFormHtml = generateFormHtml(settingsData, set, valIn, null, null);
@@ -522,7 +522,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
` `
// generate settings in the correct prefix (group) section // generate settings in the correct prefix (group) section
$(`#${prefix} .panel-body`).append(setHtml); $(`#${prefix} .panel-body`).append(setHtml);
} }
}); });
@@ -532,7 +532,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
setTimeout(() => { setTimeout(() => {
initListInteractionOptions() // init remove and edit listitem click gestures initListInteractionOptions() // init remove and edit listitem click gestures
}, 50); }, 50);
setupSmoothScrolling(); setupSmoothScrolling();
// try to initialize select2 // try to initialize select2
initSelect2(); initSelect2();
@@ -548,29 +548,29 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
var settingsNumberJSON = <?php echo count($settingsJson->data)?>; var settingsNumberJSON = <?php echo count($settingsJson->data)?>;
// Wrong number of settings processing // Wrong number of settings processing
if(settingsNumberJSON != settingsNumberDB) if(settingsNumberJSON != settingsNumberDB)
{ {
showModalOk('WARNING', getString("settings_missing")); showModalOk('WARNING', getString("settings_missing"));
} }
// --------------------------------------------------------- // ---------------------------------------------------------
function saveSettings() { function saveSettings() {
if(settingsNumberJSON != settingsNumberDB) if(settingsNumberJSON != settingsNumberDB)
{ {
console.log(`Error settingsNumberJSON != settingsNumberDB: ${settingsNumberJSON} != ${settingsNumberDB}`); console.log(`Error settingsNumberJSON != settingsNumberDB: ${settingsNumberJSON} != ${settingsNumberDB}`);
showModalOk('WARNING', getString("settings_missing_block")); showModalOk('WARNING', getString("settings_missing_block"));
setTimeout(() => { setTimeout(() => {
clearCache() clearCache()
}, 1500); }, 1500);
} else { } else {
let settingsArray = []; let settingsArray = [];
// collect values for each of the different input form controls // collect values for each of the different input form controls
// get settings to determine setting type to store values appropriately // get settings to determine setting type to store values appropriately
$.get('php/server/query_json.php', { file: 'table_settings.json', nocache: Date.now() }, function(res) { $.get('php/server/query_json.php', { file: 'table_settings.json', nocache: Date.now() }, function(res) {
// loop through the settings definitions from the json // loop through the settings definitions from the json
res["data"].forEach(set => { res["data"].forEach(set => {
@@ -578,7 +578,17 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
setType = set["setType"] setType = set["setType"]
setCodeName = set["setKey"] setCodeName = set["setKey"]
settingsArray = collectSetting(prefix, setCodeName, setType, settingsArray) // settingsArray = collectSetting(prefix, setCodeName, setType, settingsArray)
const collectSettingResult = collectSetting(prefix, setCodeName, setType, settingsArray);
settingsArray = collectSettingResult.settingsArray;
if (!collectSettingResult.dataIsValid) {
msg = getString("Gen_Invalid_Value") + ": " + collectSettingResult.failedSettingKey;
console.error(msg);
showMessage (msg, 3000, "modal_red");
// return early
return;
}
}); });
// sanity check to make sure settings were loaded & collected correctly // sanity check to make sure settings were loaded & collected correctly
@@ -586,44 +596,45 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
{ {
console.log(settingsArray); console.log(settingsArray);
console.log(settingsJSON_DB);
console.log( JSON.stringify(settingsArray)); console.log( JSON.stringify(settingsArray));
// return; // 🐛 🔺 // return; // 🐛 🔺
// trigger a save settings event in the backend // trigger a save settings event in the backend
$.ajax({ $.ajax({
method: "POST", method: "POST",
url: "php/server/util.php", url: "php/server/util.php",
data: { data: {
function: 'savesettings', function: 'savesettings',
settings: JSON.stringify(settingsArray) }, settings: JSON.stringify(settingsArray) },
success: function(data, textStatus) { success: function(data, textStatus) {
if(data == "OK") if(data == "OK")
{ {
// showMessage (getString("settings_saved"), 5000, "modal_grey"); // showMessage (getString("settings_saved"), 5000, "modal_grey");
// Remove navigation prompt "Are you sure you want to leave..." // Remove navigation prompt "Are you sure you want to leave..."
window.onbeforeunload = null; window.onbeforeunload = null;
// Reloads the current page // Reloads the current page
// setTimeout("clearCache()", 5000); // setTimeout("clearCache()", 5000);
write_notification(`[Settings] Settings saved by the user`, 'info') write_notification(`[Settings] Settings saved by the user`, 'info')
clearCache() clearCache()
} else{ } else{
// something went wrong // something went wrong
write_notification("[Important] Please take a screenshot of the Console tab in the browser (F12) and next error. Submit it (with the nginx and php error logs) as a new issue here: https://github.com/jokob-sk/NetAlertX/issues", 'interrupt') write_notification("[Important] Please take a screenshot of the Console tab in the browser (F12) and next error. Submit it (with the nginx and php error logs) as a new issue here: https://github.com/jokob-sk/NetAlertX/issues", 'interrupt')
write_notification(data, 'interrupt') write_notification(data, 'interrupt')
console.log("🔽"); console.log("🔽");
console.log(settingsArray); console.log(settingsArray);
console.log(JSON.stringify(settingsArray)); console.log(JSON.stringify(settingsArray));
console.log(data); console.log(data);
console.log("🔼"); console.log("🔼");
} }
} }
}); });
} }
}) })
} }
@@ -647,30 +658,30 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
} else } else
{ {
// check if config file has been updated // check if config file has been updated
$.get('php/server/query_json.php', { file: 'app_state.json', nocache: Date.now() }, function(appState) { $.get('php/server/query_json.php', { file: 'app_state.json', nocache: Date.now() }, function(appState) {
console.log("Settings: Got app_state.json"); console.log("Settings: Got app_state.json");
fileModificationTime = <?php echo filemtime($confPath)*1000;?>; fileModificationTime = <?php echo filemtime($confPath)*1000;?>;
// console.log(appState["settingsImported"]*1000) // console.log(appState["settingsImported"]*1000)
importedMiliseconds = parseInt((appState["settingsImported"]*1000)); importedMiliseconds = parseInt((appState["settingsImported"]*1000));
// check if displayed settings are outdated // check if displayed settings are outdated
if(appState["showSpinner"] || fileModificationTime > importedMiliseconds) if(appState["showSpinner"] || fileModificationTime > importedMiliseconds)
{ {
showSpinner("settings_old") showSpinner("settings_old")
setTimeout("handleLoadingDialog()", 1000); setTimeout("handleLoadingDialog()", 1000);
} else } else
{ {
checkInitialization(); checkInitialization();
} }
humanReadable = (new Date(importedMiliseconds)).toLocaleString("en-UK", { timeZone: "<?php echo $timeZone?>" }); humanReadable = (new Date(importedMiliseconds)).toLocaleString("en-UK", { timeZone: "<?php echo $timeZone?>" });
document.getElementById('lastImportedTime').innerHTML = humanReadable; document.getElementById('lastImportedTime').innerHTML = humanReadable;
}) })
} }
@@ -697,7 +708,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
setTimeout(checkInitialization, 1000); setTimeout(checkInitialization, 1000);
} }
} }
showSpinner() showSpinner()
handleLoadingDialog() handleLoadingDialog()