From 479b58c023957272ee8eafad8d5686c737cc12da Mon Sep 17 00:00:00 2001 From: Anatoliy Korovkin <5337923+Frumscepend@users.noreply.github.com> Date: Fri, 6 Mar 2026 00:17:34 +0100 Subject: [PATCH 01/24] Fix ReferenceError in Janus onremotetrack handler When using Janus live streaming, MonitorStream.js assigns to undeclared variables (stream, audioElement) inside onremotetrack(). In strict mode this causes "ReferenceError: stream is not defined" and breaks playback. This change declares stream/audioElement with let, fixing Janus live video/audio playback. --- web/js/MonitorStream.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index 91259a701..0e5552fd6 100644 --- a/web/js/MonitorStream.js +++ b/web/js/MonitorStream.js @@ -1779,17 +1779,17 @@ async function attachVideo(monitorStream) { Janus.debug(" ::: Got a remote track :::"); Janus.debug(track); if (track.kind ==="audio") { - stream = new MediaStream(); + let stream = new MediaStream(); stream.addTrack(track.clone()); if (document.getElementById("liveAudio" + id) == null) { - audioElement = document.createElement('audio'); + let audioElement = document.createElement('audio'); audioElement.setAttribute("id", "liveAudio" + id); audioElement.controls = true; document.getElementById("imageFeed" + id).append(audioElement); } Janus.attachMediaStream(document.getElementById("liveAudio" + id), stream); } else { - stream = new MediaStream(); + let stream = new MediaStream(); stream.addTrack(track.clone()); Janus.attachMediaStream(document.getElementById("liveStream" + id), stream); } From f8942df908d0e0d5a4162161a041240227a39799 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Thu, 5 Mar 2026 19:39:41 -0500 Subject: [PATCH 02/24] docs: add design doc for notifications API refs #4684 Design for FCM push token registration in ZoneMinder, covering database schema, CakePHP REST API, pyzm model, and zm_detect push sender integration. Co-Authored-By: Claude Opus 4.6 --- docs/plans/2026-03-05-notifications-design.md | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 docs/plans/2026-03-05-notifications-design.md diff --git a/docs/plans/2026-03-05-notifications-design.md b/docs/plans/2026-03-05-notifications-design.md new file mode 100644 index 000000000..0d270f4fd --- /dev/null +++ b/docs/plans/2026-03-05-notifications-design.md @@ -0,0 +1,173 @@ +# Push Notification Token Registration for ZoneMinder + +## Problem + +zmNg currently registers FCM push tokens with the Event Server (ES) via websocket. +When using ZM's native `EventStartCommand` to invoke `zm_detect` directly, zm_detect +has no way to know who to send push notifications to. Storing tokens in ZM's database +makes them accessible to any ZM-integrated tool. + +## Architecture + +``` +zmNg (mobile app) + | + | REST API (JWT/session auth) + v +ZoneMinder API (CakePHP) ---- Notifications table (MySQL) + | + | reads tokens via pyzm + v +zm_detect.py ---- objectconfig.yml (fcm_v1_url + fcm_v1_key) + | + | HTTP POST (FCM proxy) + v +Cloud Function -> FCM -> Device +``` + +## Component 1: Database — `Notifications` table + +Migration version: 1.39.2 + +```sql +CREATE TABLE `Notifications` ( + `Id` int unsigned NOT NULL AUTO_INCREMENT, + `UserId` int unsigned NOT NULL, + `Token` varchar(512) NOT NULL, + `Platform` enum('android','ios','web') NOT NULL, + `MonitorList` text DEFAULT NULL, + `Interval` int unsigned NOT NULL DEFAULT 0, + `PushState` enum('enabled','disabled') NOT NULL DEFAULT 'enabled', + `AppVersion` varchar(32) DEFAULT NULL, + `BadgeCount` int NOT NULL DEFAULT 0, + `LastNotifiedAt` datetime DEFAULT NULL, + `CreatedOn` datetime DEFAULT NULL, + `UpdatedOn` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`Id`), + UNIQUE KEY `Notifications_Token_idx` (`Token`), + KEY `Notifications_UserId_idx` (`UserId`) +) ENGINE=InnoDB; +``` + +Column notes: +- `UserId` — FK to Users.Id. Users see own tokens, admins see all. +- `Token` — FCM registration token. UNIQUE with upsert on re-register. +- `Platform` — android, ios, or web. Determines FCM payload format. +- `MonitorList` — Comma-separated monitor IDs. NULL = all monitors. +- `Interval` — Per-token throttle in seconds. 0 = no throttle. +- `PushState` — Master enable/disable toggle. +- `LastNotifiedAt` — Last time a push was sent to this token. Used with `Interval` for throttling. +- `CreatedOn` / `UpdatedOn` — Follow ZM timestamp conventions. + +## Component 2: ZM CakePHP API — `NotificationsController` + +Standard REST resource following existing ZM controller patterns (StatesController, GroupsController). + +Route: `Router::mapResources('notifications')` in routes.php + +Endpoints: +- `GET /api/notifications.json` — List tokens (admin: all, user: own) +- `GET /api/notifications/{id}.json` — View single token (scoped) +- `POST /api/notifications.json` — Create or upsert token (auto-sets UserId from session) +- `PUT /api/notifications/{id}.json` — Update filters, pushstate, interval, etc. +- `DELETE /api/notifications/{id}.json` — Delete token (scoped) + +Authorization: +- Admin users bypass scoping (can see/edit/delete all tokens) +- Non-admin users can only access rows where `UserId` matches their own User.Id +- POST auto-populates `UserId` from the authenticated session +- Upsert: if POST contains a Token that already exists, update the existing row (only if owned by same user or admin) + +Model: `Notification.php` with `$useTable = 'Notifications'`, `$primaryKey = 'Id'` + +## Component 3: pyzm — `Notification` model + ZMClient methods + +Following pyzm's existing dataclass + client pattern: + +Model (`pyzm/models/zm.py`): +```python +@dataclass +class Notification: + id: int + user_id: int = 0 + token: str = "" + platform: str = "" + monitor_list: str | None = None + interval: int = 0 + push_state: str = "enabled" + app_version: str | None = None + badge_count: int = 0 + last_notified_at: datetime | None = None + _raw: dict = field(default_factory=dict, repr=False, compare=False) + _client: ZMClient | None = field(default=None, repr=False, compare=False) + + @classmethod + def from_api_dict(cls, data: dict, client=None) -> "Notification": + ... + + def monitors(self) -> list[int] | None: + """Parse MonitorList into list of ints. None = all monitors.""" + if not self.monitor_list: + return None + return [int(m) for m in self.monitor_list.split(",")] +``` + +Client methods (`pyzm/client.py`): +- `notifications()` — fetch all tokens (zm_detect uses this) +- `notification(id)` — fetch single token +- `_create_notification(**kwargs)` — POST +- `_update_notification(id, **kwargs)` — PUT +- `_delete_notification(id)` — DELETE +- `_update_notification_last_sent(id)` — PUT to update LastNotifiedAt + +## Component 4: zm_detect — Push notification sender + +Config additions to `objectconfig.yml`: +```yaml +push: + enabled: yes + fcm_v1_url: https://us-central1-zmng-b7af6.cloudfunctions.net/send_push + fcm_v1_key: +``` + +After detection completes: +1. Query `Notifications` table via `zm.notifications()` +2. Filter: `PushState = 'enabled'` AND monitor ID in token's `MonitorList` (or MonitorList is NULL) +3. Throttle: skip if `LastNotifiedAt` is not None and `(now - LastNotifiedAt) < Interval` +4. For each qualifying token, HTTP POST to `fcm_v1_url` with payload: + ```json + { + "token": "", + "title": " Alarm", + "body": "", + "sound": "default", + "badge": "", + "data": { + "mid": "", + "eid": "", + "monitorName": "", + "cause": "", + "notification_foreground": "true" + }, + "android": { "priority": "high" }, + "ios": { "thread_id": "ZoneMinder_" } + } + ``` +5. Update `LastNotifiedAt` and `BadgeCount` in DB via pyzm + +FCM mode: proxy only (cloud function). + +## Component 5 (future): zmNg client changes + +Spec to be written after server components are done. zmNg will need to: +- Call ZM API to register/update/delete tokens instead of ES websocket +- Manage monitor filter UI against the new API +- Handle token refresh by calling PUT on the API + +## Implementation order + +1. DB migration + schema update (ZM repo) +2. CakePHP model + controller + route (ZM repo) +3. pyzm Notification model + ZMClient methods (pyzm repo) +4. zm_detect push sender (zmeventnotification repo) +5. zmNg client spec (separate doc) From 4ca0e03885f7de0bdef75204b3254306758b92f6 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Thu, 5 Mar 2026 19:43:35 -0500 Subject: [PATCH 03/24] docs: add implementation plan for notifications API refs #4684 11-task plan covering DB schema, CakePHP API, pyzm model, and zm_detect push sender across three repos. Co-Authored-By: Claude Opus 4.6 --- docs/plans/2026-03-05-notifications-plan.md | 966 ++++++++++++++++++++ 1 file changed, 966 insertions(+) create mode 100644 docs/plans/2026-03-05-notifications-plan.md diff --git a/docs/plans/2026-03-05-notifications-plan.md b/docs/plans/2026-03-05-notifications-plan.md new file mode 100644 index 000000000..132ffe945 --- /dev/null +++ b/docs/plans/2026-03-05-notifications-plan.md @@ -0,0 +1,966 @@ +# Push Notification Token Registration — Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Enable zm_detect to send FCM push notifications directly by storing device tokens in ZM's database via a new REST API. + +**Architecture:** New `Notifications` table in ZM DB, CakePHP REST controller with user-scoped access, pyzm `Notification` model for Python access, and push-sending logic in zm_detect using FCM proxy mode. + +**Tech Stack:** MySQL, CakePHP 2.x, Python 3 (pyzm dataclasses + requests), FCM HTTP v1 via cloud function proxy. + +**Repos:** +- ZM: `/home/arjunrc/fiddle/zm/pliablepixels_ZoneMinder.git` (branch: `notifications-api`) +- pyzm: `/home/arjunrc/fiddle/pyzm` +- ES: `/home/arjunrc/fiddle/zmeventnotification` + +**Design doc:** `docs/plans/2026-03-05-notifications-design.md` + +--- + +### Task 1: Database — Add Notifications table to fresh-install schema + +**Files:** +- Modify: `/home/arjunrc/fiddle/zm/pliablepixels_ZoneMinder.git/db/zm_create.sql.in` (after line 1330) + +**Step 1: Add CREATE TABLE after Events_Tags table** + +Insert after line 1330 (after `Events_Tags` closing), before the `source` lines: + +```sql +CREATE TABLE `Notifications` ( + `Id` int unsigned NOT NULL AUTO_INCREMENT, + `UserId` int unsigned NOT NULL, + `Token` varchar(512) NOT NULL, + `Platform` enum('android','ios','web') NOT NULL, + `MonitorList` text DEFAULT NULL, + `Interval` int unsigned NOT NULL DEFAULT 0, + `PushState` enum('enabled','disabled') NOT NULL DEFAULT 'enabled', + `AppVersion` varchar(32) DEFAULT NULL, + `BadgeCount` int NOT NULL DEFAULT 0, + `LastNotifiedAt` datetime DEFAULT NULL, + `CreatedOn` datetime DEFAULT NULL, + `UpdatedOn` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`Id`), + UNIQUE KEY `Notifications_Token_idx` (`Token`), + KEY `Notifications_UserId_idx` (`UserId`) +) ENGINE=@ZM_MYSQL_ENGINE@; +``` + +**Step 2: Verify syntax** + +Run: `grep -A 20 'CREATE TABLE .Notifications' db/zm_create.sql.in` +Expected: The full CREATE TABLE statement as above. + +**Step 3: Commit** + +```bash +git add db/zm_create.sql.in +git commit -m "feat: add Notifications table to fresh-install schema refs #4684" +``` + +--- + +### Task 2: Database — Create migration file for existing installs + +**Files:** +- Create: `/home/arjunrc/fiddle/zm/pliablepixels_ZoneMinder.git/db/zm_update-1.39.2.sql` + +**Step 1: Write migration file** + +```sql +-- +-- Add Notifications table for FCM push token registration +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_name = 'Notifications' + AND table_schema = DATABASE()) > 0, + "SELECT 'Notifications table already exists'", + "CREATE TABLE `Notifications` ( + `Id` int unsigned NOT NULL AUTO_INCREMENT, + `UserId` int unsigned NOT NULL, + `Token` varchar(512) NOT NULL, + `Platform` enum('android','ios','web') NOT NULL, + `MonitorList` text DEFAULT NULL, + `Interval` int unsigned NOT NULL DEFAULT 0, + `PushState` enum('enabled','disabled') NOT NULL DEFAULT 'enabled', + `AppVersion` varchar(32) DEFAULT NULL, + `BadgeCount` int NOT NULL DEFAULT 0, + `LastNotifiedAt` datetime DEFAULT NULL, + `CreatedOn` datetime DEFAULT NULL, + `UpdatedOn` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`Id`), + UNIQUE KEY `Notifications_Token_idx` (`Token`), + KEY `Notifications_UserId_idx` (`UserId`) + ) ENGINE=InnoDB" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +``` + +**Step 2: Test migration against local DB** + +Run: `mysql -u zmuser -pzmpass zm < db/zm_update-1.39.2.sql` +Expected: No errors. Run again to verify idempotency (should print "Notifications table already exists"). + +**Step 3: Verify table exists** + +Run: `mysql -u zmuser -pzmpass zm -e "DESCRIBE Notifications;"` +Expected: All columns listed with correct types. + +**Step 4: Commit** + +```bash +git add db/zm_update-1.39.2.sql +git commit -m "feat: add migration for Notifications table refs #4684" +``` + +--- + +### Task 3: CakePHP — Create Notification model + +**Files:** +- Create: `/home/arjunrc/fiddle/zm/pliablepixels_ZoneMinder.git/web/api/app/Model/Notification.php` + +**Step 1: Write model file** + +Reference: `web/api/app/Model/State.php` + +```php + array( + 'notBlank' => array( + 'rule' => array('notBlank'), + 'message' => 'Token is required', + ), + ), + 'Platform' => array( + 'inList' => array( + 'rule' => array('inList', array('android', 'ios', 'web')), + 'message' => 'Platform must be android, ios, or web', + ), + ), + 'PushState' => array( + 'inList' => array( + 'rule' => array('inList', array('enabled', 'disabled')), + 'message' => 'PushState must be enabled or disabled', + 'allowEmpty' => true, + ), + ), + ); + +} +``` + +**Step 2: Commit** + +```bash +git add web/api/app/Model/Notification.php +git commit -m "feat: add Notification CakePHP model refs #4684" +``` + +--- + +### Task 4: CakePHP — Create NotificationsController with user-scoped CRUD + +**Files:** +- Create: `/home/arjunrc/fiddle/zm/pliablepixels_ZoneMinder.git/web/api/app/Controller/NotificationsController.php` + +**Step 1: Write controller** + +Reference: `web/api/app/Controller/StatesController.php` for pattern, `web/api/app/Controller/UsersController.php` for `$user->Id()` scoping. + +```php +System() == 'Edit'); + } + + /** + * Return the authenticated user's Id, or null. + */ + private function _userId() { + global $user; + return $user ? $user->Id() : null; + } + + public function beforeFilter() { + parent::beforeFilter(); + global $user; + $canView = (!$user) || ($user->System() != 'None'); + if (!$canView) { + throw new UnauthorizedException(__('Insufficient Privileges')); + } + } + + /** + * index — list notifications. Admin sees all, user sees own. + */ + public function index() { + $conditions = array(); + if (!$this->_isAdmin()) { + $conditions['Notification.UserId'] = $this->_userId(); + } + $notifications = $this->Notification->find('all', array( + 'conditions' => $conditions, + 'recursive' => -1, + )); + $this->set(array( + 'notifications' => $notifications, + '_serialize' => array('notifications'), + )); + } + + /** + * view — show single notification. Scoped to user unless admin. + */ + public function view($id = null) { + $this->Notification->id = $id; + if (!$this->Notification->exists()) { + throw new NotFoundException(__('Invalid notification')); + } + $notification = $this->Notification->find('first', array( + 'conditions' => array('Notification.Id' => $id), + 'recursive' => -1, + )); + if (!$this->_isAdmin() && $notification['Notification']['UserId'] != $this->_userId()) { + throw new UnauthorizedException(__('Insufficient Privileges')); + } + $this->set(array( + 'notification' => $notification, + '_serialize' => array('notification'), + )); + } + + /** + * add — create or upsert. Auto-sets UserId from session. + * If Token already exists for this user, updates instead of creating. + */ + public function add() { + if (!$this->request->is('post')) { + throw new BadRequestException(__('POST required')); + } + + $data = $this->request->data; + if (isset($data['Notification'])) { + $data = $data['Notification']; + } + + // Force UserId to the authenticated user (admin can override) + if (!$this->_isAdmin() || !isset($data['UserId'])) { + $data['UserId'] = $this->_userId(); + } + + if (!isset($data['CreatedOn'])) { + $data['CreatedOn'] = date('Y-m-d H:i:s'); + } + + // Upsert: check if token already exists + if (isset($data['Token'])) { + $existing = $this->Notification->find('first', array( + 'conditions' => array('Notification.Token' => $data['Token']), + 'recursive' => -1, + )); + if ($existing) { + // Only allow upsert if same user or admin + if (!$this->_isAdmin() && $existing['Notification']['UserId'] != $this->_userId()) { + throw new UnauthorizedException(__('Token belongs to another user')); + } + $this->Notification->id = $existing['Notification']['Id']; + unset($data['CreatedOn']); // Don't overwrite creation date on upsert + } else { + $this->Notification->create(); + } + } else { + $this->Notification->create(); + } + + if ($this->Notification->save(array('Notification' => $data))) { + $notification = $this->Notification->find('first', array( + 'conditions' => array('Notification.Id' => $this->Notification->id), + 'recursive' => -1, + )); + $this->set(array( + 'notification' => $notification, + '_serialize' => array('notification'), + )); + } else { + $this->response->statusCode(400); + $this->set(array( + 'message' => __('Could not save notification'), + 'errors' => $this->Notification->validationErrors, + '_serialize' => array('message', 'errors'), + )); + } + } + + /** + * edit — update notification fields. Scoped to user unless admin. + */ + public function edit($id = null) { + $this->Notification->id = $id; + if (!$this->Notification->exists()) { + throw new NotFoundException(__('Invalid notification')); + } + + $existing = $this->Notification->find('first', array( + 'conditions' => array('Notification.Id' => $id), + 'recursive' => -1, + )); + if (!$this->_isAdmin() && $existing['Notification']['UserId'] != $this->_userId()) { + throw new UnauthorizedException(__('Insufficient Privileges')); + } + + if ($this->request->is(array('post', 'put'))) { + $data = $this->request->data; + if (isset($data['Notification'])) { + $data = $data['Notification']; + } + // Don't allow changing UserId unless admin + if (!$this->_isAdmin()) { + unset($data['UserId']); + } + if ($this->Notification->save(array('Notification' => $data))) { + $notification = $this->Notification->find('first', array( + 'conditions' => array('Notification.Id' => $id), + 'recursive' => -1, + )); + $this->set(array( + 'notification' => $notification, + '_serialize' => array('notification'), + )); + } else { + $this->response->statusCode(400); + $this->set(array( + 'message' => __('Could not save notification'), + 'errors' => $this->Notification->validationErrors, + '_serialize' => array('message', 'errors'), + )); + } + } + } + + /** + * delete — remove notification. Scoped to user unless admin. + */ + public function delete($id = null) { + $this->Notification->id = $id; + if (!$this->Notification->exists()) { + throw new NotFoundException(__('Invalid notification')); + } + $this->request->allowMethod('post', 'delete'); + + $existing = $this->Notification->find('first', array( + 'conditions' => array('Notification.Id' => $id), + 'recursive' => -1, + )); + if (!$this->_isAdmin() && $existing['Notification']['UserId'] != $this->_userId()) { + throw new UnauthorizedException(__('Insufficient Privileges')); + } + + if ($this->Notification->delete()) { + $this->set(array( + 'message' => __('Notification deleted'), + '_serialize' => array('message'), + )); + } else { + $this->response->statusCode(400); + $this->set(array( + 'message' => __('Could not delete notification'), + '_serialize' => array('message'), + )); + } + } + +} +``` + +**Step 2: Commit** + +```bash +git add web/api/app/Controller/NotificationsController.php +git commit -m "feat: add NotificationsController with user-scoped CRUD refs #4684" +``` + +--- + +### Task 5: CakePHP — Register route + +**Files:** +- Modify: `/home/arjunrc/fiddle/zm/pliablepixels_ZoneMinder.git/web/api/app/Config/routes.php` (line 47, after `zones`) + +**Step 1: Add mapResources call** + +Add after line 47 (`Router::mapResources('zones');`): + +```php + Router::mapResources('notifications'); +``` + +**Step 2: Commit** + +```bash +git add web/api/app/Config/routes.php +git commit -m "feat: register notifications REST route refs #4684" +``` + +--- + +### Task 6: Test ZM API manually + +**Step 1: Deploy API files to live web root** + +```bash +echo "doctoral" | sudo -S cp web/api/app/Model/Notification.php /usr/share/zoneminder/www/api/app/Model/ +echo "doctoral" | sudo -S cp web/api/app/Controller/NotificationsController.php /usr/share/zoneminder/www/api/app/Controller/ +echo "doctoral" | sudo -S cp web/api/app/Config/routes.php /usr/share/zoneminder/www/api/app/Config/ +``` + +**Step 2: Apply migration** + +```bash +mysql -u zmuser -pzmpass zm < db/zm_update-1.39.2.sql +``` + +**Step 3: Clear CakePHP cache** + +```bash +echo "doctoral" | sudo -S rm -rf /usr/share/zoneminder/www/api/app/tmp/cache/models/* +echo "doctoral" | sudo -S rm -rf /usr/share/zoneminder/www/api/app/tmp/cache/persistent/* +``` + +**Step 4: Get auth token** + +```bash +TOKEN=$(curl -s -k -X POST "http://localhost/zm/api/host/login.json" \ + -d "user=admin&pass=admin" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])") +echo $TOKEN +``` + +Expected: A JWT token string. + +**Step 5: Test POST (create)** + +```bash +curl -s -k -X POST "http://localhost/zm/api/notifications.json?token=$TOKEN" \ + -d "Notification[Token]=test_fcm_token_123" \ + -d "Notification[Platform]=android" \ + -d "Notification[MonitorList]=1,2" \ + -d "Notification[Interval]=60" \ + -d "Notification[AppVersion]=1.0.0" | python3 -m json.tool +``` + +Expected: JSON with the created notification including auto-populated `UserId`, `Id`, `CreatedOn`. + +**Step 6: Test GET (index)** + +```bash +curl -s -k "http://localhost/zm/api/notifications.json?token=$TOKEN" | python3 -m json.tool +``` + +Expected: JSON array with the notification created above. + +**Step 7: Test PUT (update MonitorList)** + +```bash +# Use the Id from the POST response +curl -s -k -X PUT "http://localhost/zm/api/notifications/1.json?token=$TOKEN" \ + -d "Notification[MonitorList]=1,2,3" | python3 -m json.tool +``` + +Expected: Updated notification with `MonitorList` = "1,2,3". + +**Step 8: Test POST upsert (same token)** + +```bash +curl -s -k -X POST "http://localhost/zm/api/notifications.json?token=$TOKEN" \ + -d "Notification[Token]=test_fcm_token_123" \ + -d "Notification[Platform]=ios" \ + -d "Notification[MonitorList]=5" | python3 -m json.tool +``` + +Expected: Same `Id` as before, updated `Platform` and `MonitorList`. + +**Step 9: Test DELETE** + +```bash +curl -s -k -X DELETE "http://localhost/zm/api/notifications/1.json?token=$TOKEN" +``` + +Expected: Success message. + +**Step 10: Verify deletion** + +```bash +curl -s -k "http://localhost/zm/api/notifications.json?token=$TOKEN" | python3 -m json.tool +``` + +Expected: Empty array. + +--- + +### Task 7: pyzm — Add Notification dataclass + +**Files:** +- Modify: `/home/arjunrc/fiddle/pyzm/pyzm/models/zm.py` (after Event class, around line 221) + +**Step 1: Add Notification dataclass** + +Insert after the Event class (after line 220): + +```python +@dataclass +class Notification: + """A ZoneMinder push notification token registration. + + Represents a device registered to receive FCM push notifications + for ZoneMinder events. + """ + id: int + user_id: int = 0 + token: str = "" + platform: str = "" + monitor_list: str | None = None + interval: int = 0 + push_state: str = "enabled" + app_version: str | None = None + badge_count: int = 0 + last_notified_at: datetime | None = None + + _raw: dict = field(default_factory=dict, repr=False, compare=False) + _client: ZMClient | None = field(default=None, repr=False, compare=False) + + def raw(self) -> dict: + return self._raw + + def monitors(self) -> list[int] | None: + """Parse MonitorList into list of ints. None = all monitors.""" + if not self.monitor_list: + return None + return [int(m.strip()) for m in self.monitor_list.split(",") if m.strip()] + + def should_notify(self, monitor_id: int) -> bool: + """Check if this token should receive notifications for the given monitor.""" + if self.push_state != "enabled": + return False + monitors = self.monitors() + if monitors is None: + return True # NULL = all monitors + return monitor_id in monitors + + def is_throttled(self) -> bool: + """Check if this token is currently throttled.""" + if self.interval <= 0 or self.last_notified_at is None: + return False + elapsed = (datetime.now() - self.last_notified_at).total_seconds() + return elapsed < self.interval + + def delete(self) -> None: + self._require_client() + self._client._delete_notification(self.id) + + def update_last_sent(self, badge: int | None = None) -> None: + """Update LastNotifiedAt to now and optionally set BadgeCount.""" + self._require_client() + self._client._update_notification_last_sent(self.id, badge) + + def _require_client(self) -> None: + if self._client is None: + raise RuntimeError( + "Notification not bound to a ZMClient. " + "Use zm.notifications() to get bound Notification objects." + ) + + @classmethod + def from_api_dict(cls, data: dict, client: ZMClient | None = None) -> Notification: + """Build from a ZM API Notification JSON dict.""" + n = data.get("Notification", data) + return cls( + id=int(n.get("Id", 0)), + user_id=int(n.get("UserId", 0)), + token=n.get("Token", ""), + platform=n.get("Platform", ""), + monitor_list=n.get("MonitorList"), + interval=int(n.get("Interval", 0)), + push_state=n.get("PushState", "enabled"), + app_version=n.get("AppVersion"), + badge_count=int(n.get("BadgeCount", 0)), + last_notified_at=_parse_dt(n.get("LastNotifiedAt")), + _raw=data, + _client=client, + ) +``` + +**Step 2: Commit** + +```bash +cd /home/arjunrc/fiddle/pyzm +git add pyzm/models/zm.py +git commit -m "feat: add Notification dataclass for push token registration" +``` + +--- + +### Task 8: pyzm — Add ZMClient notification methods + +**Files:** +- Modify: `/home/arjunrc/fiddle/pyzm/pyzm/client.py` (after `event()` method, around line 219) +- Modify: `/home/arjunrc/fiddle/pyzm/pyzm/__init__.py` (add export) + +**Step 1: Add client methods after `event()` (around line 219)** + +```python + # ------------------------------------------------------------------ + # Notifications + # ------------------------------------------------------------------ + + def notifications(self) -> list[Notification]: + """Fetch all push notification token registrations.""" + data = self._api.get("notifications.json") + items = data.get("notifications", []) if data else [] + return [Notification.from_api_dict(n, client=self) for n in items] + + def notification(self, notification_id: int) -> Notification: + """Fetch a single notification registration by ID.""" + data = self._api.get(f"notifications/{notification_id}.json") + if data and data.get("notification"): + return Notification.from_api_dict(data["notification"], client=self) + raise ValueError(f"Notification {notification_id} not found") + + def _create_notification(self, **kwargs) -> dict: + """Create or upsert a notification registration.""" + data = {f"Notification[{k}]": v for k, v in kwargs.items()} + return self._api.post("notifications.json", data=data) + + def _update_notification(self, notification_id: int, **kwargs) -> dict: + """Update fields on a notification registration.""" + data = {f"Notification[{k}]": v for k, v in kwargs.items()} + return self._api.put(f"notifications/{notification_id}.json", data=data) + + def _delete_notification(self, notification_id: int) -> None: + """Delete a notification registration.""" + self._api.delete(f"notifications/{notification_id}.json") + + def _update_notification_last_sent(self, notification_id: int, badge: int | None = None) -> None: + """Update LastNotifiedAt to now and optionally set BadgeCount.""" + from datetime import datetime + data = {"LastNotifiedAt": datetime.now().strftime("%Y-%m-%d %H:%M:%S")} + if badge is not None: + data["BadgeCount"] = str(badge) + self._update_notification(notification_id, **data) +``` + +**Step 2: Add import at top of client.py** + +Add to the existing import from `pyzm.models.zm`: + +```python +from pyzm.models.zm import Notification +``` + +(Find the existing `from pyzm.models.zm import ...` line and add `Notification` to it.) + +**Step 3: Add export to `__init__.py`** + +In `/home/arjunrc/fiddle/pyzm/pyzm/__init__.py`, add `"Notification"` to the `__all__` list (line 18-28) and add the import: + +```python +from pyzm.models.zm import Notification +``` + +Add `"Notification"` to `__all__`. + +**Step 4: Commit** + +```bash +cd /home/arjunrc/fiddle/pyzm +git add pyzm/client.py pyzm/__init__.py +git commit -m "feat: add ZMClient notification methods and export" +``` + +--- + +### Task 9: zm_detect — Add push notification config and sender + +**Files:** +- Modify: `/home/arjunrc/fiddle/zmeventnotification/hook/objectconfig.yml` (after line 78, after animation section) +- Create: `/home/arjunrc/fiddle/zmeventnotification/zmes_hook_helpers/push.py` +- Modify: `/home/arjunrc/fiddle/zmeventnotification/hook/zm_detect.py` (after line 187, after tagging) + +**Step 1: Add push config section to objectconfig.yml** + +Insert after line 78 (after `animation:` section): + +```yaml + +# Push notifications via FCM cloud function proxy +# zm_detect reads registered tokens from ZM's Notifications table (via pyzm) +# and sends push notifications directly after detection. +push: + enabled: "no" + # Cloud function proxy URL and authorization key + fcm_v1_url: "https://us-central1-zmng-b7af6.cloudfunctions.net/send_push" + fcm_v1_key: "!FCM_V1_KEY" + # Replace previous push for same monitor (collapses notifications) + replace_push_messages: "yes" + # Include event image URL in push notification + include_picture: "yes" + # Android notification priority + android_priority: "high" + # Android TTL in seconds (omit to use FCM default) + #android_ttl: 60 +``` + +**Step 2: Create push.py helper module** + +```python +"""Push notification sender for zm_detect. + +Reads registered tokens from ZM's Notifications table via pyzm, +filters by monitor, checks throttle, and sends via FCM cloud function proxy. +""" + +import json +import requests +from datetime import datetime + + +def send_push_notifications(zm, config, monitor_id, event_id, monitor_name, cause, logger): + """Send FCM push notifications to all qualifying registered tokens. + + Args: + zm: pyzm ZMClient instance (already authenticated). + config: dict with push config keys (fcm_v1_url, fcm_v1_key, etc.). + monitor_id: int, the monitor that triggered the event. + event_id: int/str, the event ID. + monitor_name: str, human-readable monitor name. + cause: str, detection result string (e.g. "person detected"). + logger: pyzm logger instance. + """ + if config.get('push', {}).get('enabled') != 'yes': + logger.Debug(1, 'push: disabled in config, skipping') + return + + push_cfg = config.get('push', {}) + fcm_url = push_cfg.get('fcm_v1_url') + fcm_key = push_cfg.get('fcm_v1_key') + + if not fcm_url or not fcm_key: + logger.Error('push: fcm_v1_url or fcm_v1_key not configured') + return + + try: + notifications = zm.notifications() + except Exception as e: + logger.Error('push: failed to fetch notifications from ZM API: {}'.format(e)) + return + + if not notifications: + logger.Debug(1, 'push: no registered tokens found') + return + + mid = int(monitor_id) + sent_count = 0 + + for notif in notifications: + if not notif.should_notify(mid): + logger.Debug(2, 'push: skipping token ...{} (monitor {} not in filter)'.format( + notif.token[-6:] if len(notif.token) > 6 else notif.token, mid)) + continue + + if notif.is_throttled(): + logger.Debug(2, 'push: skipping token ...{} (throttled, interval={}s)'.format( + notif.token[-6:] if len(notif.token) > 6 else notif.token, notif.interval)) + continue + + badge = notif.badge_count + 1 + title = '{} Alarm'.format(monitor_name) + body = cause if cause else 'Event {} on {}'.format(event_id, monitor_name) + + payload = { + 'token': notif.token, + 'title': title, + 'body': body, + 'sound': 'default', + 'badge': badge, + 'data': { + 'mid': str(mid), + 'eid': str(event_id), + 'monitorName': monitor_name, + 'cause': cause or '', + 'notification_foreground': 'true', + }, + } + + # Platform-specific fields (proxy format, matching ES FCM.pm proxy mode) + if notif.platform == 'android': + payload['android'] = { + 'icon': 'ic_stat_notification', + 'priority': push_cfg.get('android_priority', 'high'), + } + ttl = push_cfg.get('android_ttl') + if ttl: + payload['android']['ttl'] = str(ttl) + if push_cfg.get('replace_push_messages') == 'yes': + payload['android']['tag'] = 'zmninjapush' + if notif.app_version and notif.app_version != 'unknown': + payload['android']['channel'] = 'zmninja' + + elif notif.platform == 'ios': + payload['ios'] = { + 'thread_id': 'zmninja_alarm', + 'headers': { + 'apns-priority': '10', + 'apns-push-type': 'alert', + }, + } + if push_cfg.get('replace_push_messages') == 'yes': + payload['ios']['headers']['apns-collapse-id'] = 'zmninjapush' + + # Send via proxy + try: + logger.Debug(1, 'push: sending to token ...{} ({})'.format( + notif.token[-6:] if len(notif.token) > 6 else notif.token, notif.platform)) + + resp = requests.post( + fcm_url, + headers={ + 'Content-Type': 'application/json', + 'Authorization': fcm_key, + }, + data=json.dumps(payload), + timeout=10, + ) + + if resp.ok: + logger.Debug(1, 'push: FCM proxy returned 200 for token ...{}'.format( + notif.token[-6:] if len(notif.token) > 6 else notif.token)) + # Update last sent time and badge count + try: + notif.update_last_sent(badge=badge) + except Exception as e: + logger.Debug(1, 'push: failed to update LastNotifiedAt: {}'.format(e)) + sent_count += 1 + else: + body_text = resp.text + logger.Error('push: FCM proxy error for token ...{}: {}'.format( + notif.token[-6:] if len(notif.token) > 6 else notif.token, body_text)) + # If token is invalid, delete it from ZM + if 'not a valid FCM' in body_text or 'entity was not found' in body_text: + logger.Debug(1, 'push: removing invalid token ...{}'.format( + notif.token[-6:] if len(notif.token) > 6 else notif.token)) + try: + notif.delete() + except Exception as e: + logger.Debug(1, 'push: failed to delete invalid token: {}'.format(e)) + + except Exception as e: + logger.Error('push: exception sending to token ...{}: {}'.format( + notif.token[-6:] if len(notif.token) > 6 else notif.token, e)) + + logger.Debug(1, 'push: sent {} notifications for event {} on monitor {}'.format( + sent_count, event_id, monitor_name)) +``` + +**Step 3: Add push call to zm_detect.py** + +Insert after line 187 (after the tagging block), before the animation block (line 189): + +```python + # --- Push notifications --- + if g.config.get('push', {}).get('enabled') == 'yes' and args.get('eventid') and args.get('monitorid'): + try: + from zmes_hook_helpers.push import send_push_notifications + mon = zm.monitor(int(args['monitorid'])) + mon_name = mon.name if mon else 'Monitor {}'.format(args['monitorid']) + send_push_notifications( + zm, g.config, args['monitorid'], args['eventid'], + mon_name, pred, g.logger) + except Exception as e: + g.logger.Error('Push notification error: {}'.format(e)) +``` + +Note: Check how `zm.monitor()` works in pyzm. The `monitors()` method returns cached monitors, so we can get the name. If the method signature differs, adjust accordingly. + +**Step 4: Commit** + +```bash +cd /home/arjunrc/fiddle/zmeventnotification +git add hook/objectconfig.yml zmes_hook_helpers/push.py hook/zm_detect.py +git commit -m "feat: add direct FCM push notifications to zm_detect" +``` + +--- + +### Task 10: Test end-to-end push notification flow + +**Step 1: Register a test token via API** + +```bash +TOKEN=$(curl -s -k -X POST "http://localhost/zm/api/host/login.json" \ + -d "user=admin&pass=admin" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])") + +curl -s -k -X POST "http://localhost/zm/api/notifications.json?token=$TOKEN" \ + -d "Notification[Token]=test_fcm_device_token" \ + -d "Notification[Platform]=android" \ + -d "Notification[MonitorList]=1" \ + -d "Notification[Interval]=0" \ + -d "Notification[AppVersion]=1.0.0" | python3 -m json.tool +``` + +**Step 2: Enable push in objectconfig.yml** + +Set `push.enabled: "yes"` and configure `fcm_v1_key` with a valid key. + +**Step 3: Run zm_detect with --fakeit for a test event** + +```bash +python3 hook/zm_detect.py -c hook/objectconfig.yml -e -m 1 -r "test" -n --fakeit "person" +``` + +Expected: Log output showing "push: sending to token ..." and either a 200 (if key is valid) or an auth error (which confirms the flow works up to the FCM call). + +**Step 4: Verify LastNotifiedAt was updated** + +```bash +curl -s -k "http://localhost/zm/api/notifications.json?token=$TOKEN" | python3 -m json.tool +``` + +Expected: `LastNotifiedAt` should be populated with a recent timestamp (if the push succeeded). + +--- + +### Task 11: Final commit — update design doc + +**Step 1: Verify all changes across repos** + +```bash +cd /home/arjunrc/fiddle/zm/pliablepixels_ZoneMinder.git && git log --oneline -5 +cd /home/arjunrc/fiddle/pyzm && git log --oneline -3 +cd /home/arjunrc/fiddle/zmeventnotification && git log --oneline -3 +``` + +**Step 2: Update design doc status** + +Add "Implementation complete" note to the design doc. Ready for zmNg client spec (Component 5). From 2fce145a9059250cfec6bf7533da5d02c5d67adc Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Thu, 5 Mar 2026 19:45:46 -0500 Subject: [PATCH 04/24] feat: add Notifications table for push token registration refs #4684 Add Notifications table to fresh-install schema (zm_create.sql.in) and create idempotent migration (zm_update-1.39.2.sql) for existing installs. The table stores FCM push notification tokens so zm_detect can send push notifications directly. Co-Authored-By: Claude Opus 4.6 --- db/zm_create.sql.in | 18 ++++++++++++++++++ db/zm_update-1.39.2.sql | 30 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 db/zm_update-1.39.2.sql diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 59da11a92..6dcb990e3 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -1329,6 +1329,24 @@ CREATE TABLE `Events_Tags` ( CONSTRAINT `Events_Tags_ibfk_2` FOREIGN KEY (`EventId`) REFERENCES `Events` (`Id`) ON DELETE CASCADE ) ENGINE=@ZM_MYSQL_ENGINE@; +CREATE TABLE `Notifications` ( + `Id` int unsigned NOT NULL AUTO_INCREMENT, + `UserId` int unsigned NOT NULL, + `Token` varchar(512) NOT NULL, + `Platform` enum('android','ios','web') NOT NULL, + `MonitorList` text DEFAULT NULL, + `Interval` int unsigned NOT NULL DEFAULT 0, + `PushState` enum('enabled','disabled') NOT NULL DEFAULT 'enabled', + `AppVersion` varchar(32) DEFAULT NULL, + `BadgeCount` int NOT NULL DEFAULT 0, + `LastNotifiedAt` datetime DEFAULT NULL, + `CreatedOn` datetime DEFAULT NULL, + `UpdatedOn` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`Id`), + UNIQUE KEY `Notifications_Token_idx` (`Token`), + KEY `Notifications_UserId_idx` (`UserId`) +) ENGINE=@ZM_MYSQL_ENGINE@; + source @PKGDATADIR@/db/Object_Types.sql -- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts. source @PKGDATADIR@/db/triggers.sql diff --git a/db/zm_update-1.39.2.sql b/db/zm_update-1.39.2.sql new file mode 100644 index 000000000..dbe5482a8 --- /dev/null +++ b/db/zm_update-1.39.2.sql @@ -0,0 +1,30 @@ +-- +-- Add Notifications table for FCM push token registration +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_name = 'Notifications' + AND table_schema = DATABASE()) > 0, + "SELECT 'Notifications table already exists'", + "CREATE TABLE `Notifications` ( + `Id` int unsigned NOT NULL AUTO_INCREMENT, + `UserId` int unsigned NOT NULL, + `Token` varchar(512) NOT NULL, + `Platform` enum('android','ios','web') NOT NULL, + `MonitorList` text DEFAULT NULL, + `Interval` int unsigned NOT NULL DEFAULT 0, + `PushState` enum('enabled','disabled') NOT NULL DEFAULT 'enabled', + `AppVersion` varchar(32) DEFAULT NULL, + `BadgeCount` int NOT NULL DEFAULT 0, + `LastNotifiedAt` datetime DEFAULT NULL, + `CreatedOn` datetime DEFAULT NULL, + `UpdatedOn` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`Id`), + UNIQUE KEY `Notifications_Token_idx` (`Token`), + KEY `Notifications_UserId_idx` (`UserId`) + ) ENGINE=InnoDB" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; From de1f31c6e27c5e6bfbd4350f4d5c3b3a891b18c3 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Thu, 5 Mar 2026 19:47:29 -0500 Subject: [PATCH 05/24] feat: add Notification model, controller, and route refs #4684 Co-Authored-By: Claude Opus 4.6 --- web/api/app/Config/routes.php | 1 + .../Controller/NotificationsController.php | 186 ++++++++++++++++++ web/api/app/Model/Notification.php | 32 +++ 3 files changed, 219 insertions(+) create mode 100644 web/api/app/Controller/NotificationsController.php create mode 100644 web/api/app/Model/Notification.php diff --git a/web/api/app/Config/routes.php b/web/api/app/Config/routes.php index 55d494611..cd7c05c4d 100644 --- a/web/api/app/Config/routes.php +++ b/web/api/app/Config/routes.php @@ -45,6 +45,7 @@ Router::mapResources('user_preference'); Router::mapResources('zonepresets'); Router::mapResources('zones'); + Router::mapResources('notifications'); Router::parseExtensions(); diff --git a/web/api/app/Controller/NotificationsController.php b/web/api/app/Controller/NotificationsController.php new file mode 100644 index 000000000..3853128ff --- /dev/null +++ b/web/api/app/Controller/NotificationsController.php @@ -0,0 +1,186 @@ +System() == 'Edit'); + } + + private function _userId() { + global $user; + return $user ? $user->Id() : null; + } + + public function beforeFilter() { + parent::beforeFilter(); + global $user; + $canView = (!$user) || ($user->System() != 'None'); + if (!$canView) { + throw new UnauthorizedException(__('Insufficient Privileges')); + } + } + + public function index() { + $conditions = array(); + if (!$this->_isAdmin()) { + $conditions['Notification.UserId'] = $this->_userId(); + } + $notifications = $this->Notification->find('all', array( + 'conditions' => $conditions, + 'recursive' => -1, + )); + $this->set(array( + 'notifications' => $notifications, + '_serialize' => array('notifications'), + )); + } + + public function view($id = null) { + $this->Notification->id = $id; + if (!$this->Notification->exists()) { + throw new NotFoundException(__('Invalid notification')); + } + $notification = $this->Notification->find('first', array( + 'conditions' => array('Notification.Id' => $id), + 'recursive' => -1, + )); + if (!$this->_isAdmin() && $notification['Notification']['UserId'] != $this->_userId()) { + throw new UnauthorizedException(__('Insufficient Privileges')); + } + $this->set(array( + 'notification' => $notification, + '_serialize' => array('notification'), + )); + } + + public function add() { + if (!$this->request->is('post')) { + throw new BadRequestException(__('POST required')); + } + + $data = $this->request->data; + if (isset($data['Notification'])) { + $data = $data['Notification']; + } + + if (!$this->_isAdmin() || !isset($data['UserId'])) { + $data['UserId'] = $this->_userId(); + } + + if (!isset($data['CreatedOn'])) { + $data['CreatedOn'] = date('Y-m-d H:i:s'); + } + + if (isset($data['Token'])) { + $existing = $this->Notification->find('first', array( + 'conditions' => array('Notification.Token' => $data['Token']), + 'recursive' => -1, + )); + if ($existing) { + if (!$this->_isAdmin() && $existing['Notification']['UserId'] != $this->_userId()) { + throw new UnauthorizedException(__('Token belongs to another user')); + } + $this->Notification->id = $existing['Notification']['Id']; + unset($data['CreatedOn']); + } else { + $this->Notification->create(); + } + } else { + $this->Notification->create(); + } + + if ($this->Notification->save(array('Notification' => $data))) { + $notification = $this->Notification->find('first', array( + 'conditions' => array('Notification.Id' => $this->Notification->id), + 'recursive' => -1, + )); + $this->set(array( + 'notification' => $notification, + '_serialize' => array('notification'), + )); + } else { + $this->response->statusCode(400); + $this->set(array( + 'message' => __('Could not save notification'), + 'errors' => $this->Notification->validationErrors, + '_serialize' => array('message', 'errors'), + )); + } + } + + public function edit($id = null) { + $this->Notification->id = $id; + if (!$this->Notification->exists()) { + throw new NotFoundException(__('Invalid notification')); + } + + $existing = $this->Notification->find('first', array( + 'conditions' => array('Notification.Id' => $id), + 'recursive' => -1, + )); + if (!$this->_isAdmin() && $existing['Notification']['UserId'] != $this->_userId()) { + throw new UnauthorizedException(__('Insufficient Privileges')); + } + + if ($this->request->is(array('post', 'put'))) { + $data = $this->request->data; + if (isset($data['Notification'])) { + $data = $data['Notification']; + } + if (!$this->_isAdmin()) { + unset($data['UserId']); + } + if ($this->Notification->save(array('Notification' => $data))) { + $notification = $this->Notification->find('first', array( + 'conditions' => array('Notification.Id' => $id), + 'recursive' => -1, + )); + $this->set(array( + 'notification' => $notification, + '_serialize' => array('notification'), + )); + } else { + $this->response->statusCode(400); + $this->set(array( + 'message' => __('Could not save notification'), + 'errors' => $this->Notification->validationErrors, + '_serialize' => array('message', 'errors'), + )); + } + } + } + + public function delete($id = null) { + $this->Notification->id = $id; + if (!$this->Notification->exists()) { + throw new NotFoundException(__('Invalid notification')); + } + $this->request->allowMethod('post', 'delete'); + + $existing = $this->Notification->find('first', array( + 'conditions' => array('Notification.Id' => $id), + 'recursive' => -1, + )); + if (!$this->_isAdmin() && $existing['Notification']['UserId'] != $this->_userId()) { + throw new UnauthorizedException(__('Insufficient Privileges')); + } + + if ($this->Notification->delete()) { + $this->set(array( + 'message' => __('Notification deleted'), + '_serialize' => array('message'), + )); + } else { + $this->response->statusCode(400); + $this->set(array( + 'message' => __('Could not delete notification'), + '_serialize' => array('message'), + )); + } + } + +} diff --git a/web/api/app/Model/Notification.php b/web/api/app/Model/Notification.php new file mode 100644 index 000000000..1f6606492 --- /dev/null +++ b/web/api/app/Model/Notification.php @@ -0,0 +1,32 @@ + array( + 'notBlank' => array( + 'rule' => array('notBlank'), + 'message' => 'Token is required', + ), + ), + 'Platform' => array( + 'inList' => array( + 'rule' => array('inList', array('android', 'ios', 'web')), + 'message' => 'Platform must be android, ios, or web', + ), + ), + 'PushState' => array( + 'inList' => array( + 'rule' => array('inList', array('enabled', 'disabled')), + 'message' => 'PushState must be enabled or disabled', + 'allowEmpty' => true, + ), + ), + ); + +} From c6effc12ab7d9661d9dc1e03be7bf4cc33229a03 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Thu, 5 Mar 2026 20:04:03 -0500 Subject: [PATCH 06/24] fix: add FK constraint, auth guard, and belongsTo for Notifications refs #4684 - Add FOREIGN KEY on UserId -> Users.Id with ON DELETE CASCADE (both in fresh schema and migration) - Reject push token registration when auth is disabled (UserId would be null, violating NOT NULL constraint) - Add $belongsTo association to User in Notification model Co-Authored-By: Claude Opus 4.6 --- db/zm_create.sql.in | 3 ++- db/zm_update-1.39.2.sql | 3 ++- web/api/app/Controller/NotificationsController.php | 5 +++++ web/api/app/Model/Notification.php | 7 +++++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 6dcb990e3..eb2002c7a 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -1344,7 +1344,8 @@ CREATE TABLE `Notifications` ( `UpdatedOn` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`Id`), UNIQUE KEY `Notifications_Token_idx` (`Token`), - KEY `Notifications_UserId_idx` (`UserId`) + KEY `Notifications_UserId_idx` (`UserId`), + CONSTRAINT `Notifications_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE ) ENGINE=@ZM_MYSQL_ENGINE@; source @PKGDATADIR@/db/Object_Types.sql diff --git a/db/zm_update-1.39.2.sql b/db/zm_update-1.39.2.sql index dbe5482a8..08d1f307b 100644 --- a/db/zm_update-1.39.2.sql +++ b/db/zm_update-1.39.2.sql @@ -21,7 +21,8 @@ SET @s = (SELECT IF( `UpdatedOn` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`Id`), UNIQUE KEY `Notifications_Token_idx` (`Token`), - KEY `Notifications_UserId_idx` (`UserId`) + KEY `Notifications_UserId_idx` (`UserId`), + CONSTRAINT `Notifications_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE ) ENGINE=InnoDB" )); diff --git a/web/api/app/Controller/NotificationsController.php b/web/api/app/Controller/NotificationsController.php index 3853128ff..db4f4a5c3 100644 --- a/web/api/app/Controller/NotificationsController.php +++ b/web/api/app/Controller/NotificationsController.php @@ -62,6 +62,11 @@ class NotificationsController extends AppController { throw new BadRequestException(__('POST required')); } + // Push token registration requires an authenticated user + if (!$this->_userId()) { + throw new UnauthorizedException(__('Push notification registration requires authentication')); + } + $data = $this->request->data; if (isset($data['Notification'])) { $data = $data['Notification']; diff --git a/web/api/app/Model/Notification.php b/web/api/app/Model/Notification.php index 1f6606492..361ed4e53 100644 --- a/web/api/app/Model/Notification.php +++ b/web/api/app/Model/Notification.php @@ -7,6 +7,13 @@ class Notification extends AppModel { public $primaryKey = 'Id'; public $displayField = 'Token'; + public $belongsTo = array( + 'User' => array( + 'className' => 'User', + 'foreignKey' => 'UserId', + ), + ); + public $validate = array( 'Token' => array( 'notBlank' => array( From 9c455cc29d3db8d38822cc0a6f4cb7aeccc3ce28 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Thu, 5 Mar 2026 20:09:32 -0500 Subject: [PATCH 07/24] fix: make UserId nullable for no-auth mode refs #4684 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - UserId is now DEFAULT NULL instead of NOT NULL - FK changed to ON DELETE SET NULL (keep token if user deleted) - Removed auth guard from add() — no-auth mode stores NULL UserId - No-auth mode already treated as admin by _isAdmin(), so scoping works correctly (sees all tokens) Co-Authored-By: Claude Opus 4.6 --- db/zm_create.sql.in | 4 ++-- db/zm_update-1.39.2.sql | 4 ++-- web/api/app/Controller/NotificationsController.php | 5 ----- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index eb2002c7a..4e3ef9d9e 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -1331,7 +1331,7 @@ CREATE TABLE `Events_Tags` ( CREATE TABLE `Notifications` ( `Id` int unsigned NOT NULL AUTO_INCREMENT, - `UserId` int unsigned NOT NULL, + `UserId` int unsigned DEFAULT NULL, `Token` varchar(512) NOT NULL, `Platform` enum('android','ios','web') NOT NULL, `MonitorList` text DEFAULT NULL, @@ -1345,7 +1345,7 @@ CREATE TABLE `Notifications` ( PRIMARY KEY (`Id`), UNIQUE KEY `Notifications_Token_idx` (`Token`), KEY `Notifications_UserId_idx` (`UserId`), - CONSTRAINT `Notifications_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE + CONSTRAINT `Notifications_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE SET NULL ) ENGINE=@ZM_MYSQL_ENGINE@; source @PKGDATADIR@/db/Object_Types.sql diff --git a/db/zm_update-1.39.2.sql b/db/zm_update-1.39.2.sql index 08d1f307b..9503663f7 100644 --- a/db/zm_update-1.39.2.sql +++ b/db/zm_update-1.39.2.sql @@ -8,7 +8,7 @@ SET @s = (SELECT IF( "SELECT 'Notifications table already exists'", "CREATE TABLE `Notifications` ( `Id` int unsigned NOT NULL AUTO_INCREMENT, - `UserId` int unsigned NOT NULL, + `UserId` int unsigned DEFAULT NULL, `Token` varchar(512) NOT NULL, `Platform` enum('android','ios','web') NOT NULL, `MonitorList` text DEFAULT NULL, @@ -22,7 +22,7 @@ SET @s = (SELECT IF( PRIMARY KEY (`Id`), UNIQUE KEY `Notifications_Token_idx` (`Token`), KEY `Notifications_UserId_idx` (`UserId`), - CONSTRAINT `Notifications_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE + CONSTRAINT `Notifications_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE SET NULL ) ENGINE=InnoDB" )); diff --git a/web/api/app/Controller/NotificationsController.php b/web/api/app/Controller/NotificationsController.php index db4f4a5c3..3853128ff 100644 --- a/web/api/app/Controller/NotificationsController.php +++ b/web/api/app/Controller/NotificationsController.php @@ -62,11 +62,6 @@ class NotificationsController extends AppController { throw new BadRequestException(__('POST required')); } - // Push token registration requires an authenticated user - if (!$this->_userId()) { - throw new UnauthorizedException(__('Push notification registration requires authentication')); - } - $data = $this->request->data; if (isset($data['Notification'])) { $data = $data['Notification']; From 1c3c150e7e9a2c55434303f83df1f96f48db7cb8 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Thu, 5 Mar 2026 20:12:11 -0500 Subject: [PATCH 08/24] fix: change Notifications FK to ON DELETE CASCADE refs #4684 Delete notification tokens when the owning user is deleted. Co-Authored-By: Claude Opus 4.6 --- db/zm_create.sql.in | 4 ++-- db/zm_update-1.39.2.sql | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 4e3ef9d9e..5b806f745 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -869,7 +869,7 @@ CREATE TABLE `Users` ( `RoleId` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`Id`), UNIQUE KEY `UC_Username` (`Username`), - FOREIGN KEY (`RoleId`) REFERENCES `User_Roles` (`Id`) ON DELETE SET NULL + FOREIGN KEY (`RoleId`) REFERENCES `User_Roles` (`Id`) ON DELETE CASCADE ) ENGINE=@ZM_MYSQL_ENGINE@; source @PKGDATADIR@/db/User_Preferences.sql @@ -1345,7 +1345,7 @@ CREATE TABLE `Notifications` ( PRIMARY KEY (`Id`), UNIQUE KEY `Notifications_Token_idx` (`Token`), KEY `Notifications_UserId_idx` (`UserId`), - CONSTRAINT `Notifications_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE SET NULL + CONSTRAINT `Notifications_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE ) ENGINE=@ZM_MYSQL_ENGINE@; source @PKGDATADIR@/db/Object_Types.sql diff --git a/db/zm_update-1.39.2.sql b/db/zm_update-1.39.2.sql index 9503663f7..3018e7f75 100644 --- a/db/zm_update-1.39.2.sql +++ b/db/zm_update-1.39.2.sql @@ -22,7 +22,7 @@ SET @s = (SELECT IF( PRIMARY KEY (`Id`), UNIQUE KEY `Notifications_Token_idx` (`Token`), KEY `Notifications_UserId_idx` (`UserId`), - CONSTRAINT `Notifications_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE SET NULL + CONSTRAINT `Notifications_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `Users` (`Id`) ON DELETE CASCADE ) ENGINE=InnoDB" )); From a1557f6447086cae39f5fee49c46ce8056062549 Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Thu, 5 Mar 2026 20:12:37 -0500 Subject: [PATCH 09/24] chore: remove plan docs from repo refs #4684 Co-Authored-By: Claude Opus 4.6 --- docs/plans/2026-03-05-notifications-design.md | 173 ---- docs/plans/2026-03-05-notifications-plan.md | 966 ------------------ 2 files changed, 1139 deletions(-) delete mode 100644 docs/plans/2026-03-05-notifications-design.md delete mode 100644 docs/plans/2026-03-05-notifications-plan.md diff --git a/docs/plans/2026-03-05-notifications-design.md b/docs/plans/2026-03-05-notifications-design.md deleted file mode 100644 index 0d270f4fd..000000000 --- a/docs/plans/2026-03-05-notifications-design.md +++ /dev/null @@ -1,173 +0,0 @@ -# Push Notification Token Registration for ZoneMinder - -## Problem - -zmNg currently registers FCM push tokens with the Event Server (ES) via websocket. -When using ZM's native `EventStartCommand` to invoke `zm_detect` directly, zm_detect -has no way to know who to send push notifications to. Storing tokens in ZM's database -makes them accessible to any ZM-integrated tool. - -## Architecture - -``` -zmNg (mobile app) - | - | REST API (JWT/session auth) - v -ZoneMinder API (CakePHP) ---- Notifications table (MySQL) - | - | reads tokens via pyzm - v -zm_detect.py ---- objectconfig.yml (fcm_v1_url + fcm_v1_key) - | - | HTTP POST (FCM proxy) - v -Cloud Function -> FCM -> Device -``` - -## Component 1: Database — `Notifications` table - -Migration version: 1.39.2 - -```sql -CREATE TABLE `Notifications` ( - `Id` int unsigned NOT NULL AUTO_INCREMENT, - `UserId` int unsigned NOT NULL, - `Token` varchar(512) NOT NULL, - `Platform` enum('android','ios','web') NOT NULL, - `MonitorList` text DEFAULT NULL, - `Interval` int unsigned NOT NULL DEFAULT 0, - `PushState` enum('enabled','disabled') NOT NULL DEFAULT 'enabled', - `AppVersion` varchar(32) DEFAULT NULL, - `BadgeCount` int NOT NULL DEFAULT 0, - `LastNotifiedAt` datetime DEFAULT NULL, - `CreatedOn` datetime DEFAULT NULL, - `UpdatedOn` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`Id`), - UNIQUE KEY `Notifications_Token_idx` (`Token`), - KEY `Notifications_UserId_idx` (`UserId`) -) ENGINE=InnoDB; -``` - -Column notes: -- `UserId` — FK to Users.Id. Users see own tokens, admins see all. -- `Token` — FCM registration token. UNIQUE with upsert on re-register. -- `Platform` — android, ios, or web. Determines FCM payload format. -- `MonitorList` — Comma-separated monitor IDs. NULL = all monitors. -- `Interval` — Per-token throttle in seconds. 0 = no throttle. -- `PushState` — Master enable/disable toggle. -- `LastNotifiedAt` — Last time a push was sent to this token. Used with `Interval` for throttling. -- `CreatedOn` / `UpdatedOn` — Follow ZM timestamp conventions. - -## Component 2: ZM CakePHP API — `NotificationsController` - -Standard REST resource following existing ZM controller patterns (StatesController, GroupsController). - -Route: `Router::mapResources('notifications')` in routes.php - -Endpoints: -- `GET /api/notifications.json` — List tokens (admin: all, user: own) -- `GET /api/notifications/{id}.json` — View single token (scoped) -- `POST /api/notifications.json` — Create or upsert token (auto-sets UserId from session) -- `PUT /api/notifications/{id}.json` — Update filters, pushstate, interval, etc. -- `DELETE /api/notifications/{id}.json` — Delete token (scoped) - -Authorization: -- Admin users bypass scoping (can see/edit/delete all tokens) -- Non-admin users can only access rows where `UserId` matches their own User.Id -- POST auto-populates `UserId` from the authenticated session -- Upsert: if POST contains a Token that already exists, update the existing row (only if owned by same user or admin) - -Model: `Notification.php` with `$useTable = 'Notifications'`, `$primaryKey = 'Id'` - -## Component 3: pyzm — `Notification` model + ZMClient methods - -Following pyzm's existing dataclass + client pattern: - -Model (`pyzm/models/zm.py`): -```python -@dataclass -class Notification: - id: int - user_id: int = 0 - token: str = "" - platform: str = "" - monitor_list: str | None = None - interval: int = 0 - push_state: str = "enabled" - app_version: str | None = None - badge_count: int = 0 - last_notified_at: datetime | None = None - _raw: dict = field(default_factory=dict, repr=False, compare=False) - _client: ZMClient | None = field(default=None, repr=False, compare=False) - - @classmethod - def from_api_dict(cls, data: dict, client=None) -> "Notification": - ... - - def monitors(self) -> list[int] | None: - """Parse MonitorList into list of ints. None = all monitors.""" - if not self.monitor_list: - return None - return [int(m) for m in self.monitor_list.split(",")] -``` - -Client methods (`pyzm/client.py`): -- `notifications()` — fetch all tokens (zm_detect uses this) -- `notification(id)` — fetch single token -- `_create_notification(**kwargs)` — POST -- `_update_notification(id, **kwargs)` — PUT -- `_delete_notification(id)` — DELETE -- `_update_notification_last_sent(id)` — PUT to update LastNotifiedAt - -## Component 4: zm_detect — Push notification sender - -Config additions to `objectconfig.yml`: -```yaml -push: - enabled: yes - fcm_v1_url: https://us-central1-zmng-b7af6.cloudfunctions.net/send_push - fcm_v1_key: -``` - -After detection completes: -1. Query `Notifications` table via `zm.notifications()` -2. Filter: `PushState = 'enabled'` AND monitor ID in token's `MonitorList` (or MonitorList is NULL) -3. Throttle: skip if `LastNotifiedAt` is not None and `(now - LastNotifiedAt) < Interval` -4. For each qualifying token, HTTP POST to `fcm_v1_url` with payload: - ```json - { - "token": "", - "title": " Alarm", - "body": "", - "sound": "default", - "badge": "", - "data": { - "mid": "", - "eid": "", - "monitorName": "", - "cause": "", - "notification_foreground": "true" - }, - "android": { "priority": "high" }, - "ios": { "thread_id": "ZoneMinder_" } - } - ``` -5. Update `LastNotifiedAt` and `BadgeCount` in DB via pyzm - -FCM mode: proxy only (cloud function). - -## Component 5 (future): zmNg client changes - -Spec to be written after server components are done. zmNg will need to: -- Call ZM API to register/update/delete tokens instead of ES websocket -- Manage monitor filter UI against the new API -- Handle token refresh by calling PUT on the API - -## Implementation order - -1. DB migration + schema update (ZM repo) -2. CakePHP model + controller + route (ZM repo) -3. pyzm Notification model + ZMClient methods (pyzm repo) -4. zm_detect push sender (zmeventnotification repo) -5. zmNg client spec (separate doc) diff --git a/docs/plans/2026-03-05-notifications-plan.md b/docs/plans/2026-03-05-notifications-plan.md deleted file mode 100644 index 132ffe945..000000000 --- a/docs/plans/2026-03-05-notifications-plan.md +++ /dev/null @@ -1,966 +0,0 @@ -# Push Notification Token Registration — Implementation Plan - -> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. - -**Goal:** Enable zm_detect to send FCM push notifications directly by storing device tokens in ZM's database via a new REST API. - -**Architecture:** New `Notifications` table in ZM DB, CakePHP REST controller with user-scoped access, pyzm `Notification` model for Python access, and push-sending logic in zm_detect using FCM proxy mode. - -**Tech Stack:** MySQL, CakePHP 2.x, Python 3 (pyzm dataclasses + requests), FCM HTTP v1 via cloud function proxy. - -**Repos:** -- ZM: `/home/arjunrc/fiddle/zm/pliablepixels_ZoneMinder.git` (branch: `notifications-api`) -- pyzm: `/home/arjunrc/fiddle/pyzm` -- ES: `/home/arjunrc/fiddle/zmeventnotification` - -**Design doc:** `docs/plans/2026-03-05-notifications-design.md` - ---- - -### Task 1: Database — Add Notifications table to fresh-install schema - -**Files:** -- Modify: `/home/arjunrc/fiddle/zm/pliablepixels_ZoneMinder.git/db/zm_create.sql.in` (after line 1330) - -**Step 1: Add CREATE TABLE after Events_Tags table** - -Insert after line 1330 (after `Events_Tags` closing), before the `source` lines: - -```sql -CREATE TABLE `Notifications` ( - `Id` int unsigned NOT NULL AUTO_INCREMENT, - `UserId` int unsigned NOT NULL, - `Token` varchar(512) NOT NULL, - `Platform` enum('android','ios','web') NOT NULL, - `MonitorList` text DEFAULT NULL, - `Interval` int unsigned NOT NULL DEFAULT 0, - `PushState` enum('enabled','disabled') NOT NULL DEFAULT 'enabled', - `AppVersion` varchar(32) DEFAULT NULL, - `BadgeCount` int NOT NULL DEFAULT 0, - `LastNotifiedAt` datetime DEFAULT NULL, - `CreatedOn` datetime DEFAULT NULL, - `UpdatedOn` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`Id`), - UNIQUE KEY `Notifications_Token_idx` (`Token`), - KEY `Notifications_UserId_idx` (`UserId`) -) ENGINE=@ZM_MYSQL_ENGINE@; -``` - -**Step 2: Verify syntax** - -Run: `grep -A 20 'CREATE TABLE .Notifications' db/zm_create.sql.in` -Expected: The full CREATE TABLE statement as above. - -**Step 3: Commit** - -```bash -git add db/zm_create.sql.in -git commit -m "feat: add Notifications table to fresh-install schema refs #4684" -``` - ---- - -### Task 2: Database — Create migration file for existing installs - -**Files:** -- Create: `/home/arjunrc/fiddle/zm/pliablepixels_ZoneMinder.git/db/zm_update-1.39.2.sql` - -**Step 1: Write migration file** - -```sql --- --- Add Notifications table for FCM push token registration --- - -SET @s = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_name = 'Notifications' - AND table_schema = DATABASE()) > 0, - "SELECT 'Notifications table already exists'", - "CREATE TABLE `Notifications` ( - `Id` int unsigned NOT NULL AUTO_INCREMENT, - `UserId` int unsigned NOT NULL, - `Token` varchar(512) NOT NULL, - `Platform` enum('android','ios','web') NOT NULL, - `MonitorList` text DEFAULT NULL, - `Interval` int unsigned NOT NULL DEFAULT 0, - `PushState` enum('enabled','disabled') NOT NULL DEFAULT 'enabled', - `AppVersion` varchar(32) DEFAULT NULL, - `BadgeCount` int NOT NULL DEFAULT 0, - `LastNotifiedAt` datetime DEFAULT NULL, - `CreatedOn` datetime DEFAULT NULL, - `UpdatedOn` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`Id`), - UNIQUE KEY `Notifications_Token_idx` (`Token`), - KEY `Notifications_UserId_idx` (`UserId`) - ) ENGINE=InnoDB" -)); - -PREPARE stmt FROM @s; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; -``` - -**Step 2: Test migration against local DB** - -Run: `mysql -u zmuser -pzmpass zm < db/zm_update-1.39.2.sql` -Expected: No errors. Run again to verify idempotency (should print "Notifications table already exists"). - -**Step 3: Verify table exists** - -Run: `mysql -u zmuser -pzmpass zm -e "DESCRIBE Notifications;"` -Expected: All columns listed with correct types. - -**Step 4: Commit** - -```bash -git add db/zm_update-1.39.2.sql -git commit -m "feat: add migration for Notifications table refs #4684" -``` - ---- - -### Task 3: CakePHP — Create Notification model - -**Files:** -- Create: `/home/arjunrc/fiddle/zm/pliablepixels_ZoneMinder.git/web/api/app/Model/Notification.php` - -**Step 1: Write model file** - -Reference: `web/api/app/Model/State.php` - -```php - array( - 'notBlank' => array( - 'rule' => array('notBlank'), - 'message' => 'Token is required', - ), - ), - 'Platform' => array( - 'inList' => array( - 'rule' => array('inList', array('android', 'ios', 'web')), - 'message' => 'Platform must be android, ios, or web', - ), - ), - 'PushState' => array( - 'inList' => array( - 'rule' => array('inList', array('enabled', 'disabled')), - 'message' => 'PushState must be enabled or disabled', - 'allowEmpty' => true, - ), - ), - ); - -} -``` - -**Step 2: Commit** - -```bash -git add web/api/app/Model/Notification.php -git commit -m "feat: add Notification CakePHP model refs #4684" -``` - ---- - -### Task 4: CakePHP — Create NotificationsController with user-scoped CRUD - -**Files:** -- Create: `/home/arjunrc/fiddle/zm/pliablepixels_ZoneMinder.git/web/api/app/Controller/NotificationsController.php` - -**Step 1: Write controller** - -Reference: `web/api/app/Controller/StatesController.php` for pattern, `web/api/app/Controller/UsersController.php` for `$user->Id()` scoping. - -```php -System() == 'Edit'); - } - - /** - * Return the authenticated user's Id, or null. - */ - private function _userId() { - global $user; - return $user ? $user->Id() : null; - } - - public function beforeFilter() { - parent::beforeFilter(); - global $user; - $canView = (!$user) || ($user->System() != 'None'); - if (!$canView) { - throw new UnauthorizedException(__('Insufficient Privileges')); - } - } - - /** - * index — list notifications. Admin sees all, user sees own. - */ - public function index() { - $conditions = array(); - if (!$this->_isAdmin()) { - $conditions['Notification.UserId'] = $this->_userId(); - } - $notifications = $this->Notification->find('all', array( - 'conditions' => $conditions, - 'recursive' => -1, - )); - $this->set(array( - 'notifications' => $notifications, - '_serialize' => array('notifications'), - )); - } - - /** - * view — show single notification. Scoped to user unless admin. - */ - public function view($id = null) { - $this->Notification->id = $id; - if (!$this->Notification->exists()) { - throw new NotFoundException(__('Invalid notification')); - } - $notification = $this->Notification->find('first', array( - 'conditions' => array('Notification.Id' => $id), - 'recursive' => -1, - )); - if (!$this->_isAdmin() && $notification['Notification']['UserId'] != $this->_userId()) { - throw new UnauthorizedException(__('Insufficient Privileges')); - } - $this->set(array( - 'notification' => $notification, - '_serialize' => array('notification'), - )); - } - - /** - * add — create or upsert. Auto-sets UserId from session. - * If Token already exists for this user, updates instead of creating. - */ - public function add() { - if (!$this->request->is('post')) { - throw new BadRequestException(__('POST required')); - } - - $data = $this->request->data; - if (isset($data['Notification'])) { - $data = $data['Notification']; - } - - // Force UserId to the authenticated user (admin can override) - if (!$this->_isAdmin() || !isset($data['UserId'])) { - $data['UserId'] = $this->_userId(); - } - - if (!isset($data['CreatedOn'])) { - $data['CreatedOn'] = date('Y-m-d H:i:s'); - } - - // Upsert: check if token already exists - if (isset($data['Token'])) { - $existing = $this->Notification->find('first', array( - 'conditions' => array('Notification.Token' => $data['Token']), - 'recursive' => -1, - )); - if ($existing) { - // Only allow upsert if same user or admin - if (!$this->_isAdmin() && $existing['Notification']['UserId'] != $this->_userId()) { - throw new UnauthorizedException(__('Token belongs to another user')); - } - $this->Notification->id = $existing['Notification']['Id']; - unset($data['CreatedOn']); // Don't overwrite creation date on upsert - } else { - $this->Notification->create(); - } - } else { - $this->Notification->create(); - } - - if ($this->Notification->save(array('Notification' => $data))) { - $notification = $this->Notification->find('first', array( - 'conditions' => array('Notification.Id' => $this->Notification->id), - 'recursive' => -1, - )); - $this->set(array( - 'notification' => $notification, - '_serialize' => array('notification'), - )); - } else { - $this->response->statusCode(400); - $this->set(array( - 'message' => __('Could not save notification'), - 'errors' => $this->Notification->validationErrors, - '_serialize' => array('message', 'errors'), - )); - } - } - - /** - * edit — update notification fields. Scoped to user unless admin. - */ - public function edit($id = null) { - $this->Notification->id = $id; - if (!$this->Notification->exists()) { - throw new NotFoundException(__('Invalid notification')); - } - - $existing = $this->Notification->find('first', array( - 'conditions' => array('Notification.Id' => $id), - 'recursive' => -1, - )); - if (!$this->_isAdmin() && $existing['Notification']['UserId'] != $this->_userId()) { - throw new UnauthorizedException(__('Insufficient Privileges')); - } - - if ($this->request->is(array('post', 'put'))) { - $data = $this->request->data; - if (isset($data['Notification'])) { - $data = $data['Notification']; - } - // Don't allow changing UserId unless admin - if (!$this->_isAdmin()) { - unset($data['UserId']); - } - if ($this->Notification->save(array('Notification' => $data))) { - $notification = $this->Notification->find('first', array( - 'conditions' => array('Notification.Id' => $id), - 'recursive' => -1, - )); - $this->set(array( - 'notification' => $notification, - '_serialize' => array('notification'), - )); - } else { - $this->response->statusCode(400); - $this->set(array( - 'message' => __('Could not save notification'), - 'errors' => $this->Notification->validationErrors, - '_serialize' => array('message', 'errors'), - )); - } - } - } - - /** - * delete — remove notification. Scoped to user unless admin. - */ - public function delete($id = null) { - $this->Notification->id = $id; - if (!$this->Notification->exists()) { - throw new NotFoundException(__('Invalid notification')); - } - $this->request->allowMethod('post', 'delete'); - - $existing = $this->Notification->find('first', array( - 'conditions' => array('Notification.Id' => $id), - 'recursive' => -1, - )); - if (!$this->_isAdmin() && $existing['Notification']['UserId'] != $this->_userId()) { - throw new UnauthorizedException(__('Insufficient Privileges')); - } - - if ($this->Notification->delete()) { - $this->set(array( - 'message' => __('Notification deleted'), - '_serialize' => array('message'), - )); - } else { - $this->response->statusCode(400); - $this->set(array( - 'message' => __('Could not delete notification'), - '_serialize' => array('message'), - )); - } - } - -} -``` - -**Step 2: Commit** - -```bash -git add web/api/app/Controller/NotificationsController.php -git commit -m "feat: add NotificationsController with user-scoped CRUD refs #4684" -``` - ---- - -### Task 5: CakePHP — Register route - -**Files:** -- Modify: `/home/arjunrc/fiddle/zm/pliablepixels_ZoneMinder.git/web/api/app/Config/routes.php` (line 47, after `zones`) - -**Step 1: Add mapResources call** - -Add after line 47 (`Router::mapResources('zones');`): - -```php - Router::mapResources('notifications'); -``` - -**Step 2: Commit** - -```bash -git add web/api/app/Config/routes.php -git commit -m "feat: register notifications REST route refs #4684" -``` - ---- - -### Task 6: Test ZM API manually - -**Step 1: Deploy API files to live web root** - -```bash -echo "doctoral" | sudo -S cp web/api/app/Model/Notification.php /usr/share/zoneminder/www/api/app/Model/ -echo "doctoral" | sudo -S cp web/api/app/Controller/NotificationsController.php /usr/share/zoneminder/www/api/app/Controller/ -echo "doctoral" | sudo -S cp web/api/app/Config/routes.php /usr/share/zoneminder/www/api/app/Config/ -``` - -**Step 2: Apply migration** - -```bash -mysql -u zmuser -pzmpass zm < db/zm_update-1.39.2.sql -``` - -**Step 3: Clear CakePHP cache** - -```bash -echo "doctoral" | sudo -S rm -rf /usr/share/zoneminder/www/api/app/tmp/cache/models/* -echo "doctoral" | sudo -S rm -rf /usr/share/zoneminder/www/api/app/tmp/cache/persistent/* -``` - -**Step 4: Get auth token** - -```bash -TOKEN=$(curl -s -k -X POST "http://localhost/zm/api/host/login.json" \ - -d "user=admin&pass=admin" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])") -echo $TOKEN -``` - -Expected: A JWT token string. - -**Step 5: Test POST (create)** - -```bash -curl -s -k -X POST "http://localhost/zm/api/notifications.json?token=$TOKEN" \ - -d "Notification[Token]=test_fcm_token_123" \ - -d "Notification[Platform]=android" \ - -d "Notification[MonitorList]=1,2" \ - -d "Notification[Interval]=60" \ - -d "Notification[AppVersion]=1.0.0" | python3 -m json.tool -``` - -Expected: JSON with the created notification including auto-populated `UserId`, `Id`, `CreatedOn`. - -**Step 6: Test GET (index)** - -```bash -curl -s -k "http://localhost/zm/api/notifications.json?token=$TOKEN" | python3 -m json.tool -``` - -Expected: JSON array with the notification created above. - -**Step 7: Test PUT (update MonitorList)** - -```bash -# Use the Id from the POST response -curl -s -k -X PUT "http://localhost/zm/api/notifications/1.json?token=$TOKEN" \ - -d "Notification[MonitorList]=1,2,3" | python3 -m json.tool -``` - -Expected: Updated notification with `MonitorList` = "1,2,3". - -**Step 8: Test POST upsert (same token)** - -```bash -curl -s -k -X POST "http://localhost/zm/api/notifications.json?token=$TOKEN" \ - -d "Notification[Token]=test_fcm_token_123" \ - -d "Notification[Platform]=ios" \ - -d "Notification[MonitorList]=5" | python3 -m json.tool -``` - -Expected: Same `Id` as before, updated `Platform` and `MonitorList`. - -**Step 9: Test DELETE** - -```bash -curl -s -k -X DELETE "http://localhost/zm/api/notifications/1.json?token=$TOKEN" -``` - -Expected: Success message. - -**Step 10: Verify deletion** - -```bash -curl -s -k "http://localhost/zm/api/notifications.json?token=$TOKEN" | python3 -m json.tool -``` - -Expected: Empty array. - ---- - -### Task 7: pyzm — Add Notification dataclass - -**Files:** -- Modify: `/home/arjunrc/fiddle/pyzm/pyzm/models/zm.py` (after Event class, around line 221) - -**Step 1: Add Notification dataclass** - -Insert after the Event class (after line 220): - -```python -@dataclass -class Notification: - """A ZoneMinder push notification token registration. - - Represents a device registered to receive FCM push notifications - for ZoneMinder events. - """ - id: int - user_id: int = 0 - token: str = "" - platform: str = "" - monitor_list: str | None = None - interval: int = 0 - push_state: str = "enabled" - app_version: str | None = None - badge_count: int = 0 - last_notified_at: datetime | None = None - - _raw: dict = field(default_factory=dict, repr=False, compare=False) - _client: ZMClient | None = field(default=None, repr=False, compare=False) - - def raw(self) -> dict: - return self._raw - - def monitors(self) -> list[int] | None: - """Parse MonitorList into list of ints. None = all monitors.""" - if not self.monitor_list: - return None - return [int(m.strip()) for m in self.monitor_list.split(",") if m.strip()] - - def should_notify(self, monitor_id: int) -> bool: - """Check if this token should receive notifications for the given monitor.""" - if self.push_state != "enabled": - return False - monitors = self.monitors() - if monitors is None: - return True # NULL = all monitors - return monitor_id in monitors - - def is_throttled(self) -> bool: - """Check if this token is currently throttled.""" - if self.interval <= 0 or self.last_notified_at is None: - return False - elapsed = (datetime.now() - self.last_notified_at).total_seconds() - return elapsed < self.interval - - def delete(self) -> None: - self._require_client() - self._client._delete_notification(self.id) - - def update_last_sent(self, badge: int | None = None) -> None: - """Update LastNotifiedAt to now and optionally set BadgeCount.""" - self._require_client() - self._client._update_notification_last_sent(self.id, badge) - - def _require_client(self) -> None: - if self._client is None: - raise RuntimeError( - "Notification not bound to a ZMClient. " - "Use zm.notifications() to get bound Notification objects." - ) - - @classmethod - def from_api_dict(cls, data: dict, client: ZMClient | None = None) -> Notification: - """Build from a ZM API Notification JSON dict.""" - n = data.get("Notification", data) - return cls( - id=int(n.get("Id", 0)), - user_id=int(n.get("UserId", 0)), - token=n.get("Token", ""), - platform=n.get("Platform", ""), - monitor_list=n.get("MonitorList"), - interval=int(n.get("Interval", 0)), - push_state=n.get("PushState", "enabled"), - app_version=n.get("AppVersion"), - badge_count=int(n.get("BadgeCount", 0)), - last_notified_at=_parse_dt(n.get("LastNotifiedAt")), - _raw=data, - _client=client, - ) -``` - -**Step 2: Commit** - -```bash -cd /home/arjunrc/fiddle/pyzm -git add pyzm/models/zm.py -git commit -m "feat: add Notification dataclass for push token registration" -``` - ---- - -### Task 8: pyzm — Add ZMClient notification methods - -**Files:** -- Modify: `/home/arjunrc/fiddle/pyzm/pyzm/client.py` (after `event()` method, around line 219) -- Modify: `/home/arjunrc/fiddle/pyzm/pyzm/__init__.py` (add export) - -**Step 1: Add client methods after `event()` (around line 219)** - -```python - # ------------------------------------------------------------------ - # Notifications - # ------------------------------------------------------------------ - - def notifications(self) -> list[Notification]: - """Fetch all push notification token registrations.""" - data = self._api.get("notifications.json") - items = data.get("notifications", []) if data else [] - return [Notification.from_api_dict(n, client=self) for n in items] - - def notification(self, notification_id: int) -> Notification: - """Fetch a single notification registration by ID.""" - data = self._api.get(f"notifications/{notification_id}.json") - if data and data.get("notification"): - return Notification.from_api_dict(data["notification"], client=self) - raise ValueError(f"Notification {notification_id} not found") - - def _create_notification(self, **kwargs) -> dict: - """Create or upsert a notification registration.""" - data = {f"Notification[{k}]": v for k, v in kwargs.items()} - return self._api.post("notifications.json", data=data) - - def _update_notification(self, notification_id: int, **kwargs) -> dict: - """Update fields on a notification registration.""" - data = {f"Notification[{k}]": v for k, v in kwargs.items()} - return self._api.put(f"notifications/{notification_id}.json", data=data) - - def _delete_notification(self, notification_id: int) -> None: - """Delete a notification registration.""" - self._api.delete(f"notifications/{notification_id}.json") - - def _update_notification_last_sent(self, notification_id: int, badge: int | None = None) -> None: - """Update LastNotifiedAt to now and optionally set BadgeCount.""" - from datetime import datetime - data = {"LastNotifiedAt": datetime.now().strftime("%Y-%m-%d %H:%M:%S")} - if badge is not None: - data["BadgeCount"] = str(badge) - self._update_notification(notification_id, **data) -``` - -**Step 2: Add import at top of client.py** - -Add to the existing import from `pyzm.models.zm`: - -```python -from pyzm.models.zm import Notification -``` - -(Find the existing `from pyzm.models.zm import ...` line and add `Notification` to it.) - -**Step 3: Add export to `__init__.py`** - -In `/home/arjunrc/fiddle/pyzm/pyzm/__init__.py`, add `"Notification"` to the `__all__` list (line 18-28) and add the import: - -```python -from pyzm.models.zm import Notification -``` - -Add `"Notification"` to `__all__`. - -**Step 4: Commit** - -```bash -cd /home/arjunrc/fiddle/pyzm -git add pyzm/client.py pyzm/__init__.py -git commit -m "feat: add ZMClient notification methods and export" -``` - ---- - -### Task 9: zm_detect — Add push notification config and sender - -**Files:** -- Modify: `/home/arjunrc/fiddle/zmeventnotification/hook/objectconfig.yml` (after line 78, after animation section) -- Create: `/home/arjunrc/fiddle/zmeventnotification/zmes_hook_helpers/push.py` -- Modify: `/home/arjunrc/fiddle/zmeventnotification/hook/zm_detect.py` (after line 187, after tagging) - -**Step 1: Add push config section to objectconfig.yml** - -Insert after line 78 (after `animation:` section): - -```yaml - -# Push notifications via FCM cloud function proxy -# zm_detect reads registered tokens from ZM's Notifications table (via pyzm) -# and sends push notifications directly after detection. -push: - enabled: "no" - # Cloud function proxy URL and authorization key - fcm_v1_url: "https://us-central1-zmng-b7af6.cloudfunctions.net/send_push" - fcm_v1_key: "!FCM_V1_KEY" - # Replace previous push for same monitor (collapses notifications) - replace_push_messages: "yes" - # Include event image URL in push notification - include_picture: "yes" - # Android notification priority - android_priority: "high" - # Android TTL in seconds (omit to use FCM default) - #android_ttl: 60 -``` - -**Step 2: Create push.py helper module** - -```python -"""Push notification sender for zm_detect. - -Reads registered tokens from ZM's Notifications table via pyzm, -filters by monitor, checks throttle, and sends via FCM cloud function proxy. -""" - -import json -import requests -from datetime import datetime - - -def send_push_notifications(zm, config, monitor_id, event_id, monitor_name, cause, logger): - """Send FCM push notifications to all qualifying registered tokens. - - Args: - zm: pyzm ZMClient instance (already authenticated). - config: dict with push config keys (fcm_v1_url, fcm_v1_key, etc.). - monitor_id: int, the monitor that triggered the event. - event_id: int/str, the event ID. - monitor_name: str, human-readable monitor name. - cause: str, detection result string (e.g. "person detected"). - logger: pyzm logger instance. - """ - if config.get('push', {}).get('enabled') != 'yes': - logger.Debug(1, 'push: disabled in config, skipping') - return - - push_cfg = config.get('push', {}) - fcm_url = push_cfg.get('fcm_v1_url') - fcm_key = push_cfg.get('fcm_v1_key') - - if not fcm_url or not fcm_key: - logger.Error('push: fcm_v1_url or fcm_v1_key not configured') - return - - try: - notifications = zm.notifications() - except Exception as e: - logger.Error('push: failed to fetch notifications from ZM API: {}'.format(e)) - return - - if not notifications: - logger.Debug(1, 'push: no registered tokens found') - return - - mid = int(monitor_id) - sent_count = 0 - - for notif in notifications: - if not notif.should_notify(mid): - logger.Debug(2, 'push: skipping token ...{} (monitor {} not in filter)'.format( - notif.token[-6:] if len(notif.token) > 6 else notif.token, mid)) - continue - - if notif.is_throttled(): - logger.Debug(2, 'push: skipping token ...{} (throttled, interval={}s)'.format( - notif.token[-6:] if len(notif.token) > 6 else notif.token, notif.interval)) - continue - - badge = notif.badge_count + 1 - title = '{} Alarm'.format(monitor_name) - body = cause if cause else 'Event {} on {}'.format(event_id, monitor_name) - - payload = { - 'token': notif.token, - 'title': title, - 'body': body, - 'sound': 'default', - 'badge': badge, - 'data': { - 'mid': str(mid), - 'eid': str(event_id), - 'monitorName': monitor_name, - 'cause': cause or '', - 'notification_foreground': 'true', - }, - } - - # Platform-specific fields (proxy format, matching ES FCM.pm proxy mode) - if notif.platform == 'android': - payload['android'] = { - 'icon': 'ic_stat_notification', - 'priority': push_cfg.get('android_priority', 'high'), - } - ttl = push_cfg.get('android_ttl') - if ttl: - payload['android']['ttl'] = str(ttl) - if push_cfg.get('replace_push_messages') == 'yes': - payload['android']['tag'] = 'zmninjapush' - if notif.app_version and notif.app_version != 'unknown': - payload['android']['channel'] = 'zmninja' - - elif notif.platform == 'ios': - payload['ios'] = { - 'thread_id': 'zmninja_alarm', - 'headers': { - 'apns-priority': '10', - 'apns-push-type': 'alert', - }, - } - if push_cfg.get('replace_push_messages') == 'yes': - payload['ios']['headers']['apns-collapse-id'] = 'zmninjapush' - - # Send via proxy - try: - logger.Debug(1, 'push: sending to token ...{} ({})'.format( - notif.token[-6:] if len(notif.token) > 6 else notif.token, notif.platform)) - - resp = requests.post( - fcm_url, - headers={ - 'Content-Type': 'application/json', - 'Authorization': fcm_key, - }, - data=json.dumps(payload), - timeout=10, - ) - - if resp.ok: - logger.Debug(1, 'push: FCM proxy returned 200 for token ...{}'.format( - notif.token[-6:] if len(notif.token) > 6 else notif.token)) - # Update last sent time and badge count - try: - notif.update_last_sent(badge=badge) - except Exception as e: - logger.Debug(1, 'push: failed to update LastNotifiedAt: {}'.format(e)) - sent_count += 1 - else: - body_text = resp.text - logger.Error('push: FCM proxy error for token ...{}: {}'.format( - notif.token[-6:] if len(notif.token) > 6 else notif.token, body_text)) - # If token is invalid, delete it from ZM - if 'not a valid FCM' in body_text or 'entity was not found' in body_text: - logger.Debug(1, 'push: removing invalid token ...{}'.format( - notif.token[-6:] if len(notif.token) > 6 else notif.token)) - try: - notif.delete() - except Exception as e: - logger.Debug(1, 'push: failed to delete invalid token: {}'.format(e)) - - except Exception as e: - logger.Error('push: exception sending to token ...{}: {}'.format( - notif.token[-6:] if len(notif.token) > 6 else notif.token, e)) - - logger.Debug(1, 'push: sent {} notifications for event {} on monitor {}'.format( - sent_count, event_id, monitor_name)) -``` - -**Step 3: Add push call to zm_detect.py** - -Insert after line 187 (after the tagging block), before the animation block (line 189): - -```python - # --- Push notifications --- - if g.config.get('push', {}).get('enabled') == 'yes' and args.get('eventid') and args.get('monitorid'): - try: - from zmes_hook_helpers.push import send_push_notifications - mon = zm.monitor(int(args['monitorid'])) - mon_name = mon.name if mon else 'Monitor {}'.format(args['monitorid']) - send_push_notifications( - zm, g.config, args['monitorid'], args['eventid'], - mon_name, pred, g.logger) - except Exception as e: - g.logger.Error('Push notification error: {}'.format(e)) -``` - -Note: Check how `zm.monitor()` works in pyzm. The `monitors()` method returns cached monitors, so we can get the name. If the method signature differs, adjust accordingly. - -**Step 4: Commit** - -```bash -cd /home/arjunrc/fiddle/zmeventnotification -git add hook/objectconfig.yml zmes_hook_helpers/push.py hook/zm_detect.py -git commit -m "feat: add direct FCM push notifications to zm_detect" -``` - ---- - -### Task 10: Test end-to-end push notification flow - -**Step 1: Register a test token via API** - -```bash -TOKEN=$(curl -s -k -X POST "http://localhost/zm/api/host/login.json" \ - -d "user=admin&pass=admin" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])") - -curl -s -k -X POST "http://localhost/zm/api/notifications.json?token=$TOKEN" \ - -d "Notification[Token]=test_fcm_device_token" \ - -d "Notification[Platform]=android" \ - -d "Notification[MonitorList]=1" \ - -d "Notification[Interval]=0" \ - -d "Notification[AppVersion]=1.0.0" | python3 -m json.tool -``` - -**Step 2: Enable push in objectconfig.yml** - -Set `push.enabled: "yes"` and configure `fcm_v1_key` with a valid key. - -**Step 3: Run zm_detect with --fakeit for a test event** - -```bash -python3 hook/zm_detect.py -c hook/objectconfig.yml -e -m 1 -r "test" -n --fakeit "person" -``` - -Expected: Log output showing "push: sending to token ..." and either a 200 (if key is valid) or an auth error (which confirms the flow works up to the FCM call). - -**Step 4: Verify LastNotifiedAt was updated** - -```bash -curl -s -k "http://localhost/zm/api/notifications.json?token=$TOKEN" | python3 -m json.tool -``` - -Expected: `LastNotifiedAt` should be populated with a recent timestamp (if the push succeeded). - ---- - -### Task 11: Final commit — update design doc - -**Step 1: Verify all changes across repos** - -```bash -cd /home/arjunrc/fiddle/zm/pliablepixels_ZoneMinder.git && git log --oneline -5 -cd /home/arjunrc/fiddle/pyzm && git log --oneline -3 -cd /home/arjunrc/fiddle/zmeventnotification && git log --oneline -3 -``` - -**Step 2: Update design doc status** - -Add "Implementation complete" note to the design doc. Ready for zmNg client spec (Component 5). From fea1c850ac90629e97ec07d97784dae87a4a7f8b Mon Sep 17 00:00:00 2001 From: Pliable Pixels Date: Thu, 5 Mar 2026 20:39:54 -0500 Subject: [PATCH 10/24] fix: address Copilot review feedback on Notifications API refs #4684 - Revert accidental Users.RoleId FK change from CASCADE back to SET NULL - Remove System != 'None' gate in beforeFilter; any authenticated user can manage their own notifications, per-row ownership checks suffice - Add allowMethod('post', 'put') guard to edit() for consistent REST behavior - Change PushState validation from allowEmpty to required=false Co-Authored-By: Claude Opus 4.6 --- db/zm_create.sql.in | 2 +- .../Controller/NotificationsController.php | 57 +++++++++---------- web/api/app/Model/Notification.php | 2 +- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 5b806f745..4b5e40f75 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -869,7 +869,7 @@ CREATE TABLE `Users` ( `RoleId` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`Id`), UNIQUE KEY `UC_Username` (`Username`), - FOREIGN KEY (`RoleId`) REFERENCES `User_Roles` (`Id`) ON DELETE CASCADE + FOREIGN KEY (`RoleId`) REFERENCES `User_Roles` (`Id`) ON DELETE SET NULL ) ENGINE=@ZM_MYSQL_ENGINE@; source @PKGDATADIR@/db/User_Preferences.sql diff --git a/web/api/app/Controller/NotificationsController.php b/web/api/app/Controller/NotificationsController.php index 3853128ff..0a913d474 100644 --- a/web/api/app/Controller/NotificationsController.php +++ b/web/api/app/Controller/NotificationsController.php @@ -17,11 +17,9 @@ class NotificationsController extends AppController { public function beforeFilter() { parent::beforeFilter(); - global $user; - $canView = (!$user) || ($user->System() != 'None'); - if (!$canView) { - throw new UnauthorizedException(__('Insufficient Privileges')); - } + // Any authenticated user can manage their own notifications. + // Per-row ownership checks are enforced in each action. + // When auth is disabled ($user is null), allow all access. } public function index() { @@ -117,6 +115,7 @@ class NotificationsController extends AppController { if (!$this->Notification->exists()) { throw new NotFoundException(__('Invalid notification')); } + $this->request->allowMethod('post', 'put'); $existing = $this->Notification->find('first', array( 'conditions' => array('Notification.Id' => $id), @@ -126,31 +125,29 @@ class NotificationsController extends AppController { throw new UnauthorizedException(__('Insufficient Privileges')); } - if ($this->request->is(array('post', 'put'))) { - $data = $this->request->data; - if (isset($data['Notification'])) { - $data = $data['Notification']; - } - if (!$this->_isAdmin()) { - unset($data['UserId']); - } - if ($this->Notification->save(array('Notification' => $data))) { - $notification = $this->Notification->find('first', array( - 'conditions' => array('Notification.Id' => $id), - 'recursive' => -1, - )); - $this->set(array( - 'notification' => $notification, - '_serialize' => array('notification'), - )); - } else { - $this->response->statusCode(400); - $this->set(array( - 'message' => __('Could not save notification'), - 'errors' => $this->Notification->validationErrors, - '_serialize' => array('message', 'errors'), - )); - } + $data = $this->request->data; + if (isset($data['Notification'])) { + $data = $data['Notification']; + } + if (!$this->_isAdmin()) { + unset($data['UserId']); + } + if ($this->Notification->save(array('Notification' => $data))) { + $notification = $this->Notification->find('first', array( + 'conditions' => array('Notification.Id' => $id), + 'recursive' => -1, + )); + $this->set(array( + 'notification' => $notification, + '_serialize' => array('notification'), + )); + } else { + $this->response->statusCode(400); + $this->set(array( + 'message' => __('Could not save notification'), + 'errors' => $this->Notification->validationErrors, + '_serialize' => array('message', 'errors'), + )); } } diff --git a/web/api/app/Model/Notification.php b/web/api/app/Model/Notification.php index 361ed4e53..eb9ff7897 100644 --- a/web/api/app/Model/Notification.php +++ b/web/api/app/Model/Notification.php @@ -31,7 +31,7 @@ class Notification extends AppModel { 'inList' => array( 'rule' => array('inList', array('enabled', 'disabled')), 'message' => 'PushState must be enabled or disabled', - 'allowEmpty' => true, + 'required' => false, ), ), ); From 84465bd28da547e56f61d8e6f9d1ce1f03ec6b93 Mon Sep 17 00:00:00 2001 From: Anatoliy Korovkin <5337923+Frumscepend@users.noreply.github.com> Date: Fri, 6 Mar 2026 10:09:59 +0100 Subject: [PATCH 11/24] Use const for Janus onremotetrack locals Replace let with const for stream/audioElement since they are not reassigned. --- web/js/MonitorStream.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/js/MonitorStream.js b/web/js/MonitorStream.js index 0e5552fd6..ec5d01085 100644 --- a/web/js/MonitorStream.js +++ b/web/js/MonitorStream.js @@ -1779,17 +1779,17 @@ async function attachVideo(monitorStream) { Janus.debug(" ::: Got a remote track :::"); Janus.debug(track); if (track.kind ==="audio") { - let stream = new MediaStream(); + const stream = new MediaStream(); stream.addTrack(track.clone()); if (document.getElementById("liveAudio" + id) == null) { - let audioElement = document.createElement('audio'); + const audioElement = document.createElement('audio'); audioElement.setAttribute("id", "liveAudio" + id); audioElement.controls = true; document.getElementById("imageFeed" + id).append(audioElement); } Janus.attachMediaStream(document.getElementById("liveAudio" + id), stream); } else { - let stream = new MediaStream(); + const stream = new MediaStream(); stream.addTrack(track.clone()); Janus.attachMediaStream(document.getElementById("liveStream" + id), stream); } From 3f0ede73a68faf699c30c28e763beae8e2e10a64 Mon Sep 17 00:00:00 2001 From: Simpler1 Date: Fri, 6 Mar 2026 12:04:57 -0500 Subject: [PATCH 12/24] feat(logs): New button to clear logs --- web/ajax/log.php | 7 ++++ web/ajax/modals/clearlogsconfirm.php | 23 ++++++++++++ web/lang/en_gb.php | 3 ++ web/skins/classic/views/js/log.js | 54 ++++++++++++++++++++++++++++ web/skins/classic/views/log.php | 3 ++ 5 files changed, 90 insertions(+) create mode 100644 web/ajax/modals/clearlogsconfirm.php diff --git a/web/ajax/log.php b/web/ajax/log.php index 6d0d974ec..5791db77d 100644 --- a/web/ajax/log.php +++ b/web/ajax/log.php @@ -22,6 +22,13 @@ if (!isset($_REQUEST['task'])) { } else { createRequest(); } +} else if ($_REQUEST['task'] == 'clear') { + global $user; + if (!canEdit('System')) { + $message = 'Insufficient permissions to clear logs for user '.$user->Username(); + } else { + dbQuery('TRUNCATE TABLE Logs'); + } } else { // Only the query and create tasks are supported at the moment $message = 'Unrecognised task '.$_REQUEST['task']; diff --git a/web/ajax/modals/clearlogsconfirm.php b/web/ajax/modals/clearlogsconfirm.php new file mode 100644 index 000000000..571aff0cd --- /dev/null +++ b/web/ajax/modals/clearlogsconfirm.php @@ -0,0 +1,23 @@ + + diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index 1bc7f57c7..b41be37ef 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -218,6 +218,7 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', 'ChoosePreset' => 'Choose Preset', 'ClassLabel' => 'Label', + 'ClearLogs' => 'Clear Logs', 'CloneMonitor' => 'Clone', 'ConcurrentFilter' => 'Run filter concurrently', 'ConfigOptions' => 'ConfigOptions', @@ -225,6 +226,8 @@ $SLANG = array( 'ConfiguredFor' => 'Configured for', 'ConfigURL' => 'Config Base URL', 'ConfirmAction' => 'Action Confirmation', + 'ConfirmClearLogs' => 'Are you sure you wish to clear all logs?', + 'ConfirmClearLogsTitle' => 'Clear Logs Confirmation', 'ConfirmDeleteControl' => 'Warning, deleting a control will reset all monitors that use it to be uncontrollable.

Are you sure you wish to delete?', 'ConfirmDeleteDevices' => 'Are you sure you wish to delete the selected devices?', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', diff --git a/web/skins/classic/views/js/log.js b/web/skins/classic/views/js/log.js index 3b3f7e116..2be57c62f 100644 --- a/web/skins/classic/views/js/log.js +++ b/web/skins/classic/views/js/log.js @@ -105,6 +105,33 @@ function updateHeaderStats(data) { $j('#displayLogs').text(startRow + ' to ' + stopRow); } +function manageClearLogsModalBtns() { + document.getElementById('clearLogsConfirmBtn').addEventListener('click', function onClearLogsConfirmClick(evt) { + evt.preventDefault(); + document.getElementById('clearLogsConfirmBtn').disabled = true; + clearLogs(); + }); + document.getElementById('clearLogsCancelBtn').addEventListener('click', function onClearLogsCancelClick(evt) { + $j('#clearLogsConfirm').modal('hide'); + }); +} + +function clearLogs() { + $j.ajax({ + method: 'get', + timeout: 0, + url: thisUrl + '?view=request&request=log&task=clear', + success: function(data) { + $j('#clearLogsConfirm').modal('hide'); + table.bootstrapTable('refresh'); + }, + error: function(jqxhr) { + logAjaxFail(jqxhr); + $j('#clearLogsConfirm').modal('hide'); + } + }); +} + function initPage() { var backBtn = $j('#backBtn'); @@ -148,6 +175,33 @@ function initPage() { window.location.reload(true); }); +// Manage the CLEAR LOGS button + const clearLogsBtn = document.getElementById('clearLogsBtn'); + if (clearLogsBtn) { + clearLogsBtn.addEventListener('click', function onClearLogsClick(evt) { + evt.preventDefault(); + if (evt.ctrlKey) { + clearLogs(); + } else { + if (!document.getElementById('clearLogsConfirm')) { + $j.getJSON(thisUrl + '?request=modal&modal=clearlogsconfirm') + .done(function(data) { + insertModalHtml('clearLogsConfirm', data.html); + manageClearLogsModalBtns(); + $j('#clearLogsConfirm').modal('show'); + }) + .fail(function(jqXHR) { + console.log('error getting clearlogsconfirm', jqXHR); + logAjaxFail(jqXHR); + }); + } else { + document.getElementById('clearLogsConfirmBtn').disabled = false; + $j('#clearLogsConfirm').modal('show'); + } + } + }); + } + $j('#filterStartDateTime, #filterEndDateTime') .datetimepicker({timeFormat: "HH:mm:ss", dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false, onClose: filterLog}); $j('#filterServerId') diff --git a/web/skins/classic/views/log.php b/web/skins/classic/views/log.php index 8fd15b5c7..920d538f6 100644 --- a/web/skins/classic/views/log.php +++ b/web/skins/classic/views/log.php @@ -57,6 +57,9 @@ getBodyTopHTML();
+ + +
From bb0d054ed056745ef19773f06dfa16dbf45ab985 Mon Sep 17 00:00:00 2001 From: Simpler1 Date: Fri, 6 Mar 2026 12:09:43 -0500 Subject: [PATCH 13/24] fix(lint): Add spaces before comment --- web/skins/classic/views/js/log.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/skins/classic/views/js/log.js b/web/skins/classic/views/js/log.js index 2be57c62f..3a965c5d6 100644 --- a/web/skins/classic/views/js/log.js +++ b/web/skins/classic/views/js/log.js @@ -175,7 +175,7 @@ function initPage() { window.location.reload(true); }); -// Manage the CLEAR LOGS button + // Manage the CLEAR LOGS button const clearLogsBtn = document.getElementById('clearLogsBtn'); if (clearLogsBtn) { clearLogsBtn.addEventListener('click', function onClearLogsClick(evt) { From 07d328a1644eb6f787159f6c8ac34776d060b689 Mon Sep 17 00:00:00 2001 From: Simpler1 Date: Fri, 6 Mar 2026 14:50:24 -0500 Subject: [PATCH 14/24] feat(log): Add selection to logs and delete for selected logs. --- web/ajax/log.php | 13 +++++--- web/lang/en_gb.php | 2 +- web/skins/classic/css/dark/views/log.css | 6 ++++ web/skins/classic/views/js/log.js | 42 ++++++++++++++++++++---- web/skins/classic/views/log.php | 6 +++- 5 files changed, 56 insertions(+), 13 deletions(-) diff --git a/web/ajax/log.php b/web/ajax/log.php index 5791db77d..117cc82d9 100644 --- a/web/ajax/log.php +++ b/web/ajax/log.php @@ -22,12 +22,16 @@ if (!isset($_REQUEST['task'])) { } else { createRequest(); } -} else if ($_REQUEST['task'] == 'clear') { +} else if ($_REQUEST['task'] == 'delete') { global $user; if (!canEdit('System')) { - $message = 'Insufficient permissions to clear logs for user '.$user->Username(); + $message = 'Insufficient permissions to delete log entries for user '.$user->Username(); } else { - dbQuery('TRUNCATE TABLE Logs'); + if (!empty($_REQUEST['ids'])) { + $ids = array_map('intval', (array)$_REQUEST['ids']); + $placeholders = implode(',', array_fill(0, count($ids), '?')); + dbQuery('DELETE FROM Logs WHERE Id IN (' . $placeholders . ')', $ids); + } } } else { // Only the query and create tasks are supported at the moment @@ -87,8 +91,7 @@ function queryRequest() { $table = 'Logs'; // The names of the dB columns in the log table we are interested in - $columns = array('TimeKey', 'Component', 'ServerId', 'Pid', 'Code', 'Message', 'File', 'Line'); - + $columns = array('Id', 'TimeKey', 'Component', 'ServerId', 'Pid', 'Code', 'Message', 'File', 'Line'); // The names of columns shown in the log view that are NOT dB columns in the database $col_alt = array('DateTime', 'Server'); diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index b41be37ef..032fd8a36 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -226,7 +226,7 @@ $SLANG = array( 'ConfiguredFor' => 'Configured for', 'ConfigURL' => 'Config Base URL', 'ConfirmAction' => 'Action Confirmation', - 'ConfirmClearLogs' => 'Are you sure you wish to clear all logs?', + 'ConfirmClearLogs' => 'Are you sure you wish to delete the selected log entries?', 'ConfirmClearLogsTitle' => 'Clear Logs Confirmation', 'ConfirmDeleteControl' => 'Warning, deleting a control will reset all monitors that use it to be uncontrollable.

Are you sure you wish to delete?', 'ConfirmDeleteDevices' => 'Are you sure you wish to delete the selected devices?', diff --git a/web/skins/classic/css/dark/views/log.css b/web/skins/classic/css/dark/views/log.css index 50732326c..ac4d7ded9 100644 --- a/web/skins/classic/css/dark/views/log.css +++ b/web/skins/classic/css/dark/views/log.css @@ -16,6 +16,12 @@ line-height: 15px; } +/* Selected row styles for dark mode - neutral highlight, log level colors via text */ +.bootstrap-table .fixed-table-container .table tbody tr.selected td { + background-color: #2d333b; + color: inherit; +} + tr.log-fat td { color: #222222; background-color:#ffcccc; diff --git a/web/skins/classic/views/js/log.js b/web/skins/classic/views/js/log.js index 3a965c5d6..544b15060 100644 --- a/web/skins/classic/views/js/log.js +++ b/web/skins/classic/views/js/log.js @@ -109,25 +109,46 @@ function manageClearLogsModalBtns() { document.getElementById('clearLogsConfirmBtn').addEventListener('click', function onClearLogsConfirmClick(evt) { evt.preventDefault(); document.getElementById('clearLogsConfirmBtn').disabled = true; - clearLogs(); + deleteLogs(getIdSelections()); }); document.getElementById('clearLogsCancelBtn').addEventListener('click', function onClearLogsCancelClick(evt) { $j('#clearLogsConfirm').modal('hide'); }); } -function clearLogs() { +function getIdSelections() { + return $j.map(table.bootstrapTable('getSelections'), function(row) { + return row.Id; + }); +} + +function deleteLogs(log_ids) { + const ticker = document.getElementById('clearLogsProgressTicker'); + const chunk = log_ids.splice(0, 100); + console.log('Deleting ' + chunk.length + ' log entries. ' + log_ids.length + ' remaining.'); + $j.ajax({ method: 'get', timeout: 0, - url: thisUrl + '?view=request&request=log&task=clear', + url: thisUrl + '?request=log&task=delete', + data: {'ids[]': chunk}, success: function(data) { - $j('#clearLogsConfirm').modal('hide'); - table.bootstrapTable('refresh'); + if (!log_ids.length) { + $j('#clearLogsConfirm').modal('hide'); + table.bootstrapTable('refresh'); + } else { + if (ticker.innerHTML.length < 1 || ticker.innerHTML.length > 10) { + ticker.innerHTML = '.'; + } else { + ticker.innerHTML = ticker.innerHTML + '.'; + } + deleteLogs(log_ids); + } }, error: function(jqxhr) { logAjaxFail(jqxhr); $j('#clearLogsConfirm').modal('hide'); + table.bootstrapTable('refresh'); } }); } @@ -181,7 +202,7 @@ function initPage() { clearLogsBtn.addEventListener('click', function onClearLogsClick(evt) { evt.preventDefault(); if (evt.ctrlKey) { - clearLogs(); + deleteLogs(getIdSelections()); } else { if (!document.getElementById('clearLogsConfirm')) { $j.getJSON(thisUrl + '?request=modal&modal=clearlogsconfirm') @@ -202,6 +223,15 @@ function initPage() { }); } + // Enable or disable clear button based on selection + table.on('check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table', function() { + const selections = table.bootstrapTable('getSelections'); + const clearLogsBtn = document.getElementById('clearLogsBtn'); + if (clearLogsBtn) { + clearLogsBtn.disabled = !selections.length; + } + }); + $j('#filterStartDateTime, #filterEndDateTime') .datetimepicker({timeFormat: "HH:mm:ss", dateFormat: "yy-mm-dd", maxDate: 0, constrainInput: false, onClose: filterLog}); $j('#filterServerId') diff --git a/web/skins/classic/views/log.php b/web/skins/classic/views/log.php index 920d538f6..5862d9059 100644 --- a/web/skins/classic/views/log.php +++ b/web/skins/classic/views/log.php @@ -58,7 +58,7 @@ getBodyTopHTML(); - +
@@ -149,9 +149,13 @@ echo ''; data-auto-refresh-silent="true" data-show-refresh="true" data-auto-refresh-interval="30" + data-click-to-select="true" + data-id-field="Id" > + + 1) { ?> From e1bd60116aa2325a0e638b0b24363bc8cee21996 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 7 Mar 2026 08:50:12 -0500 Subject: [PATCH 15/24] fix: add credential fallback chain in ONVIF control module When ControlAddress does not contain authentication info, fall back to Monitor->ONVIF_Username/ONVIF_Password, then Monitor->User/Pass. Co-Authored-By: Claude Opus 4.6 --- scripts/ZoneMinder/lib/ZoneMinder/Control/ONVIF.pm | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/ONVIF.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/ONVIF.pm index c0f18b33a..6fc223435 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/ONVIF.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/ONVIF.pm @@ -232,6 +232,19 @@ sub open { } } + # --- Credential fallback: ONVIF_Username/Password, then User/Pass ----- + if (!$$self{username}) { + if ($self->{Monitor}->{ONVIF_Username}) { + $$self{username} = $self->{Monitor}->{ONVIF_Username}; + $$self{password} = $self->{Monitor}->{ONVIF_Password} if $self->{Monitor}->{ONVIF_Password}; + Debug('Using ONVIF_Username/ONVIF_Password from Monitor'); + } elsif ($self->{Monitor}->{User}) { + $$self{username} = $self->{Monitor}->{User}; + $$self{password} = $self->{Monitor}->{Pass} if $self->{Monitor}->{Pass}; + Debug('Using User/Pass from Monitor'); + } + } + # --- Connectivity check (non-fatal) ------------------------------------ if ($$self{BaseURL}) { my $res = $self->sendCmd('/onvif/device_service', From 1b689d6e67f005524e9ff5b176f649f80a060660 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 7 Mar 2026 11:09:32 -0500 Subject: [PATCH 16/24] fix: include alarm_frame_count in ready_count calculation ready_count only considered warmup_count and pre_event_count, but openEvent walks back max(pre_event_count, alarm_frame_count) frames. When pre_event_count=0 and alarm_frame_count=2, analysis started before the queue had enough packets, causing spurious "Hit end of packetqueue before satisfying pre_event_count" warnings. Co-Authored-By: Claude Opus 4.6 --- src/zm_monitor.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 6c42e8910..470a02135 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -741,8 +741,10 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) { startup_delay = dbrow[col] ? atoi(dbrow[col]) : 0; col++; - // How many frames we need to have before we start analysing - ready_count = std::max(warmup_count, pre_event_count); + // How many frames we need to have before we start analysing. + // Must account for alarm_frame_count because openEvent walks back + // max(pre_event_count, alarm_frame_count) frames from the analysis point. + ready_count = std::max({warmup_count, pre_event_count, alarm_frame_count}); //shared_data->image_count = 0; last_alarm_count = 0; From d2a26aee590cf1a4aa04de5e8c84e14b574bf403 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 7 Mar 2026 11:09:43 -0500 Subject: [PATCH 17/24] fix: use +1 instead of +last_duration for equal DTS fixup When two packets have the same DTS (common with B-frames near keyframes), bumping by last_duration overshoots and causes a cascading misalignment where every subsequent packet triggers a "non increasing dts" warning with a growing gap. The comment already said "add 1" but the code used last_duration. Co-Authored-By: Claude Opus 4.6 --- src/zm_videostore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 6fd634773..f69f38743 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -1497,7 +1497,7 @@ int VideoStore::write_packet(AVPacket *pkt, AVStream *stream) { Debug(1, "non increasing dts, fixing. our dts %" PRId64 " stream %d last_dts %" PRId64 " stream %d. reorder_queue_size=%zu", pkt->dts, stream->index, last_dts[stream->index], stream->index, reorder_queue_size); // dts MUST monotonically increase, so add 1 which should be a small enough time difference to not matter. - pkt->dts = last_dts[stream->index]+last_duration[stream->index]; + pkt->dts = last_dts[stream->index]+1; if (pkt->dts > pkt->pts) pkt->pts = pkt->dts; // Do it here to avoid warning below } } From c27f32b7ad381c00129d41e76e3c34c619429a0d Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 7 Mar 2026 11:27:11 -0500 Subject: [PATCH 18/24] Upgrade bootstrap-table to 1.27.0 --- web/skins/classic/assets/bootstrap-table | 1 + .../bootstrap-table-locale-all.min.js | 10 - .../bootstrap-table-vue.js | 2001 --- .../bootstrap-table-vue.umd.js | 9 - .../bootstrap-table.min.css | 10 - .../bootstrap-table.min.js | 10 - .../addrbar/bootstrap-table-addrbar.min.js | 10 - .../bootstrap-table-auto-refresh.min.js | 10 - .../cookie/bootstrap-table-cookie.min.js | 10 - .../bootstrap-table-copy-rows.min.js | 10 - .../bootstrap-table-custom-view.min.js | 10 - .../bootstrap-table-defer-url.min.js | 10 - .../editable/bootstrap-table-editable.min.js | 10 - .../export/bootstrap-table-export.min.js | 10 - .../bootstrap-table-filter-control.min.js | 10 - .../extensions/filter-control/utils.min.js | 10 - .../bootstrap-table-fixed-columns.min.js | 10 - .../bootstrap-table-group-by.min.js | 10 - .../bootstrap-table-i18n-enhance.min.js | 10 - .../bootstrap-table-key-events.min.js | 10 - .../mobile/bootstrap-table-mobile.min.js | 10 - .../bootstrap-table-multiple-sort.min.js | 10 - .../bootstrap-table-page-jump-to.min.js | 10 - .../pipeline/bootstrap-table-pipeline.min.js | 10 - .../print/bootstrap-table-print.min.js | 10 - .../bootstrap-table-reorder-columns.min.js | 10 - .../bootstrap-table-reorder-rows.min.js | 10 - .../bootstrap-table-resizable.min.js | 10 - .../bootstrap-table-sticky-header.min.js | 10 - .../toolbar/bootstrap-table-toolbar.min.js | 10 - .../treegrid/bootstrap-table-treegrid.min.js | 10 - .../locale/bootstrap-table-af-ZA.min.js | 10 - .../locale/bootstrap-table-ar-SA.min.js | 10 - .../locale/bootstrap-table-bg-BG.min.js | 10 - .../locale/bootstrap-table-ca-ES.min.js | 10 - .../locale/bootstrap-table-cs-CZ.min.js | 10 - .../locale/bootstrap-table-da-DK.min.js | 10 - .../locale/bootstrap-table-de-DE.min.js | 10 - .../locale/bootstrap-table-el-GR.min.js | 10 - .../locale/bootstrap-table-en-US.min.js | 10 - .../locale/bootstrap-table-es-AR.min.js | 10 - .../locale/bootstrap-table-es-CL.min.js | 10 - .../locale/bootstrap-table-es-CR.min.js | 10 - .../locale/bootstrap-table-es-ES.min.js | 10 - .../locale/bootstrap-table-es-MX.min.js | 10 - .../locale/bootstrap-table-es-NI.min.js | 10 - .../locale/bootstrap-table-es-SP.min.js | 10 - .../locale/bootstrap-table-et-EE.min.js | 10 - .../locale/bootstrap-table-eu-EU.min.js | 10 - .../locale/bootstrap-table-fa-IR.min.js | 10 - .../locale/bootstrap-table-fi-FI.min.js | 10 - .../locale/bootstrap-table-fr-BE.min.js | 10 - .../locale/bootstrap-table-fr-CH.min.js | 10 - .../locale/bootstrap-table-fr-FR.min.js | 10 - .../locale/bootstrap-table-fr-LU.min.js | 10 - .../locale/bootstrap-table-he-IL.min.js | 10 - .../locale/bootstrap-table-hi-IN.min.js | 10 - .../locale/bootstrap-table-hr-HR.min.js | 10 - .../locale/bootstrap-table-hu-HU.min.js | 10 - .../locale/bootstrap-table-id-ID.min.js | 10 - .../locale/bootstrap-table-it-IT.min.js | 10 - .../locale/bootstrap-table-ja-JP.min.js | 10 - .../locale/bootstrap-table-ka-GE.min.js | 10 - .../locale/bootstrap-table-ko-KR.min.js | 10 - .../locale/bootstrap-table-lb-LU.min.js | 10 - .../locale/bootstrap-table-lt-LT.min.js | 10 - .../locale/bootstrap-table-ms-MY.min.js | 10 - .../locale/bootstrap-table-nb-NO.min.js | 10 - .../locale/bootstrap-table-nl-BE.min.js | 10 - .../locale/bootstrap-table-nl-NL.min.js | 10 - .../locale/bootstrap-table-pl-PL.min.js | 10 - .../locale/bootstrap-table-pt-BR.min.js | 10 - .../locale/bootstrap-table-pt-PT.min.js | 10 - .../locale/bootstrap-table-ro-RO.min.js | 10 - .../locale/bootstrap-table-ru-RU.min.js | 10 - .../locale/bootstrap-table-sk-SK.min.js | 10 - .../locale/bootstrap-table-sl-SI.min.js | 10 - .../locale/bootstrap-table-sr-Cyrl-RS.min.js | 10 - .../locale/bootstrap-table-sr-Latn-RS.min.js | 10 - .../locale/bootstrap-table-sv-SE.min.js | 10 - .../locale/bootstrap-table-th-TH.min.js | 10 - .../locale/bootstrap-table-tr-TR.min.js | 10 - .../locale/bootstrap-table-uk-UA.min.js | 10 - .../locale/bootstrap-table-ur-PK.min.js | 10 - .../locale/bootstrap-table-uz-Latn-UZ.min.js | 10 - .../locale/bootstrap-table-vi-VN.min.js | 10 - .../locale/bootstrap-table-zh-CN.min.js | 10 - .../locale/bootstrap-table-zh-TW.min.js | 10 - .../bootstrap-table/bootstrap-table.min.css | 10 - .../bootstrap-table/bootstrap-table.min.js | 10 - .../bulma/bootstrap-table-bulma.min.css | 10 - .../themes/bulma/bootstrap-table-bulma.min.js | 10 - .../bootstrap-table-foundation.min.css | 10 - .../bootstrap-table-foundation.min.js | 10 - .../bootstrap-table-materialize.min.css | 10 - .../bootstrap-table-materialize.min.js | 10 - .../semantic/bootstrap-table-semantic.min.css | 10 - .../semantic/bootstrap-table-semantic.min.js | 10 - .../bootstrap-table-locale-all.js | 12581 ++++++++------ .../bootstrap-table-locale-all.min.js | 10 + .../bootstrap-table-vue.js | 827 + .../bootstrap-table-vue.umd.js | 5 + .../bootstrap-table.css | 34 +- .../bootstrap-table.js | 13892 +++++++++------- .../bootstrap-table.min.css | 10 + .../bootstrap-table.min.js | 10 + .../bootstrap-table-1.27.0/config/index.js | 9005 ++++++++++ .../config/index.min.js | 10 + .../addrbar/bootstrap-table-addrbar.js | 154 +- .../addrbar/bootstrap-table-addrbar.min.js | 10 + .../bootstrap-table-auto-refresh.js | 67 +- .../bootstrap-table-auto-refresh.min.js | 10 + .../cookie/bootstrap-table-cookie.js | 322 +- .../cookie/bootstrap-table-cookie.min.js | 10 + .../copy-rows/bootstrap-table-copy-rows.js | 143 +- .../bootstrap-table-copy-rows.min.js | 10 + .../bootstrap-table-custom-view.js | 91 +- .../bootstrap-table-custom-view.min.js | 10 + .../defer-url/bootstrap-table-defer-url.js | 24 +- .../bootstrap-table-defer-url.min.js | 10 + .../editable/bootstrap-table-editable.js | 207 +- .../editable/bootstrap-table-editable.min.js | 10 + .../export/bootstrap-table-export.js | 279 +- .../export/bootstrap-table-export.min.js | 10 + .../bootstrap-table-filter-control.css | 0 .../bootstrap-table-filter-control.js | 448 +- .../bootstrap-table-filter-control.min.css | 2 +- .../bootstrap-table-filter-control.min.js | 10 + .../extensions/filter-control/utils.js | 213 +- .../extensions/filter-control/utils.min.js | 10 + .../bootstrap-table-fixed-columns.css | 0 .../bootstrap-table-fixed-columns.js | 220 +- .../bootstrap-table-fixed-columns.min.css | 2 +- .../bootstrap-table-fixed-columns.min.js | 10 + .../group-by-v2/bootstrap-table-group-by.css | 0 .../group-by-v2/bootstrap-table-group-by.js | 2906 +++- .../bootstrap-table-group-by.min.css | 2 +- .../bootstrap-table-group-by.min.js | 10 + .../bootstrap-table-i18n-enhance.js | 66 +- .../bootstrap-table-i18n-enhance.min.js | 10 + .../key-events/bootstrap-table-key-events.js | 87 +- .../bootstrap-table-key-events.min.js | 10 + .../mobile/bootstrap-table-mobile.js | 67 +- .../mobile/bootstrap-table-mobile.min.js | 10 + .../bootstrap-table-multiple-sort.js | 284 +- .../bootstrap-table-multiple-sort.min.js | 10 + .../bootstrap-table-page-jump-to.css | 0 .../bootstrap-table-page-jump-to.js | 98 +- .../bootstrap-table-page-jump-to.min.css | 2 +- .../bootstrap-table-page-jump-to.min.js | 10 + .../pipeline/bootstrap-table-pipeline.js | 193 +- .../pipeline/bootstrap-table-pipeline.min.js | 10 + .../extensions/print/bootstrap-table-print.js | 241 +- .../print/bootstrap-table-print.min.js | 10 + .../bootstrap-table-reorder-columns.js | 210 +- .../bootstrap-table-reorder-columns.min.js | 10 + .../bootstrap-table-reorder-rows.css | 0 .../bootstrap-table-reorder-rows.js | 28 +- .../bootstrap-table-reorder-rows.min.css | 2 +- .../bootstrap-table-reorder-rows.min.js | 10 + .../resizable/bootstrap-table-resizable.js | 24 +- .../bootstrap-table-resizable.min.js | 10 + .../bootstrap-table-sticky-header.css | 0 .../bootstrap-table-sticky-header.js | 73 +- .../bootstrap-table-sticky-header.min.css | 2 +- .../bootstrap-table-sticky-header.min.js | 10 + .../toolbar/bootstrap-table-toolbar.js | 148 +- .../toolbar/bootstrap-table-toolbar.min.js | 10 + .../treegrid/bootstrap-table-treegrid.js | 127 +- .../treegrid/bootstrap-table-treegrid.min.js | 10 + .../locale/bootstrap-table-af-ZA.js | 241 +- .../locale/bootstrap-table-af-ZA.min.js | 10 + .../locale/bootstrap-table-ar-SA.js | 223 +- .../locale/bootstrap-table-ar-SA.min.js | 10 + .../locale/bootstrap-table-bg-BG.js | 223 +- .../locale/bootstrap-table-bg-BG.min.js | 10 + .../locale/bootstrap-table-ca-ES.js | 223 +- .../locale/bootstrap-table-ca-ES.min.js | 10 + .../locale/bootstrap-table-cs-CZ.js | 223 +- .../locale/bootstrap-table-cs-CZ.min.js | 10 + .../locale/bootstrap-table-da-DK.js | 223 +- .../locale/bootstrap-table-da-DK.min.js | 10 + .../locale/bootstrap-table-de-DE.js | 238 +- .../locale/bootstrap-table-de-DE.min.js | 10 + .../locale/bootstrap-table-el-GR.js | 223 +- .../locale/bootstrap-table-el-GR.min.js | 10 + .../locale/bootstrap-table-en-US.js | 223 +- .../locale/bootstrap-table-en-US.min.js | 10 + .../locale/bootstrap-table-es-AR.js | 223 +- .../locale/bootstrap-table-es-AR.min.js | 10 + .../locale/bootstrap-table-es-CL.js | 223 +- .../locale/bootstrap-table-es-CL.min.js | 10 + .../locale/bootstrap-table-es-CR.js | 223 +- .../locale/bootstrap-table-es-CR.min.js | 10 + .../locale/bootstrap-table-es-ES.js | 240 +- .../locale/bootstrap-table-es-ES.min.js | 10 + .../locale/bootstrap-table-es-MX.js | 223 +- .../locale/bootstrap-table-es-MX.min.js | 10 + .../locale/bootstrap-table-es-NI.js | 223 +- .../locale/bootstrap-table-es-NI.min.js | 10 + .../locale/bootstrap-table-es-SP.js | 223 +- .../locale/bootstrap-table-es-SP.min.js | 10 + .../locale/bootstrap-table-et-EE.js | 223 +- .../locale/bootstrap-table-et-EE.min.js | 10 + .../locale/bootstrap-table-eu-EU.js | 223 +- .../locale/bootstrap-table-eu-EU.min.js | 10 + .../locale/bootstrap-table-fa-IR.js | 223 +- .../locale/bootstrap-table-fa-IR.min.js | 10 + .../locale/bootstrap-table-fi-FI.js | 223 +- .../locale/bootstrap-table-fi-FI.min.js | 10 + .../locale/bootstrap-table-fr-BE.js | 241 +- .../locale/bootstrap-table-fr-BE.min.js | 10 + .../locale/bootstrap-table-fr-CH.js | 241 +- .../locale/bootstrap-table-fr-CH.min.js | 10 + .../locale/bootstrap-table-fr-FR.js | 241 +- .../locale/bootstrap-table-fr-FR.min.js | 10 + .../locale/bootstrap-table-fr-LU.js | 241 +- .../locale/bootstrap-table-fr-LU.min.js | 10 + .../locale/bootstrap-table-he-IL.js | 223 +- .../locale/bootstrap-table-he-IL.min.js | 10 + .../locale/bootstrap-table-hi-IN.js | 223 +- .../locale/bootstrap-table-hi-IN.min.js | 10 + .../locale/bootstrap-table-hr-HR.js | 223 +- .../locale/bootstrap-table-hr-HR.min.js | 10 + .../locale/bootstrap-table-hu-HU.js | 223 +- .../locale/bootstrap-table-hu-HU.min.js | 10 + .../locale/bootstrap-table-id-ID.js | 241 +- .../locale/bootstrap-table-id-ID.min.js | 10 + .../locale/bootstrap-table-it-IT.js | 223 +- .../locale/bootstrap-table-it-IT.min.js | 10 + .../locale/bootstrap-table-ja-JP.js | 223 +- .../locale/bootstrap-table-ja-JP.min.js | 10 + .../locale/bootstrap-table-ka-GE.js | 223 +- .../locale/bootstrap-table-ka-GE.min.js | 10 + .../locale/bootstrap-table-ko-KR.js | 223 +- .../locale/bootstrap-table-ko-KR.min.js | 10 + .../locale/bootstrap-table-lb-LU.js | 223 +- .../locale/bootstrap-table-lb-LU.min.js | 10 + .../locale/bootstrap-table-lt-LT.js | 223 +- .../locale/bootstrap-table-lt-LT.min.js | 10 + .../locale/bootstrap-table-ms-MY.js | 223 +- .../locale/bootstrap-table-ms-MY.min.js | 10 + .../locale/bootstrap-table-nb-NO.js | 223 +- .../locale/bootstrap-table-nb-NO.min.js | 10 + .../locale/bootstrap-table-nl-BE.js | 238 +- .../locale/bootstrap-table-nl-BE.min.js | 10 + .../locale/bootstrap-table-nl-NL.js | 238 +- .../locale/bootstrap-table-nl-NL.min.js | 10 + .../locale/bootstrap-table-pl-PL.js | 223 +- .../locale/bootstrap-table-pl-PL.min.js | 10 + .../locale/bootstrap-table-pt-BR.js | 240 +- .../locale/bootstrap-table-pt-BR.min.js | 10 + .../locale/bootstrap-table-pt-PT.js | 240 +- .../locale/bootstrap-table-pt-PT.min.js | 10 + .../locale/bootstrap-table-ro-RO.js | 223 +- .../locale/bootstrap-table-ro-RO.min.js | 10 + .../locale/bootstrap-table-ru-RU.js | 223 +- .../locale/bootstrap-table-ru-RU.min.js | 10 + .../locale/bootstrap-table-sk-SK.js | 223 +- .../locale/bootstrap-table-sk-SK.min.js | 10 + .../locale/bootstrap-table-sl-SI.js | 223 +- .../locale/bootstrap-table-sl-SI.min.js | 10 + .../locale/bootstrap-table-sr-Cyrl-RS.js | 223 +- .../locale/bootstrap-table-sr-Cyrl-RS.min.js | 10 + .../locale/bootstrap-table-sr-Latn-RS.js | 223 +- .../locale/bootstrap-table-sr-Latn-RS.min.js | 10 + .../locale/bootstrap-table-sv-SE.js | 223 +- .../locale/bootstrap-table-sv-SE.min.js | 10 + .../locale/bootstrap-table-th-TH.js | 223 +- .../locale/bootstrap-table-th-TH.min.js | 10 + .../locale/bootstrap-table-tr-TR.js | 223 +- .../locale/bootstrap-table-tr-TR.min.js | 10 + .../locale/bootstrap-table-uk-UA.js | 223 +- .../locale/bootstrap-table-uk-UA.min.js | 10 + .../locale/bootstrap-table-ur-PK.js | 223 +- .../locale/bootstrap-table-ur-PK.min.js | 10 + .../locale/bootstrap-table-uz-Latn-UZ.js | 223 +- .../locale/bootstrap-table-uz-Latn-UZ.min.js | 10 + .../locale/bootstrap-table-vi-VN.js | 223 +- .../locale/bootstrap-table-vi-VN.min.js | 10 + .../locale/bootstrap-table-zh-CN.js | 223 +- .../locale/bootstrap-table-zh-CN.min.js | 10 + .../locale/bootstrap-table-zh-TW.js | 223 +- .../locale/bootstrap-table-zh-TW.min.js | 10 + .../bootstrap-table/bootstrap-table.css | 32 +- .../themes/bootstrap-table/bootstrap-table.js | 66 +- .../bootstrap-table/bootstrap-table.min.css | 10 + .../bootstrap-table/bootstrap-table.min.js | 10 + .../bootstrap-table/fonts/bootstrap-table.eot | Bin .../bootstrap-table/fonts/bootstrap-table.svg | 0 .../bootstrap-table/fonts/bootstrap-table.ttf | Bin .../fonts/bootstrap-table.woff | Bin .../themes/bulma/bootstrap-table-bulma.css | 32 +- .../themes/bulma/bootstrap-table-bulma.js | 66 +- .../bulma/bootstrap-table-bulma.min.css | 10 + .../themes/bulma/bootstrap-table-bulma.min.js | 10 + .../foundation/bootstrap-table-foundation.css | 32 +- .../foundation/bootstrap-table-foundation.js | 66 +- .../bootstrap-table-foundation.min.css | 10 + .../bootstrap-table-foundation.min.js | 10 + .../bootstrap-table-materialize.css | 32 +- .../bootstrap-table-materialize.js | 66 +- .../bootstrap-table-materialize.min.css | 10 + .../bootstrap-table-materialize.min.js | 10 + .../semantic/bootstrap-table-semantic.css | 32 +- .../semantic/bootstrap-table-semantic.js | 66 +- .../semantic/bootstrap-table-semantic.min.css | 10 + .../semantic/bootstrap-table-semantic.min.js | 10 + web/skins/classic/includes/functions.php | 20 +- 309 files changed, 39389 insertions(+), 21033 deletions(-) create mode 120000 web/skins/classic/assets/bootstrap-table delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/bootstrap-table-locale-all.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/bootstrap-table-vue.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/bootstrap-table-vue.umd.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/bootstrap-table.min.css delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/bootstrap-table.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/addrbar/bootstrap-table-addrbar.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/auto-refresh/bootstrap-table-auto-refresh.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/cookie/bootstrap-table-cookie.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/copy-rows/bootstrap-table-copy-rows.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/custom-view/bootstrap-table-custom-view.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/defer-url/bootstrap-table-defer-url.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/editable/bootstrap-table-editable.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/export/bootstrap-table-export.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/filter-control/bootstrap-table-filter-control.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/filter-control/utils.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/fixed-columns/bootstrap-table-fixed-columns.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/group-by-v2/bootstrap-table-group-by.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/i18n-enhance/bootstrap-table-i18n-enhance.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/key-events/bootstrap-table-key-events.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/mobile/bootstrap-table-mobile.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/multiple-sort/bootstrap-table-multiple-sort.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/pipeline/bootstrap-table-pipeline.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/print/bootstrap-table-print.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/reorder-columns/bootstrap-table-reorder-columns.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/reorder-rows/bootstrap-table-reorder-rows.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/resizable/bootstrap-table-resizable.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/sticky-header/bootstrap-table-sticky-header.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/toolbar/bootstrap-table-toolbar.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/extensions/treegrid/bootstrap-table-treegrid.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-af-ZA.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-ar-SA.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-bg-BG.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-ca-ES.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-cs-CZ.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-da-DK.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-de-DE.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-el-GR.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-en-US.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-es-AR.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-es-CL.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-es-CR.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-es-ES.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-es-MX.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-es-NI.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-es-SP.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-et-EE.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-eu-EU.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-fa-IR.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-fi-FI.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-fr-BE.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-fr-CH.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-fr-FR.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-fr-LU.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-he-IL.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-hi-IN.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-hr-HR.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-hu-HU.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-id-ID.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-it-IT.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-ja-JP.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-ka-GE.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-ko-KR.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-lb-LU.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-lt-LT.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-ms-MY.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-nb-NO.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-nl-BE.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-nl-NL.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-pl-PL.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-pt-BR.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-pt-PT.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-ro-RO.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-ru-RU.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-sk-SK.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-sl-SI.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-sr-Cyrl-RS.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-sr-Latn-RS.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-sv-SE.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-th-TH.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-tr-TR.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-uk-UA.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-ur-PK.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-uz-Latn-UZ.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-vi-VN.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-zh-CN.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/locale/bootstrap-table-zh-TW.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/themes/bootstrap-table/bootstrap-table.min.css delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/themes/bootstrap-table/bootstrap-table.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/themes/bulma/bootstrap-table-bulma.min.css delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/themes/bulma/bootstrap-table-bulma.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/themes/foundation/bootstrap-table-foundation.min.css delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/themes/foundation/bootstrap-table-foundation.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/themes/materialize/bootstrap-table-materialize.min.css delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/themes/materialize/bootstrap-table-materialize.min.js delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/themes/semantic/bootstrap-table-semantic.min.css delete mode 100644 web/skins/classic/assets/bootstrap-table-1.24.1/themes/semantic/bootstrap-table-semantic.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/bootstrap-table-locale-all.js (82%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/bootstrap-table-locale-all.min.js create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/bootstrap-table-vue.js create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/bootstrap-table-vue.umd.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/bootstrap-table.css (93%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/bootstrap-table.js (57%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/bootstrap-table.min.css create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/bootstrap-table.min.js create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/config/index.js create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/config/index.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/addrbar/bootstrap-table-addrbar.js (96%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/addrbar/bootstrap-table-addrbar.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/auto-refresh/bootstrap-table-auto-refresh.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/auto-refresh/bootstrap-table-auto-refresh.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/cookie/bootstrap-table-cookie.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/cookie/bootstrap-table-cookie.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/copy-rows/bootstrap-table-copy-rows.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/copy-rows/bootstrap-table-copy-rows.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/custom-view/bootstrap-table-custom-view.js (96%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/custom-view/bootstrap-table-custom-view.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/defer-url/bootstrap-table-defer-url.js (98%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/defer-url/bootstrap-table-defer-url.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/editable/bootstrap-table-editable.js (94%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/editable/bootstrap-table-editable.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/export/bootstrap-table-export.js (94%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/export/bootstrap-table-export.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/filter-control/bootstrap-table-filter-control.css (100%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/filter-control/bootstrap-table-filter-control.js (97%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/filter-control/bootstrap-table-filter-control.min.css (94%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/filter-control/bootstrap-table-filter-control.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/filter-control/utils.js (96%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/filter-control/utils.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/fixed-columns/bootstrap-table-fixed-columns.css (100%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/fixed-columns/bootstrap-table-fixed-columns.js (93%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/fixed-columns/bootstrap-table-fixed-columns.min.css (89%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/fixed-columns/bootstrap-table-fixed-columns.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/group-by-v2/bootstrap-table-group-by.css (100%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/group-by-v2/bootstrap-table-group-by.js (51%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/group-by-v2/bootstrap-table-group-by.min.css (87%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/group-by-v2/bootstrap-table-group-by.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/i18n-enhance/bootstrap-table-i18n-enhance.js (96%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/i18n-enhance/bootstrap-table-i18n-enhance.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/key-events/bootstrap-table-key-events.js (96%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/key-events/bootstrap-table-key-events.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/mobile/bootstrap-table-mobile.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/mobile/bootstrap-table-mobile.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/multiple-sort/bootstrap-table-multiple-sort.js (86%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/multiple-sort/bootstrap-table-multiple-sort.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/page-jump-to/bootstrap-table-page-jump-to.css (100%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/page-jump-to/bootstrap-table-page-jump-to.js (96%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/page-jump-to/bootstrap-table-page-jump-to.min.css (96%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/pipeline/bootstrap-table-pipeline.js (94%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/pipeline/bootstrap-table-pipeline.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/print/bootstrap-table-print.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/print/bootstrap-table-print.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/reorder-columns/bootstrap-table-reorder-columns.js (94%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/reorder-columns/bootstrap-table-reorder-columns.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/reorder-rows/bootstrap-table-reorder-rows.css (100%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/reorder-rows/bootstrap-table-reorder-rows.js (98%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/reorder-rows/bootstrap-table-reorder-rows.min.css (92%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/reorder-rows/bootstrap-table-reorder-rows.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/resizable/bootstrap-table-resizable.js (98%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/resizable/bootstrap-table-resizable.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/sticky-header/bootstrap-table-sticky-header.css (100%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/sticky-header/bootstrap-table-sticky-header.js (97%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/sticky-header/bootstrap-table-sticky-header.min.css (92%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/sticky-header/bootstrap-table-sticky-header.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/toolbar/bootstrap-table-toolbar.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/toolbar/bootstrap-table-toolbar.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/extensions/treegrid/bootstrap-table-treegrid.js (96%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/extensions/treegrid/bootstrap-table-treegrid.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-af-ZA.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-af-ZA.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-ar-SA.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-ar-SA.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-bg-BG.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-bg-BG.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-ca-ES.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-ca-ES.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-cs-CZ.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-cs-CZ.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-da-DK.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-da-DK.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-de-DE.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-de-DE.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-el-GR.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-el-GR.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-en-US.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-en-US.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-es-AR.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-es-AR.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-es-CL.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-es-CL.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-es-CR.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-es-CR.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-es-ES.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-es-ES.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-es-MX.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-es-MX.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-es-NI.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-es-NI.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-es-SP.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-es-SP.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-et-EE.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-et-EE.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-eu-EU.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-eu-EU.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-fa-IR.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-fa-IR.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-fi-FI.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-fi-FI.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-fr-BE.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-fr-BE.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-fr-CH.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-fr-CH.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-fr-FR.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-fr-FR.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-fr-LU.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-fr-LU.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-he-IL.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-he-IL.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-hi-IN.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-hi-IN.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-hr-HR.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-hr-HR.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-hu-HU.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-hu-HU.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-id-ID.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-id-ID.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-it-IT.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-it-IT.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-ja-JP.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-ja-JP.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-ka-GE.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-ka-GE.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-ko-KR.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-ko-KR.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-lb-LU.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-lb-LU.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-lt-LT.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-lt-LT.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-ms-MY.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-ms-MY.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-nb-NO.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-nb-NO.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-nl-BE.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-nl-BE.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-nl-NL.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-nl-NL.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-pl-PL.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-pl-PL.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-pt-BR.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-pt-BR.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-pt-PT.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-pt-PT.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-ro-RO.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-ro-RO.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-ru-RU.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-ru-RU.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-sk-SK.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-sk-SK.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-sl-SI.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-sl-SI.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-sr-Cyrl-RS.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-sr-Cyrl-RS.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-sr-Latn-RS.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-sr-Latn-RS.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-sv-SE.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-sv-SE.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-th-TH.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-th-TH.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-tr-TR.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-tr-TR.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-uk-UA.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-uk-UA.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-ur-PK.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-ur-PK.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-uz-Latn-UZ.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-uz-Latn-UZ.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-vi-VN.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-vi-VN.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-zh-CN.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-zh-CN.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/locale/bootstrap-table-zh-TW.js (95%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/locale/bootstrap-table-zh-TW.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/themes/bootstrap-table/bootstrap-table.css (96%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/themes/bootstrap-table/bootstrap-table.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/themes/bootstrap-table/bootstrap-table.min.css create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/themes/bootstrap-table/bootstrap-table.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/themes/bootstrap-table/fonts/bootstrap-table.eot (100%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/themes/bootstrap-table/fonts/bootstrap-table.svg (100%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/themes/bootstrap-table/fonts/bootstrap-table.ttf (100%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/themes/bootstrap-table/fonts/bootstrap-table.woff (100%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/themes/bulma/bootstrap-table-bulma.css (94%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/themes/bulma/bootstrap-table-bulma.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/themes/bulma/bootstrap-table-bulma.min.css create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/themes/bulma/bootstrap-table-bulma.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/themes/foundation/bootstrap-table-foundation.css (94%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/themes/foundation/bootstrap-table-foundation.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/themes/foundation/bootstrap-table-foundation.min.css create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/themes/foundation/bootstrap-table-foundation.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/themes/materialize/bootstrap-table-materialize.css (94%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/themes/materialize/bootstrap-table-materialize.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/themes/materialize/bootstrap-table-materialize.min.css create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/themes/materialize/bootstrap-table-materialize.min.js rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/themes/semantic/bootstrap-table-semantic.css (93%) rename web/skins/classic/assets/{bootstrap-table-1.24.1 => bootstrap-table-1.27.0}/themes/semantic/bootstrap-table-semantic.js (97%) create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/themes/semantic/bootstrap-table-semantic.min.css create mode 100644 web/skins/classic/assets/bootstrap-table-1.27.0/themes/semantic/bootstrap-table-semantic.min.js diff --git a/web/skins/classic/assets/bootstrap-table b/web/skins/classic/assets/bootstrap-table new file mode 120000 index 000000000..bf503099b --- /dev/null +++ b/web/skins/classic/assets/bootstrap-table @@ -0,0 +1 @@ +bootstrap-table-1.27.0/ \ No newline at end of file diff --git a/web/skins/classic/assets/bootstrap-table-1.24.1/bootstrap-table-locale-all.min.js b/web/skins/classic/assets/bootstrap-table-1.24.1/bootstrap-table-locale-all.min.js deleted file mode 100644 index 360703695..000000000 --- a/web/skins/classic/assets/bootstrap-table-1.24.1/bootstrap-table-locale-all.min.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation) - * - * @version v1.24.1 - * @homepage https://bootstrap-table.com - * @author wenzhixin (http://wenzhixin.net.cn/) - * @license MIT - */ - -!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],n):n((t="undefined"!=typeof globalThis?globalThis:t||self).jQuery)}(this,(function(t){"use strict";var n,r,o="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},e={};function a(){if(r)return n;r=1;var t=function(t){return t&&t.Math===Math&&t};return n=t("object"==typeof globalThis&&globalThis)||t("object"==typeof window&&window)||t("object"==typeof self&&self)||t("object"==typeof o&&o)||t("object"==typeof n&&n)||function(){return this}()||Function("return this")()}var i,u,c,f,l,s,m,g,d={};function h(){return u?i:(u=1,i=function(t){try{return!!t()}catch(t){return!0}})}function p(){if(f)return c;f=1;var t=h();return c=!t((function(){return 7!==Object.defineProperty({},1,{get:function(){return 7}})[1]}))}function S(){if(s)return l;s=1;var t=h();return l=!t((function(){var t=function(){}.bind();return"function"!=typeof t||t.hasOwnProperty("prototype")}))}function w(){if(g)return m;g=1;var t=S(),n=Function.prototype.call;return m=t?n.bind(n):function(){return n.apply(n,arguments)},m}var P,T,b,v,C,R,A,x,y,O,k,F,M,j,D,N,E,H,z,L,B,U,V,G,J,Z,I,K,q,W,Y,_,X,Q,$,tt,nt,rt,ot,et,at,it={};function ut(){if(P)return it;P=1;var t={}.propertyIsEnumerable,n=Object.getOwnPropertyDescriptor,r=n&&!t.call({1:2},1);return it.f=r?function(t){var r=n(this,t);return!!r&&r.enumerable}:t,it}function ct(){return b?T:(b=1,T=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}})}function ft(){if(C)return v;C=1;var t=S(),n=Function.prototype,r=n.call,o=t&&n.bind.bind(r,r);return v=t?o:function(t){return function(){return r.apply(t,arguments)}},v}function lt(){if(A)return R;A=1;var t=ft(),n=t({}.toString),r=t("".slice);return R=function(t){return r(n(t),8,-1)}}function st(){if(y)return x;y=1;var t=ft(),n=h(),r=lt(),o=Object,e=t("".split);return x=n((function(){return!o("z").propertyIsEnumerable(0)}))?function(t){return"String"===r(t)?e(t,""):o(t)}:o}function mt(){return k?O:(k=1,O=function(t){return null==t})}function gt(){if(M)return F;M=1;var t=mt(),n=TypeError;return F=function(r){if(t(r))throw new n("Can't call method on "+r);return r}}function dt(){if(D)return j;D=1;var t=st(),n=gt();return j=function(r){return t(n(r))}}function ht(){if(E)return N;E=1;var t="object"==typeof document&&document.all;return N=void 0===t&&void 0!==t?function(n){return"function"==typeof n||n===t}:function(t){return"function"==typeof t}}function pt(){if(z)return H;z=1;var t=ht();return H=function(n){return"object"==typeof n?null!==n:t(n)}}function St(){if(B)return L;B=1;var t=a(),n=ht();return L=function(r,o){return arguments.length<2?(e=t[r],n(e)?e:void 0):t[r]&&t[r][o];var e},L}function wt(){if(I)return Z;I=1;var t,n,r=a(),o=function(){if(J)return G;J=1;var t=a().navigator,n=t&&t.userAgent;return G=n?String(n):""}(),e=r.process,i=r.Deno,u=e&&e.versions||i&&i.version,c=u&&u.v8;return c&&(n=(t=c.split("."))[0]>0&&t[0]<4?1:+(t[0]+t[1])),!n&&o&&(!(t=o.match(/Edge\/(\d+)/))||t[1]>=74)&&(t=o.match(/Chrome\/(\d+)/))&&(n=+t[1]),Z=n}function Pt(){if(q)return K;q=1;var t=wt(),n=h(),r=a().String;return K=!!Object.getOwnPropertySymbols&&!n((function(){var n=Symbol("symbol detection");return!r(n)||!(Object(n)instanceof Symbol)||!Symbol.sham&&t&&t<41}))}function Tt(){if(Y)return W;Y=1;var t=Pt();return W=t&&!Symbol.sham&&"symbol"==typeof Symbol.iterator}function bt(){if(X)return _;X=1;var t=St(),n=ht(),r=function(){if(V)return U;V=1;var t=ft();return U=t({}.isPrototypeOf)}(),o=Tt(),e=Object;return _=o?function(t){return"symbol"==typeof t}:function(o){var a=t("Symbol");return n(a)&&r(a.prototype,e(o))}}function vt(){if($)return Q;$=1;var t=String;return Q=function(n){try{return t(n)}catch(t){return"Object"}}}function Ct(){if(nt)return tt;nt=1;var t=ht(),n=vt(),r=TypeError;return tt=function(o){if(t(o))return o;throw new r(n(o)+" is not a function")}}function Rt(){if(ot)return rt;ot=1;var t=Ct(),n=mt();return rt=function(r,o){var e=r[o];return n(e)?void 0:t(e)}}function At(){if(at)return et;at=1;var t=w(),n=ht(),r=pt(),o=TypeError;return et=function(e,a){var i,u;if("string"===a&&n(i=e.toString)&&!r(u=t(i,e)))return u;if(n(i=e.valueOf)&&!r(u=t(i,e)))return u;if("string"!==a&&n(i=e.toString)&&!r(u=t(i,e)))return u;throw new o("Can't convert object to primitive value")}}var xt,yt,Ot,kt,Ft,Mt,jt,Dt,Nt,Et,Ht,zt,Lt,Bt,Ut,Vt,Gt,Jt,Zt,It,Kt,qt,Wt,Yt,_t={exports:{}};function Xt(){if(kt)return Ot;kt=1;var t=a(),n=Object.defineProperty;return Ot=function(r,o){try{n(t,r,{value:o,configurable:!0,writable:!0})}catch(n){t[r]=o}return o}}function Qt(){if(Ft)return _t.exports;Ft=1;var t=yt?xt:(yt=1,xt=!1),n=a(),r=Xt(),o="__core-js_shared__",e=_t.exports=n[o]||r(o,{});return(e.versions||(e.versions=[])).push({version:"3.39.0",mode:t?"pure":"global",copyright:"© 2014-2024 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.39.0/LICENSE",source:"https://github.com/zloirock/core-js"}),_t.exports}function $t(){if(jt)return Mt;jt=1;var t=Qt();return Mt=function(n,r){return t[n]||(t[n]=r||{})}}function tn(){if(Nt)return Dt;Nt=1;var t=gt(),n=Object;return Dt=function(r){return n(t(r))}}function nn(){if(Ht)return Et;Ht=1;var t=ft(),n=tn(),r=t({}.hasOwnProperty);return Et=Object.hasOwn||function(t,o){return r(n(t),o)}}function rn(){if(Lt)return zt;Lt=1;var t=ft(),n=0,r=Math.random(),o=t(1..toString);return zt=function(t){return"Symbol("+(void 0===t?"":t)+")_"+o(++n+r,36)}}function on(){if(Ut)return Bt;Ut=1;var t=a(),n=$t(),r=nn(),o=rn(),e=Pt(),i=Tt(),u=t.Symbol,c=n("wks"),f=i?u.for||u:u&&u.withoutSetter||o;return Bt=function(t){return r(c,t)||(c[t]=e&&r(u,t)?u[t]:f("Symbol."+t)),c[t]}}function en(){if(Gt)return Vt;Gt=1;var t=w(),n=pt(),r=bt(),o=Rt(),e=At(),a=on(),i=TypeError,u=a("toPrimitive");return Vt=function(a,c){if(!n(a)||r(a))return a;var f,l=o(a,u);if(l){if(void 0===c&&(c="default"),f=t(l,a,c),!n(f)||r(f))return f;throw new i("Can't convert object to primitive value")}return void 0===c&&(c="number"),e(a,c)}}function an(){if(Zt)return Jt;Zt=1;var t=en(),n=bt();return Jt=function(r){var o=t(r,"string");return n(o)?o:o+""}}function un(){if(Wt)return qt;Wt=1;var t=p(),n=h(),r=function(){if(Kt)return It;Kt=1;var t=a(),n=pt(),r=t.document,o=n(r)&&n(r.createElement);return It=function(t){return o?r.createElement(t):{}}}();return qt=!t&&!n((function(){return 7!==Object.defineProperty(r("div"),"a",{get:function(){return 7}}).a}))}function cn(){if(Yt)return d;Yt=1;var t=p(),n=w(),r=ut(),o=ct(),e=dt(),a=an(),i=nn(),u=un(),c=Object.getOwnPropertyDescriptor;return d.f=t?c:function(t,f){if(t=e(t),f=a(f),u)try{return c(t,f)}catch(t){}if(i(t,f))return o(!n(r.f,t,f),t[f])},d}var fn,ln,sn,mn,gn,dn,hn,pn={};function Sn(){if(mn)return sn;mn=1;var t=pt(),n=String,r=TypeError;return sn=function(o){if(t(o))return o;throw new r(n(o)+" is not an object")}}function wn(){if(gn)return pn;gn=1;var t=p(),n=un(),r=function(){if(ln)return fn;ln=1;var t=p(),n=h();return fn=t&&n((function(){return 42!==Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype}))}(),o=Sn(),e=an(),a=TypeError,i=Object.defineProperty,u=Object.getOwnPropertyDescriptor,c="enumerable",f="configurable",l="writable";return pn.f=t?r?function(t,n,r){if(o(t),n=e(n),o(r),"function"==typeof t&&"prototype"===n&&"value"in r&&l in r&&!r[l]){var a=u(t,n);a&&a[l]&&(t[n]=r.value,r={configurable:f in r?r[f]:a[f],enumerable:c in r?r[c]:a[c],writable:!1})}return i(t,n,r)}:i:function(t,r,u){if(o(t),r=e(r),o(u),n)try{return i(t,r,u)}catch(t){}if("get"in u||"set"in u)throw new a("Accessors not supported");return"value"in u&&(t[r]=u.value),t},pn}function Pn(){if(hn)return dn;hn=1;var t=p(),n=wn(),r=ct();return dn=t?function(t,o,e){return n.f(t,o,r(1,e))}:function(t,n,r){return t[n]=r,t}}var Tn,bn,vn,Cn,Rn,An,xn,yn,On,kn,Fn,Mn,jn,Dn,Nn,En={exports:{}};function Hn(){if(Cn)return vn;Cn=1;var t=ft(),n=ht(),r=Qt(),o=t(Function.toString);return n(r.inspectSource)||(r.inspectSource=function(t){return o(t)}),vn=r.inspectSource}function zn(){if(yn)return xn;yn=1;var t=$t(),n=rn(),r=t("keys");return xn=function(t){return r[t]||(r[t]=n(t))}}function Ln(){return kn?On:(kn=1,On={})}function Bn(){if(Mn)return Fn;Mn=1;var t,n,r,o=function(){if(An)return Rn;An=1;var t=a(),n=ht(),r=t.WeakMap;return Rn=n(r)&&/native code/.test(String(r))}(),e=a(),i=pt(),u=Pn(),c=nn(),f=Qt(),l=zn(),s=Ln(),m="Object already initialized",g=e.TypeError,d=e.WeakMap;if(o||f.state){var h=f.state||(f.state=new d);h.get=h.get,h.has=h.has,h.set=h.set,t=function(t,n){if(h.has(t))throw new g(m);return n.facade=t,h.set(t,n),n},n=function(t){return h.get(t)||{}},r=function(t){return h.has(t)}}else{var p=l("state");s[p]=!0,t=function(t,n){if(c(t,p))throw new g(m);return n.facade=t,u(t,p,n),n},n=function(t){return c(t,p)?t[p]:{}},r=function(t){return c(t,p)}}return Fn={set:t,get:n,has:r,enforce:function(o){return r(o)?n(o):t(o,{})},getterFor:function(t){return function(r){var o;if(!i(r)||(o=n(r)).type!==t)throw new g("Incompatible receiver, "+t+" required");return o}}}}function Un(){if(jn)return En.exports;jn=1;var t=ft(),n=h(),r=ht(),o=nn(),e=p(),a=function(){if(bn)return Tn;bn=1;var t=p(),n=nn(),r=Function.prototype,o=t&&Object.getOwnPropertyDescriptor,e=n(r,"name"),a=e&&"something"===function(){}.name,i=e&&(!t||t&&o(r,"name").configurable);return Tn={EXISTS:e,PROPER:a,CONFIGURABLE:i}}().CONFIGURABLE,i=Hn(),u=Bn(),c=u.enforce,f=u.get,l=String,s=Object.defineProperty,m=t("".slice),g=t("".replace),d=t([].join),S=e&&!n((function(){return 8!==s((function(){}),"length",{value:8}).length})),w=String(String).split("String"),P=En.exports=function(t,n,r){"Symbol("===m(l(n),0,7)&&(n="["+g(l(n),/^Symbol\(([^)]*)\).*$/,"$1")+"]"),r&&r.getter&&(n="get "+n),r&&r.setter&&(n="set "+n),(!o(t,"name")||a&&t.name!==n)&&(e?s(t,"name",{value:n,configurable:!0}):t.name=n),S&&r&&o(r,"arity")&&t.length!==r.arity&&s(t,"length",{value:r.arity});try{r&&o(r,"constructor")&&r.constructor?e&&s(t,"prototype",{writable:!1}):t.prototype&&(t.prototype=void 0)}catch(t){}var i=c(t);return o(i,"source")||(i.source=d(w,"string"==typeof n?n:"")),t};return Function.prototype.toString=P((function(){return r(this)&&f(this).source||i(this)}),"toString"),En.exports}function Vn(){if(Nn)return Dn;Nn=1;var t=ht(),n=wn(),r=Un(),o=Xt();return Dn=function(e,a,i,u){u||(u={});var c=u.enumerable,f=void 0!==u.name?u.name:a;if(t(i)&&r(i,f,u),u.global)c?e[a]=i:o(a,i);else{try{u.unsafe?e[a]&&(c=!0):delete e[a]}catch(t){}c?e[a]=i:n.f(e,a,{value:i,enumerable:!1,configurable:!u.nonConfigurable,writable:!u.nonWritable})}return e}}var Gn,Jn,Zn,In,Kn,qn,Wn,Yn,_n,Xn,Qn,$n,tr,nr,rr,or,er,ar={};function ir(){if(In)return Zn;In=1;var t=function(){if(Jn)return Gn;Jn=1;var t=Math.ceil,n=Math.floor;return Gn=Math.trunc||function(r){var o=+r;return(o>0?n:t)(o)}}();return Zn=function(n){var r=+n;return r!=r||0===r?0:t(r)}}function ur(){if(qn)return Kn;qn=1;var t=ir(),n=Math.max,r=Math.min;return Kn=function(o,e){var a=t(o);return a<0?n(a+e,0):r(a,e)}}function cr(){if(Yn)return Wn;Yn=1;var t=ir(),n=Math.min;return Wn=function(r){var o=t(r);return o>0?n(o,9007199254740991):0}}function fr(){if(Xn)return _n;Xn=1;var t=cr();return _n=function(n){return t(n.length)}}function lr(){if(nr)return tr;nr=1;var t=ft(),n=nn(),r=dt(),o=function(){if($n)return Qn;$n=1;var t=dt(),n=ur(),r=fr(),o=function(o){return function(e,a,i){var u=t(e),c=r(u);if(0===c)return!o&&-1;var f,l=n(i,c);if(o&&a!=a){for(;c>l;)if((f=u[l++])!=f)return!0}else for(;c>l;l++)if((o||l in u)&&u[l]===a)return o||l||0;return!o&&-1}};return Qn={includes:o(!0),indexOf:o(!1)}}().indexOf,e=Ln(),a=t([].push);return tr=function(t,i){var u,c=r(t),f=0,l=[];for(u in c)!n(e,u)&&n(c,u)&&a(l,u);for(;i.length>f;)n(c,u=i[f++])&&(~o(l,u)||a(l,u));return l}}function sr(){return or?rr:(or=1,rr=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"])}var mr,gr,dr,hr,pr,Sr,wr,Pr,Tr,br,vr,Cr,Rr,Ar,xr,yr,Or,kr,Fr,Mr,jr,Dr,Nr,Er,Hr,zr,Lr,Br,Ur={};function Vr(){return mr||(mr=1,Ur.f=Object.getOwnPropertySymbols),Ur}function Gr(){if(dr)return gr;dr=1;var t=St(),n=ft(),r=function(){if(er)return ar;er=1;var t=lr(),n=sr().concat("length","prototype");return ar.f=Object.getOwnPropertyNames||function(r){return t(r,n)},ar}(),o=Vr(),e=Sn(),a=n([].concat);return gr=t("Reflect","ownKeys")||function(t){var n=r.f(e(t)),i=o.f;return i?a(n,i(t)):n}}function Jr(){if(pr)return hr;pr=1;var t=nn(),n=Gr(),r=cn(),o=wn();return hr=function(e,a,i){for(var u=n(a),c=o.f,f=r.f,l=0;l9007199254740991)throw t("Maximum allowed index exceeded");return n}}function qr(){if(xr)return Ar;xr=1;var t=p(),n=wn(),r=ct();return Ar=function(o,e,a){t?n.f(o,e,r(0,a)):o[e]=a}}function Wr(){if(Fr)return kr;Fr=1;var t=function(){if(Or)return yr;Or=1;var t={};return t[on()("toStringTag")]="z",yr="[object z]"===String(t)}(),n=ht(),r=lt(),o=on()("toStringTag"),e=Object,a="Arguments"===r(function(){return arguments}());return kr=t?r:function(t){var i,u,c;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(u=function(t,n){try{return t[n]}catch(t){}}(i=e(t),o))?u:a?r(i):"Object"===(c=r(i))&&n(i.callee)?"Arguments":c}}function Yr(){if(jr)return Mr;jr=1;var t=ft(),n=h(),r=ht(),o=Wr(),e=St(),a=Hn(),i=function(){},u=e("Reflect","construct"),c=/^\s*(?:class|function)\b/,f=t(c.exec),l=!c.test(i),s=function(t){if(!r(t))return!1;try{return u(i,[],t),!0}catch(t){return!1}},m=function(t){if(!r(t))return!1;switch(o(t)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return l||!!f(c,a(t))}catch(t){return!0}};return m.sham=!0,Mr=!u||n((function(){var t;return s(s.call)||!s(Object)||!s((function(){t=!0}))||t}))?m:s}function _r(){if(Nr)return Dr;Nr=1;var t=Ir(),n=Yr(),r=pt(),o=on()("species"),e=Array;return Dr=function(a){var i;return t(a)&&(i=a.constructor,(n(i)&&(i===e||t(i.prototype))||r(i)&&null===(i=i[o]))&&(i=void 0)),void 0===i?e:i}}function Xr(){if(Hr)return Er;Hr=1;var t=_r();return Er=function(n,r){return new(t(n))(0===r?0:r)}}function Qr(){if(Lr)return zr;Lr=1;var t=h(),n=on(),r=wt(),o=n("species");return zr=function(n){return r>=51||!t((function(){var t=[];return(t.constructor={})[o]=function(){return{foo:1}},1!==t[n](Boolean).foo}))}}!function(){if(Br)return e;Br=1;var t=Zr(),n=h(),r=Ir(),o=pt(),a=tn(),i=fr(),u=Kr(),c=qr(),f=Xr(),l=Qr(),s=on(),m=wt(),g=s("isConcatSpreadable"),d=m>=51||!n((function(){var t=[];return t[g]=!1,t.concat()[0]!==t})),p=function(t){if(!o(t))return!1;var n=t[g];return void 0!==n?!!n:r(t)};t({target:"Array",proto:!0,arity:1,forced:!d||!l("concat")},{concat:function(t){var n,r,o,e,l,s=a(this),m=f(s,0),g=0;for(n=-1,o=arguments.length;nm;)for(var h,p=c(arguments[m++]),S=g?s(e(p),g(p)):e(p),w=S.length,P=0;w>P;)h=S[P++],t&&!r(d,p,h)||(f[h]=p[h]);return f}:f,no}();t({target:"Object",stat:!0,arity:2,forced:Object.assign!==n},{assign:n})}(),t.fn.bootstrapTable.locales["af-ZA"]=t.fn.bootstrapTable.locales.af={formatCopyRows:function(){return"Kopieer lyne"},formatPrint:function(){return"Druk uit"},formatLoadingMessage:function(){return"Laai tans"},formatRecordsPerPage:function(t){return"".concat(t," reëls per bladsy")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Wys ".concat(t," tot ").concat(n," van ").concat(r," lyne (gefiltreer vanaf ").concat(o," lyne)"):"Wys ".concat(t," tot ").concat(n," van ").concat(r," lyne")},formatSRPaginationPreText:function(){return"vorige bladsy"},formatSRPaginationPageText:function(t){return"na bladsy ".concat(t)},formatSRPaginationNextText:function(){return"volgende bladsy"},formatDetailPagination:function(t){return"".concat(t,"-reël vertoon")},formatClearSearch:function(){return"Duidelike soektog"},formatSearch:function(){return"Navorsing"},formatNoMatches:function(){return"Geen resultate nie"},formatPaginationSwitch:function(){return"Versteek/Wys paginasie"},formatPaginationSwitchDown:function(){return"Wys paginasie"},formatPaginationSwitchUp:function(){return"Versteek paginasie"},formatRefresh:function(){return"Verfris"},formatToggleOn:function(){return"Wys kaartaansig"},formatToggleOff:function(){return"Versteek kaartaansig"},formatColumns:function(){return"Kolomme"},formatColumnsToggleAll:function(){return"Wys alles"},formatFullscreen:function(){return"Volskerm"},formatAllRows:function(){return"Alles"},formatAutoRefresh:function(){return"Verfris outomaties"},formatExport:function(){return"Voer data uit"},formatJumpTo:function(){return"Gaan na"},formatAdvancedSearch:function(){return"Gevorderde soektog"},formatAdvancedCloseButton:function(){return"Maak"},formatFilterControlSwitch:function(){return"Versteek/Wys kontroles"},formatFilterControlSwitchHide:function(){return"Versteek kontroles"},formatFilterControlSwitchShow:function(){return"Wys kontroles"},formatToggleCustomViewOn:function(){return"Wys pasgemaakte aansig"},formatToggleCustomViewOff:function(){return"Versteek pasgemaakte aansig"},formatClearFilters:function(){return"Verwyder filters"},formatAddLevel:function(){return"Voeg 'n vlak by"},formatCancel:function(){return"Kanselleer"},formatColumn:function(){return"Kolom"},formatDeleteLevel:function(){return"Vee 'n vlak uit"},formatDuplicateAlertTitle:function(){return"Duplikaatinskrywings is gevind!"},formatDuplicateAlertDescription:function(){return"Verwyder of wysig asseblief duplikaatinskrywings"},formatMultipleSort:function(){return"Multi-sorteer"},formatOrder:function(){return"Bestelling"},formatSort:function(){return"Rangskik"},formatSortBy:function(){return"Sorteer volgens"},formatSortOrders:function(){return{asc:"Stygende",desc:"Dalende"}},formatThenBy:function(){return"Dan deur"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["af-ZA"]),t.fn.bootstrapTable.locales["ca-ES"]=t.fn.bootstrapTable.locales.ca={formatCopyRows:function(){return"Copia resultats"},formatPrint:function(){return"Imprimeix"},formatLoadingMessage:function(){return"Espereu, si us plau"},formatRecordsPerPage:function(t){return"".concat(t," resultats per pàgina")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Mostrant resultats ".concat(t," fins ").concat(n," - ").concat(r," resultats (filtrats d'un total de ").concat(o," resultats)"):"Mostrant resultats ".concat(t," fins ").concat(n," - ").concat(r," resultats en total")},formatSRPaginationPreText:function(){return"Pàgina anterior"},formatSRPaginationPageText:function(t){return"A la pàgina ".concat(t)},formatSRPaginationNextText:function(){return"Pàgina següent"},formatDetailPagination:function(t){return"Mostrant ".concat(t," resultats")},formatClearSearch:function(){return"Neteja cerca"},formatSearch:function(){return"Cerca"},formatNoMatches:function(){return"No s'han trobat resultats"},formatPaginationSwitch:function(){return"Amaga/Mostra paginació"},formatPaginationSwitchDown:function(){return"Mostra paginació"},formatPaginationSwitchUp:function(){return"Amaga paginació"},formatRefresh:function(){return"Refresca"},formatToggleOn:function(){return"Mostra vista de tarjeta"},formatToggleOff:function(){return"Amaga vista de tarjeta"},formatColumns:function(){return"Columnes"},formatColumnsToggleAll:function(){return"Alterna totes"},formatFullscreen:function(){return"Pantalla completa"},formatAllRows:function(){return"Tots"},formatAutoRefresh:function(){return"Auto Refresca"},formatExport:function(){return"Exporta dades"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Cerca avançada"},formatAdvancedCloseButton:function(){return"Tanca"},formatFilterControlSwitch:function(){return"Mostra/Amaga controls"},formatFilterControlSwitchHide:function(){return"Mostra controls"},formatFilterControlSwitchShow:function(){return"Amaga controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ca-ES"]),t.fn.bootstrapTable.locales["ar-SA"]=t.fn.bootstrapTable.locales.ar={formatCopyRows:function(){return"نسخ الصفوف"},formatPrint:function(){return"طباعة"},formatLoadingMessage:function(){return"جارٍ التحميل، يرجى الانتظار..."},formatRecordsPerPage:function(t){return"".concat(t," صف لكل صفحة")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"الظاهر ".concat(t," إلى ").concat(n," من ").concat(r," سجل ").concat(o," إجمالي الصفوف)"):"الظاهر ".concat(t," إلى ").concat(n," من ").concat(r," سجل")},formatSRPaginationPreText:function(){return"الصفحة السابقة"},formatSRPaginationPageText:function(t){return"إلى الصفحة ".concat(t)},formatSRPaginationNextText:function(){return"الصفحة التالية"},formatDetailPagination:function(t){return"عرض ".concat(t," أعمدة")},formatClearSearch:function(){return"مسح مربع البحث"},formatSearch:function(){return"بحث"},formatNoMatches:function(){return"لا توجد نتائج مطابقة للبحث"},formatPaginationSwitch:function(){return"إخفاء/إظهار ترقيم الصفحات"},formatPaginationSwitchDown:function(){return"إظهار ترقيم الصفحات"},formatPaginationSwitchUp:function(){return"إخفاء ترقيم الصفحات"},formatRefresh:function(){return"تحديث"},formatToggleOn:function(){return"إظهار كبطاقات"},formatToggleOff:function(){return"إلغاء البطاقات"},formatColumns:function(){return"أعمدة"},formatColumnsToggleAll:function(){return"تبديل الكل"},formatFullscreen:function(){return"الشاشة كاملة"},formatAllRows:function(){return"الكل"},formatAutoRefresh:function(){return"تحديث تلقائي"},formatExport:function(){return"تصدير البيانات"},formatJumpTo:function(){return"قفز"},formatAdvancedSearch:function(){return"بحث متقدم"},formatAdvancedCloseButton:function(){return"إغلاق"},formatFilterControlSwitch:function(){return"عرض/إخفاء عناصر التصفية"},formatFilterControlSwitchHide:function(){return"إخفاء عناصر التصفية"},formatFilterControlSwitchShow:function(){return"عرض عناصر التصفية"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ar-SA"]),t.fn.bootstrapTable.locales["da-DK"]=t.fn.bootstrapTable.locales.da={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Indlæser, vent venligst"},formatRecordsPerPage:function(t){return"".concat(t," poster pr side")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Viser ".concat(t," til ").concat(n," af ").concat(r," række").concat(r>1?"r":""," (filtered from ").concat(o," total rows)"):"Viser ".concat(t," til ").concat(n," af ").concat(r," række").concat(r>1?"r":"")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Viser ".concat(t," række").concat(t>1?"r":"")},formatClearSearch:function(){return"Ryd filtre"},formatSearch:function(){return"Søg"},formatNoMatches:function(){return"Ingen poster fundet"},formatPaginationSwitch:function(){return"Skjul/vis nummerering"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Opdater"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Kolonner"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Alle"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Eksporter"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["da-DK"]),t.fn.bootstrapTable.locales["bg-BG"]=t.fn.bootstrapTable.locales.bg={formatCopyRows:function(){return"Копиране на редове"},formatPrint:function(){return"Печат"},formatLoadingMessage:function(){return"Зареждане, моля изчакайте"},formatRecordsPerPage:function(t){return"".concat(t," реда на страница")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Показани редове от ".concat(t," до ").concat(n," от ").concat(r," (филтрирани от общо ").concat(o,")"):"Показани редове от ".concat(t," до ").concat(n," от общо ").concat(r)},formatSRPaginationPreText:function(){return"предишна страница"},formatSRPaginationPageText:function(t){return"до страница ".concat(t)},formatSRPaginationNextText:function(){return"следваща страница"},formatDetailPagination:function(t){return"Показани ".concat(t," реда")},formatClearSearch:function(){return"Изчистване на търсенето"},formatSearch:function(){return"Търсене"},formatNoMatches:function(){return"Не са намерени съвпадащи записи"},formatPaginationSwitch:function(){return"Скриване/Показване на странициране"},formatPaginationSwitchDown:function(){return"Показване на странициране"},formatPaginationSwitchUp:function(){return"Скриване на странициране"},formatRefresh:function(){return"Обновяване"},formatToggleOn:function(){return"Показване на изглед карта"},formatToggleOff:function(){return"Скриване на изглед карта"},formatColumns:function(){return"Колони"},formatColumnsToggleAll:function(){return"Превключване на всички"},formatFullscreen:function(){return"Цял екран"},formatAllRows:function(){return"Всички"},formatAutoRefresh:function(){return"Автоматично обновяване"},formatExport:function(){return"Експорт на данни"},formatJumpTo:function(){return"ОТИДИ"},formatAdvancedSearch:function(){return"Разширено търсене"},formatAdvancedCloseButton:function(){return"Затваряне"},formatFilterControlSwitch:function(){return"Скрива/показва контроли"},formatFilterControlSwitchHide:function(){return"Скрива контроли"},formatFilterControlSwitchShow:function(){return"Показва контроли"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["bg-BG"]),t.fn.bootstrapTable.locales["de-DE"]=t.fn.bootstrapTable.locales.de={formatCopyRows:function(){return"Zeilen kopieren"},formatPrint:function(){return"Drucken"},formatLoadingMessage:function(){return"Lade, bitte warten"},formatRecordsPerPage:function(t){return"".concat(t," Zeilen pro Seite.")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Zeige Zeile ".concat(t," bis ").concat(n," von ").concat(r," Zeile").concat(r>1?"n":""," (Gefiltert von ").concat(o," Zeile").concat(o>1?"n":"",")"):"Zeige Zeile ".concat(t," bis ").concat(n," von ").concat(r," Zeile").concat(r>1?"n":"",".")},formatSRPaginationPreText:function(){return"Vorherige Seite"},formatSRPaginationPageText:function(t){return"Zu Seite ".concat(t)},formatSRPaginationNextText:function(){return"Nächste Seite"},formatDetailPagination:function(t){return"Zeige ".concat(t," Zeile").concat(t>1?"n":"",".")},formatClearSearch:function(){return"Lösche Filter"},formatSearch:function(){return"Suchen"},formatNoMatches:function(){return"Keine passenden Ergebnisse gefunden"},formatPaginationSwitch:function(){return"Verstecke/Zeige Nummerierung"},formatPaginationSwitchDown:function(){return"Zeige Nummerierung"},formatPaginationSwitchUp:function(){return"Verstecke Nummerierung"},formatRefresh:function(){return"Neu laden"},formatToggleOn:function(){return"Normale Ansicht"},formatToggleOff:function(){return"Kartenansicht"},formatColumns:function(){return"Spalten"},formatColumnsToggleAll:function(){return"Alle umschalten"},formatFullscreen:function(){return"Vollbild"},formatAllRows:function(){return"Alle"},formatAutoRefresh:function(){return"Automatisches Neuladen"},formatExport:function(){return"Datenexport"},formatJumpTo:function(){return"Springen"},formatAdvancedSearch:function(){return"Erweiterte Suche"},formatAdvancedCloseButton:function(){return"Schließen"},formatFilterControlSwitch:function(){return"Verstecke/Zeige Filter"},formatFilterControlSwitchHide:function(){return"Verstecke Filter"},formatFilterControlSwitchShow:function(){return"Zeige Filter"},formatAddLevel:function(){return"Ebene hinzufügen"},formatCancel:function(){return"Abbrechen"},formatColumn:function(){return"Spalte"},formatDeleteLevel:function(){return"Ebene entfernen"},formatDuplicateAlertTitle:function(){return"Doppelte Einträge gefunden!"},formatDuplicateAlertDescription:function(){return"Bitte doppelte Spalten entfenen oder ändern"},formatMultipleSort:function(){return"Mehrfachsortierung"},formatOrder:function(){return"Reihenfolge"},formatSort:function(){return"Sortieren"},formatSortBy:function(){return"Sortieren nach"},formatThenBy:function(){return"anschließend"},formatSortOrders:function(){return{asc:"Aufsteigend",desc:"Absteigend"}}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["de-DE"]),t.fn.bootstrapTable.locales["el-GR"]=t.fn.bootstrapTable.locales.el={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Φορτώνει, παρακαλώ περιμένετε"},formatRecordsPerPage:function(t){return"".concat(t," αποτελέσματα ανά σελίδα")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Εμφανίζονται από την ".concat(t," ως την ").concat(n," από σύνολο ").concat(r," σειρών (filtered from ").concat(o," total rows)"):"Εμφανίζονται από την ".concat(t," ως την ").concat(n," από σύνολο ").concat(r," σειρών")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Αναζητήστε"},formatNoMatches:function(){return"Δεν βρέθηκαν αποτελέσματα"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Refresh"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columns"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["el-GR"]),t.fn.bootstrapTable.locales["es-CR"]={formatCopyRows:function(){return"Copiar filas"},formatPrint:function(){return"Imprimir"},formatLoadingMessage:function(){return"Cargando, por favor espere"},formatRecordsPerPage:function(t){return"".concat(t," filas por página")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Mostrando ".concat(t," a ").concat(n," de ").concat(r," filas (filtrado de un total de ").concat(o," filas)"):"Mostrando ".concat(t," a ").concat(n," de ").concat(r," filas")},formatSRPaginationPreText:function(){return"página anterior"},formatSRPaginationPageText:function(t){return"ir a la página ".concat(t)},formatSRPaginationNextText:function(){return"página siguiente"},formatDetailPagination:function(t){return"Mostrando ".concat(t," filas")},formatClearSearch:function(){return"Limpiar búsqueda"},formatSearch:function(){return"Buscar"},formatNoMatches:function(){return"No se encontraron resultados"},formatPaginationSwitch:function(){return"Mostrar/ocultar paginación"},formatPaginationSwitchDown:function(){return"Mostrar paginación"},formatPaginationSwitchUp:function(){return"Ocultar paginación"},formatRefresh:function(){return"Actualizar"},formatToggleOn:function(){return"Mostrar vista en tarjetas"},formatToggleOff:function(){return"Ocultar vista en tarjetas"},formatColumns:function(){return"Columnas"},formatColumnsToggleAll:function(){return"Alternar todo"},formatFullscreen:function(){return"Pantalla completa"},formatAllRows:function(){return"Todas las filas"},formatAutoRefresh:function(){return"Actualización automática"},formatExport:function(){return"Exportar"},formatJumpTo:function(){return"Ver"},formatAdvancedSearch:function(){return"Búsqueda avanzada"},formatAdvancedCloseButton:function(){return"Cerrar"},formatFilterControlSwitch:function(){return"Mostrar/ocultar controles"},formatFilterControlSwitchHide:function(){return"Ocultar controles"},formatFilterControlSwitchShow:function(){return"Mostrar controles"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["es-CR"]),t.fn.bootstrapTable.locales["en-US"]=t.fn.bootstrapTable.locales.en={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Loading, please wait"},formatRecordsPerPage:function(t){return"".concat(t," rows per page")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Showing ".concat(t," to ").concat(n," of ").concat(r," rows (filtered from ").concat(o," total rows)"):"Showing ".concat(t," to ").concat(n," of ").concat(r," rows")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Search"},formatNoMatches:function(){return"No matching records found"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Refresh"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columns"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["en-US"]),t.fn.bootstrapTable.locales["es-CL"]={formatCopyRows:function(){return"Copiar Filas"},formatPrint:function(){return"Imprimir"},formatLoadingMessage:function(){return"Cargando, espere por favor"},formatRecordsPerPage:function(t){return"".concat(t," filas por página")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Mostrando ".concat(t," a ").concat(n," de ").concat(r," filas (filtrado de ").concat(o," filas totales)"):"Mostrando ".concat(t," a ").concat(n," de ").concat(r," filas")},formatSRPaginationPreText:function(){return"página anterior"},formatSRPaginationPageText:function(t){return"a la página ".concat(t)},formatSRPaginationNextText:function(){return"siguiente página"},formatDetailPagination:function(t){return"Mostrando ".concat(t," filas")},formatClearSearch:function(){return"Limpiar búsqueda"},formatSearch:function(){return"Buscar"},formatNoMatches:function(){return"No se encontraron registros"},formatPaginationSwitch:function(){return"Ocultar/Mostrar paginación"},formatPaginationSwitchDown:function(){return"Mostrar paginación"},formatPaginationSwitchUp:function(){return"Ocultar paginación"},formatRefresh:function(){return"Refrescar"},formatToggleOn:function(){return"Mostrar vista de carta"},formatToggleOff:function(){return"Ocultar vista de carta"},formatColumns:function(){return"Columnas"},formatColumnsToggleAll:function(){return"Cambiar todo"},formatFullscreen:function(){return"Pantalla completa"},formatAllRows:function(){return"Todo"},formatAutoRefresh:function(){return"Auto Recargar"},formatExport:function(){return"Exportar datos"},formatJumpTo:function(){return"IR"},formatAdvancedSearch:function(){return"Búsqueda avanzada"},formatAdvancedCloseButton:function(){return"Cerrar"},formatFilterControlSwitch:function(){return"Ocultar/Mostrar controles"},formatFilterControlSwitchHide:function(){return"Ocultar controles"},formatFilterControlSwitchShow:function(){return"Mostrar controles"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["es-CL"]),t.fn.bootstrapTable.locales["es-ES"]=t.fn.bootstrapTable.locales.es={formatCopyRows:function(){return"Copiar filas"},formatPrint:function(){return"Imprimir"},formatLoadingMessage:function(){return"Cargando, por favor espere"},formatRecordsPerPage:function(t){return"".concat(t," resultados por página")},formatShowingRows:function(t,n,r,o){var e=r>1?"s":"";return void 0!==o&&o>0&&o>r?"Mostrando desde ".concat(t," hasta ").concat(n," - En total ").concat(r," resultado").concat(e," (filtrado de un total de ").concat(o," fila").concat(e,")"):"Mostrando desde ".concat(t," hasta ").concat(n," - En total ").concat(r," resultado").concat(e)},formatSRPaginationPreText:function(){return"página anterior"},formatSRPaginationPageText:function(t){return"a la página ".concat(t)},formatSRPaginationNextText:function(){return"siguiente página"},formatDetailPagination:function(t){return"Mostrando ".concat(t," fila").concat(t>1?"s":"")},formatClearSearch:function(){return"Limpiar búsqueda"},formatSearch:function(){return"Buscar"},formatNoMatches:function(){return"No se encontraron resultados coincidentes"},formatPaginationSwitch:function(){return"Ocultar/Mostrar paginación"},formatPaginationSwitchDown:function(){return"Mostrar paginación"},formatPaginationSwitchUp:function(){return"Ocultar paginación"},formatRefresh:function(){return"Recargar"},formatToggleOn:function(){return"Mostrar vista de carta"},formatToggleOff:function(){return"Ocultar vista de carta"},formatColumns:function(){return"Columnas"},formatColumnsToggleAll:function(){return"Cambiar todo"},formatFullscreen:function(){return"Pantalla completa"},formatAllRows:function(){return"Todos"},formatAutoRefresh:function(){return"Auto Recargar"},formatExport:function(){return"Exportar los datos"},formatJumpTo:function(){return"IR"},formatAdvancedSearch:function(){return"Búsqueda avanzada"},formatAdvancedCloseButton:function(){return"Cerrar"},formatFilterControlSwitch:function(){return"Ocultar/Exibir controles"},formatFilterControlSwitchHide:function(){return"Ocultar controles"},formatFilterControlSwitchShow:function(){return"Mostrar controles"},formatAddLevel:function(){return"Agregar nivel"},formatCancel:function(){return"Cancelar"},formatColumn:function(){return"Columna"},formatDeleteLevel:function(){return"Eliminar nivel"},formatDuplicateAlertTitle:function(){return"¡Se encontraron entradas duplicadas!"},formatDuplicateAlertDescription:function(){return"Por favor, elimine o modifique las columnas duplicadas"},formatMultipleSort:function(){return"Ordenación múltiple"},formatOrder:function(){return"Orden"},formatSort:function(){return"Ordenar"},formatSortBy:function(){return"Ordenar por"},formatThenBy:function(){return"a continuación"},formatSortOrders:function(){return{asc:"Ascendente",desc:"Descendente"}}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["es-ES"]),t.fn.bootstrapTable.locales["es-MX"]={formatCopyRows:function(){return"Copiar Filas"},formatPrint:function(){return"Imprimir"},formatLoadingMessage:function(){return"Cargando, espere por favor"},formatRecordsPerPage:function(t){return"".concat(t," resultados por página")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Mostrando ".concat(t," a ").concat(n," de ").concat(r," filas (filtrado de ").concat(o," filas totales)"):"Mostrando ".concat(t," a ").concat(n," de ").concat(r," filas")},formatSRPaginationPreText:function(){return"página anterior"},formatSRPaginationPageText:function(t){return"ir a la página ".concat(t)},formatSRPaginationNextText:function(){return"página siguiente"},formatDetailPagination:function(t){return"Mostrando ".concat(t," filas")},formatClearSearch:function(){return"Limpiar búsqueda"},formatSearch:function(){return"Buscar"},formatNoMatches:function(){return"No se encontraron registros que coincidan"},formatPaginationSwitch:function(){return"Mostrar/ocultar paginación"},formatPaginationSwitchDown:function(){return"Mostrar paginación"},formatPaginationSwitchUp:function(){return"Ocultar paginación"},formatRefresh:function(){return"Actualizar"},formatToggleOn:function(){return"Mostrar vista"},formatToggleOff:function(){return"Ocultar vista"},formatColumns:function(){return"Columnas"},formatColumnsToggleAll:function(){return"Alternar todo"},formatFullscreen:function(){return"Pantalla completa"},formatAllRows:function(){return"Todo"},formatAutoRefresh:function(){return"Auto actualizar"},formatExport:function(){return"Exportar datos"},formatJumpTo:function(){return"IR"},formatAdvancedSearch:function(){return"Búsqueda avanzada"},formatAdvancedCloseButton:function(){return"Cerrar"},formatFilterControlSwitch:function(){return"Ocultar/Mostrar controles"},formatFilterControlSwitchHide:function(){return"Ocultar controles"},formatFilterControlSwitchShow:function(){return"Mostrar controles"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["es-MX"]),t.fn.bootstrapTable.locales["es-NI"]={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Cargando, por favor espere"},formatRecordsPerPage:function(t){return"".concat(t," registros por página")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Mostrando de ".concat(t," a ").concat(n," registros de ").concat(r," registros en total (filtered from ").concat(o," total rows)"):"Mostrando de ".concat(t," a ").concat(n," registros de ").concat(r," registros en total")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Limpiar búsqueda"},formatSearch:function(){return"Buscar"},formatNoMatches:function(){return"No se encontraron registros"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Refrescar"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columnas"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Todo"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Ocultar/Mostrar controles"},formatFilterControlSwitchHide:function(){return"Ocultar controles"},formatFilterControlSwitchShow:function(){return"Mostrar controles"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["es-NI"]),t.fn.bootstrapTable.locales["es-SP"]={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Cargando, por favor espera"},formatRecordsPerPage:function(t){return"".concat(t," registros por página.")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"".concat(t," - ").concat(n," de ").concat(r," registros (filtered from ").concat(o," total rows)"):"".concat(t," - ").concat(n," de ").concat(r," registros.")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Limpiar búsqueda"},formatSearch:function(){return"Buscar"},formatNoMatches:function(){return"No se han encontrado registros."},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Actualizar"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columnas"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Todo"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Ocultar/Mostrar controles"},formatFilterControlSwitchHide:function(){return"Ocultar controles"},formatFilterControlSwitchShow:function(){return"Mostrar controles"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["es-SP"]),t.fn.bootstrapTable.locales["eu-EU"]=t.fn.bootstrapTable.locales.eu={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Itxaron mesedez"},formatRecordsPerPage:function(t){return"".concat(t," emaitza orriko.")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"".concat(r," erregistroetatik ").concat(t,"etik ").concat(n,"erakoak erakusten (filtered from ").concat(o," total rows)"):"".concat(r," erregistroetatik ").concat(t,"etik ").concat(n,"erakoak erakusten.")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Bilatu"},formatNoMatches:function(){return"Ez da emaitzarik aurkitu"},formatPaginationSwitch:function(){return"Ezkutatu/Erakutsi orrikatzea"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Eguneratu"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Zutabeak"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Guztiak"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["eu-EU"]),t.fn.bootstrapTable.locales["cs-CZ"]=t.fn.bootstrapTable.locales.cs={formatCopyRows:function(){return"Kopírovat řádky"},formatPrint:function(){return"Tisk"},formatLoadingMessage:function(){return"Čekejte, prosím"},formatRecordsPerPage:function(t){return"".concat(t," položek na stránku")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Zobrazena ".concat(t,". - ").concat(n," . položka z celkových ").concat(r," (filtered from ").concat(o," total rows)"):"Zobrazena ".concat(t,". - ").concat(n," . položka z celkových ").concat(r)},formatSRPaginationPreText:function(){return"předchozí strana"},formatSRPaginationPageText:function(t){return"na stranu ".concat(t)},formatSRPaginationNextText:function(){return"další strana"},formatDetailPagination:function(t){return"Zobrazuji ".concat(t," řádek")},formatClearSearch:function(){return"Smazat hledání"},formatSearch:function(){return"Vyhledávání"},formatNoMatches:function(){return"Nenalezena žádná vyhovující položka"},formatPaginationSwitch:function(){return"Skrýt/Zobrazit stránkování"},formatPaginationSwitchDown:function(){return"Zobrazit stránkování"},formatPaginationSwitchUp:function(){return"Skrýt stránkování"},formatRefresh:function(){return"Aktualizovat"},formatToggleOn:function(){return"Zobrazit karty"},formatToggleOff:function(){return"Zobrazit tabulku"},formatColumns:function(){return"Sloupce"},formatColumnsToggleAll:function(){return"Zobrazit/Skrýt vše"},formatFullscreen:function(){return"Zapnout/Vypnout fullscreen"},formatAllRows:function(){return"Vše"},formatAutoRefresh:function(){return"Automatické obnovení"},formatExport:function(){return"Export dat"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Pokročilé hledání"},formatAdvancedCloseButton:function(){return"Zavřít"},formatFilterControlSwitch:function(){return"Skrýt/Zobrazit ovladače"},formatFilterControlSwitchHide:function(){return"Skrýt ovladače"},formatFilterControlSwitchShow:function(){return"Zobrazit ovladače"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["cs-CZ"]),t.fn.bootstrapTable.locales["et-EE"]=t.fn.bootstrapTable.locales.et={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Päring käib, palun oota"},formatRecordsPerPage:function(t){return"".concat(t," rida lehe kohta")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Näitan tulemusi ".concat(t," kuni ").concat(n," - kokku ").concat(r," tulemust (filtered from ").concat(o," total rows)"):"Näitan tulemusi ".concat(t," kuni ").concat(n," - kokku ").concat(r," tulemust")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Otsi"},formatNoMatches:function(){return"Päringu tingimustele ei vastanud ühtegi tulemust"},formatPaginationSwitch:function(){return"Näita/Peida lehtedeks jagamine"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Värskenda"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Veerud"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Kõik"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["et-EE"]),t.fn.bootstrapTable.locales["fi-FI"]=t.fn.bootstrapTable.locales.fi={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Ladataan, ole hyvä ja odota"},formatRecordsPerPage:function(t){return"".concat(t," riviä sivulla")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Näytetään rivit ".concat(t," - ").concat(n," / ").concat(r," (filtered from ").concat(o," total rows)"):"Näytetään rivit ".concat(t," - ").concat(n," / ").concat(r)},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Poista suodattimet"},formatSearch:function(){return"Hae"},formatNoMatches:function(){return"Hakuehtoja vastaavia tuloksia ei löytynyt"},formatPaginationSwitch:function(){return"Näytä/Piilota sivutus"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Päivitä"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Sarakkeet"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Kaikki"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Vie tiedot"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["fi-FI"]),t.fn.bootstrapTable.locales["fa-IR"]=t.fn.bootstrapTable.locales.fa={formatCopyRows:function(){return"کپی ردیف ها"},formatPrint:function(){return"پرینت"},formatLoadingMessage:function(){return"در حال بارگذاری, لطفا صبر کنید"},formatRecordsPerPage:function(t){return"".concat(t," رکورد در صفحه")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"نمایش ".concat(t," تا ").concat(n," از ").concat(r," ردیف (filtered from ").concat(o," total rows)"):"نمایش ".concat(t," تا ").concat(n," از ").concat(r," ردیف")},formatSRPaginationPreText:function(){return"صفحه قبلی"},formatSRPaginationPageText:function(t){return"به صفحه ".concat(t)},formatSRPaginationNextText:function(){return"صفحه بعدی"},formatDetailPagination:function(t){return"نمایش ".concat(t," سطرها")},formatClearSearch:function(){return"پاک کردن جستجو"},formatSearch:function(){return"جستجو"},formatNoMatches:function(){return"رکوردی یافت نشد."},formatPaginationSwitch:function(){return"نمایش/مخفی صفحه بندی"},formatPaginationSwitchDown:function(){return"نمایش صفحه بندی"},formatPaginationSwitchUp:function(){return"پنهان کردن صفحه بندی"},formatRefresh:function(){return"به روز رسانی"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"سطر ها"},formatColumnsToggleAll:function(){return"تغییر وضعیت همه"},formatFullscreen:function(){return"تمام صفحه"},formatAllRows:function(){return"همه"},formatAutoRefresh:function(){return"رفرش اتوماتیک"},formatExport:function(){return"خروجی دیتا"},formatJumpTo:function(){return"برو"},formatAdvancedSearch:function(){return"جستجوی پیشرفته"},formatAdvancedCloseButton:function(){return"بستن"},formatFilterControlSwitch:function(){return"پنهان/نمایش دادن کنترل ها"},formatFilterControlSwitchHide:function(){return"پنهان کردن کنترل ها"},formatFilterControlSwitchShow:function(){return"نمایش کنترل ها"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["fa-IR"]),t.fn.bootstrapTable.locales["fr-BE"]={formatCopyRows:function(){return"Copier les lignes"},formatPrint:function(){return"Imprimer"},formatLoadingMessage:function(){return"Chargement en cours"},formatRecordsPerPage:function(t){return"".concat(t," lignes par page")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Affichage de ".concat(t," à ").concat(n," sur ").concat(r," lignes (filtrées à partir de ").concat(o," lignes)"):"Affichage de ".concat(t," à ").concat(n," sur ").concat(r," lignes")},formatSRPaginationPreText:function(){return"page précédente"},formatSRPaginationPageText:function(t){return"vers la page ".concat(t)},formatSRPaginationNextText:function(){return"page suivante"},formatDetailPagination:function(t){return"Affichage de ".concat(t," lignes")},formatClearSearch:function(){return"Effacer la recherche"},formatSearch:function(){return"Rechercher"},formatNoMatches:function(){return"Aucun résultat"},formatPaginationSwitch:function(){return"Masquer/Afficher la pagination"},formatPaginationSwitchDown:function(){return"Afficher la pagination"},formatPaginationSwitchUp:function(){return"Masquer la pagination"},formatRefresh:function(){return"Actualiser"},formatToggleOn:function(){return"Afficher la vue en cartes"},formatToggleOff:function(){return"Cacher la vue en cartes"},formatColumns:function(){return"Colonnes"},formatColumnsToggleAll:function(){return"Tout afficher"},formatFullscreen:function(){return"Plein écran"},formatAllRows:function(){return"Tout"},formatAutoRefresh:function(){return"Actualiser automatiquement"},formatExport:function(){return"Exporter"},formatJumpTo:function(){return"Aller à"},formatAdvancedSearch:function(){return"Recherche avancée"},formatAdvancedCloseButton:function(){return"Fermer"},formatFilterControlSwitch:function(){return"Masquer/Afficher les contrôles"},formatFilterControlSwitchHide:function(){return"Masquer les contrôles"},formatFilterControlSwitchShow:function(){return"Afficher les contrôles"},formatToggleCustomViewOn:function(){return"Afficher la vue personnalisée"},formatToggleCustomViewOff:function(){return"Cacher la vue personnalisée"},formatClearFilters:function(){return"Retirer les filtres"},formatAddLevel:function(){return"Ajouter un niveau"},formatCancel:function(){return"Annuler"},formatColumn:function(){return"Colonne"},formatDeleteLevel:function(){return"Supprimer un niveau"},formatDuplicateAlertTitle:function(){return"Des entrées en double ont été trouvées !"},formatDuplicateAlertDescription:function(){return"Veuillez supprimer ou modifier les entrées en double"},formatMultipleSort:function(){return"Tri multiple"},formatOrder:function(){return"Ordre"},formatSort:function(){return"Trier"},formatSortBy:function(){return"Trier par"},formatSortOrders:function(){return{asc:"Ascendant",desc:"Descendant"}},formatThenBy:function(){return"Puis par"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["fr-BE"]),t.fn.bootstrapTable.locales["fr-CH"]={formatCopyRows:function(){return"Copier les lignes"},formatPrint:function(){return"Imprimer"},formatLoadingMessage:function(){return"Chargement en cours"},formatRecordsPerPage:function(t){return"".concat(t," lignes par page")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Affichage de ".concat(t," à ").concat(n," sur ").concat(r," lignes (filtrées à partir de ").concat(o," lignes)"):"Affichage de ".concat(t," à ").concat(n," sur ").concat(r," lignes")},formatSRPaginationPreText:function(){return"page précédente"},formatSRPaginationPageText:function(t){return"vers la page ".concat(t)},formatSRPaginationNextText:function(){return"page suivante"},formatDetailPagination:function(t){return"Affichage de ".concat(t," lignes")},formatClearSearch:function(){return"Effacer la recherche"},formatSearch:function(){return"Rechercher"},formatNoMatches:function(){return"Aucun résultat"},formatPaginationSwitch:function(){return"Masquer/Afficher la pagination"},formatPaginationSwitchDown:function(){return"Afficher la pagination"},formatPaginationSwitchUp:function(){return"Masquer la pagination"},formatRefresh:function(){return"Actualiser"},formatToggleOn:function(){return"Afficher la vue en cartes"},formatToggleOff:function(){return"Cacher la vue en cartes"},formatColumns:function(){return"Colonnes"},formatColumnsToggleAll:function(){return"Tout afficher"},formatFullscreen:function(){return"Plein écran"},formatAllRows:function(){return"Tout"},formatAutoRefresh:function(){return"Actualiser automatiquement"},formatExport:function(){return"Exporter"},formatJumpTo:function(){return"Aller à"},formatAdvancedSearch:function(){return"Recherche avancée"},formatAdvancedCloseButton:function(){return"Fermer"},formatFilterControlSwitch:function(){return"Masquer/Afficher les contrôles"},formatFilterControlSwitchHide:function(){return"Masquer les contrôles"},formatFilterControlSwitchShow:function(){return"Afficher les contrôles"},formatToggleCustomViewOn:function(){return"Afficher la vue personnalisée"},formatToggleCustomViewOff:function(){return"Cacher la vue personnalisée"},formatClearFilters:function(){return"Retirer les filtres"},formatAddLevel:function(){return"Ajouter un niveau"},formatCancel:function(){return"Annuler"},formatColumn:function(){return"Colonne"},formatDeleteLevel:function(){return"Supprimer un niveau"},formatDuplicateAlertTitle:function(){return"Des entrées en double ont été trouvées !"},formatDuplicateAlertDescription:function(){return"Veuillez supprimer ou modifier les entrées en double"},formatMultipleSort:function(){return"Tri multiple"},formatOrder:function(){return"Ordre"},formatSort:function(){return"Trier"},formatSortBy:function(){return"Trier par"},formatSortOrders:function(){return{asc:"Ascendant",desc:"Descendant"}},formatThenBy:function(){return"Puis par"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["fr-CH"]),t.fn.bootstrapTable.locales["fr-FR"]=t.fn.bootstrapTable.locales.fr={formatCopyRows:function(){return"Copier les lignes"},formatPrint:function(){return"Imprimer"},formatLoadingMessage:function(){return"Chargement en cours"},formatRecordsPerPage:function(t){return"".concat(t," lignes par page")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Affichage de ".concat(t," à ").concat(n," sur ").concat(r," lignes (filtrées à partir de ").concat(o," lignes)"):"Affichage de ".concat(t," à ").concat(n," sur ").concat(r," lignes")},formatSRPaginationPreText:function(){return"page précédente"},formatSRPaginationPageText:function(t){return"vers la page ".concat(t)},formatSRPaginationNextText:function(){return"page suivante"},formatDetailPagination:function(t){return"Affichage de ".concat(t," lignes")},formatClearSearch:function(){return"Effacer la recherche"},formatSearch:function(){return"Rechercher"},formatNoMatches:function(){return"Aucun résultat"},formatPaginationSwitch:function(){return"Masquer/Afficher la pagination"},formatPaginationSwitchDown:function(){return"Afficher la pagination"},formatPaginationSwitchUp:function(){return"Masquer la pagination"},formatRefresh:function(){return"Actualiser"},formatToggleOn:function(){return"Afficher la vue en cartes"},formatToggleOff:function(){return"Cacher la vue en cartes"},formatColumns:function(){return"Colonnes"},formatColumnsToggleAll:function(){return"Tout afficher"},formatFullscreen:function(){return"Plein écran"},formatAllRows:function(){return"Tout"},formatAutoRefresh:function(){return"Actualiser automatiquement"},formatExport:function(){return"Exporter"},formatJumpTo:function(){return"Aller à"},formatAdvancedSearch:function(){return"Recherche avancée"},formatAdvancedCloseButton:function(){return"Fermer"},formatFilterControlSwitch:function(){return"Masquer/Afficher les contrôles"},formatFilterControlSwitchHide:function(){return"Masquer les contrôles"},formatFilterControlSwitchShow:function(){return"Afficher les contrôles"},formatToggleCustomViewOn:function(){return"Afficher la vue personnalisée"},formatToggleCustomViewOff:function(){return"Cacher la vue personnalisée"},formatClearFilters:function(){return"Retirer les filtres"},formatAddLevel:function(){return"Ajouter un niveau"},formatCancel:function(){return"Annuler"},formatColumn:function(){return"Colonne"},formatDeleteLevel:function(){return"Supprimer un niveau"},formatDuplicateAlertTitle:function(){return"Des entrées en double ont été trouvées !"},formatDuplicateAlertDescription:function(){return"Veuillez supprimer ou modifier les entrées en double"},formatMultipleSort:function(){return"Tri multiple"},formatOrder:function(){return"Ordre"},formatSort:function(){return"Trier"},formatSortBy:function(){return"Trier par"},formatSortOrders:function(){return{asc:"Ascendant",desc:"Descendant"}},formatThenBy:function(){return"Puis par"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["fr-FR"]),t.fn.bootstrapTable.locales["he-IL"]=t.fn.bootstrapTable.locales.he={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"טוען, נא להמתין"},formatRecordsPerPage:function(t){return"".concat(t," שורות בעמוד")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"מציג ".concat(t," עד ").concat(n," מ-").concat(r,"שורות").concat(o," total rows)"):"מציג ".concat(t," עד ").concat(n," מ-").concat(r," שורות")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"חיפוש"},formatNoMatches:function(){return"לא נמצאו רשומות תואמות"},formatPaginationSwitch:function(){return"הסתר/הצג מספור דפים"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"רענן"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"עמודות"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"הכל"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["he-IL"]),t.fn.bootstrapTable.locales["hi-IN"]={formatCopyRows:function(){return"पंक्तियों की कॉपी करें"},formatPrint:function(){return"प्रिंट"},formatLoadingMessage:function(){return"लोड हो रहा है कृपया प्रतीक्षा करें"},formatRecordsPerPage:function(t){return"".concat(t," प्रति पृष्ठ पंक्तियाँ")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"".concat(t," - ").concat(n," पक्तिया ").concat(r," में से ( ").concat(o," पक्तिया)"):"".concat(t," - ").concat(n," पक्तिया ").concat(r," में से")},formatSRPaginationPreText:function(){return"पिछला पृष्ठ"},formatSRPaginationPageText:function(t){return"".concat(t," पृष्ठ पर")},formatSRPaginationNextText:function(){return"अगला पृष्ठ"},formatDetailPagination:function(t){return"".concat(t," पंक्तियां")},formatClearSearch:function(){return"सर्च क्लिअर करें"},formatSearch:function(){return"सर्च"},formatNoMatches:function(){return"मेल खाते रिकॉर्ड नही मिले"},formatPaginationSwitch:function(){return"छुपाओ/दिखाओ पृष्ठ संख्या"},formatPaginationSwitchDown:function(){return"दिखाओ पृष्ठ संख्या"},formatPaginationSwitchUp:function(){return"छुपाओ पृष्ठ संख्या"},formatRefresh:function(){return"रिफ्रेश"},formatToggleOn:function(){return"कार्ड दृश्य दिखाएं"},formatToggleOff:function(){return"कार्ड दृश्य छुपाएं"},formatColumns:function(){return"कॉलम"},formatColumnsToggleAll:function(){return"टॉगल आल"},formatFullscreen:function(){return"पूर्ण स्क्रीन"},formatAllRows:function(){return"सब"},formatAutoRefresh:function(){return"ऑटो रिफ्रेश"},formatExport:function(){return"एक्सपोर्ट डाटा"},formatJumpTo:function(){return"जाओ"},formatAdvancedSearch:function(){return"एडवांस सर्च"},formatAdvancedCloseButton:function(){return"बंद करे"},formatFilterControlSwitch:function(){return"छुपाओ/दिखाओ कंट्रोल्स"},formatFilterControlSwitchHide:function(){return"छुपाओ कंट्रोल्स"},formatFilterControlSwitchShow:function(){return"दिखाओ कंट्रोल्स"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["hi-IN"]),t.fn.bootstrapTable.locales["hr-HR"]=t.fn.bootstrapTable.locales.hr={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Molimo pričekajte"},formatRecordsPerPage:function(t){return"".concat(t," broj zapisa po stranici")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Prikazujem ".concat(t,". - ").concat(n,". od ukupnog broja zapisa ").concat(r," (filtered from ").concat(o," total rows)"):"Prikazujem ".concat(t,". - ").concat(n,". od ukupnog broja zapisa ").concat(r)},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Pretraži"},formatNoMatches:function(){return"Nije pronađen niti jedan zapis"},formatPaginationSwitch:function(){return"Prikaži/sakrij stranice"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Osvježi"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Kolone"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Sve"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["hr-HR"]),t.fn.bootstrapTable.locales["hu-HU"]=t.fn.bootstrapTable.locales.hu={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Betöltés, kérem várjon"},formatRecordsPerPage:function(t){return"".concat(t," rekord per oldal")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Megjelenítve ".concat(t," - ").concat(n," / ").concat(r," összesen (filtered from ").concat(o," total rows)"):"Megjelenítve ".concat(t," - ").concat(n," / ").concat(r," összesen")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Keresés"},formatNoMatches:function(){return"Nincs találat"},formatPaginationSwitch:function(){return"Lapozó elrejtése/megjelenítése"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Frissítés"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Oszlopok"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Összes"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["hu-HU"]),t.fn.bootstrapTable.locales["it-IT"]=t.fn.bootstrapTable.locales.it={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Caricamento in corso"},formatRecordsPerPage:function(t){return"".concat(t," elementi per pagina")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Visualizzazione da ".concat(t," a ").concat(n," di ").concat(r," elementi (filtrati da ").concat(o," elementi totali)"):"Visualizzazione da ".concat(t," a ").concat(n," di ").concat(r," elementi")},formatSRPaginationPreText:function(){return"pagina precedente"},formatSRPaginationPageText:function(t){return"alla pagina ".concat(t)},formatSRPaginationNextText:function(){return"pagina successiva"},formatDetailPagination:function(t){return"Mostrando ".concat(t," elementi")},formatClearSearch:function(){return"Pulisci filtri"},formatSearch:function(){return"Cerca"},formatNoMatches:function(){return"Nessun elemento trovato"},formatPaginationSwitch:function(){return"Nascondi/Mostra paginazione"},formatPaginationSwitchDown:function(){return"Mostra paginazione"},formatPaginationSwitchUp:function(){return"Nascondi paginazione"},formatRefresh:function(){return"Aggiorna"},formatToggleOn:function(){return"Mostra visuale a scheda"},formatToggleOff:function(){return"Nascondi visuale a scheda"},formatColumns:function(){return"Colonne"},formatColumnsToggleAll:function(){return"Mostra tutte"},formatFullscreen:function(){return"Schermo intero"},formatAllRows:function(){return"Tutto"},formatAutoRefresh:function(){return"Auto Aggiornamento"},formatExport:function(){return"Esporta dati"},formatJumpTo:function(){return"VAI"},formatAdvancedSearch:function(){return"Filtri avanzati"},formatAdvancedCloseButton:function(){return"Chiudi"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["it-IT"]),t.fn.bootstrapTable.locales["fr-LU"]={formatCopyRows:function(){return"Copier les lignes"},formatPrint:function(){return"Imprimer"},formatLoadingMessage:function(){return"Chargement en cours"},formatRecordsPerPage:function(t){return"".concat(t," lignes par page")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Affichage de ".concat(t," à ").concat(n," sur ").concat(r," lignes (filtrées à partir de ").concat(o," lignes)"):"Affichage de ".concat(t," à ").concat(n," sur ").concat(r," lignes")},formatSRPaginationPreText:function(){return"page précédente"},formatSRPaginationPageText:function(t){return"vers la page ".concat(t)},formatSRPaginationNextText:function(){return"page suivante"},formatDetailPagination:function(t){return"Affichage de ".concat(t," lignes")},formatClearSearch:function(){return"Effacer la recherche"},formatSearch:function(){return"Rechercher"},formatNoMatches:function(){return"Aucun résultat"},formatPaginationSwitch:function(){return"Masquer/Afficher la pagination"},formatPaginationSwitchDown:function(){return"Afficher la pagination"},formatPaginationSwitchUp:function(){return"Masquer la pagination"},formatRefresh:function(){return"Actualiser"},formatToggleOn:function(){return"Afficher la vue en cartes"},formatToggleOff:function(){return"Cacher la vue en cartes"},formatColumns:function(){return"Colonnes"},formatColumnsToggleAll:function(){return"Tout afficher"},formatFullscreen:function(){return"Plein écran"},formatAllRows:function(){return"Tout"},formatAutoRefresh:function(){return"Actualiser automatiquement"},formatExport:function(){return"Exporter"},formatJumpTo:function(){return"Aller à"},formatAdvancedSearch:function(){return"Recherche avancée"},formatAdvancedCloseButton:function(){return"Fermer"},formatFilterControlSwitch:function(){return"Masquer/Afficher les contrôles"},formatFilterControlSwitchHide:function(){return"Masquer les contrôles"},formatFilterControlSwitchShow:function(){return"Afficher les contrôles"},formatToggleCustomViewOn:function(){return"Afficher la vue personnalisée"},formatToggleCustomViewOff:function(){return"Cacher la vue personnalisée"},formatClearFilters:function(){return"Retirer les filtres"},formatAddLevel:function(){return"Ajouter un niveau"},formatCancel:function(){return"Annuler"},formatColumn:function(){return"Colonne"},formatDeleteLevel:function(){return"Supprimer un niveau"},formatDuplicateAlertTitle:function(){return"Des entrées en double ont été trouvées !"},formatDuplicateAlertDescription:function(){return"Veuillez supprimer ou modifier les entrées en double"},formatMultipleSort:function(){return"Tri multiple"},formatOrder:function(){return"Ordre"},formatSort:function(){return"Trier"},formatSortBy:function(){return"Trier par"},formatSortOrders:function(){return{asc:"Ascendant",desc:"Descendant"}},formatThenBy:function(){return"Puis par"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["fr-LU"]),t.fn.bootstrapTable.locales["id-ID"]=t.fn.bootstrapTable.locales.id={formatCopyRows:function(){return"Salin baris"},formatPrint:function(){return"Mencetak"},formatLoadingMessage:function(){return"Pemuatan sedang berlangsung"},formatRecordsPerPage:function(t){return"".concat(t," baris per halaman")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Menampilkan dari ".concat(t," hingga ").concat(n," pada ").concat(r," baris (difilter dari ").concat(o," baris)"):"Menampilkan dari ".concat(t," hingga ").concat(n," pada ").concat(r," baris")},formatSRPaginationPreText:function(){return"halaman sebelumnya"},formatSRPaginationPageText:function(t){return"ke halaman ".concat(t)},formatSRPaginationNextText:function(){return"halaman berikutnya"},formatDetailPagination:function(t){return"Tampilan ".concat(t," baris")},formatClearSearch:function(){return"Menghapus pencarian"},formatSearch:function(){return"Pencarian"},formatNoMatches:function(){return"Tidak ada hasil"},formatPaginationSwitch:function(){return"Sembunyikan/Tampilkan penomoran halaman"},formatPaginationSwitchDown:function(){return"Tampilkan penomoran halaman"},formatPaginationSwitchUp:function(){return"Sembunyikan penomoran halaman"},formatRefresh:function(){return"Segarkan"},formatToggleOn:function(){return"Menampilkan tampilan peta"},formatToggleOff:function(){return"Menyembunyikan tampilan peta"},formatColumns:function(){return"Kolom"},formatColumnsToggleAll:function(){return"Tampilkan semua"},formatFullscreen:function(){return"Layar penuh"},formatAllRows:function(){return"Semua"},formatAutoRefresh:function(){return"Penyegaran otomatis"},formatExport:function(){return"Mengekspor data"},formatJumpTo:function(){return"Pergi ke"},formatAdvancedSearch:function(){return"Pencarian lanjutan"},formatAdvancedCloseButton:function(){return"Tutup"},formatFilterControlSwitch:function(){return"Menyembunyikan/Menampilkan kontrol"},formatFilterControlSwitchHide:function(){return"Menyembunyikan kontrol"},formatFilterControlSwitchShow:function(){return"Menampilkan kontrol"},formatToggleCustomViewOn:function(){return"Menampilkan tampilan khusus"},formatToggleCustomViewOff:function(){return"Menyembunyikan tampilan khusus"},formatClearFilters:function(){return"Menghapus filter"},formatAddLevel:function(){return"Menambahkan level"},formatCancel:function(){return"Batal"},formatColumn:function(){return"Kolom"},formatDeleteLevel:function(){return"Menghapus level"},formatDuplicateAlertTitle:function(){return"Entri duplikat telah ditemukan!"},formatDuplicateAlertDescription:function(){return"Harap hapus atau ubah entri duplikat"},formatMultipleSort:function(){return"Penyortiran ganda"},formatOrder:function(){return"Urutan"},formatSort:function(){return"Penyortiran"},formatSortBy:function(){return"Urutkan berdasarkan"},formatSortOrders:function(){return{asc:"Menaik",desc:"Menurun"}},formatThenBy:function(){return"Kemudian oleh"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["id-ID"]),t.fn.bootstrapTable.locales["ja-JP"]=t.fn.bootstrapTable.locales.ja={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"読み込み中です。少々お待ちください。"},formatRecordsPerPage:function(t){return"ページ当たり最大".concat(t,"件")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"全".concat(r,"件から、").concat(t,"から").concat(n,"件目まで表示しています (filtered from ").concat(o," total rows)"):"全".concat(r,"件から、").concat(t,"から").concat(n,"件目まで表示しています")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"検索"},formatNoMatches:function(){return"該当するレコードが見つかりません"},formatPaginationSwitch:function(){return"ページ数を表示・非表示"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"更新"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"列"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"すべて"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ja-JP"]),t.fn.bootstrapTable.locales["lb-LU"]=t.fn.bootstrapTable.locales.lb={formatCopyRows:function(){return"Zeilen kopéieren"},formatPrint:function(){return"Drécken"},formatLoadingMessage:function(){return"Gëtt gelueden, gedellëgt Iech wannechgelift ee Moment"},formatRecordsPerPage:function(t){return"".concat(t," Zeilen per Säit")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Weist Zeil ".concat(t," bis ").concat(n," vun ").concat(r," Zeil").concat(r>1?"en":""," (gefiltert vun insgesamt ").concat(o," Zeil").concat(r>1?"en":"",")"):"Weist Zeil ".concat(t," bis ").concat(n," vun ").concat(r," Zeil").concat(r>1?"en":"")},formatSRPaginationPreText:function(){return"viregt Säit"},formatSRPaginationPageText:function(t){return"op Säit ".concat(t)},formatSRPaginationNextText:function(){return"nächst Säit"},formatDetailPagination:function(t){return"Weist ".concat(t," Zeilen")},formatClearSearch:function(){return"Sich réckgängeg maachen"},formatSearch:function(){return"Sich"},formatNoMatches:function(){return"Keng passend Anträg fonnt"},formatPaginationSwitch:function(){return"Paginatioun uweisen/verstoppen"},formatPaginationSwitchDown:function(){return"Paginatioun uweisen"},formatPaginationSwitchUp:function(){return"Paginatioun verstoppen"},formatRefresh:function(){return"Nei lueden"},formatToggleOn:function(){return"Kaartenusiicht uweisen"},formatToggleOff:function(){return"Kaartenusiicht verstoppen"},formatColumns:function(){return"Kolonnen"},formatColumnsToggleAll:function(){return"All ëmschalten"},formatFullscreen:function(){return"Vollbild"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Automatescht neilueden"},formatExport:function(){return"Daten exportéieren"},formatJumpTo:function(){return"Sprangen"},formatAdvancedSearch:function(){return"Erweidert Sich"},formatAdvancedCloseButton:function(){return"Zoumaachen"},formatFilterControlSwitch:function(){return"Schaltelementer uweisen/verstoppen"},formatFilterControlSwitchHide:function(){return"Schaltelementer verstoppen"},formatFilterControlSwitchShow:function(){return"Schaltelementer uweisen"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["lb-LU"]),t.fn.bootstrapTable.locales["ko-KR"]=t.fn.bootstrapTable.locales.ko={formatCopyRows:function(){return"행 복사"},formatPrint:function(){return"프린트"},formatLoadingMessage:function(){return"데이터를 불러오는 중입니다"},formatRecordsPerPage:function(t){return"페이지 당 ".concat(t,"개 데이터 출력")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"전체 ".concat(r,"개 중 ").concat(t,"~").concat(n,"번째 데이터 출력, (전체 ").concat(o," 행에서 필터됨)"):"전체 ".concat(r,"개 중 ").concat(t,"~").concat(n,"번째 데이터 출력,")},formatSRPaginationPreText:function(){return"이전 페이지"},formatSRPaginationPageText:function(t){return"".concat(t," 페이지로 이동")},formatSRPaginationNextText:function(){return"다음 페이지"},formatDetailPagination:function(t){return"".concat(t," 행들 표시 중")},formatClearSearch:function(){return"검색 초기화"},formatSearch:function(){return"검색"},formatNoMatches:function(){return"조회된 데이터가 없습니다."},formatPaginationSwitch:function(){return"페이지 넘버 보기/숨기기"},formatPaginationSwitchDown:function(){return"페이지 넘버 보기"},formatPaginationSwitchUp:function(){return"페이지 넘버 숨기기"},formatRefresh:function(){return"새로 고침"},formatToggleOn:function(){return"카드뷰 보기"},formatToggleOff:function(){return"카드뷰 숨기기"},formatColumns:function(){return"컬럼 필터링"},formatColumnsToggleAll:function(){return"전체 토글"},formatFullscreen:function(){return"전체 화면"},formatAllRows:function(){return"전체"},formatAutoRefresh:function(){return"자동 갱신"},formatExport:function(){return"데이터 추출"},formatJumpTo:function(){return"이동"},formatAdvancedSearch:function(){return"심화 검색"},formatAdvancedCloseButton:function(){return"닫기"},formatFilterControlSwitch:function(){return"컨트롤 보기/숨기기"},formatFilterControlSwitchHide:function(){return"컨트롤 숨기기"},formatFilterControlSwitchShow:function(){return"컨트롤 보기"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ko-KR"]),t.fn.bootstrapTable.locales["lt-LT"]=t.fn.bootstrapTable.locales.lt={formatCopyRows:function(){return"Kopijuoti eilutes"},formatPrint:function(){return"Spausdinti"},formatLoadingMessage:function(){return"Įkeliama, palaukite"},formatRecordsPerPage:function(t){return"".concat(t," eilučių puslapyje")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Rodomos eilutės nuo ".concat(t," iki ").concat(n," iš ").concat(r," eilučių (atrinktos iš visų ").concat(o," eilučių)"):"Rodomos eilutės nuo ".concat(t," iki ").concat(n," iš ").concat(r," eilučių")},formatSRPaginationPreText:function(){return"ankstesnis puslapis"},formatSRPaginationPageText:function(t){return"į puslapį ".concat(t)},formatSRPaginationNextText:function(){return"sekantis puslapis"},formatDetailPagination:function(t){return"Rodomos ".concat(t," eilutės (-čių)")},formatClearSearch:function(){return"Išvalyti paiešką"},formatSearch:function(){return"Ieškoti"},formatNoMatches:function(){return"Atitinkančių įrašų nerasta"},formatPaginationSwitch:function(){return"Slėpti/rodyti puslapių rūšiavimą"},formatPaginationSwitchDown:function(){return"Rodyti puslapių rūšiavimą"},formatPaginationSwitchUp:function(){return"Slėpti puslapių rūšiavimą"},formatRefresh:function(){return"Atnaujinti"},formatToggleOn:function(){return"Rodyti kortelių rodinį"},formatToggleOff:function(){return"Slėpti kortelių rodinį"},formatColumns:function(){return"Stulpeliai"},formatColumnsToggleAll:function(){return"Perjungti viską"},formatFullscreen:function(){return"Visame ekrane"},formatAllRows:function(){return"Viskas"},formatAutoRefresh:function(){return"Automatinis atnaujinimas"},formatExport:function(){return"Eksportuoti duomenis"},formatJumpTo:function(){return"Eiti"},formatAdvancedSearch:function(){return"Išplėstinė paieška"},formatAdvancedCloseButton:function(){return"Uždaryti"},formatFilterControlSwitch:function(){return"Slėpti/rodyti valdiklius"},formatFilterControlSwitchHide:function(){return"Slėpti valdiklius"},formatFilterControlSwitchShow:function(){return"Rodyti valdiklius"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["lt-LT"]),t.fn.bootstrapTable.locales["ms-MY"]=t.fn.bootstrapTable.locales.ms={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Permintaan sedang dimuatkan. Sila tunggu sebentar"},formatRecordsPerPage:function(t){return"".concat(t," rekod setiap muka surat")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Sedang memaparkan rekod ".concat(t," hingga ").concat(n," daripada jumlah ").concat(r," rekod (filtered from ").concat(o," total rows)"):"Sedang memaparkan rekod ".concat(t," hingga ").concat(n," daripada jumlah ").concat(r," rekod")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Cari"},formatNoMatches:function(){return"Tiada rekod yang menyamai permintaan"},formatPaginationSwitch:function(){return"Tunjuk/sembunyi muka surat"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Muatsemula"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Lajur"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Semua"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ms-MY"]),t.fn.bootstrapTable.locales["nb-NO"]=t.fn.bootstrapTable.locales.nb={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Oppdaterer, vennligst vent"},formatRecordsPerPage:function(t){return"".concat(t," poster pr side")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Viser ".concat(t," til ").concat(n," av ").concat(r," rekker (filtered from ").concat(o," total rows)"):"Viser ".concat(t," til ").concat(n," av ").concat(r," rekker")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Søk"},formatNoMatches:function(){return"Ingen poster funnet"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Oppdater"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Kolonner"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["nb-NO"]),t.fn.bootstrapTable.locales["nl-BE"]={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Laden, even geduld"},formatRecordsPerPage:function(t){return"".concat(t," records per pagina")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Toon ".concat(t," tot ").concat(n," van ").concat(r," record").concat(r>1?"s":""," (gefilterd van ").concat(o," records in totaal)"):"Toon ".concat(t," tot ").concat(n," van ").concat(r," record").concat(r>1?"s":"")},formatSRPaginationPreText:function(){return"vorige pagina"},formatSRPaginationPageText:function(t){return"tot pagina ".concat(t)},formatSRPaginationNextText:function(){return"volgende pagina"},formatDetailPagination:function(t){return"Toon ".concat(t," record").concat(t>1?"s":"")},formatClearSearch:function(){return"Verwijder filters"},formatSearch:function(){return"Zoeken"},formatNoMatches:function(){return"Geen resultaten gevonden"},formatPaginationSwitch:function(){return"Verberg/Toon paginering"},formatPaginationSwitchDown:function(){return"Toon paginering"},formatPaginationSwitchUp:function(){return"Verberg paginering"},formatRefresh:function(){return"Vernieuwen"},formatToggleOn:function(){return"Toon kaartweergave"},formatToggleOff:function(){return"Verberg kaartweergave"},formatColumns:function(){return"Kolommen"},formatColumnsToggleAll:function(){return"Allen omschakelen"},formatFullscreen:function(){return"Volledig scherm"},formatAllRows:function(){return"Alle"},formatAutoRefresh:function(){return"Automatisch vernieuwen"},formatExport:function(){return"Exporteer gegevens"},formatJumpTo:function(){return"GA"},formatAdvancedSearch:function(){return"Geavanceerd zoeken"},formatAdvancedCloseButton:function(){return"Sluiten"},formatFilterControlSwitch:function(){return"Verberg/Toon controls"},formatFilterControlSwitchHide:function(){return"Verberg controls"},formatFilterControlSwitchShow:function(){return"Toon controls"},formatAddLevel:function(){return"Niveau toevoegen"},formatCancel:function(){return"Annuleren"},formatColumn:function(){return"Kolom"},formatDeleteLevel:function(){return"Niveau verwijderen"},formatDuplicateAlertTitle:function(){return"Duplicaten gevonden!"},formatDuplicateAlertDescription:function(){return"Gelieve dubbele kolommen te verwijderen of wijzigen"},formatMultipleSort:function(){return"Meervoudige sortering"},formatOrder:function(){return"Volgorde"},formatSort:function(){return"Sorteren"},formatSortBy:function(){return"Sorteren op"},formatThenBy:function(){return"vervolgens"},formatSortOrders:function(){return{asc:"Oplopend",desc:"Aflopend"}}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["nl-BE"]),t.fn.bootstrapTable.locales["nl-NL"]=t.fn.bootstrapTable.locales.nl={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Laden, even geduld"},formatRecordsPerPage:function(t){return"".concat(t," records per pagina")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Toon ".concat(t," tot ").concat(n," van ").concat(r," record").concat(r>1?"s":""," (gefilterd van ").concat(o," records in totaal)"):"Toon ".concat(t," tot ").concat(n," van ").concat(r," record").concat(r>1?"s":"")},formatSRPaginationPreText:function(){return"vorige pagina"},formatSRPaginationPageText:function(t){return"tot pagina ".concat(t)},formatSRPaginationNextText:function(){return"volgende pagina"},formatDetailPagination:function(t){return"Toon ".concat(t," record").concat(t>1?"s":"")},formatClearSearch:function(){return"Verwijder filters"},formatSearch:function(){return"Zoeken"},formatNoMatches:function(){return"Geen resultaten gevonden"},formatPaginationSwitch:function(){return"Verberg/Toon paginering"},formatPaginationSwitchDown:function(){return"Toon paginering"},formatPaginationSwitchUp:function(){return"Verberg paginering"},formatRefresh:function(){return"Vernieuwen"},formatToggleOn:function(){return"Toon kaartweergave"},formatToggleOff:function(){return"Verberg kaartweergave"},formatColumns:function(){return"Kolommen"},formatColumnsToggleAll:function(){return"Allen omschakelen"},formatFullscreen:function(){return"Volledig scherm"},formatAllRows:function(){return"Alle"},formatAutoRefresh:function(){return"Automatisch vernieuwen"},formatExport:function(){return"Exporteer gegevens"},formatJumpTo:function(){return"GA"},formatAdvancedSearch:function(){return"Geavanceerd zoeken"},formatAdvancedCloseButton:function(){return"Sluiten"},formatFilterControlSwitch:function(){return"Verberg/Toon controls"},formatFilterControlSwitchHide:function(){return"Verberg controls"},formatFilterControlSwitchShow:function(){return"Toon controls"},formatAddLevel:function(){return"Niveau toevoegen"},formatCancel:function(){return"Annuleren"},formatColumn:function(){return"Kolom"},formatDeleteLevel:function(){return"Niveau verwijderen"},formatDuplicateAlertTitle:function(){return"Duplicaten gevonden!"},formatDuplicateAlertDescription:function(){return"Gelieve dubbele kolommen te verwijderen of wijzigen"},formatMultipleSort:function(){return"Meervoudige sortering"},formatOrder:function(){return"Volgorde"},formatSort:function(){return"Sorteren"},formatSortBy:function(){return"Sorteren op"},formatThenBy:function(){return"vervolgens"},formatSortOrders:function(){return{asc:"Oplopend",desc:"Aflopend"}}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["nl-NL"]),t.fn.bootstrapTable.locales["pl-PL"]=t.fn.bootstrapTable.locales.pl={formatCopyRows:function(){return"Kopiuj wiersze"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Ładowanie, proszę czekać"},formatRecordsPerPage:function(t){return"".concat(t," rekordów na stronę")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Wyświetlanie rekordów od ".concat(t," do ").concat(n," z ").concat(r," (filtered from ").concat(o," total rows)"):"Wyświetlanie rekordów od ".concat(t," do ").concat(n," z ").concat(r)},formatSRPaginationPreText:function(){return"poprzednia strona"},formatSRPaginationPageText:function(t){return"z ".concat(t)},formatSRPaginationNextText:function(){return"następna strona"},formatDetailPagination:function(t){return"Wyświetla ".concat(t," wierszy")},formatClearSearch:function(){return"Wyczyść wyszukiwanie"},formatSearch:function(){return"Szukaj"},formatNoMatches:function(){return"Niestety, nic nie znaleziono"},formatPaginationSwitch:function(){return"Pokaż/ukryj stronicowanie"},formatPaginationSwitchDown:function(){return"Pokaż stronicowanie"},formatPaginationSwitchUp:function(){return"Ukryj stronicowanie"},formatRefresh:function(){return"Odśwież"},formatToggleOn:function(){return"Pokaż układ karty"},formatToggleOff:function(){return"Ukryj układ karty"},formatColumns:function(){return"Kolumny"},formatColumnsToggleAll:function(){return"Zaznacz wszystko"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Wszystkie"},formatAutoRefresh:function(){return"Auto odświeżanie"},formatExport:function(){return"Eksport danych"},formatJumpTo:function(){return"Przejdź"},formatAdvancedSearch:function(){return"Wyszukiwanie zaawansowane"},formatAdvancedCloseButton:function(){return"Zamknij"},formatFilterControlSwitch:function(){return"Pokaż/Ukryj"},formatFilterControlSwitchHide:function(){return"Pokaż"},formatFilterControlSwitchShow:function(){return"Ukryj"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["pl-PL"]),t.fn.bootstrapTable.locales["pt-BR"]=t.fn.bootstrapTable.locales.br={formatCopyRows:function(){return"Copiar linhas"},formatPrint:function(){return"Imprimir"},formatLoadingMessage:function(){return"Carregando, aguarde"},formatRecordsPerPage:function(t){return"".concat(t," registros por página")},formatShowingRows:function(t,n,r,o){var e=r>1?"s":"";return void 0!==o&&o>0&&o>r?"Exibindo ".concat(t," até ").concat(n," de ").concat(r," linha").concat(e," (filtrado de um total de ").concat(o," linha").concat(e,")"):"Exibindo ".concat(t," até ").concat(n," de ").concat(r," linha").concat(e)},formatSRPaginationPreText:function(){return"página anterior"},formatSRPaginationPageText:function(t){return"ir para a página ".concat(t)},formatSRPaginationNextText:function(){return"próxima página"},formatDetailPagination:function(t){return"Mostrando ".concat(t," linha").concat(t>1?"s":"")},formatClearSearch:function(){return"Limpar Pesquisa"},formatSearch:function(){return"Pesquisar"},formatNoMatches:function(){return"Nenhum registro encontrado"},formatPaginationSwitch:function(){return"Ocultar/Exibir paginação"},formatPaginationSwitchDown:function(){return"Mostrar Paginação"},formatPaginationSwitchUp:function(){return"Esconder Paginação"},formatRefresh:function(){return"Recarregar"},formatToggleOn:function(){return"Mostrar visualização de cartão"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Colunas"},formatColumnsToggleAll:function(){return"Alternar tudo"},formatFullscreen:function(){return"Tela cheia"},formatAllRows:function(){return"Tudo"},formatAutoRefresh:function(){return"Atualização Automática"},formatExport:function(){return"Exportar dados"},formatJumpTo:function(){return"Ir"},formatAdvancedSearch:function(){return"Pesquisa Avançada"},formatAdvancedCloseButton:function(){return"Fechar"},formatFilterControlSwitch:function(){return"Ocultar/Exibir controles"},formatFilterControlSwitchHide:function(){return"Ocultar controles"},formatFilterControlSwitchShow:function(){return"Exibir controles"},formatAddLevel:function(){return"Adicionar nível"},formatCancel:function(){return"Cancelar"},formatColumn:function(){return"Coluna"},formatDeleteLevel:function(){return"Remover nível"},formatDuplicateAlertTitle:function(){return"Encontradas entradas duplicadas!"},formatDuplicateAlertDescription:function(){return"Por favor, remova ou altere as colunas duplicadas"},formatMultipleSort:function(){return"Ordenação múltipla"},formatOrder:function(){return"Ordem"},formatSort:function(){return"Ordenar"},formatSortBy:function(){return"Ordenar por"},formatThenBy:function(){return"em seguida"},formatSortOrders:function(){return{asc:"Crescente",desc:"Decrescente"}}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["pt-BR"]),t.fn.bootstrapTable.locales["pt-PT"]=t.fn.bootstrapTable.locales.pt={formatCopyRows:function(){return"Copiar Linhas"},formatPrint:function(){return"Imprimir"},formatLoadingMessage:function(){return"A carregar, por favor aguarde"},formatRecordsPerPage:function(t){return"".concat(t," registos por página")},formatShowingRows:function(t,n,r,o){var e=r>1?"s":"";return void 0!==o&&o>0&&o>r?"A mostrar ".concat(t," até ").concat(n," de ").concat(r," linha").concat(e," (filtrado de um total de ").concat(o," linha").concat(e,")"):"A mostrar ".concat(t," até ").concat(n," de ").concat(r," linha").concat(e)},formatSRPaginationPreText:function(){return"página anterior"},formatSRPaginationPageText:function(t){return"ir para página ".concat(t)},formatSRPaginationNextText:function(){return"próxima página"},formatDetailPagination:function(t){return"Mostrando ".concat(t," linha").concat(t>1?"s":"")},formatClearSearch:function(){return"Limpar Pesquisa"},formatSearch:function(){return"Pesquisa"},formatNoMatches:function(){return"Nenhum registo encontrado"},formatPaginationSwitch:function(){return"Esconder/Mostrar paginação"},formatPaginationSwitchDown:function(){return"Mostrar página"},formatPaginationSwitchUp:function(){return"Esconder página"},formatRefresh:function(){return"Actualizar"},formatToggleOn:function(){return"Mostrar vista em forma de cartão"},formatToggleOff:function(){return"Esconder vista em forma de cartão"},formatColumns:function(){return"Colunas"},formatColumnsToggleAll:function(){return"Activar tudo"},formatFullscreen:function(){return"Ecrã completo"},formatAllRows:function(){return"Tudo"},formatAutoRefresh:function(){return"Actualização autmática"},formatExport:function(){return"Exportar dados"},formatJumpTo:function(){return"Avançar"},formatAdvancedSearch:function(){return"Pesquisa avançada"},formatAdvancedCloseButton:function(){return"Fechar"},formatFilterControlSwitch:function(){return"Ocultar/Exibir controles"},formatFilterControlSwitchHide:function(){return"Esconder controlos"},formatFilterControlSwitchShow:function(){return"Exibir controlos"},formatAddLevel:function(){return"Adicionar nível"},formatCancel:function(){return"Cancelar"},formatColumn:function(){return"Coluna"},formatDeleteLevel:function(){return"Remover nível"},formatDuplicateAlertTitle:function(){return"Foram encontradas entradas duplicadas!"},formatDuplicateAlertDescription:function(){return"Por favor, remova ou altere as colunas duplicadas"},formatMultipleSort:function(){return"Ordenação múltipla"},formatOrder:function(){return"Ordem"},formatSort:function(){return"Ordenar"},formatSortBy:function(){return"Ordenar por"},formatThenBy:function(){return"em seguida"},formatSortOrders:function(){return{asc:"Ascendente",desc:"Descendente"}}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["pt-PT"]),t.fn.bootstrapTable.locales["ro-RO"]=t.fn.bootstrapTable.locales.ro={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Se incarca, va rugam asteptati"},formatRecordsPerPage:function(t){return"".concat(t," inregistrari pe pagina")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Arata de la ".concat(t," pana la ").concat(n," din ").concat(r," randuri (filtered from ").concat(o," total rows)"):"Arata de la ".concat(t," pana la ").concat(n," din ").concat(r," randuri")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Cauta"},formatNoMatches:function(){return"Nu au fost gasite inregistrari"},formatPaginationSwitch:function(){return"Ascunde/Arata paginatia"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Reincarca"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Coloane"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Toate"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ro-RO"]),t.fn.bootstrapTable.locales["ka-GE"]=t.fn.bootstrapTable.locales.ka={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"იტვირთება, გთხოვთ მოიცადოთ"},formatRecordsPerPage:function(t){return"".concat(t," ჩანაწერი თითო გვერდზე")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"ნაჩვენებია ".concat(t,"-დან ").concat(n,"-მდე ჩანაწერი ჯამური ").concat(r,"-დან (filtered from ").concat(o," total rows)"):"ნაჩვენებია ".concat(t,"-დან ").concat(n,"-მდე ჩანაწერი ჯამური ").concat(r,"-დან")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"ძებნა"},formatNoMatches:function(){return"მონაცემები არ არის"},formatPaginationSwitch:function(){return"გვერდების გადამრთველის დამალვა/გამოჩენა"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"განახლება"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"სვეტები"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ka-GE"]),t.fn.bootstrapTable.locales["ru-RU"]=t.fn.bootstrapTable.locales.ru={formatCopyRows:function(){return"Скопировать строки"},formatPrint:function(){return"Печать"},formatLoadingMessage:function(){return"Пожалуйста, подождите, идёт загрузка"},formatRecordsPerPage:function(t){return"".concat(t," записей на страницу")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Записи с ".concat(t," по ").concat(n," из ").concat(r," (отфильтровано, всего на сервере ").concat(o," записей)"):"Записи с ".concat(t," по ").concat(n," из ").concat(r)},formatSRPaginationPreText:function(){return"предыдущая страница"},formatSRPaginationPageText:function(t){return"перейти к странице ".concat(t)},formatSRPaginationNextText:function(){return"следующая страница"},formatDetailPagination:function(t){return"Загружено ".concat(t," строк")},formatClearSearch:function(){return"Очистить фильтры"},formatSearch:function(){return"Поиск"},formatNoMatches:function(){return"Ничего не найдено"},formatPaginationSwitch:function(){return"Скрыть/Показать постраничную навигацию"},formatPaginationSwitchDown:function(){return"Показать постраничную навигацию"},formatPaginationSwitchUp:function(){return"Скрыть постраничную навигацию"},formatRefresh:function(){return"Обновить"},formatToggleOn:function(){return"Показать записи в виде карточек"},formatToggleOff:function(){return"Табличный режим просмотра"},formatColumns:function(){return"Колонки"},formatColumnsToggleAll:function(){return"Выбрать все"},formatFullscreen:function(){return"Полноэкранный режим"},formatAllRows:function(){return"Все"},formatAutoRefresh:function(){return"Автоматическое обновление"},formatExport:function(){return"Экспортировать данные"},formatJumpTo:function(){return"Стр."},formatAdvancedSearch:function(){return"Расширенный поиск"},formatAdvancedCloseButton:function(){return"Закрыть"},formatFilterControlSwitch:function(){return"Скрыть/Показать панель инструментов"},formatFilterControlSwitchHide:function(){return"Скрыть панель инструментов"},formatFilterControlSwitchShow:function(){return"Показать панель инструментов"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ru-RU"]),t.fn.bootstrapTable.locales["sk-SK"]=t.fn.bootstrapTable.locales.sk={formatCopyRows:function(){return"Skopírovať riadky"},formatPrint:function(){return"Vytlačiť"},formatLoadingMessage:function(){return"Prosím čakajte"},formatRecordsPerPage:function(t){return"".concat(t," záznamov na stranu")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Zobrazená ".concat(t,". - ").concat(n,". položka z celkových ").concat(r," (filtered from ").concat(o," total rows)"):"Zobrazená ".concat(t,". - ").concat(n,". položka z celkových ").concat(r)},formatSRPaginationPreText:function(){return"Predchádzajúca strana"},formatSRPaginationPageText:function(t){return"na stranu ".concat(t)},formatSRPaginationNextText:function(){return"Nasledujúca strana"},formatDetailPagination:function(t){return"Zobrazuje sa ".concat(t," riadkov")},formatClearSearch:function(){return"Odstráň filtre"},formatSearch:function(){return"Vyhľadávanie"},formatNoMatches:function(){return"Nenájdená žiadna vyhovujúca položka"},formatPaginationSwitch:function(){return"Skry/Zobraz stránkovanie"},formatPaginationSwitchDown:function(){return"Zobraziť stránkovanie"},formatPaginationSwitchUp:function(){return"Skryť stránkovanie"},formatRefresh:function(){return"Obnoviť"},formatToggleOn:function(){return"Zobraziť kartové zobrazenie"},formatToggleOff:function(){return"skryť kartové zobrazenie"},formatColumns:function(){return"Stĺpce"},formatColumnsToggleAll:function(){return"Prepnúť všetky"},formatFullscreen:function(){return"Celá obrazovka"},formatAllRows:function(){return"Všetky"},formatAutoRefresh:function(){return"Automatické obnovenie"},formatExport:function(){return"Exportuj dáta"},formatJumpTo:function(){return"Ísť"},formatAdvancedSearch:function(){return"Pokročilé vyhľadávanie"},formatAdvancedCloseButton:function(){return"Zatvoriť"},formatFilterControlSwitch:function(){return"Zobraziť/Skryť tlačidlá"},formatFilterControlSwitchHide:function(){return"Skryť tlačidlá"},formatFilterControlSwitchShow:function(){return"Zobraziť tlačidlá"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["sk-SK"]),t.fn.bootstrapTable.locales["sl-SI"]=t.fn.bootstrapTable.locales.sl={formatCopyRows:function(){return"Kopiraj vrstice"},formatPrint:function(){return"Natisni"},formatLoadingMessage:function(){return"Prosim počakajte..."},formatRecordsPerPage:function(t){return"".concat(t," vrstic na stran")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Prikaz ".concat(t," do ").concat(n," od ").concat(r," vrstic (filtrirano od skupno ").concat(o," vrstic)"):"Prikaz ".concat(t," do ").concat(n," od ").concat(r," vrstic")},formatSRPaginationPreText:function(){return"prejšnja stran"},formatSRPaginationPageText:function(t){return"na stran ".concat(t)},formatSRPaginationNextText:function(){return"na slednja stran"},formatDetailPagination:function(t){return"Prikaz ".concat(t," vrstic")},formatClearSearch:function(){return"Počisti"},formatSearch:function(){return"Iskanje"},formatNoMatches:function(){return"Ni najdenih rezultatov"},formatPaginationSwitch:function(){return"Skrij/Pokaži oštevilčevanje strani"},formatPaginationSwitchDown:function(){return"Pokaži oštevilčevanje strani"},formatPaginationSwitchUp:function(){return"Skrij oštevilčevanje strani"},formatRefresh:function(){return"Osveži"},formatToggleOn:function(){return"Prikaži kartični pogled"},formatToggleOff:function(){return"Skrij kartični pogled"},formatColumns:function(){return"Stolpci"},formatColumnsToggleAll:function(){return"Preklopi vse"},formatFullscreen:function(){return"Celozaslonski prikaz"},formatAllRows:function(){return"Vse"},formatAutoRefresh:function(){return"Samodejna osvežitev"},formatExport:function(){return"Izvoz podatkov"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Napredno iskanje"},formatAdvancedCloseButton:function(){return"Zapri"},formatFilterControlSwitch:function(){return"Skrij/Pokaži kontrole"},formatFilterControlSwitchHide:function(){return"Skrij kontrole"},formatFilterControlSwitchShow:function(){return"Pokaži kontrole"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["sl-SI"]),t.fn.bootstrapTable.locales["sr-Cyrl-RS"]=t.fn.bootstrapTable.locales.sr={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Молим сачекај"},formatRecordsPerPage:function(t){return"".concat(t," редова по страни")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Приказано ".concat(t,". - ").concat(n,". од укупног броја редова ").concat(r," (филтрирано од ").concat(o,")"):"Приказано ".concat(t,". - ").concat(n,". од укупног броја редова ").concat(r)},formatSRPaginationPreText:function(){return"претходна страна"},formatSRPaginationPageText:function(t){return"на страну ".concat(t)},formatSRPaginationNextText:function(){return"следећа страна"},formatDetailPagination:function(t){return"Приказано ".concat(t," редова")},formatClearSearch:function(){return"Обриши претрагу"},formatSearch:function(){return"Пронађи"},formatNoMatches:function(){return"Није пронађен ни један податак"},formatPaginationSwitch:function(){return"Прикажи/сакриј пагинацију"},formatPaginationSwitchDown:function(){return"Прикажи пагинацију"},formatPaginationSwitchUp:function(){return"Сакриј пагинацију"},formatRefresh:function(){return"Освежи"},formatToggleOn:function(){return"Прикажи картице"},formatToggleOff:function(){return"Сакриј картице"},formatColumns:function(){return"Колоне"},formatColumnsToggleAll:function(){return"Прикажи/сакриј све"},formatFullscreen:function(){return"Цео екран"},formatAllRows:function(){return"Све"},formatAutoRefresh:function(){return"Аутоматско освежавање"},formatExport:function(){return"Извези податке"},formatJumpTo:function(){return"Иди"},formatAdvancedSearch:function(){return"Напредна претрага"},formatAdvancedCloseButton:function(){return"Затвори"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["sr-Cyrl-RS"]),t.fn.bootstrapTable.locales["sr-Latn-RS"]={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Molim sačekaj"},formatRecordsPerPage:function(t){return"".concat(t," redova po strani")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Prikazano ".concat(t,". - ").concat(n,". od ukupnog broja redova ").concat(r," (filtrirano od ").concat(o,")"):"Prikazano ".concat(t,". - ").concat(n,". od ukupnog broja redova ").concat(r)},formatSRPaginationPreText:function(){return"prethodna strana"},formatSRPaginationPageText:function(t){return"na stranu ".concat(t)},formatSRPaginationNextText:function(){return"sledeća strana"},formatDetailPagination:function(t){return"Prikazano ".concat(t," redova")},formatClearSearch:function(){return"Obriši pretragu"},formatSearch:function(){return"Pronađi"},formatNoMatches:function(){return"Nije pronađen ni jedan podatak"},formatPaginationSwitch:function(){return"Prikaži/sakrij paginaciju"},formatPaginationSwitchDown:function(){return"Prikaži paginaciju"},formatPaginationSwitchUp:function(){return"Sakrij paginaciju"},formatRefresh:function(){return"Osveži"},formatToggleOn:function(){return"Prikaži kartice"},formatToggleOff:function(){return"Sakrij kartice"},formatColumns:function(){return"Kolone"},formatColumnsToggleAll:function(){return"Prikaži/sakrij sve"},formatFullscreen:function(){return"Ceo ekran"},formatAllRows:function(){return"Sve"},formatAutoRefresh:function(){return"Automatsko osvežavanje"},formatExport:function(){return"Izvezi podatke"},formatJumpTo:function(){return"Idi"},formatAdvancedSearch:function(){return"Napredna pretraga"},formatAdvancedCloseButton:function(){return"Zatvori"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["sr-Latn-RS"]),t.fn.bootstrapTable.locales["sv-SE"]=t.fn.bootstrapTable.locales.sv={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Laddar, vänligen vänta"},formatRecordsPerPage:function(t){return"".concat(t," rader per sida")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Visa ".concat(t," till ").concat(n," av ").concat(r," rader (filtered from ").concat(o," total rows)"):"Visa ".concat(t," till ").concat(n," av ").concat(r," rader")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"Sök"},formatNoMatches:function(){return"Inga matchande resultat funna."},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Uppdatera"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"kolumn"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["sv-SE"]),t.fn.bootstrapTable.locales["th-TH"]=t.fn.bootstrapTable.locales.th={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"กำลังโหลดข้อมูล, กรุณารอสักครู่"},formatRecordsPerPage:function(t){return"".concat(t," รายการต่อหน้า")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"รายการที่ ".concat(t," ถึง ").concat(n," จากทั้งหมด ").concat(r," รายการ (filtered from ").concat(o," total rows)"):"รายการที่ ".concat(t," ถึง ").concat(n," จากทั้งหมด ").concat(r," รายการ")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"ค้นหา"},formatNoMatches:function(){return"ไม่พบรายการที่ค้นหา !"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"รีเฟรส"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"คอลัมน์"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["th-TH"]),t.fn.bootstrapTable.locales["uk-UA"]=t.fn.bootstrapTable.locales.uk={formatCopyRows:function(){return"Скопіювати рядки"},formatPrint:function(){return"Друк"},formatLoadingMessage:function(){return"Завантаження, будь ласка, зачекайте"},formatRecordsPerPage:function(t){return"".concat(t," рядків на сторінку")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Відображено рядки з ".concat(t," по ").concat(n," з ").concat(r," загалом (відфільтровано з ").concat(o," рядків)"):"Відображено рядки з ".concat(t," по ").concat(n," з ").concat(r," загалом")},formatSRPaginationPreText:function(){return"попередня сторінка"},formatSRPaginationPageText:function(t){return"до сторінки ".concat(t)},formatSRPaginationNextText:function(){return"наступна сторінка"},formatDetailPagination:function(t){return"Відображено ".concat(t," рядків")},formatClearSearch:function(){return"Скинути фільтри"},formatSearch:function(){return"Пошук"},formatNoMatches:function(){return"Не знайдено жодного запису"},formatPaginationSwitch:function(){return"Сховати/Відобразити пагінацію"},formatPaginationSwitchDown:function(){return"Відобразити пагінацію"},formatPaginationSwitchUp:function(){return"Сховати пагінацію"},formatRefresh:function(){return"Оновити"},formatToggleOn:function(){return"Відобразити у форматі карток"},formatToggleOff:function(){return"Вимкнути формат карток"},formatColumns:function(){return"Стовпці"},formatColumnsToggleAll:function(){return"Переключити усі"},formatFullscreen:function(){return"Повноекранний режим"},formatAllRows:function(){return"Усі"},formatAutoRefresh:function(){return"Автооновлення"},formatExport:function(){return"Експортувати дані"},formatJumpTo:function(){return"Швидкий перехід до"},formatAdvancedSearch:function(){return"Розширений пошук"},formatAdvancedCloseButton:function(){return"Закрити"},formatFilterControlSwitch:function(){return"Сховати/Відобразити елементи керування"},formatFilterControlSwitchHide:function(){return"Сховати елементи керування"},formatFilterControlSwitchShow:function(){return"Відобразити елементи керування"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["uk-UA"]),t.fn.bootstrapTable.locales["tr-TR"]=t.fn.bootstrapTable.locales.tr={formatCopyRows:function(){return"Satırları Kopyala"},formatPrint:function(){return"Yazdır"},formatLoadingMessage:function(){return"Yükleniyor, lütfen bekleyin"},formatRecordsPerPage:function(t){return"Sayfa başına ".concat(t," kayıt.")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"".concat(r," kayıttan ").concat(t,"-").concat(n," arası gösteriliyor (").concat(o," toplam satır filtrelendi)."):"".concat(r," kayıttan ").concat(t,"-").concat(n," arası gösteriliyor.")},formatSRPaginationPreText:function(){return"önceki sayfa"},formatSRPaginationPageText:function(t){return"sayfa ".concat(t)},formatSRPaginationNextText:function(){return"sonraki sayfa"},formatDetailPagination:function(t){return"".concat(t," satır gösteriliyor")},formatClearSearch:function(){return"Aramayı Temizle"},formatSearch:function(){return"Ara"},formatNoMatches:function(){return"Eşleşen kayıt bulunamadı."},formatPaginationSwitch:function(){return"Sayfalamayı Gizle/Göster"},formatPaginationSwitchDown:function(){return"Sayfalamayı Göster"},formatPaginationSwitchUp:function(){return"Sayfalamayı Gizle"},formatRefresh:function(){return"Yenile"},formatToggleOn:function(){return"Kart Görünümünü Göster"},formatToggleOff:function(){return"Kart Görünümünü Gizle"},formatColumns:function(){return"Sütunlar"},formatColumnsToggleAll:function(){return"Tümünü Kapat"},formatFullscreen:function(){return"Tam Ekran"},formatAllRows:function(){return"Tüm Satırlar"},formatAutoRefresh:function(){return"Otomatik Yenileme"},formatExport:function(){return"Verileri Dışa Aktar"},formatJumpTo:function(){return"Git"},formatAdvancedSearch:function(){return"Gelişmiş Arama"},formatAdvancedCloseButton:function(){return"Kapat"},formatFilterControlSwitch:function(){return"Kontrolleri Gizle/Göster"},formatFilterControlSwitchHide:function(){return"Kontrolleri Gizle"},formatFilterControlSwitchShow:function(){return"Kontrolleri Göster"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["tr-TR"]),t.fn.bootstrapTable.locales["ur-PK"]=t.fn.bootstrapTable.locales.ur={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"براۓ مہربانی انتظار کیجئے"},formatRecordsPerPage:function(t){return"".concat(t," ریکارڈز فی صفہ ")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"دیکھیں ".concat(t," سے ").concat(n," کے ").concat(r,"ریکارڈز (filtered from ").concat(o," total rows)"):"دیکھیں ".concat(t," سے ").concat(n," کے ").concat(r,"ریکارڈز")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Clear Search"},formatSearch:function(){return"تلاش"},formatNoMatches:function(){return"کوئی ریکارڈ نہیں ملا"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"تازہ کریں"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"کالم"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Export data"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["ur-PK"]),t.fn.bootstrapTable.locales["uz-Latn-UZ"]=t.fn.bootstrapTable.locales.uz={formatCopyRows:function(){return"Copy Rows"},formatPrint:function(){return"Print"},formatLoadingMessage:function(){return"Yuklanyapti, iltimos kuting"},formatRecordsPerPage:function(t){return"".concat(t," qator har sahifada")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Ko'rsatypati ".concat(t," dan ").concat(n," gacha ").concat(r," qatorlarni (filtered from ").concat(o," total rows)"):"Ko'rsatypati ".concat(t," dan ").concat(n," gacha ").concat(r," qatorlarni")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(t){return"to page ".concat(t)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(t){return"Showing ".concat(t," rows")},formatClearSearch:function(){return"Filtrlarni tozalash"},formatSearch:function(){return"Qidirish"},formatNoMatches:function(){return"Hech narsa topilmadi"},formatPaginationSwitch:function(){return"Sahifalashni yashirish/ko'rsatish"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Yangilash"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Ustunlar"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"Hammasi"},formatAutoRefresh:function(){return"Auto Refresh"},formatExport:function(){return"Eksport"},formatJumpTo:function(){return"GO"},formatAdvancedSearch:function(){return"Advanced search"},formatAdvancedCloseButton:function(){return"Close"},formatFilterControlSwitch:function(){return"Hide/Show controls"},formatFilterControlSwitchHide:function(){return"Hide controls"},formatFilterControlSwitchShow:function(){return"Show controls"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["uz-Latn-UZ"]),t.fn.bootstrapTable.locales["vi-VN"]=t.fn.bootstrapTable.locales.vi={formatCopyRows:function(){return"Sao chép hàng"},formatPrint:function(){return"In"},formatLoadingMessage:function(){return"Đang tải"},formatRecordsPerPage:function(t){return"".concat(t," bản ghi mỗi trang")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Hiển thị từ trang ".concat(t," đến ").concat(n," của ").concat(r," bản ghi (được lọc từ tổng ").concat(o," hàng)"):"Hiển thị từ trang ".concat(t," đến ").concat(n," của ").concat(r," bản ghi")},formatSRPaginationPreText:function(){return"trang trước"},formatSRPaginationPageText:function(t){return"đến trang ".concat(t)},formatSRPaginationNextText:function(){return"trang sau"},formatDetailPagination:function(t){return"Đang hiện ".concat(t," hàng")},formatClearSearch:function(){return"Xoá tìm kiếm"},formatSearch:function(){return"Tìm kiếm"},formatNoMatches:function(){return"Không có dữ liệu"},formatPaginationSwitch:function(){return"Ẩn/Hiện phân trang"},formatPaginationSwitchDown:function(){return"Hiện phân trang"},formatPaginationSwitchUp:function(){return"Ẩn phân trang"},formatRefresh:function(){return"Làm mới"},formatToggleOn:function(){return"Hiển thị các thẻ"},formatToggleOff:function(){return"Ẩn các thẻ"},formatColumns:function(){return"Cột"},formatColumnsToggleAll:function(){return"Hiện tất cả"},formatFullscreen:function(){return"Toàn màn hình"},formatAllRows:function(){return"Tất cả"},formatAutoRefresh:function(){return"Tự động làm mới"},formatExport:function(){return"Xuất dữ liệu"},formatJumpTo:function(){return"Đến"},formatAdvancedSearch:function(){return"Tìm kiếm nâng cao"},formatAdvancedCloseButton:function(){return"Đóng"},formatFilterControlSwitch:function(){return"Ẩn/Hiện điều khiển"},formatFilterControlSwitchHide:function(){return"Ẩn điều khiển"},formatFilterControlSwitchShow:function(){return"Hiện điều khiển"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["vi-VN"]),t.fn.bootstrapTable.locales["zh-CN"]=t.fn.bootstrapTable.locales.zh={formatCopyRows:function(){return"复制行"},formatPrint:function(){return"打印"},formatLoadingMessage:function(){return"正在努力地加载数据中,请稍候"},formatRecordsPerPage:function(t){return"每页显示 ".concat(t," 条记录")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"显示第 ".concat(t," 到第 ").concat(n," 条记录,总共 ").concat(r," 条记录(从 ").concat(o," 总记录中过滤)"):"显示第 ".concat(t," 到第 ").concat(n," 条记录,总共 ").concat(r," 条记录")},formatSRPaginationPreText:function(){return"上一页"},formatSRPaginationPageText:function(t){return"第".concat(t,"页")},formatSRPaginationNextText:function(){return"下一页"},formatDetailPagination:function(t){return"总共 ".concat(t," 条记录")},formatClearSearch:function(){return"清空过滤"},formatSearch:function(){return"搜索"},formatNoMatches:function(){return"没有找到匹配的记录"},formatPaginationSwitch:function(){return"隐藏/显示分页"},formatPaginationSwitchDown:function(){return"显示分页"},formatPaginationSwitchUp:function(){return"隐藏分页"},formatRefresh:function(){return"刷新"},formatToggleOn:function(){return"显示卡片视图"},formatToggleOff:function(){return"隐藏卡片视图"},formatColumns:function(){return"列"},formatColumnsToggleAll:function(){return"切换所有"},formatFullscreen:function(){return"全屏"},formatAllRows:function(){return"所有"},formatAutoRefresh:function(){return"自动刷新"},formatExport:function(){return"导出数据"},formatJumpTo:function(){return"跳转"},formatAdvancedSearch:function(){return"高级搜索"},formatAdvancedCloseButton:function(){return"关闭"},formatFilterControlSwitch:function(){return"隐藏/显示过滤控制"},formatFilterControlSwitchHide:function(){return"隐藏过滤控制"},formatFilterControlSwitchShow:function(){return"显示过滤控制"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["zh-CN"]),t.fn.bootstrapTable.locales["zh-TW"]={formatCopyRows:function(){return"複製行"},formatPrint:function(){return"列印"},formatLoadingMessage:function(){return"正在努力地載入資料,請稍候"},formatRecordsPerPage:function(t){return"每頁顯示 ".concat(t," 項記錄")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"顯示第 ".concat(t," 到第 ").concat(n," 項記錄,總共 ").concat(r," 項記錄(從 ").concat(o," 總記錄中過濾)"):"顯示第 ".concat(t," 到第 ").concat(n," 項記錄,總共 ").concat(r," 項記錄")},formatSRPaginationPreText:function(){return"上一頁"},formatSRPaginationPageText:function(t){return"第".concat(t,"頁")},formatSRPaginationNextText:function(){return"下一頁"},formatDetailPagination:function(t){return"總共 ".concat(t," 項記錄")},formatClearSearch:function(){return"清空過濾"},formatSearch:function(){return"搜尋"},formatNoMatches:function(){return"沒有找到符合的結果"},formatPaginationSwitch:function(){return"隱藏/顯示分頁"},formatPaginationSwitchDown:function(){return"顯示分頁"},formatPaginationSwitchUp:function(){return"隱藏分頁"},formatRefresh:function(){return"重新整理"},formatToggleOn:function(){return"顯示卡片視圖"},formatToggleOff:function(){return"隱藏卡片視圖"},formatColumns:function(){return"列"},formatColumnsToggleAll:function(){return"切換所有"},formatFullscreen:function(){return"全屏"},formatAllRows:function(){return"所有"},formatAutoRefresh:function(){return"自動刷新"},formatExport:function(){return"導出數據"},formatJumpTo:function(){return"跳轉"},formatAdvancedSearch:function(){return"高級搜尋"},formatAdvancedCloseButton:function(){return"關閉"},formatFilterControlSwitch:function(){return"隱藏/顯示過濾控制"},formatFilterControlSwitchHide:function(){return"隱藏過濾控制"},formatFilterControlSwitchShow:function(){return"顯示過濾控制"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["zh-TW"]),t.fn.bootstrapTable.locales["es-AR"]={formatCopyRows:function(){return"Copiar Filas"},formatPrint:function(){return"Imprimir"},formatLoadingMessage:function(){return"Cargando, espere por favor"},formatRecordsPerPage:function(t){return"".concat(t," registros por página")},formatShowingRows:function(t,n,r,o){return void 0!==o&&o>0&&o>r?"Mostrando desde ".concat(t," a ").concat(n," de ").concat(r," filas (filtrado de ").concat(o," columnas totales)"):"Mostrando desde ".concat(t," a ").concat(n," de ").concat(r," filas")},formatSRPaginationPreText:function(){return"página anterior"},formatSRPaginationPageText:function(t){return"a la página ".concat(t)},formatSRPaginationNextText:function(){return"siguiente página"},formatDetailPagination:function(t){return"Mostrando ".concat(t," columnas")},formatClearSearch:function(){return"Limpiar búsqueda"},formatSearch:function(){return"Buscar"},formatNoMatches:function(){return"No se encontraron registros"},formatPaginationSwitch:function(){return"Ocultar/Mostrar paginación"},formatPaginationSwitchDown:function(){return"Mostrar paginación"},formatPaginationSwitchUp:function(){return"Ocultar paginación"},formatRefresh:function(){return"Recargar"},formatToggleOn:function(){return"Mostrar vista de carta"},formatToggleOff:function(){return"Ocultar vista de carta"},formatColumns:function(){return"Columnas"},formatColumnsToggleAll:function(){return"Cambiar todo"},formatFullscreen:function(){return"Pantalla completa"},formatAllRows:function(){return"Todo"},formatAutoRefresh:function(){return"Auto Recargar"},formatExport:function(){return"Exportar datos"},formatJumpTo:function(){return"Ir"},formatAdvancedSearch:function(){return"Búsqueda avanzada"},formatAdvancedCloseButton:function(){return"Cerrar"},formatFilterControlSwitch:function(){return"Ocultar/Mostrar controles"},formatFilterControlSwitchHide:function(){return"Ocultar controles"},formatFilterControlSwitchShow:function(){return"Mostrar controles"}},Object.assign(t.fn.bootstrapTable.defaults,t.fn.bootstrapTable.locales["es-AR"])})); diff --git a/web/skins/classic/assets/bootstrap-table-1.24.1/bootstrap-table-vue.js b/web/skins/classic/assets/bootstrap-table-1.24.1/bootstrap-table-vue.js deleted file mode 100644 index 22cb557b1..000000000 --- a/web/skins/classic/assets/bootstrap-table-1.24.1/bootstrap-table-vue.js +++ /dev/null @@ -1,2001 +0,0 @@ -var wt = {}; -/** -* @vue/shared v3.5.13 -* (c) 2018-present Yuxi (Evan) You and Vue contributors -* @license MIT -**/ -/*! #__NO_SIDE_EFFECTS__ */ -// @__NO_SIDE_EFFECTS__ -function kt(e) { - const t = /* @__PURE__ */ Object.create(null); - for (const n of e.split(",")) t[n] = 1; - return (n) => n in t; -} -const $ = wt.NODE_ENV !== "production" ? Object.freeze({}) : {}, en = wt.NODE_ENV !== "production" ? Object.freeze([]) : [], ie = () => { -}, tn = (e) => e.charCodeAt(0) === 111 && e.charCodeAt(1) === 110 && // uppercase letter -(e.charCodeAt(2) > 122 || e.charCodeAt(2) < 97), R = Object.assign, nn = Object.prototype.hasOwnProperty, N = (e, t) => nn.call(e, t), b = Array.isArray, k = (e) => Ce(e) === "[object Map]", rn = (e) => Ce(e) === "[object Set]", O = (e) => typeof e == "function", F = (e) => typeof e == "string", pe = (e) => typeof e == "symbol", y = (e) => e !== null && typeof e == "object", sn = (e) => (y(e) || O(e)) && O(e.then) && O(e.catch), on = Object.prototype.toString, Ce = (e) => on.call(e), Nt = (e) => Ce(e).slice(8, -1), cn = (e) => Ce(e) === "[object Object]", ke = (e) => F(e) && e !== "NaN" && e[0] !== "-" && "" + parseInt(e, 10) === e, ln = (e) => { - const t = /* @__PURE__ */ Object.create(null); - return (n) => t[n] || (t[n] = e(n)); -}, an = ln((e) => e.charAt(0).toUpperCase() + e.slice(1)), q = (e, t) => !Object.is(e, t), un = (e, t, n, s = !1) => { - Object.defineProperty(e, t, { - configurable: !0, - enumerable: !1, - writable: s, - value: n - }); -}; -let ft; -const Ie = () => ft || (ft = typeof globalThis < "u" ? globalThis : typeof self < "u" ? self : typeof window < "u" ? window : typeof global < "u" ? global : {}); -function et(e) { - if (b(e)) { - const t = {}; - for (let n = 0; n < e.length; n++) { - const s = e[n], r = F(s) ? hn(s) : et(s); - if (r) - for (const i in r) - t[i] = r[i]; - } - return t; - } else if (F(e) || y(e)) - return e; -} -const fn = /;(?![^(]*\))/g, pn = /:([^]+)/, dn = /\/\*[^]*?\*\//g; -function hn(e) { - const t = {}; - return e.replace(dn, "").split(fn).forEach((n) => { - if (n) { - const s = n.split(pn); - s.length > 1 && (t[s[0].trim()] = s[1].trim()); - } - }), t; -} -function tt(e) { - let t = ""; - if (F(e)) - t = e; - else if (b(e)) - for (let n = 0; n < e.length; n++) { - const s = tt(e[n]); - s && (t += s + " "); - } - else if (y(e)) - for (const n in e) - e[n] && (t += n + " "); - return t.trim(); -} -var E = {}; -function z(e, ...t) { - console.warn(`[Vue warn] ${e}`, ...t); -} -let m; -const je = /* @__PURE__ */ new WeakSet(); -class gn { - constructor(t) { - this.fn = t, this.deps = void 0, this.depsTail = void 0, this.flags = 5, this.next = void 0, this.cleanup = void 0, this.scheduler = void 0; - } - pause() { - this.flags |= 64; - } - resume() { - this.flags & 64 && (this.flags &= -65, je.has(this) && (je.delete(this), this.trigger())); - } - /** - * @internal - */ - notify() { - this.flags & 2 && !(this.flags & 32) || this.flags & 8 || _n(this); - } - run() { - if (!(this.flags & 1)) - return this.fn(); - this.flags |= 2, pt(this), Ot(this); - const t = m, n = A; - m = this, A = !0; - try { - return this.fn(); - } finally { - E.NODE_ENV !== "production" && m !== this && z( - "Active effect was not restored correctly - this is likely a Vue internal bug." - ), xt(this), m = t, A = n, this.flags &= -3; - } - } - stop() { - if (this.flags & 1) { - for (let t = this.deps; t; t = t.nextDep) - st(t); - this.deps = this.depsTail = void 0, pt(this), this.onStop && this.onStop(), this.flags &= -2; - } - } - trigger() { - this.flags & 64 ? je.add(this) : this.scheduler ? this.scheduler() : this.runIfDirty(); - } - /** - * @internal - */ - runIfDirty() { - Be(this) && this.run(); - } - get dirty() { - return Be(this); - } -} -let St = 0, oe, ce; -function _n(e, t = !1) { - if (e.flags |= 8, t) { - e.next = ce, ce = e; - return; - } - e.next = oe, oe = e; -} -function nt() { - St++; -} -function rt() { - if (--St > 0) - return; - if (ce) { - let t = ce; - for (ce = void 0; t; ) { - const n = t.next; - t.next = void 0, t.flags &= -9, t = n; - } - } - let e; - for (; oe; ) { - let t = oe; - for (oe = void 0; t; ) { - const n = t.next; - if (t.next = void 0, t.flags &= -9, t.flags & 1) - try { - t.trigger(); - } catch (s) { - e || (e = s); - } - t = n; - } - } - if (e) throw e; -} -function Ot(e) { - for (let t = e.deps; t; t = t.nextDep) - t.version = -1, t.prevActiveLink = t.dep.activeLink, t.dep.activeLink = t; -} -function xt(e) { - let t, n = e.depsTail, s = n; - for (; s; ) { - const r = s.prevDep; - s.version === -1 ? (s === n && (n = r), st(s), bn(s)) : t = s, s.dep.activeLink = s.prevActiveLink, s.prevActiveLink = void 0, s = r; - } - e.deps = t, e.depsTail = n; -} -function Be(e) { - for (let t = e.deps; t; t = t.nextDep) - if (t.dep.version !== t.version || t.dep.computed && (mn(t.dep.computed) || t.dep.version !== t.version)) - return !0; - return !!e._dirty; -} -function mn(e) { - if (e.flags & 4 && !(e.flags & 16) || (e.flags &= -17, e.globalVersion === Oe)) - return; - e.globalVersion = Oe; - const t = e.dep; - if (e.flags |= 2, t.version > 0 && !e.isSSR && e.deps && !Be(e)) { - e.flags &= -3; - return; - } - const n = m, s = A; - m = e, A = !0; - try { - Ot(e); - const r = e.fn(e._value); - (t.version === 0 || q(r, e._value)) && (e._value = r, t.version++); - } catch (r) { - throw t.version++, r; - } finally { - m = n, A = s, xt(e), e.flags &= -3; - } -} -function st(e, t = !1) { - const { dep: n, prevSub: s, nextSub: r } = e; - if (s && (s.nextSub = r, e.prevSub = void 0), r && (r.prevSub = s, e.nextSub = void 0), E.NODE_ENV !== "production" && n.subsHead === e && (n.subsHead = r), n.subs === e && (n.subs = s, !s && n.computed)) { - n.computed.flags &= -5; - for (let i = n.computed.deps; i; i = i.nextDep) - st(i, !0); - } - !t && !--n.sc && n.map && n.map.delete(n.key); -} -function bn(e) { - const { prevDep: t, nextDep: n } = e; - t && (t.nextDep = n, e.prevDep = void 0), n && (n.prevDep = t, e.nextDep = void 0); -} -let A = !0; -const vt = []; -function $e() { - vt.push(A), A = !1; -} -function Pe() { - const e = vt.pop(); - A = e === void 0 ? !0 : e; -} -function pt(e) { - const { cleanup: t } = e; - if (e.cleanup = void 0, t) { - const n = m; - m = void 0; - try { - t(); - } finally { - m = n; - } - } -} -let Oe = 0; -class En { - constructor(t, n) { - this.sub = t, this.dep = n, this.version = n.version, this.nextDep = this.prevDep = this.nextSub = this.prevSub = this.prevActiveLink = void 0; - } -} -class wn { - constructor(t) { - this.computed = t, this.version = 0, this.activeLink = void 0, this.subs = void 0, this.map = void 0, this.key = void 0, this.sc = 0, E.NODE_ENV !== "production" && (this.subsHead = void 0); - } - track(t) { - if (!m || !A || m === this.computed) - return; - let n = this.activeLink; - if (n === void 0 || n.sub !== m) - n = this.activeLink = new En(m, this), m.deps ? (n.prevDep = m.depsTail, m.depsTail.nextDep = n, m.depsTail = n) : m.deps = m.depsTail = n, yt(n); - else if (n.version === -1 && (n.version = this.version, n.nextDep)) { - const s = n.nextDep; - s.prevDep = n.prevDep, n.prevDep && (n.prevDep.nextDep = s), n.prevDep = m.depsTail, n.nextDep = void 0, m.depsTail.nextDep = n, m.depsTail = n, m.deps === n && (m.deps = s); - } - return E.NODE_ENV !== "production" && m.onTrack && m.onTrack( - R( - { - effect: m - }, - t - ) - ), n; - } - trigger(t) { - this.version++, Oe++, this.notify(t); - } - notify(t) { - nt(); - try { - if (E.NODE_ENV !== "production") - for (let n = this.subsHead; n; n = n.nextSub) - n.sub.onTrigger && !(n.sub.flags & 8) && n.sub.onTrigger( - R( - { - effect: n.sub - }, - t - ) - ); - for (let n = this.subs; n; n = n.prevSub) - n.sub.notify() && n.sub.dep.notify(); - } finally { - rt(); - } - } -} -function yt(e) { - if (e.dep.sc++, e.sub.flags & 4) { - const t = e.dep.computed; - if (t && !e.dep.subs) { - t.flags |= 20; - for (let s = t.deps; s; s = s.nextDep) - yt(s); - } - const n = e.dep.subs; - n !== e && (e.prevSub = n, n && (n.nextSub = e)), E.NODE_ENV !== "production" && e.dep.subsHead === void 0 && (e.dep.subsHead = e), e.dep.subs = e; - } -} -const Je = /* @__PURE__ */ new WeakMap(), Y = Symbol( - E.NODE_ENV !== "production" ? "Object iterate" : "" -), qe = Symbol( - E.NODE_ENV !== "production" ? "Map keys iterate" : "" -), ae = Symbol( - E.NODE_ENV !== "production" ? "Array iterate" : "" -); -function x(e, t, n) { - if (A && m) { - let s = Je.get(e); - s || Je.set(e, s = /* @__PURE__ */ new Map()); - let r = s.get(n); - r || (s.set(n, r = new wn()), r.map = s, r.key = n), E.NODE_ENV !== "production" ? r.track({ - target: e, - type: t, - key: n - }) : r.track(); - } -} -function K(e, t, n, s, r, i) { - const o = Je.get(e); - if (!o) { - Oe++; - return; - } - const c = (a) => { - a && (E.NODE_ENV !== "production" ? a.trigger({ - target: e, - type: t, - key: n, - newValue: s, - oldValue: r, - oldTarget: i - }) : a.trigger()); - }; - if (nt(), t === "clear") - o.forEach(c); - else { - const a = b(e), f = a && ke(n); - if (a && n === "length") { - const d = Number(s); - o.forEach((l, u) => { - (u === "length" || u === ae || !pe(u) && u >= d) && c(l); - }); - } else - switch ((n !== void 0 || o.has(void 0)) && c(o.get(n)), f && c(o.get(ae)), t) { - case "add": - a ? f && c(o.get("length")) : (c(o.get(Y)), k(e) && c(o.get(qe))); - break; - case "delete": - a || (c(o.get(Y)), k(e) && c(o.get(qe))); - break; - case "set": - k(e) && c(o.get(Y)); - break; - } - } - rt(); -} -function Z(e) { - const t = h(e); - return t === e ? t : (x(t, "iterate", ae), V(e) ? t : t.map(T)); -} -function it(e) { - return x(e = h(e), "iterate", ae), e; -} -const Nn = { - __proto__: null, - [Symbol.iterator]() { - return He(this, Symbol.iterator, T); - }, - concat(...e) { - return Z(this).concat( - ...e.map((t) => b(t) ? Z(t) : t) - ); - }, - entries() { - return He(this, "entries", (e) => (e[1] = T(e[1]), e)); - }, - every(e, t) { - return j(this, "every", e, t, void 0, arguments); - }, - filter(e, t) { - return j(this, "filter", e, t, (n) => n.map(T), arguments); - }, - find(e, t) { - return j(this, "find", e, t, T, arguments); - }, - findIndex(e, t) { - return j(this, "findIndex", e, t, void 0, arguments); - }, - findLast(e, t) { - return j(this, "findLast", e, t, T, arguments); - }, - findLastIndex(e, t) { - return j(this, "findLastIndex", e, t, void 0, arguments); - }, - // flat, flatMap could benefit from ARRAY_ITERATE but are not straight-forward to implement - forEach(e, t) { - return j(this, "forEach", e, t, void 0, arguments); - }, - includes(...e) { - return We(this, "includes", e); - }, - indexOf(...e) { - return We(this, "indexOf", e); - }, - join(e) { - return Z(this).join(e); - }, - // keys() iterator only reads `length`, no optimisation required - lastIndexOf(...e) { - return We(this, "lastIndexOf", e); - }, - map(e, t) { - return j(this, "map", e, t, void 0, arguments); - }, - pop() { - return re(this, "pop"); - }, - push(...e) { - return re(this, "push", e); - }, - reduce(e, ...t) { - return dt(this, "reduce", e, t); - }, - reduceRight(e, ...t) { - return dt(this, "reduceRight", e, t); - }, - shift() { - return re(this, "shift"); - }, - // slice could use ARRAY_ITERATE but also seems to beg for range tracking - some(e, t) { - return j(this, "some", e, t, void 0, arguments); - }, - splice(...e) { - return re(this, "splice", e); - }, - toReversed() { - return Z(this).toReversed(); - }, - toSorted(e) { - return Z(this).toSorted(e); - }, - toSpliced(...e) { - return Z(this).toSpliced(...e); - }, - unshift(...e) { - return re(this, "unshift", e); - }, - values() { - return He(this, "values", T); - } -}; -function He(e, t, n) { - const s = it(e), r = s[t](); - return s !== e && !V(e) && (r._next = r.next, r.next = () => { - const i = r._next(); - return i.value && (i.value = n(i.value)), i; - }), r; -} -const Sn = Array.prototype; -function j(e, t, n, s, r, i) { - const o = it(e), c = o !== e && !V(e), a = o[t]; - if (a !== Sn[t]) { - const l = a.apply(e, i); - return c ? T(l) : l; - } - let f = n; - o !== e && (c ? f = function(l, u) { - return n.call(this, T(l), u, e); - } : n.length > 2 && (f = function(l, u) { - return n.call(this, l, u, e); - })); - const d = a.call(o, f, s); - return c && r ? r(d) : d; -} -function dt(e, t, n, s) { - const r = it(e); - let i = n; - return r !== e && (V(e) ? n.length > 3 && (i = function(o, c, a) { - return n.call(this, o, c, a, e); - }) : i = function(o, c, a) { - return n.call(this, o, T(c), a, e); - }), r[t](i, ...s); -} -function We(e, t, n) { - const s = h(e); - x(s, "iterate", ae); - const r = s[t](...n); - return (r === -1 || r === !1) && xe(n[0]) ? (n[0] = h(n[0]), s[t](...n)) : r; -} -function re(e, t, n = []) { - $e(), nt(); - const s = h(e)[t].apply(e, n); - return rt(), Pe(), s; -} -const On = /* @__PURE__ */ kt("__proto__,__v_isRef,__isVue"), Dt = new Set( - /* @__PURE__ */ Object.getOwnPropertyNames(Symbol).filter((e) => e !== "arguments" && e !== "caller").map((e) => Symbol[e]).filter(pe) -); -function xn(e) { - pe(e) || (e = String(e)); - const t = h(this); - return x(t, "has", e), t.hasOwnProperty(e); -} -class Tt { - constructor(t = !1, n = !1) { - this._isReadonly = t, this._isShallow = n; - } - get(t, n, s) { - if (n === "__v_skip") return t.__v_skip; - const r = this._isReadonly, i = this._isShallow; - if (n === "__v_isReactive") - return !r; - if (n === "__v_isReadonly") - return r; - if (n === "__v_isShallow") - return i; - if (n === "__v_raw") - return s === (r ? i ? It : Ct : i ? Pn : Rt).get(t) || // receiver is not the reactive proxy, but has the same prototype - // this means the receiver is a user proxy of the reactive proxy - Object.getPrototypeOf(t) === Object.getPrototypeOf(s) ? t : void 0; - const o = b(t); - if (!r) { - let a; - if (o && (a = Nn[n])) - return a; - if (n === "hasOwnProperty") - return xn; - } - const c = Reflect.get( - t, - n, - // if this is a proxy wrapping a ref, return methods using the raw ref - // as receiver so that we don't have to call `toRaw` on the ref in all - // its class methods - D(t) ? t : s - ); - return (pe(n) ? Dt.has(n) : On(n)) || (r || x(t, "get", n), i) ? c : D(c) ? o && ke(n) ? c : c.value : y(c) ? r ? Pt(c) : $t(c) : c; - } -} -class vn extends Tt { - constructor(t = !1) { - super(!1, t); - } - set(t, n, s, r) { - let i = t[n]; - if (!this._isShallow) { - const a = U(i); - if (!V(s) && !U(s) && (i = h(i), s = h(s)), !b(t) && D(i) && !D(s)) - return a ? !1 : (i.value = s, !0); - } - const o = b(t) && ke(n) ? Number(n) < t.length : N(t, n), c = Reflect.set( - t, - n, - s, - D(t) ? t : r - ); - return t === h(r) && (o ? q(s, i) && K(t, "set", n, s, i) : K(t, "add", n, s)), c; - } - deleteProperty(t, n) { - const s = N(t, n), r = t[n], i = Reflect.deleteProperty(t, n); - return i && s && K(t, "delete", n, void 0, r), i; - } - has(t, n) { - const s = Reflect.has(t, n); - return (!pe(n) || !Dt.has(n)) && x(t, "has", n), s; - } - ownKeys(t) { - return x( - t, - "iterate", - b(t) ? "length" : Y - ), Reflect.ownKeys(t); - } -} -class Vt extends Tt { - constructor(t = !1) { - super(!0, t); - } - set(t, n) { - return E.NODE_ENV !== "production" && z( - `Set operation on key "${String(n)}" failed: target is readonly.`, - t - ), !0; - } - deleteProperty(t, n) { - return E.NODE_ENV !== "production" && z( - `Delete operation on key "${String(n)}" failed: target is readonly.`, - t - ), !0; - } -} -const yn = /* @__PURE__ */ new vn(), Dn = /* @__PURE__ */ new Vt(), Tn = /* @__PURE__ */ new Vt(!0), Ye = (e) => e, ge = (e) => Reflect.getPrototypeOf(e); -function Vn(e, t, n) { - return function(...s) { - const r = this.__v_raw, i = h(r), o = k(i), c = e === "entries" || e === Symbol.iterator && o, a = e === "keys" && o, f = r[e](...s), d = n ? Ye : t ? Ge : T; - return !t && x( - i, - "iterate", - a ? qe : Y - ), { - // iterator protocol - next() { - const { value: l, done: u } = f.next(); - return u ? { value: l, done: u } : { - value: c ? [d(l[0]), d(l[1])] : d(l), - done: u - }; - }, - // iterable protocol - [Symbol.iterator]() { - return this; - } - }; - }; -} -function _e(e) { - return function(...t) { - if (E.NODE_ENV !== "production") { - const n = t[0] ? `on key "${t[0]}" ` : ""; - z( - `${an(e)} operation ${n}failed: target is readonly.`, - h(this) - ); - } - return e === "delete" ? !1 : e === "clear" ? void 0 : this; - }; -} -function Rn(e, t) { - const n = { - get(r) { - const i = this.__v_raw, o = h(i), c = h(r); - e || (q(r, c) && x(o, "get", r), x(o, "get", c)); - const { has: a } = ge(o), f = t ? Ye : e ? Ge : T; - if (a.call(o, r)) - return f(i.get(r)); - if (a.call(o, c)) - return f(i.get(c)); - i !== o && i.get(r); - }, - get size() { - const r = this.__v_raw; - return !e && x(h(r), "iterate", Y), Reflect.get(r, "size", r); - }, - has(r) { - const i = this.__v_raw, o = h(i), c = h(r); - return e || (q(r, c) && x(o, "has", r), x(o, "has", c)), r === c ? i.has(r) : i.has(r) || i.has(c); - }, - forEach(r, i) { - const o = this, c = o.__v_raw, a = h(c), f = t ? Ye : e ? Ge : T; - return !e && x(a, "iterate", Y), c.forEach((d, l) => r.call(i, f(d), f(l), o)); - } - }; - return R( - n, - e ? { - add: _e("add"), - set: _e("set"), - delete: _e("delete"), - clear: _e("clear") - } : { - add(r) { - !t && !V(r) && !U(r) && (r = h(r)); - const i = h(this); - return ge(i).has.call(i, r) || (i.add(r), K(i, "add", r, r)), this; - }, - set(r, i) { - !t && !V(i) && !U(i) && (i = h(i)); - const o = h(this), { has: c, get: a } = ge(o); - let f = c.call(o, r); - f ? E.NODE_ENV !== "production" && ht(o, c, r) : (r = h(r), f = c.call(o, r)); - const d = a.call(o, r); - return o.set(r, i), f ? q(i, d) && K(o, "set", r, i, d) : K(o, "add", r, i), this; - }, - delete(r) { - const i = h(this), { has: o, get: c } = ge(i); - let a = o.call(i, r); - a ? E.NODE_ENV !== "production" && ht(i, o, r) : (r = h(r), a = o.call(i, r)); - const f = c ? c.call(i, r) : void 0, d = i.delete(r); - return a && K(i, "delete", r, void 0, f), d; - }, - clear() { - const r = h(this), i = r.size !== 0, o = E.NODE_ENV !== "production" ? k(r) ? new Map(r) : new Set(r) : void 0, c = r.clear(); - return i && K( - r, - "clear", - void 0, - void 0, - o - ), c; - } - } - ), [ - "keys", - "values", - "entries", - Symbol.iterator - ].forEach((r) => { - n[r] = Vn(r, e, t); - }), n; -} -function ot(e, t) { - const n = Rn(e, t); - return (s, r, i) => r === "__v_isReactive" ? !e : r === "__v_isReadonly" ? e : r === "__v_raw" ? s : Reflect.get( - N(n, r) && r in s ? n : s, - r, - i - ); -} -const Cn = { - get: /* @__PURE__ */ ot(!1, !1) -}, In = { - get: /* @__PURE__ */ ot(!0, !1) -}, $n = { - get: /* @__PURE__ */ ot(!0, !0) -}; -function ht(e, t, n) { - const s = h(n); - if (s !== n && t.call(e, s)) { - const r = Nt(e); - z( - `Reactive ${r} contains both the raw and reactive versions of the same object${r === "Map" ? " as keys" : ""}, which can lead to inconsistencies. Avoid differentiating between the raw and reactive versions of an object and only use the reactive version if possible.` - ); - } -} -const Rt = /* @__PURE__ */ new WeakMap(), Pn = /* @__PURE__ */ new WeakMap(), Ct = /* @__PURE__ */ new WeakMap(), It = /* @__PURE__ */ new WeakMap(); -function An(e) { - switch (e) { - case "Object": - case "Array": - return 1; - case "Map": - case "Set": - case "WeakMap": - case "WeakSet": - return 2; - default: - return 0; - } -} -function Mn(e) { - return e.__v_skip || !Object.isExtensible(e) ? 0 : An(Nt(e)); -} -function $t(e) { - return U(e) ? e : ct( - e, - !1, - yn, - Cn, - Rt - ); -} -function Pt(e) { - return ct( - e, - !0, - Dn, - In, - Ct - ); -} -function me(e) { - return ct( - e, - !0, - Tn, - $n, - It - ); -} -function ct(e, t, n, s, r) { - if (!y(e)) - return E.NODE_ENV !== "production" && z( - `value cannot be made ${t ? "readonly" : "reactive"}: ${String( - e - )}` - ), e; - if (e.__v_raw && !(t && e.__v_isReactive)) - return e; - const i = r.get(e); - if (i) - return i; - const o = Mn(e); - if (o === 0) - return e; - const c = new Proxy( - e, - o === 2 ? s : n - ); - return r.set(e, c), c; -} -function ee(e) { - return U(e) ? ee(e.__v_raw) : !!(e && e.__v_isReactive); -} -function U(e) { - return !!(e && e.__v_isReadonly); -} -function V(e) { - return !!(e && e.__v_isShallow); -} -function xe(e) { - return e ? !!e.__v_raw : !1; -} -function h(e) { - const t = e && e.__v_raw; - return t ? h(t) : e; -} -function Fn(e) { - return !N(e, "__v_skip") && Object.isExtensible(e) && un(e, "__v_skip", !0), e; -} -const T = (e) => y(e) ? $t(e) : e, Ge = (e) => y(e) ? Pt(e) : e; -function D(e) { - return e ? e.__v_isRef === !0 : !1; -} -function jn(e) { - return D(e) ? e.value : e; -} -const Hn = { - get: (e, t, n) => t === "__v_raw" ? e : jn(Reflect.get(e, t, n)), - set: (e, t, n, s) => { - const r = e[t]; - return D(r) && !D(n) ? (r.value = n, !0) : Reflect.set(e, t, n, s); - } -}; -function Wn(e) { - return ee(e) ? e : new Proxy(e, Hn); -} -const be = {}, ve = /* @__PURE__ */ new WeakMap(); -let J; -function Kn(e, t = !1, n = J) { - if (n) { - let s = ve.get(n); - s || ve.set(n, s = []), s.push(e); - } else E.NODE_ENV !== "production" && !t && z( - "onWatcherCleanup() was called when there was no active watcher to associate with." - ); -} -function Ln(e, t, n = $) { - const { immediate: s, deep: r, once: i, scheduler: o, augmentJob: c, call: a } = n, f = (g) => { - (n.onWarn || z)( - "Invalid watch source: ", - g, - "A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types." - ); - }, d = (g) => r ? g : V(g) || r === !1 || r === 0 ? L(g, 1) : L(g); - let l, u, p, w, C = !1, de = !1; - if (D(e) ? (u = () => e.value, C = V(e)) : ee(e) ? (u = () => d(e), C = !0) : b(e) ? (de = !0, C = e.some((g) => ee(g) || V(g)), u = () => e.map((g) => { - if (D(g)) - return g.value; - if (ee(g)) - return d(g); - if (O(g)) - return a ? a(g, 2) : g(); - E.NODE_ENV !== "production" && f(g); - })) : O(e) ? t ? u = a ? () => a(e, 2) : e : u = () => { - if (p) { - $e(); - try { - p(); - } finally { - Pe(); - } - } - const g = J; - J = l; - try { - return a ? a(e, 3, [w]) : e(w); - } finally { - J = g; - } - } : (u = ie, E.NODE_ENV !== "production" && f(e)), t && r) { - const g = u, M = r === !0 ? 1 / 0 : r; - u = () => L(g(), M); - } - const Q = () => { - l.stop(); - }; - if (i && t) { - const g = t; - t = (...M) => { - g(...M), Q(); - }; - } - let B = de ? new Array(e.length).fill(be) : be; - const ne = (g) => { - if (!(!(l.flags & 1) || !l.dirty && !g)) - if (t) { - const M = l.run(); - if (r || C || (de ? M.some((Fe, he) => q(Fe, B[he])) : q(M, B))) { - p && p(); - const Fe = J; - J = l; - try { - const he = [ - M, - // pass undefined as the old value when it's changed for the first time - B === be ? void 0 : de && B[0] === be ? [] : B, - w - ]; - a ? a(t, 3, he) : ( - // @ts-expect-error - t(...he) - ), B = M; - } finally { - J = Fe; - } - } - } else - l.run(); - }; - return c && c(ne), l = new gn(u), l.scheduler = o ? () => o(ne, !1) : ne, w = (g) => Kn(g, !1, l), p = l.onStop = () => { - const g = ve.get(l); - if (g) { - if (a) - a(g, 4); - else - for (const M of g) M(); - ve.delete(l); - } - }, E.NODE_ENV !== "production" && (l.onTrack = n.onTrack, l.onTrigger = n.onTrigger), t ? s ? ne(!0) : B = l.run() : o ? o(ne.bind(null, !0), !0) : l.run(), Q.pause = l.pause.bind(l), Q.resume = l.resume.bind(l), Q.stop = Q, Q; -} -function L(e, t = 1 / 0, n) { - if (t <= 0 || !y(e) || e.__v_skip || (n = n || /* @__PURE__ */ new Set(), n.has(e))) - return e; - if (n.add(e), t--, D(e)) - L(e.value, t, n); - else if (b(e)) - for (let s = 0; s < e.length; s++) - L(e[s], t, n); - else if (rn(e) || k(e)) - e.forEach((s) => { - L(s, t, n); - }); - else if (cn(e)) { - for (const s in e) - L(e[s], t, n); - for (const s of Object.getOwnPropertySymbols(e)) - Object.prototype.propertyIsEnumerable.call(e, s) && L(e[s], t, n); - } - return e; -} -var _ = {}; -const G = []; -function zn(e) { - G.push(e); -} -function Un() { - G.pop(); -} -let Ke = !1; -function S(e, ...t) { - if (Ke) return; - Ke = !0, $e(); - const n = G.length ? G[G.length - 1].component : null, s = n && n.appContext.config.warnHandler, r = Bn(); - if (s) - Ae( - s, - n, - 11, - [ - // eslint-disable-next-line no-restricted-syntax - e + t.map((i) => { - var o, c; - return (c = (o = i.toString) == null ? void 0 : o.call(i)) != null ? c : JSON.stringify(i); - }).join(""), - n && n.proxy, - r.map( - ({ vnode: i }) => `at <${Zt(n, i.type)}>` - ).join(` -`), - r - ] - ); - else { - const i = [`[Vue warn]: ${e}`, ...t]; - r.length && i.push(` -`, ...Jn(r)), console.warn(...i); - } - Pe(), Ke = !1; -} -function Bn() { - let e = G[G.length - 1]; - if (!e) - return []; - const t = []; - for (; e; ) { - const n = t[0]; - n && n.vnode === e ? n.recurseCount++ : t.push({ - vnode: e, - recurseCount: 0 - }); - const s = e.component && e.component.parent; - e = s && s.vnode; - } - return t; -} -function Jn(e) { - const t = []; - return e.forEach((n, s) => { - t.push(...s === 0 ? [] : [` -`], ...qn(n)); - }), t; -} -function qn({ vnode: e, recurseCount: t }) { - const n = t > 0 ? `... (${t} recursive calls)` : "", s = e.component ? e.component.parent == null : !1, r = ` at <${Zt( - e.component, - e.type, - s - )}`, i = ">" + n; - return e.props ? [r, ...Yn(e.props), i] : [r + i]; -} -function Yn(e) { - const t = [], n = Object.keys(e); - return n.slice(0, 3).forEach((s) => { - t.push(...At(s, e[s])); - }), n.length > 3 && t.push(" ..."), t; -} -function At(e, t, n) { - return F(t) ? (t = JSON.stringify(t), n ? t : [`${e}=${t}`]) : typeof t == "number" || typeof t == "boolean" || t == null ? n ? t : [`${e}=${t}`] : D(t) ? (t = At(e, h(t.value), !0), n ? t : [`${e}=Ref<`, t, ">"]) : O(t) ? [`${e}=fn${t.name ? `<${t.name}>` : ""}`] : (t = h(t), n ? t : [`${e}=`, t]); -} -const Mt = { - sp: "serverPrefetch hook", - bc: "beforeCreate hook", - c: "created hook", - bm: "beforeMount hook", - m: "mounted hook", - bu: "beforeUpdate hook", - u: "updated", - bum: "beforeUnmount hook", - um: "unmounted hook", - a: "activated hook", - da: "deactivated hook", - ec: "errorCaptured hook", - rtc: "renderTracked hook", - rtg: "renderTriggered hook", - 0: "setup function", - 1: "render function", - 2: "watcher getter", - 3: "watcher callback", - 4: "watcher cleanup function", - 5: "native event handler", - 6: "component event handler", - 7: "vnode hook", - 8: "directive hook", - 9: "transition hook", - 10: "app errorHandler", - 11: "app warnHandler", - 12: "ref function", - 13: "async component loader", - 14: "scheduler flush", - 15: "component update", - 16: "app unmount cleanup function" -}; -function Ae(e, t, n, s) { - try { - return s ? e(...s) : e(); - } catch (r) { - lt(r, t, n); - } -} -function Ft(e, t, n, s) { - if (O(e)) { - const r = Ae(e, t, n, s); - return r && sn(r) && r.catch((i) => { - lt(i, t, n); - }), r; - } - if (b(e)) { - const r = []; - for (let i = 0; i < e.length; i++) - r.push(Ft(e[i], t, n, s)); - return r; - } else _.NODE_ENV !== "production" && S( - `Invalid value type passed to callWithAsyncErrorHandling(): ${typeof e}` - ); -} -function lt(e, t, n, s = !0) { - const r = t ? t.vnode : null, { errorHandler: i, throwUnhandledErrorInProduction: o } = t && t.appContext.config || $; - if (t) { - let c = t.parent; - const a = t.proxy, f = _.NODE_ENV !== "production" ? Mt[n] : `https://vuejs.org/error-reference/#runtime-${n}`; - for (; c; ) { - const d = c.ec; - if (d) { - for (let l = 0; l < d.length; l++) - if (d[l](e, a, f) === !1) - return; - } - c = c.parent; - } - if (i) { - $e(), Ae(i, null, 10, [ - e, - a, - f - ]), Pe(); - return; - } - } - Gn(e, n, r, s, o); -} -function Gn(e, t, n, s = !0, r = !1) { - if (_.NODE_ENV !== "production") { - const i = Mt[t]; - if (n && zn(n), S(`Unhandled error${i ? ` during execution of ${i}` : ""}`), n && Un(), s) - throw e; - console.error(e); - } else { - if (r) - throw e; - console.error(e); - } -} -const I = []; -let H = -1; -const te = []; -let W = null, X = 0; -const jt = /* @__PURE__ */ Promise.resolve(); -let ye = null; -const Qn = 100; -function Zn(e) { - const t = ye || jt; - return e ? t.then(this ? e.bind(this) : e) : t; -} -function Xn(e) { - let t = H + 1, n = I.length; - for (; t < n; ) { - const s = t + n >>> 1, r = I[s], i = ue(r); - i < e || i === e && r.flags & 2 ? t = s + 1 : n = s; - } - return t; -} -function at(e) { - if (!(e.flags & 1)) { - const t = ue(e), n = I[I.length - 1]; - !n || // fast path when the job id is larger than the tail - !(e.flags & 2) && t >= ue(n) ? I.push(e) : I.splice(Xn(t), 0, e), e.flags |= 1, Ht(); - } -} -function Ht() { - ye || (ye = jt.then(Kt)); -} -function Wt(e) { - b(e) ? te.push(...e) : W && e.id === -1 ? W.splice(X + 1, 0, e) : e.flags & 1 || (te.push(e), e.flags |= 1), Ht(); -} -function kn(e) { - if (te.length) { - const t = [...new Set(te)].sort( - (n, s) => ue(n) - ue(s) - ); - if (te.length = 0, W) { - W.push(...t); - return; - } - for (W = t, _.NODE_ENV !== "production" && (e = e || /* @__PURE__ */ new Map()), X = 0; X < W.length; X++) { - const n = W[X]; - _.NODE_ENV !== "production" && Lt(e, n) || (n.flags & 4 && (n.flags &= -2), n.flags & 8 || n(), n.flags &= -2); - } - W = null, X = 0; - } -} -const ue = (e) => e.id == null ? e.flags & 2 ? -1 : 1 / 0 : e.id; -function Kt(e) { - _.NODE_ENV !== "production" && (e = e || /* @__PURE__ */ new Map()); - const t = _.NODE_ENV !== "production" ? (n) => Lt(e, n) : ie; - try { - for (H = 0; H < I.length; H++) { - const n = I[H]; - if (n && !(n.flags & 8)) { - if (_.NODE_ENV !== "production" && t(n)) - continue; - n.flags & 4 && (n.flags &= -2), Ae( - n, - n.i, - n.i ? 15 : 14 - ), n.flags & 4 || (n.flags &= -2); - } - } - } finally { - for (; H < I.length; H++) { - const n = I[H]; - n && (n.flags &= -2); - } - H = -1, I.length = 0, kn(e), ye = null, (I.length || te.length) && Kt(e); - } -} -function Lt(e, t) { - const n = e.get(t) || 0; - if (n > Qn) { - const s = t.i, r = s && Qt(s.type); - return lt( - `Maximum recursive updates exceeded${r ? ` in component <${r}>` : ""}. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.`, - null, - 10 - ), !0; - } - return e.set(t, n + 1), !1; -} -const Le = /* @__PURE__ */ new Map(); -_.NODE_ENV !== "production" && (Ie().__VUE_HMR_RUNTIME__ = { - createRecord: ze(er), - rerender: ze(tr), - reload: ze(nr) -}); -const De = /* @__PURE__ */ new Map(); -function er(e, t) { - return De.has(e) ? !1 : (De.set(e, { - initialDef: Te(t), - instances: /* @__PURE__ */ new Set() - }), !0); -} -function Te(e) { - return Xt(e) ? e.__vccOpts : e; -} -function tr(e, t) { - const n = De.get(e); - n && (n.initialDef.render = t, [...n.instances].forEach((s) => { - t && (s.render = t, Te(s.type).render = t), s.renderCache = [], s.update(); - })); -} -function nr(e, t) { - const n = De.get(e); - if (!n) return; - t = Te(t), gt(n.initialDef, t); - const s = [...n.instances]; - for (let r = 0; r < s.length; r++) { - const i = s[r], o = Te(i.type); - let c = Le.get(o); - c || (o !== n.initialDef && gt(o, t), Le.set(o, c = /* @__PURE__ */ new Set())), c.add(i), i.appContext.propsCache.delete(i.type), i.appContext.emitsCache.delete(i.type), i.appContext.optionsCache.delete(i.type), i.ceReload ? (c.add(i), i.ceReload(t.styles), c.delete(i)) : i.parent ? at(() => { - i.parent.update(), c.delete(i); - }) : i.appContext.reload ? i.appContext.reload() : typeof window < "u" ? window.location.reload() : console.warn( - "[HMR] Root or manually mounted instance modified. Full reload required." - ), i.root.ce && i !== i.root && i.root.ce._removeChildStyle(o); - } - Wt(() => { - Le.clear(); - }); -} -function gt(e, t) { - R(e, t); - for (const n in e) - n !== "__file" && !(n in t) && delete e[n]; -} -function ze(e) { - return (t, n) => { - try { - return e(t, n); - } catch (s) { - console.error(s), console.warn( - "[HMR] Something went wrong during Vue component hot-reload. Full reload required." - ); - } - }; -} -let fe = null, rr = null; -const sr = (e) => e.__isTeleport; -function zt(e, t) { - e.shapeFlag & 6 && e.component ? (e.transition = t, zt(e.component.subTree, t)) : e.shapeFlag & 128 ? (e.ssContent.transition = t.clone(e.ssContent), e.ssFallback.transition = t.clone(e.ssFallback)) : e.transition = t; -} -Ie().requestIdleCallback; -Ie().cancelIdleCallback; -const ir = Symbol.for("v-ndc"), Qe = (e) => e ? Ar(e) ? Mr(e) : Qe(e.parent) : null, le = ( - // Move PURE marker to new line to workaround compiler discarding it - // due to type annotation - /* @__PURE__ */ R(/* @__PURE__ */ Object.create(null), { - $: (e) => e, - $el: (e) => e.vnode.el, - $data: (e) => e.data, - $props: (e) => _.NODE_ENV !== "production" ? me(e.props) : e.props, - $attrs: (e) => _.NODE_ENV !== "production" ? me(e.attrs) : e.attrs, - $slots: (e) => _.NODE_ENV !== "production" ? me(e.slots) : e.slots, - $refs: (e) => _.NODE_ENV !== "production" ? me(e.refs) : e.refs, - $parent: (e) => Qe(e.parent), - $root: (e) => Qe(e.root), - $host: (e) => e.ce, - $emit: (e) => e.emit, - $options: (e) => cr(e), - $forceUpdate: (e) => e.f || (e.f = () => { - at(e.update); - }), - $nextTick: (e) => e.n || (e.n = Zn.bind(e.proxy)), - $watch: (e) => br.bind(e) - }) -), Ue = (e, t) => e !== $ && !e.__isScriptSetup && N(e, t), or = { - get({ _: e }, t) { - if (t === "__v_skip") - return !0; - const { ctx: n, setupState: s, data: r, props: i, accessCache: o, type: c, appContext: a } = e; - if (_.NODE_ENV !== "production" && t === "__isVue") - return !0; - let f; - if (t[0] !== "$") { - const p = o[t]; - if (p !== void 0) - switch (p) { - case 1: - return s[t]; - case 2: - return r[t]; - case 4: - return n[t]; - case 3: - return i[t]; - } - else { - if (Ue(s, t)) - return o[t] = 1, s[t]; - if (r !== $ && N(r, t)) - return o[t] = 2, r[t]; - if ( - // only cache other properties when instance has declared (thus stable) - // props - (f = e.propsOptions[0]) && N(f, t) - ) - return o[t] = 3, i[t]; - if (n !== $ && N(n, t)) - return o[t] = 4, n[t]; - o[t] = 0; - } - } - const d = le[t]; - let l, u; - if (d) - return t === "$attrs" ? x(e.attrs, "get", "") : _.NODE_ENV !== "production" && t === "$slots" && x(e, "get", t), d(e); - if ( - // css module (injected by vue-loader) - (l = c.__cssModules) && (l = l[t]) - ) - return l; - if (n !== $ && N(n, t)) - return o[t] = 4, n[t]; - if ( - // global properties - u = a.config.globalProperties, N(u, t) - ) - return u[t]; - }, - set({ _: e }, t, n) { - const { data: s, setupState: r, ctx: i } = e; - return Ue(r, t) ? (r[t] = n, !0) : _.NODE_ENV !== "production" && r.__isScriptSetup && N(r, t) ? (S(`Cannot mutate